diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000000..93ac1604cc5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+.project
+**/Moose.lua
diff --git a/.project b/.project
deleted file mode 100644
index 05df89ee447..00000000000
--- a/.project
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
- MOOSE_MISSIONS
-
-
-
-
-
-
-
-
diff --git a/Sound/MSRS/MSRS - 010 - Config Windows/Moose.lua b/Sound/MSRS/MSRS - 010 - Config Windows/Moose.lua
deleted file mode 100644
index 649be1df294..00000000000
--- a/Sound/MSRS/MSRS - 010 - Config Windows/Moose.lua
+++ /dev/null
@@ -1,121459 +0,0 @@
-env.info('*** MOOSE GITHUB Commit Hash ID: 2023-12-23T14:53:42+01:00-a14435ce5499109647c5f23934d039efe29f8c00 ***')
-env.info('*** MOOSE STATIC INCLUDE START *** ')
-ENUMS={}
-env.setErrorMessageBoxEnabled(false)
-ENUMS.ROE={
-WeaponFree=0,
-OpenFireWeaponFree=1,
-OpenFire=2,
-ReturnFire=3,
-WeaponHold=4,
-}
-ENUMS.ROT={
-NoReaction=0,
-PassiveDefense=1,
-EvadeFire=2,
-BypassAndEscape=3,
-AllowAbortMission=4,
-}
-ENUMS.AlarmState={
-Auto=0,
-Green=1,
-Red=2,
-}
-ENUMS.WeaponFlag={
-LGB=2,
-TvGB=4,
-SNSGB=8,
-HEBomb=16,
-Penetrator=32,
-NapalmBomb=64,
-FAEBomb=128,
-ClusterBomb=256,
-Dispencer=512,
-CandleBomb=1024,
-ParachuteBomb=2147483648,
-LightRocket=2048,
-MarkerRocket=4096,
-CandleRocket=8192,
-HeavyRocket=16384,
-AntiRadarMissile=32768,
-AntiShipMissile=65536,
-AntiTankMissile=131072,
-FireAndForgetASM=262144,
-LaserASM=524288,
-TeleASM=1048576,
-CruiseMissile=2097152,
-AntiRadarMissile2=1073741824,
-SRAM=4194304,
-MRAAM=8388608,
-LRAAM=16777216,
-IR_AAM=33554432,
-SAR_AAM=67108864,
-AR_AAM=134217728,
-GunPod=268435456,
-BuiltInCannon=536870912,
-GuidedBomb=14,
-AnyUnguidedBomb=2147485680,
-AnyBomb=2147485694,
-AnyRocket=30720,
-GuidedASM=1572864,
-TacticalASM=1835008,
-AnyASM=4161536,
-AnyASM2=1077903360,
-AnyAAM=264241152,
-AnyAutonomousMissile=36012032,
-AnyMissile=268402688,
-Cannons=805306368,
-Torpedo=4294967296,
-Auto=3221225470,
-AutoDCS=1073741822,
-AnyAG=2956984318,
-AnyAA=264241152,
-AnyUnguided=2952822768,
-AnyGuided=268402702,
-}
-ENUMS.WeaponType={}
-ENUMS.WeaponType.Bomb={
-LGB=2,
-TvGB=4,
-SNSGB=8,
-HEBomb=16,
-Penetrator=32,
-NapalmBomb=64,
-FAEBomb=128,
-ClusterBomb=256,
-Dispencer=512,
-CandleBomb=1024,
-ParachuteBomb=2147483648,
-GuidedBomb=14,
-AnyUnguidedBomb=2147485680,
-AnyBomb=2147485694,
-}
-ENUMS.WeaponType.Rocket={
-LightRocket=2048,
-MarkerRocket=4096,
-CandleRocket=8192,
-HeavyRocket=16384,
-AnyRocket=30720,
-}
-ENUMS.WeaponType.Gun={
-GunPod=268435456,
-BuiltInCannon=536870912,
-Cannons=805306368,
-}
-ENUMS.WeaponType.Missile={
-AntiRadarMissile=32768,
-AntiShipMissile=65536,
-AntiTankMissile=131072,
-FireAndForgetASM=262144,
-LaserASM=524288,
-TeleASM=1048576,
-CruiseMissile=2097152,
-AntiRadarMissile2=1073741824,
-GuidedASM=1572864,
-TacticalASM=1835008,
-AnyASM=4161536,
-AnyASM2=1077903360,
-AnyAutonomousMissile=36012032,
-AnyMissile=268402688,
-}
-ENUMS.WeaponType.AAM={
-SRAM=4194304,
-MRAAM=8388608,
-LRAAM=16777216,
-IR_AAM=33554432,
-SAR_AAM=67108864,
-AR_AAM=134217728,
-AnyAAM=264241152,
-}
-ENUMS.WeaponType.Torpedo={
-Torpedo=4294967296,
-}
-ENUMS.WeaponType.Any={
-Weapon=3221225470,
-AG=2956984318,
-AA=264241152,
-Unguided=2952822768,
-Guided=268402702,
-}
-ENUMS.MissionTask={
-NOTHING="Nothing",
-AFAC="AFAC",
-ANTISHIPSTRIKE="Antiship Strike",
-AWACS="AWACS",
-CAP="CAP",
-CAS="CAS",
-ESCORT="Escort",
-GROUNDESCORT="Ground escort",
-FIGHTERSWEEP="Fighter Sweep",
-GROUNDATTACK="Ground Attack",
-INTERCEPT="Intercept",
-PINPOINTSTRIKE="Pinpoint Strike",
-RECONNAISSANCE="Reconnaissance",
-REFUELING="Refueling",
-RUNWAYATTACK="Runway Attack",
-SEAD="SEAD",
-TRANSPORT="Transport",
-}
-ENUMS.Formation={}
-ENUMS.Formation.FixedWing={}
-ENUMS.Formation.FixedWing.LineAbreast={}
-ENUMS.Formation.FixedWing.LineAbreast.Close=65537
-ENUMS.Formation.FixedWing.LineAbreast.Open=65538
-ENUMS.Formation.FixedWing.LineAbreast.Group=65539
-ENUMS.Formation.FixedWing.Trail={}
-ENUMS.Formation.FixedWing.Trail.Close=131073
-ENUMS.Formation.FixedWing.Trail.Open=131074
-ENUMS.Formation.FixedWing.Trail.Group=131075
-ENUMS.Formation.FixedWing.Wedge={}
-ENUMS.Formation.FixedWing.Wedge.Close=196609
-ENUMS.Formation.FixedWing.Wedge.Open=196610
-ENUMS.Formation.FixedWing.Wedge.Group=196611
-ENUMS.Formation.FixedWing.EchelonRight={}
-ENUMS.Formation.FixedWing.EchelonRight.Close=262145
-ENUMS.Formation.FixedWing.EchelonRight.Open=262146
-ENUMS.Formation.FixedWing.EchelonRight.Group=262147
-ENUMS.Formation.FixedWing.EchelonLeft={}
-ENUMS.Formation.FixedWing.EchelonLeft.Close=327681
-ENUMS.Formation.FixedWing.EchelonLeft.Open=327682
-ENUMS.Formation.FixedWing.EchelonLeft.Group=327683
-ENUMS.Formation.FixedWing.FingerFour={}
-ENUMS.Formation.FixedWing.FingerFour.Close=393217
-ENUMS.Formation.FixedWing.FingerFour.Open=393218
-ENUMS.Formation.FixedWing.FingerFour.Group=393219
-ENUMS.Formation.FixedWing.Spread={}
-ENUMS.Formation.FixedWing.Spread.Close=458753
-ENUMS.Formation.FixedWing.Spread.Open=458754
-ENUMS.Formation.FixedWing.Spread.Group=458755
-ENUMS.Formation.FixedWing.BomberElement={}
-ENUMS.Formation.FixedWing.BomberElement.Close=786433
-ENUMS.Formation.FixedWing.BomberElement.Open=786434
-ENUMS.Formation.FixedWing.BomberElement.Group=786435
-ENUMS.Formation.FixedWing.BomberElementHeight={}
-ENUMS.Formation.FixedWing.BomberElementHeight.Close=851968
-ENUMS.Formation.FixedWing.FighterVic={}
-ENUMS.Formation.FixedWing.FighterVic.Close=917505
-ENUMS.Formation.FixedWing.FighterVic.Open=917506
-ENUMS.Formation.RotaryWing={}
-ENUMS.Formation.RotaryWing.Column={}
-ENUMS.Formation.RotaryWing.Column.D70=720896
-ENUMS.Formation.RotaryWing.Wedge={}
-ENUMS.Formation.RotaryWing.Wedge.D70=8
-ENUMS.Formation.RotaryWing.FrontRight={}
-ENUMS.Formation.RotaryWing.FrontRight.D300=655361
-ENUMS.Formation.RotaryWing.FrontRight.D600=655362
-ENUMS.Formation.RotaryWing.FrontLeft={}
-ENUMS.Formation.RotaryWing.FrontLeft.D300=655617
-ENUMS.Formation.RotaryWing.FrontLeft.D600=655618
-ENUMS.Formation.RotaryWing.EchelonRight={}
-ENUMS.Formation.RotaryWing.EchelonRight.D70=589825
-ENUMS.Formation.RotaryWing.EchelonRight.D300=589826
-ENUMS.Formation.RotaryWing.EchelonRight.D600=589827
-ENUMS.Formation.RotaryWing.EchelonLeft={}
-ENUMS.Formation.RotaryWing.EchelonLeft.D70=590081
-ENUMS.Formation.RotaryWing.EchelonLeft.D300=590082
-ENUMS.Formation.RotaryWing.EchelonLeft.D600=590083
-ENUMS.Formation.Vehicle={}
-ENUMS.Formation.Vehicle.Vee="Vee"
-ENUMS.Formation.Vehicle.EchelonRight="EchelonR"
-ENUMS.Formation.Vehicle.OffRoad="Off Road"
-ENUMS.Formation.Vehicle.Rank="Rank"
-ENUMS.Formation.Vehicle.EchelonLeft="EchelonL"
-ENUMS.Formation.Vehicle.OnRoad="On Road"
-ENUMS.Formation.Vehicle.Cone="Cone"
-ENUMS.Formation.Vehicle.Diamond="Diamond"
-ENUMS.FormationOld={}
-ENUMS.FormationOld.FixedWing={}
-ENUMS.FormationOld.FixedWing.LineAbreast=1
-ENUMS.FormationOld.FixedWing.Trail=2
-ENUMS.FormationOld.FixedWing.Wedge=3
-ENUMS.FormationOld.FixedWing.EchelonRight=4
-ENUMS.FormationOld.FixedWing.EchelonLeft=5
-ENUMS.FormationOld.FixedWing.FingerFour=6
-ENUMS.FormationOld.FixedWing.SpreadFour=7
-ENUMS.FormationOld.FixedWing.BomberElement=12
-ENUMS.FormationOld.FixedWing.BomberElementHeight=13
-ENUMS.FormationOld.FixedWing.FighterVic=14
-ENUMS.FormationOld.RotaryWing={}
-ENUMS.FormationOld.RotaryWing.Wedge=8
-ENUMS.FormationOld.RotaryWing.Echelon=9
-ENUMS.FormationOld.RotaryWing.Front=10
-ENUMS.FormationOld.RotaryWing.Column=11
-ENUMS.Morse={}
-ENUMS.Morse.A="* -"
-ENUMS.Morse.B="- * * *"
-ENUMS.Morse.C="- * - *"
-ENUMS.Morse.D="- * *"
-ENUMS.Morse.E="*"
-ENUMS.Morse.F="* * - *"
-ENUMS.Morse.G="- - *"
-ENUMS.Morse.H="* * * *"
-ENUMS.Morse.I="* *"
-ENUMS.Morse.J="* - - -"
-ENUMS.Morse.K="- * -"
-ENUMS.Morse.L="* - * *"
-ENUMS.Morse.M="- -"
-ENUMS.Morse.N="- *"
-ENUMS.Morse.O="- - -"
-ENUMS.Morse.P="* - - *"
-ENUMS.Morse.Q="- - * -"
-ENUMS.Morse.R="* - *"
-ENUMS.Morse.S="* * *"
-ENUMS.Morse.T="-"
-ENUMS.Morse.U="* * -"
-ENUMS.Morse.V="* * * -"
-ENUMS.Morse.W="* - -"
-ENUMS.Morse.X="- * * -"
-ENUMS.Morse.Y="- * - -"
-ENUMS.Morse.Z="- - * *"
-ENUMS.Morse.N1="* - - - -"
-ENUMS.Morse.N2="* * - - -"
-ENUMS.Morse.N3="* * * - -"
-ENUMS.Morse.N4="* * * * -"
-ENUMS.Morse.N5="* * * * *"
-ENUMS.Morse.N6="- * * * *"
-ENUMS.Morse.N7="- - * * *"
-ENUMS.Morse.N8="- - - * *"
-ENUMS.Morse.N9="- - - - *"
-ENUMS.Morse.N0="- - - - -"
-ENUMS.Morse[" "]=" "
-ENUMS.ISOLang=
-{
-Arabic='AR',
-Chinese='ZH',
-English='EN',
-French='FR',
-German='DE',
-Russian='RU',
-Spanish='ES',
-Japanese='JA',
-Italian='IT',
-}
-ENUMS.Phonetic=
-{
-A='Alpha',
-B='Bravo',
-C='Charlie',
-D='Delta',
-E='Echo',
-F='Foxtrot',
-G='Golf',
-H='Hotel',
-I='India',
-J='Juliett',
-K='Kilo',
-L='Lima',
-M='Mike',
-N='November',
-O='Oscar',
-P='Papa',
-Q='Quebec',
-R='Romeo',
-S='Sierra',
-T='Tango',
-U='Uniform',
-V='Victor',
-W='Whiskey',
-X='Xray',
-Y='Yankee',
-Z='Zulu',
-}
-ENUMS.ReportingName=
-{
-NATO={
-Dragon="JF-17",
-Fagot="MiG-15",
-Farmer="MiG-19",
-Felon="Su-57",
-Fencer="Su-24",
-Fishbed="MiG-21",
-Fitter="Su-17",
-Flogger="MiG-23",
-Flogger_D="MiG-27",
-Flagon="Su-15",
-Foxbat="MiG-25",
-Fulcrum="MiG-29",
-Foxhound="MiG-31",
-Flanker="Su-27",
-Flanker_C="Su-30",
-Flanker_E="Su-35",
-Flanker_F="Su-37",
-Flanker_L="J-11A",
-Firebird="J-10",
-Sea_Flanker="Su-33",
-Fullback="Su-34",
-Frogfoot="Su-25",
-Tomcat="F-14",
-Mirage="Mirage",
-Codling="Yak-40",
-Maya="L-39",
-Warthog="A-10",
-Skyhawk="A-4E",
-Viggen="AJS37",
-Harrier_B="AV8BNA",
-Harrier="AV-8B",
-Spirit="B-2",
-Aviojet="C-101",
-Nighthawk="F-117A",
-Eagle="F-15C",
-Mudhen="F-15E",
-Viper="F-16",
-Phantom="F-4E",
-Tiger="F-5",
-Sabre="F-86",
-Hornet="A-18",
-Hawk="Hawk",
-Albatros="L-39",
-Goshawk="T-45",
-Starfighter="F-104",
-Tornado="Tornado",
-Atlas="A400",
-Lancer="B1-B",
-Stratofortress="B-52H",
-Hercules="C-130",
-Super_Hercules="Hercules",
-Globemaster="C-17",
-Greyhound="C-2A",
-Galaxy="C-5",
-Hawkeye="E-2D",
-Sentry="E-3A",
-Stratotanker="KC-135",
-Extender="KC-10",
-Orion="P-3C",
-Viking="S-3B",
-Osprey="V-22",
-Badger="H6-J",
-Bear_J="Tu-142",
-Bear="Tu-95",
-Blinder="Tu-22",
-Blackjack="Tu-160",
-Clank="An-30",
-Curl="An-26",
-Candid="IL-76",
-Midas="IL-78",
-Mainstay="A-50",
-Mainring="KJ-2000",
-Yak="Yak-52",
-Helix="Ka-27",
-Shark="Ka-50",
-Hind="Mi-24",
-Halo="Mi-26",
-Hip="Mi-8",
-Havoc="Mi-28",
-Gazelle="SA342",
-Huey="UH-1H",
-Cobra="AH-1",
-Apache="AH-64",
-Chinook="CH-47",
-Sea_Stallion="CH-53",
-Kiowa="OH-58",
-Seahawk="SH-60",
-Blackhawk="UH-60",
-Sea_King="S-61",
-UCAV="WingLoong",
-Reaper="MQ-9",
-Predator="MQ-1A",
-}
-}
-ENUMS.Link16Power={
-none=0,
-low=1,
-medium=2,
-high=3,
-}
-ENUMS.Storage={
-weapons={
-missiles={},
-bombs={},
-nurs={},
-containers={},
-droptanks={},
-adapters={},
-torpedoes={},
-}
-}
-ENUMS.Storage.weapons.nurs.SNEB_TYPE253_F1B="weapons.nurs.SNEB_TYPE253_F1B"
-ENUMS.Storage.weapons.missiles.P_24T="weapons.missiles.P_24T"
-ENUMS.Storage.weapons.bombs.BLU_3B_OLD="weapons.bombs.BLU-3B_OLD"
-ENUMS.Storage.weapons.missiles.AGM_154="weapons.missiles.AGM_154"
-ENUMS.Storage.weapons.nurs.HYDRA_70_M151_M433="weapons.nurs.HYDRA_70_M151_M433"
-ENUMS.Storage.weapons.bombs.SAM_Avenger_M1097_Skid_7090lb="weapons.bombs.SAM Avenger M1097 Skid [7090lb]"
-ENUMS.Storage.weapons.bombs.British_GP_250LB_Bomb_Mk5="weapons.bombs.British_GP_250LB_Bomb_Mk5"
-ENUMS.Storage.weapons.containers.OV10_SMOKE="weapons.containers.{OV10_SMOKE}"
-ENUMS.Storage.weapons.bombs.BLU_4B_OLD="weapons.bombs.BLU-4B_OLD"
-ENUMS.Storage.weapons.bombs.FAB_500M54="weapons.bombs.FAB-500M54"
-ENUMS.Storage.weapons.bombs.GBU_38="weapons.bombs.GBU_38"
-ENUMS.Storage.weapons.containers.F_15E_AXQ_14_DATALINK="weapons.containers.F-15E_AXQ-14_DATALINK"
-ENUMS.Storage.weapons.bombs.BEER_BOMB="weapons.bombs.BEER_BOMB"
-ENUMS.Storage.weapons.bombs.P_50T="weapons.bombs.P-50T"
-ENUMS.Storage.weapons.nurs.C_8CM_GN="weapons.nurs.C_8CM_GN"
-ENUMS.Storage.weapons.bombs.FAB_500SL="weapons.bombs.FAB-500SL"
-ENUMS.Storage.weapons.bombs.KAB_1500Kr="weapons.bombs.KAB_1500Kr"
-ENUMS.Storage.weapons.bombs.two50_2="weapons.bombs.250-2"
-ENUMS.Storage.weapons.droptanks.Spitfire_tank_1="weapons.droptanks.Spitfire_tank_1"
-ENUMS.Storage.weapons.missiles.AGM_65G="weapons.missiles.AGM_65G"
-ENUMS.Storage.weapons.missiles.AGM_65A="weapons.missiles.AGM_65A"
-ENUMS.Storage.weapons.containers.Hercules_JATO="weapons.containers.Hercules_JATO"
-ENUMS.Storage.weapons.nurs.HYDRA_70_M259="weapons.nurs.HYDRA_70_M259"
-ENUMS.Storage.weapons.missiles.AGM_84E="weapons.missiles.AGM_84E"
-ENUMS.Storage.weapons.bombs.AN_M30A1="weapons.bombs.AN_M30A1"
-ENUMS.Storage.weapons.nurs.C_25="weapons.nurs.C_25"
-ENUMS.Storage.weapons.containers.AV8BNA_ALQ164="weapons.containers.AV8BNA_ALQ164"
-ENUMS.Storage.weapons.containers.lav_25="weapons.containers.lav-25"
-ENUMS.Storage.weapons.missiles.P_60="weapons.missiles.P_60"
-ENUMS.Storage.weapons.bombs.FAB_1500="weapons.bombs.FAB_1500"
-ENUMS.Storage.weapons.droptanks.FuelTank_350L="weapons.droptanks.FuelTank_350L"
-ENUMS.Storage.weapons.bombs.AAA_Vulcan_M163_Skid_21577lb="weapons.bombs.AAA Vulcan M163 Skid [21577lb]"
-ENUMS.Storage.weapons.missiles.Kormoran="weapons.missiles.Kormoran"
-ENUMS.Storage.weapons.droptanks.HB_F14_EXT_DROPTANK_EMPTY="weapons.droptanks.HB_F14_EXT_DROPTANK_EMPTY"
-ENUMS.Storage.weapons.droptanks.FuelTank_150L="weapons.droptanks.FuelTank_150L"
-ENUMS.Storage.weapons.missiles.Rb_15F_for_A_I="weapons.missiles.Rb 15F (for A.I.)"
-ENUMS.Storage.weapons.missiles.RB75T="weapons.missiles.RB75T"
-ENUMS.Storage.weapons.missiles.Vikhr_M="weapons.missiles.Vikhr_M"
-ENUMS.Storage.weapons.nurs.FFAR_M156_WP="weapons.nurs.FFAR M156 WP"
-ENUMS.Storage.weapons.nurs.British_HE_60LBSAPNo2_3INCHNo1="weapons.nurs.British_HE_60LBSAPNo2_3INCHNo1"
-ENUMS.Storage.weapons.missiles.DWS39_MJ2="weapons.missiles.DWS39_MJ2"
-ENUMS.Storage.weapons.bombs.HEBOMBD="weapons.bombs.HEBOMBD"
-ENUMS.Storage.weapons.missiles.CATM_9M="weapons.missiles.CATM_9M"
-ENUMS.Storage.weapons.bombs.Mk_81="weapons.bombs.Mk_81"
-ENUMS.Storage.weapons.droptanks.Drop_Tank_300_Liter="weapons.droptanks.Drop_Tank_300_Liter"
-ENUMS.Storage.weapons.containers.HMMWV_M1025="weapons.containers.HMMWV_M1025"
-ENUMS.Storage.weapons.bombs.SAM_CHAPARRAL_Air_21624lb="weapons.bombs.SAM CHAPARRAL Air [21624lb]"
-ENUMS.Storage.weapons.missiles.AGM_154A="weapons.missiles.AGM_154A"
-ENUMS.Storage.weapons.bombs.Mk_84AIR_TP="weapons.bombs.Mk_84AIR_TP"
-ENUMS.Storage.weapons.bombs.GBU_31_V_3B="weapons.bombs.GBU_31_V_3B"
-ENUMS.Storage.weapons.nurs.C_8CM_WH="weapons.nurs.C_8CM_WH"
-ENUMS.Storage.weapons.missiles.Matra_Super_530D="weapons.missiles.Matra Super 530D"
-ENUMS.Storage.weapons.nurs.ARF8M3TPSM="weapons.nurs.ARF8M3TPSM"
-ENUMS.Storage.weapons.missiles.TGM_65H="weapons.missiles.TGM_65H"
-ENUMS.Storage.weapons.nurs.M8rocket="weapons.nurs.M8rocket"
-ENUMS.Storage.weapons.bombs.GBU_27="weapons.bombs.GBU_27"
-ENUMS.Storage.weapons.missiles.AGR_20A="weapons.missiles.AGR_20A"
-ENUMS.Storage.weapons.missiles.LS_6_250="weapons.missiles.LS-6-250"
-ENUMS.Storage.weapons.droptanks.M2KC_RPL_522_EMPTY="weapons.droptanks.M2KC_RPL_522_EMPTY"
-ENUMS.Storage.weapons.droptanks.M2KC_02_RPL541="weapons.droptanks.M2KC_02_RPL541"
-ENUMS.Storage.weapons.missiles.AGM_45="weapons.missiles.AGM_45"
-ENUMS.Storage.weapons.missiles.AGM_84A="weapons.missiles.AGM_84A"
-ENUMS.Storage.weapons.bombs.APC_BTR_80_Air_23936lb="weapons.bombs.APC BTR-80 Air [23936lb]"
-ENUMS.Storage.weapons.missiles.P_33E="weapons.missiles.P_33E"
-ENUMS.Storage.weapons.missiles.Ataka_9M120="weapons.missiles.Ataka_9M120"
-ENUMS.Storage.weapons.bombs.MK76="weapons.bombs.MK76"
-ENUMS.Storage.weapons.bombs.AB_250_2_SD_2="weapons.bombs.AB_250_2_SD_2"
-ENUMS.Storage.weapons.missiles.Rb_05A="weapons.missiles.Rb 05A"
-ENUMS.Storage.weapons.bombs.ART_GVOZDIKA_34720lb="weapons.bombs.ART GVOZDIKA [34720lb]"
-ENUMS.Storage.weapons.bombs.Generic_Crate_20000lb="weapons.bombs.Generic Crate [20000lb]"
-ENUMS.Storage.weapons.bombs.FAB_100SV="weapons.bombs.FAB_100SV"
-ENUMS.Storage.weapons.bombs.BetAB_500="weapons.bombs.BetAB_500"
-ENUMS.Storage.weapons.droptanks.M2KC_02_RPL541_EMPTY="weapons.droptanks.M2KC_02_RPL541_EMPTY"
-ENUMS.Storage.weapons.droptanks.PTB600_MIG15="weapons.droptanks.PTB600_MIG15"
-ENUMS.Storage.weapons.missiles.Rb_24J="weapons.missiles.Rb 24J"
-ENUMS.Storage.weapons.nurs.C_8CM_BU="weapons.nurs.C_8CM_BU"
-ENUMS.Storage.weapons.nurs.SNEB_TYPE259E_F1B="weapons.nurs.SNEB_TYPE259E_F1B"
-ENUMS.Storage.weapons.nurs.WGr21="weapons.nurs.WGr21"
-ENUMS.Storage.weapons.bombs.SAMP250HD="weapons.bombs.SAMP250HD"
-ENUMS.Storage.weapons.containers.alq_184long="weapons.containers.alq-184long"
-ENUMS.Storage.weapons.nurs.SNEB_TYPE259E_H1="weapons.nurs.SNEB_TYPE259E_H1"
-ENUMS.Storage.weapons.bombs.British_SAP_250LB_Bomb_Mk5="weapons.bombs.British_SAP_250LB_Bomb_Mk5"
-ENUMS.Storage.weapons.bombs.Transport_UAZ_469_Air_3747lb="weapons.bombs.Transport UAZ-469 Air [3747lb]"
-ENUMS.Storage.weapons.bombs.Mk_83CT="weapons.bombs.Mk_83CT"
-ENUMS.Storage.weapons.missiles.AIM_7P="weapons.missiles.AIM-7P"
-ENUMS.Storage.weapons.missiles.AT_6="weapons.missiles.AT_6"
-ENUMS.Storage.weapons.nurs.SNEB_TYPE254_H1_GREEN="weapons.nurs.SNEB_TYPE254_H1_GREEN"
-ENUMS.Storage.weapons.nurs.SNEB_TYPE250_F1B="weapons.nurs.SNEB_TYPE250_F1B"
-ENUMS.Storage.weapons.containers.U22A="weapons.containers.U22A"
-ENUMS.Storage.weapons.bombs.British_GP_250LB_Bomb_Mk1="weapons.bombs.British_GP_250LB_Bomb_Mk1"
-ENUMS.Storage.weapons.bombs.CBU_105="weapons.bombs.CBU_105"
-ENUMS.Storage.weapons.droptanks.FW_190_Fuel_Tank="weapons.droptanks.FW-190_Fuel-Tank"
-ENUMS.Storage.weapons.missiles.X_58="weapons.missiles.X_58"
-ENUMS.Storage.weapons.missiles.BK90_MJ1_MJ2="weapons.missiles.BK90_MJ1_MJ2"
-ENUMS.Storage.weapons.missiles.TGM_65D="weapons.missiles.TGM_65D"
-ENUMS.Storage.weapons.containers.BRD_4_250="weapons.containers.BRD-4-250"
-ENUMS.Storage.weapons.missiles.P_73="weapons.missiles.P_73"
-ENUMS.Storage.weapons.bombs.AN_M66="weapons.bombs.AN_M66"
-ENUMS.Storage.weapons.bombs.APC_LAV_25_Air_22520lb="weapons.bombs.APC LAV-25 Air [22520lb]"
-ENUMS.Storage.weapons.missiles.AIM_7MH="weapons.missiles.AIM-7MH"
-ENUMS.Storage.weapons.containers.MB339_TravelPod="weapons.containers.MB339_TravelPod"
-ENUMS.Storage.weapons.bombs.GBU_12="weapons.bombs.GBU_12"
-ENUMS.Storage.weapons.bombs.SC_250_T3_J="weapons.bombs.SC_250_T3_J"
-ENUMS.Storage.weapons.missiles.KD_20="weapons.missiles.KD-20"
-ENUMS.Storage.weapons.missiles.AGM_86C="weapons.missiles.AGM_86C"
-ENUMS.Storage.weapons.missiles.X_35="weapons.missiles.X_35"
-ENUMS.Storage.weapons.bombs.MK106="weapons.bombs.MK106"
-ENUMS.Storage.weapons.bombs.BETAB_500S="weapons.bombs.BETAB-500S"
-ENUMS.Storage.weapons.nurs.C_5="weapons.nurs.C_5"
-ENUMS.Storage.weapons.nurs.S_24B="weapons.nurs.S-24B"
-ENUMS.Storage.weapons.bombs.British_MC_500LB_Bomb_Mk2="weapons.bombs.British_MC_500LB_Bomb_Mk2"
-ENUMS.Storage.weapons.containers.ANAWW_13="weapons.containers.ANAWW_13"
-ENUMS.Storage.weapons.droptanks.droptank_108_gal="weapons.droptanks.droptank_108_gal"
-ENUMS.Storage.weapons.droptanks.DFT_300_GAL_A4E_LR="weapons.droptanks.DFT_300_GAL_A4E_LR"
-ENUMS.Storage.weapons.bombs.CBU_87="weapons.bombs.CBU_87"
-ENUMS.Storage.weapons.missiles.GAR_8="weapons.missiles.GAR-8"
-ENUMS.Storage.weapons.bombs.BELOUGA="weapons.bombs.BELOUGA"
-ENUMS.Storage.weapons.containers.EclairM_33="weapons.containers.{EclairM_33}"
-ENUMS.Storage.weapons.bombs.ART_2S9_NONA_Air_19140lb="weapons.bombs.ART 2S9 NONA Air [19140lb]"
-ENUMS.Storage.weapons.bombs.BR_250="weapons.bombs.BR_250"
-ENUMS.Storage.weapons.bombs.IAB_500="weapons.bombs.IAB-500"
-ENUMS.Storage.weapons.containers.AN_ASQ_228="weapons.containers.AN_ASQ_228"
-ENUMS.Storage.weapons.missiles.P_27P="weapons.missiles.P_27P"
-ENUMS.Storage.weapons.bombs.SD_250_Stg="weapons.bombs.SD_250_Stg"
-ENUMS.Storage.weapons.missiles.R_530F_IR="weapons.missiles.R_530F_IR"
-ENUMS.Storage.weapons.bombs.British_SAP_500LB_Bomb_Mk5="weapons.bombs.British_SAP_500LB_Bomb_Mk5"
-ENUMS.Storage.weapons.bombs.FAB_250M54="weapons.bombs.FAB-250M54"
-ENUMS.Storage.weapons.containers.M2KC_AAF="weapons.containers.{M2KC_AAF}"
-ENUMS.Storage.weapons.missiles.CM_802AKG_AI="weapons.missiles.CM-802AKG_AI"
-ENUMS.Storage.weapons.bombs.CBU_103="weapons.bombs.CBU_103"
-ENUMS.Storage.weapons.containers.US_M10_SMOKE_TANK_RED="weapons.containers.{US_M10_SMOKE_TANK_RED}"
-ENUMS.Storage.weapons.missiles.X_29T="weapons.missiles.X_29T"
-ENUMS.Storage.weapons.bombs.HEMTT_TFFT_34400lb="weapons.bombs.HEMTT TFFT [34400lb]"
-ENUMS.Storage.weapons.missiles.C_701IR="weapons.missiles.C-701IR"
-ENUMS.Storage.weapons.containers.fullCargoSeats="weapons.containers.fullCargoSeats"
-ENUMS.Storage.weapons.bombs.GBU_15_V_31_B="weapons.bombs.GBU_15_V_31_B"
-ENUMS.Storage.weapons.bombs.APC_M1043_HMMWV_Armament_Air_7023lb="weapons.bombs.APC M1043 HMMWV Armament Air [7023lb]"
-ENUMS.Storage.weapons.missiles.PL_5EII="weapons.missiles.PL-5EII"
-ENUMS.Storage.weapons.bombs.SC_250_T1_L2="weapons.bombs.SC_250_T1_L2"
-ENUMS.Storage.weapons.torpedoes.mk46torp_name="weapons.torpedoes.mk46torp_name"
-ENUMS.Storage.weapons.containers.F_15E_AAQ_33_XR_ATP_SE="weapons.containers.F-15E_AAQ-33_XR_ATP-SE"
-ENUMS.Storage.weapons.missiles.AIM_7="weapons.missiles.AIM_7"
-ENUMS.Storage.weapons.missiles.AGM_122="weapons.missiles.AGM_122"
-ENUMS.Storage.weapons.bombs.HEBOMB="weapons.bombs.HEBOMB"
-ENUMS.Storage.weapons.bombs.CBU_97="weapons.bombs.CBU_97"
-ENUMS.Storage.weapons.bombs.MK_81SE="weapons.bombs.MK-81SE"
-ENUMS.Storage.weapons.nurs.Zuni_127="weapons.nurs.Zuni_127"
-ENUMS.Storage.weapons.containers.M2KC_AGF="weapons.containers.{M2KC_AGF}"
-ENUMS.Storage.weapons.droptanks.Hercules_ExtFuelTank="weapons.droptanks.Hercules_ExtFuelTank"
-ENUMS.Storage.weapons.containers.SMOKE_WHITE="weapons.containers.{SMOKE_WHITE}"
-ENUMS.Storage.weapons.droptanks.droptank_150_gal="weapons.droptanks.droptank_150_gal"
-ENUMS.Storage.weapons.nurs.HYDRA_70_WTU1B="weapons.nurs.HYDRA_70_WTU1B"
-ENUMS.Storage.weapons.missiles.GB_6_SFW="weapons.missiles.GB-6-SFW"
-ENUMS.Storage.weapons.missiles.KD_63="weapons.missiles.KD-63"
-ENUMS.Storage.weapons.bombs.GBU_28="weapons.bombs.GBU_28"
-ENUMS.Storage.weapons.nurs.C_8CM_YE="weapons.nurs.C_8CM_YE"
-ENUMS.Storage.weapons.droptanks.HB_F14_EXT_DROPTANK="weapons.droptanks.HB_F14_EXT_DROPTANK"
-ENUMS.Storage.weapons.missiles.Super_530F="weapons.missiles.Super_530F"
-ENUMS.Storage.weapons.missiles.Ataka_9M220="weapons.missiles.Ataka_9M220"
-ENUMS.Storage.weapons.bombs.BDU_33="weapons.bombs.BDU_33"
-ENUMS.Storage.weapons.bombs.British_GP_250LB_Bomb_Mk4="weapons.bombs.British_GP_250LB_Bomb_Mk4"
-ENUMS.Storage.weapons.missiles.TOW="weapons.missiles.TOW"
-ENUMS.Storage.weapons.bombs.ATGM_M1045_HMMWV_TOW_Air_7183lb="weapons.bombs.ATGM M1045 HMMWV TOW Air [7183lb]"
-ENUMS.Storage.weapons.missiles.X_25MR="weapons.missiles.X_25MR"
-ENUMS.Storage.weapons.droptanks.fueltank230="weapons.droptanks.fueltank230"
-ENUMS.Storage.weapons.droptanks.PTB_490C_MIG21="weapons.droptanks.PTB-490C-MIG21"
-ENUMS.Storage.weapons.bombs.M1025_HMMWV_Air_6160lb="weapons.bombs.M1025 HMMWV Air [6160lb]"
-ENUMS.Storage.weapons.nurs.SNEB_TYPE254_F1B_GREEN="weapons.nurs.SNEB_TYPE254_F1B_GREEN"
-ENUMS.Storage.weapons.missiles.R_550="weapons.missiles.R_550"
-ENUMS.Storage.weapons.bombs.KAB_1500LG="weapons.bombs.KAB_1500LG"
-ENUMS.Storage.weapons.missiles.AGM_84D="weapons.missiles.AGM_84D"
-ENUMS.Storage.weapons.missiles.YJ_83K="weapons.missiles.YJ-83K"
-ENUMS.Storage.weapons.missiles.AIM_54C_Mk47="weapons.missiles.AIM_54C_Mk47"
-ENUMS.Storage.weapons.missiles.BRM_1_90MM="weapons.missiles.BRM-1_90MM"
-ENUMS.Storage.weapons.missiles.Ataka_9M120F="weapons.missiles.Ataka_9M120F"
-ENUMS.Storage.weapons.droptanks.Eleven00L_Tank="weapons.droptanks.1100L Tank"
-ENUMS.Storage.weapons.bombs.BAP_100="weapons.bombs.BAP_100"
-ENUMS.Storage.weapons.adapters.lau_88="weapons.adapters.lau-88"
-ENUMS.Storage.weapons.missiles.P_40T="weapons.missiles.P_40T"
-ENUMS.Storage.weapons.missiles.GB_6="weapons.missiles.GB-6"
-ENUMS.Storage.weapons.bombs.FAB_250M54TU="weapons.bombs.FAB-250M54TU"
-ENUMS.Storage.weapons.missiles.DWS39_MJ1="weapons.missiles.DWS39_MJ1"
-ENUMS.Storage.weapons.missiles.CM_802AKG="weapons.missiles.CM-802AKG"
-ENUMS.Storage.weapons.bombs.FAB_250="weapons.bombs.FAB_250"
-ENUMS.Storage.weapons.missiles.C_802AK="weapons.missiles.C_802AK"
-ENUMS.Storage.weapons.bombs.SD_500_A="weapons.bombs.SD_500_A"
-ENUMS.Storage.weapons.bombs.GBU_32_V_2B="weapons.bombs.GBU_32_V_2B"
-ENUMS.Storage.weapons.containers.marder="weapons.containers.marder"
-ENUMS.Storage.weapons.missiles.ADM_141B="weapons.missiles.ADM_141B"
-ENUMS.Storage.weapons.bombs.ROCKEYE="weapons.bombs.ROCKEYE"
-ENUMS.Storage.weapons.missiles.BK90_MJ1="weapons.missiles.BK90_MJ1"
-ENUMS.Storage.weapons.containers.BTR_80="weapons.containers.BTR-80"
-ENUMS.Storage.weapons.bombs.SAM_ROLAND_ADS_34720lb="weapons.bombs.SAM ROLAND ADS [34720lb]"
-ENUMS.Storage.weapons.containers.wmd7="weapons.containers.wmd7"
-ENUMS.Storage.weapons.missiles.C_701T="weapons.missiles.C-701T"
-ENUMS.Storage.weapons.missiles.AIM_7E_2="weapons.missiles.AIM-7E-2"
-ENUMS.Storage.weapons.nurs.HVAR="weapons.nurs.HVAR"
-ENUMS.Storage.weapons.containers.HMMWV_M1043="weapons.containers.HMMWV_M1043"
-ENUMS.Storage.weapons.droptanks.PTB_800_MIG21="weapons.droptanks.PTB-800-MIG21"
-ENUMS.Storage.weapons.missiles.AGM_114="weapons.missiles.AGM_114"
-ENUMS.Storage.weapons.bombs.APC_M1126_Stryker_ICV_29542lb="weapons.bombs.APC M1126 Stryker ICV [29542lb]"
-ENUMS.Storage.weapons.bombs.APC_M113_Air_21624lb="weapons.bombs.APC M113 Air [21624lb]"
-ENUMS.Storage.weapons.bombs.M_117="weapons.bombs.M_117"
-ENUMS.Storage.weapons.missiles.AGM_65D="weapons.missiles.AGM_65D"
-ENUMS.Storage.weapons.droptanks.MB339_TT320_L="weapons.droptanks.MB339_TT320_L"
-ENUMS.Storage.weapons.missiles.AGM_86="weapons.missiles.AGM_86"
-ENUMS.Storage.weapons.bombs.BDU_45LGB="weapons.bombs.BDU_45LGB"
-ENUMS.Storage.weapons.missiles.AGM_65H="weapons.missiles.AGM_65H"
-ENUMS.Storage.weapons.nurs.RS_82="weapons.nurs.RS-82"
-ENUMS.Storage.weapons.nurs.SNEB_TYPE252_F1B="weapons.nurs.SNEB_TYPE252_F1B"
-ENUMS.Storage.weapons.bombs.BL_755="weapons.bombs.BL_755"
-ENUMS.Storage.weapons.containers.F_15E_AAQ_28_LITENING="weapons.containers.F-15E_AAQ-28_LITENING"
-ENUMS.Storage.weapons.nurs.SNEB_TYPE256_F1B="weapons.nurs.SNEB_TYPE256_F1B"
-ENUMS.Storage.weapons.missiles.AGM_84H="weapons.missiles.AGM_84H"
-ENUMS.Storage.weapons.missiles.AIM_54="weapons.missiles.AIM_54"
-ENUMS.Storage.weapons.missiles.X_31A="weapons.missiles.X_31A"
-ENUMS.Storage.weapons.bombs.KAB_500Kr="weapons.bombs.KAB_500Kr"
-ENUMS.Storage.weapons.containers.SPS_141_100="weapons.containers.SPS-141-100"
-ENUMS.Storage.weapons.missiles.BK90_MJ2="weapons.missiles.BK90_MJ2"
-ENUMS.Storage.weapons.missiles.Super_530D="weapons.missiles.Super_530D"
-ENUMS.Storage.weapons.bombs.CBU_52B="weapons.bombs.CBU_52B"
-ENUMS.Storage.weapons.droptanks.PTB_450="weapons.droptanks.PTB-450"
-ENUMS.Storage.weapons.bombs.IFV_MCV_80_34720lb="weapons.bombs.IFV MCV-80 [34720lb]"
-ENUMS.Storage.weapons.containers.Two_c9="weapons.containers.2-c9"
-ENUMS.Storage.weapons.missiles.AIM_9JULI="weapons.missiles.AIM-9JULI"
-ENUMS.Storage.weapons.droptanks.MB339_TT500_R="weapons.droptanks.MB339_TT500_R"
-ENUMS.Storage.weapons.nurs.C_8CM="weapons.nurs.C_8CM"
-ENUMS.Storage.weapons.containers.BARAX="weapons.containers.BARAX"
-ENUMS.Storage.weapons.missiles.P_40R="weapons.missiles.P_40R"
-ENUMS.Storage.weapons.missiles.YJ_12="weapons.missiles.YJ-12"
-ENUMS.Storage.weapons.missiles.CM_802AKG="weapons.missiles.CM_802AKG"
-ENUMS.Storage.weapons.nurs.SNEB_TYPE254_H1_YELLOW="weapons.nurs.SNEB_TYPE254_H1_YELLOW"
-ENUMS.Storage.weapons.bombs.Durandal="weapons.bombs.Durandal"
-ENUMS.Storage.weapons.droptanks.i16_eft="weapons.droptanks.i16_eft"
-ENUMS.Storage.weapons.droptanks.AV8BNA_AERO1D_EMPTY="weapons.droptanks.AV8BNA_AERO1D_EMPTY"
-ENUMS.Storage.weapons.containers.Hercules_Battle_Station_TGP="weapons.containers.Hercules_Battle_Station_TGP"
-ENUMS.Storage.weapons.nurs.C_8CM_VT="weapons.nurs.C_8CM_VT"
-ENUMS.Storage.weapons.missiles.PL_12="weapons.missiles.PL-12"
-ENUMS.Storage.weapons.missiles.R_3R="weapons.missiles.R-3R"
-ENUMS.Storage.weapons.bombs.GBU_54_V_1B="weapons.bombs.GBU_54_V_1B"
-ENUMS.Storage.weapons.droptanks.MB339_TT320_R="weapons.droptanks.MB339_TT320_R"
-ENUMS.Storage.weapons.bombs.RN_24="weapons.bombs.RN-24"
-ENUMS.Storage.weapons.containers.Twoc6m="weapons.containers.2c6m"
-ENUMS.Storage.weapons.bombs.ARV_BRDM_2_Air_12320lb="weapons.bombs.ARV BRDM-2 Air [12320lb]"
-ENUMS.Storage.weapons.bombs.ARV_BRDM_2_Skid_12210lb="weapons.bombs.ARV BRDM-2 Skid [12210lb]"
-ENUMS.Storage.weapons.nurs.SNEB_TYPE251_F1B="weapons.nurs.SNEB_TYPE251_F1B"
-ENUMS.Storage.weapons.missiles.X_41="weapons.missiles.X_41"
-ENUMS.Storage.weapons.containers.MIG21_SMOKE_WHITE="weapons.containers.{MIG21_SMOKE_WHITE}"
-ENUMS.Storage.weapons.bombs.MK_82AIR="weapons.bombs.MK_82AIR"
-ENUMS.Storage.weapons.missiles.R_530F_EM="weapons.missiles.R_530F_EM"
-ENUMS.Storage.weapons.bombs.SAMP400LD="weapons.bombs.SAMP400LD"
-ENUMS.Storage.weapons.bombs.FAB_50="weapons.bombs.FAB_50"
-ENUMS.Storage.weapons.bombs.AB_250_2_SD_10A="weapons.bombs.AB_250_2_SD_10A"
-ENUMS.Storage.weapons.missiles.ADM_141A="weapons.missiles.ADM_141A"
-ENUMS.Storage.weapons.containers.KBpod="weapons.containers.KBpod"
-ENUMS.Storage.weapons.bombs.British_GP_500LB_Bomb_Mk4="weapons.bombs.British_GP_500LB_Bomb_Mk4"
-ENUMS.Storage.weapons.missiles.AGM_65E="weapons.missiles.AGM_65E"
-ENUMS.Storage.weapons.containers.sa342_dipole_antenna="weapons.containers.sa342_dipole_antenna"
-ENUMS.Storage.weapons.bombs.OFAB_100_Jupiter="weapons.bombs.OFAB-100 Jupiter"
-ENUMS.Storage.weapons.nurs.SNEB_TYPE257_F1B="weapons.nurs.SNEB_TYPE257_F1B"
-ENUMS.Storage.weapons.missiles.Rb_04E_for_A_I="weapons.missiles.Rb 04E (for A.I.)"
-ENUMS.Storage.weapons.bombs.AN_M66A2="weapons.bombs.AN-M66A2"
-ENUMS.Storage.weapons.missiles.P_27T="weapons.missiles.P_27T"
-ENUMS.Storage.weapons.droptanks.LNS_VIG_XTANK="weapons.droptanks.LNS_VIG_XTANK"
-ENUMS.Storage.weapons.missiles.R_55="weapons.missiles.R-55"
-ENUMS.Storage.weapons.torpedoes.YU_6="weapons.torpedoes.YU-6"
-ENUMS.Storage.weapons.bombs.British_MC_250LB_Bomb_Mk2="weapons.bombs.British_MC_250LB_Bomb_Mk2"
-ENUMS.Storage.weapons.droptanks.PTB_120_F86F35="weapons.droptanks.PTB_120_F86F35"
-ENUMS.Storage.weapons.missiles.PL_8B="weapons.missiles.PL-8B"
-ENUMS.Storage.weapons.droptanks.F_15E_Drop_Tank_Empty="weapons.droptanks.F-15E_Drop_Tank_Empty"
-ENUMS.Storage.weapons.nurs.British_HE_60LBFNo1_3INCHNo1="weapons.nurs.British_HE_60LBFNo1_3INCHNo1"
-ENUMS.Storage.weapons.missiles.P_77="weapons.missiles.P_77"
-ENUMS.Storage.weapons.torpedoes.LTF_5B="weapons.torpedoes.LTF_5B"
-ENUMS.Storage.weapons.missiles.R_3S="weapons.missiles.R-3S"
-ENUMS.Storage.weapons.nurs.SNEB_TYPE253_H1="weapons.nurs.SNEB_TYPE253_H1"
-ENUMS.Storage.weapons.missiles.PL_8A="weapons.missiles.PL-8A"
-ENUMS.Storage.weapons.bombs.APC_BTR_82A_Skid_24888lb="weapons.bombs.APC BTR-82A Skid [24888lb]"
-ENUMS.Storage.weapons.containers.Sborka="weapons.containers.Sborka"
-ENUMS.Storage.weapons.missiles.AGM_65L="weapons.missiles.AGM_65L"
-ENUMS.Storage.weapons.missiles.X_28="weapons.missiles.X_28"
-ENUMS.Storage.weapons.missiles.TGM_65G="weapons.missiles.TGM_65G"
-ENUMS.Storage.weapons.nurs.SNEB_TYPE257_H1="weapons.nurs.SNEB_TYPE257_H1"
-ENUMS.Storage.weapons.missiles.RB75B="weapons.missiles.RB75B"
-ENUMS.Storage.weapons.missiles.X_25ML="weapons.missiles.X_25ML"
-ENUMS.Storage.weapons.droptanks.FPU_8A="weapons.droptanks.FPU_8A"
-ENUMS.Storage.weapons.bombs.BLG66="weapons.bombs.BLG66"
-ENUMS.Storage.weapons.nurs.C_8CM_RD="weapons.nurs.C_8CM_RD"
-ENUMS.Storage.weapons.containers.EclairM_06="weapons.containers.{EclairM_06}"
-ENUMS.Storage.weapons.bombs.RBK_500AO="weapons.bombs.RBK_500AO"
-ENUMS.Storage.weapons.missiles.AIM_9P="weapons.missiles.AIM-9P"
-ENUMS.Storage.weapons.bombs.British_GP_500LB_Bomb_Mk4_Short="weapons.bombs.British_GP_500LB_Bomb_Mk4_Short"
-ENUMS.Storage.weapons.containers.MB339_Vinten="weapons.containers.MB339_Vinten"
-ENUMS.Storage.weapons.missiles.Rb_15F="weapons.missiles.Rb 15F"
-ENUMS.Storage.weapons.nurs.ARAKM70BHE="weapons.nurs.ARAKM70BHE"
-ENUMS.Storage.weapons.bombs.AAA_Vulcan_M163_Air_21666lb="weapons.bombs.AAA Vulcan M163 Air [21666lb]"
-ENUMS.Storage.weapons.missiles.X_29L="weapons.missiles.X_29L"
-ENUMS.Storage.weapons.containers.F14_LANTIRN_TP="weapons.containers.{F14-LANTIRN-TP}"
-ENUMS.Storage.weapons.bombs.FAB_250_M62="weapons.bombs.FAB-250-M62"
-ENUMS.Storage.weapons.missiles.AIM_120C="weapons.missiles.AIM_120C"
-ENUMS.Storage.weapons.bombs.EWR_SBORKA_Air_21624lb="weapons.bombs.EWR SBORKA Air [21624lb]"
-ENUMS.Storage.weapons.bombs.SAMP250LD="weapons.bombs.SAMP250LD"
-ENUMS.Storage.weapons.droptanks.Spitfire_slipper_tank="weapons.droptanks.Spitfire_slipper_tank"
-ENUMS.Storage.weapons.missiles.LS_6_500="weapons.missiles.LS-6-500"
-ENUMS.Storage.weapons.bombs.GBU_31_V_4B="weapons.bombs.GBU_31_V_4B"
-ENUMS.Storage.weapons.droptanks.PTB400_MIG15="weapons.droptanks.PTB400_MIG15"
-ENUMS.Storage.weapons.containers.m_113="weapons.containers.m-113"
-ENUMS.Storage.weapons.bombs.SPG_M1128_Stryker_MGS_33036lb="weapons.bombs.SPG M1128 Stryker MGS [33036lb]"
-ENUMS.Storage.weapons.missiles.AIM_9L="weapons.missiles.AIM-9L"
-ENUMS.Storage.weapons.missiles.AIM_9X="weapons.missiles.AIM_9X"
-ENUMS.Storage.weapons.nurs.C_8="weapons.nurs.C_8"
-ENUMS.Storage.weapons.bombs.SAM_CHAPARRAL_Skid_21516lb="weapons.bombs.SAM CHAPARRAL Skid [21516lb]"
-ENUMS.Storage.weapons.missiles.P_27TE="weapons.missiles.P_27TE"
-ENUMS.Storage.weapons.bombs.ODAB_500PM="weapons.bombs.ODAB-500PM"
-ENUMS.Storage.weapons.bombs.MK77mod1_WPN="weapons.bombs.MK77mod1-WPN"
-ENUMS.Storage.weapons.droptanks.PTB400_MIG19="weapons.droptanks.PTB400_MIG19"
-ENUMS.Storage.weapons.torpedoes.Mark_46="weapons.torpedoes.Mark_46"
-ENUMS.Storage.weapons.containers.rightSeat="weapons.containers.rightSeat"
-ENUMS.Storage.weapons.containers.US_M10_SMOKE_TANK_ORANGE="weapons.containers.{US_M10_SMOKE_TANK_ORANGE}"
-ENUMS.Storage.weapons.bombs.SAB_100MN="weapons.bombs.SAB_100MN"
-ENUMS.Storage.weapons.nurs.FFAR_Mk5_HEAT="weapons.nurs.FFAR Mk5 HEAT"
-ENUMS.Storage.weapons.bombs.IFV_TPZ_FUCH_33440lb="weapons.bombs.IFV TPZ FUCH [33440lb]"
-ENUMS.Storage.weapons.bombs.IFV_M2A2_Bradley_34720lb="weapons.bombs.IFV M2A2 Bradley [34720lb]"
-ENUMS.Storage.weapons.bombs.MK77mod0_WPN="weapons.bombs.MK77mod0-WPN"
-ENUMS.Storage.weapons.containers.ASO_2="weapons.containers.ASO-2"
-ENUMS.Storage.weapons.bombs.Mk_84AIR_GP="weapons.bombs.Mk_84AIR_GP"
-ENUMS.Storage.weapons.nurs.S_24A="weapons.nurs.S-24A"
-ENUMS.Storage.weapons.bombs.RBK_250_275_AO_1SCH="weapons.bombs.RBK_250_275_AO_1SCH"
-ENUMS.Storage.weapons.bombs.Transport_Tigr_Skid_15730lb="weapons.bombs.Transport Tigr Skid [15730lb]"
-ENUMS.Storage.weapons.missiles.AIM_7F="weapons.missiles.AIM-7F"
-ENUMS.Storage.weapons.bombs.CBU_99="weapons.bombs.CBU_99"
-ENUMS.Storage.weapons.bombs.LUU_2B="weapons.bombs.LUU_2B"
-ENUMS.Storage.weapons.bombs.FAB_500TA="weapons.bombs.FAB-500TA"
-ENUMS.Storage.weapons.missiles.AGR_20_M282="weapons.missiles.AGR_20_M282"
-ENUMS.Storage.weapons.droptanks.MB339_FT330="weapons.droptanks.MB339_FT330"
-ENUMS.Storage.weapons.bombs.SAMP125LD="weapons.bombs.SAMP125LD"
-ENUMS.Storage.weapons.missiles.X_25MP="weapons.missiles.X_25MP"
-ENUMS.Storage.weapons.nurs.SNEB_TYPE252_H1="weapons.nurs.SNEB_TYPE252_H1"
-ENUMS.Storage.weapons.missiles.AGM_65F="weapons.missiles.AGM_65F"
-ENUMS.Storage.weapons.missiles.AIM_9P5="weapons.missiles.AIM-9P5"
-ENUMS.Storage.weapons.bombs.Transport_Tigr_Air_15900lb="weapons.bombs.Transport Tigr Air [15900lb]"
-ENUMS.Storage.weapons.nurs.SNEB_TYPE254_H1_RED="weapons.nurs.SNEB_TYPE254_H1_RED"
-ENUMS.Storage.weapons.nurs.FFAR_Mk1_HE="weapons.nurs.FFAR Mk1 HE"
-ENUMS.Storage.weapons.nurs.SPRD_99="weapons.nurs.SPRD-99"
-ENUMS.Storage.weapons.bombs.BIN_200="weapons.bombs.BIN_200"
-ENUMS.Storage.weapons.bombs.BLU_4B_GROUP="weapons.bombs.BLU_4B_GROUP"
-ENUMS.Storage.weapons.bombs.GBU_24="weapons.bombs.GBU_24"
-ENUMS.Storage.weapons.missiles.Rb_04E="weapons.missiles.Rb 04E"
-ENUMS.Storage.weapons.missiles.Rb_74="weapons.missiles.Rb 74"
-ENUMS.Storage.weapons.containers.leftSeat="weapons.containers.leftSeat"
-ENUMS.Storage.weapons.bombs.LS_6_100="weapons.bombs.LS-6-100"
-ENUMS.Storage.weapons.bombs.Transport_URAL_375_14815lb="weapons.bombs.Transport URAL-375 [14815lb]"
-ENUMS.Storage.weapons.containers.US_M10_SMOKE_TANK_GREEN="weapons.containers.{US_M10_SMOKE_TANK_GREEN}"
-ENUMS.Storage.weapons.missiles.X_22="weapons.missiles.X_22"
-ENUMS.Storage.weapons.containers.FAS="weapons.containers.FAS"
-ENUMS.Storage.weapons.nurs.S_25_O="weapons.nurs.S-25-O"
-ENUMS.Storage.weapons.droptanks.para="weapons.droptanks.para"
-ENUMS.Storage.weapons.droptanks.F_15E_Drop_Tank="weapons.droptanks.F-15E_Drop_Tank"
-ENUMS.Storage.weapons.droptanks.M2KC_08_RPL541_EMPTY="weapons.droptanks.M2KC_08_RPL541_EMPTY"
-ENUMS.Storage.weapons.missiles.X_31P="weapons.missiles.X_31P"
-ENUMS.Storage.weapons.bombs.RBK_500U="weapons.bombs.RBK_500U"
-ENUMS.Storage.weapons.missiles.AIM_54A_Mk47="weapons.missiles.AIM_54A_Mk47"
-ENUMS.Storage.weapons.droptanks.oiltank="weapons.droptanks.oiltank"
-ENUMS.Storage.weapons.missiles.AGM_154B="weapons.missiles.AGM_154B"
-ENUMS.Storage.weapons.containers.MB339_SMOKE_POD="weapons.containers.MB339_SMOKE-POD"
-ENUMS.Storage.weapons.containers.ECM_POD_L_175V="weapons.containers.{ECM_POD_L_175V}"
-ENUMS.Storage.weapons.droptanks.PTB_580G_F1="weapons.droptanks.PTB_580G_F1"
-ENUMS.Storage.weapons.containers.EclairM_15="weapons.containers.{EclairM_15}"
-ENUMS.Storage.weapons.containers.F_15E_AAQ_13_LANTIRN="weapons.containers.F-15E_AAQ-13_LANTIRN"
-ENUMS.Storage.weapons.droptanks.Eight00L_Tank_Empty="weapons.droptanks.800L Tank Empty"
-ENUMS.Storage.weapons.containers.One6c_hts_pod="weapons.containers.16c_hts_pod"
-ENUMS.Storage.weapons.bombs.AN_M81="weapons.bombs.AN-M81"
-ENUMS.Storage.weapons.droptanks.Mosquito_Drop_Tank_100gal="weapons.droptanks.Mosquito_Drop_Tank_100gal"
-ENUMS.Storage.weapons.droptanks.Mosquito_Drop_Tank_50gal="weapons.droptanks.Mosquito_Drop_Tank_50gal"
-ENUMS.Storage.weapons.droptanks.DFT_150_GAL_A4E="weapons.droptanks.DFT_150_GAL_A4E"
-ENUMS.Storage.weapons.missiles.AIM_9="weapons.missiles.AIM_9"
-ENUMS.Storage.weapons.bombs.IFV_BTR_D_Air_18040lb="weapons.bombs.IFV BTR-D Air [18040lb]"
-ENUMS.Storage.weapons.containers.EclairM_42="weapons.containers.{EclairM_42}"
-ENUMS.Storage.weapons.bombs.KAB_1500T="weapons.bombs.KAB_1500T"
-ENUMS.Storage.weapons.droptanks.PTB_490_MIG21="weapons.droptanks.PTB-490-MIG21"
-ENUMS.Storage.weapons.droptanks.PTB_200_F86F35="weapons.droptanks.PTB_200_F86F35"
-ENUMS.Storage.weapons.droptanks.PTB760_MIG19="weapons.droptanks.PTB760_MIG19"
-ENUMS.Storage.weapons.bombs.GBU_43_B_MOAB="weapons.bombs.GBU-43/B(MOAB)"
-ENUMS.Storage.weapons.torpedoes.G7A_T1="weapons.torpedoes.G7A_T1"
-ENUMS.Storage.weapons.bombs.IFV_BMD_1_Air_18040lb="weapons.bombs.IFV BMD-1 Air [18040lb]"
-ENUMS.Storage.weapons.bombs.SAM_LINEBACKER_34720lb="weapons.bombs.SAM LINEBACKER [34720lb]"
-ENUMS.Storage.weapons.containers.ais_pod_t50_r="weapons.containers.ais-pod-t50_r"
-ENUMS.Storage.weapons.containers.CE2_SMOKE_WHITE="weapons.containers.{CE2_SMOKE_WHITE}"
-ENUMS.Storage.weapons.droptanks.fuel_tank_230="weapons.droptanks.fuel_tank_230"
-ENUMS.Storage.weapons.droptanks.M2KC_RPL_522="weapons.droptanks.M2KC_RPL_522"
-ENUMS.Storage.weapons.missiles.AGM_130="weapons.missiles.AGM_130"
-ENUMS.Storage.weapons.droptanks.Eight00L_Tank="weapons.droptanks.800L Tank"
-ENUMS.Storage.weapons.bombs.IFV_BTR_D_Skid_17930lb="weapons.bombs.IFV BTR-D Skid [17930lb]"
-ENUMS.Storage.weapons.containers.bmp_1="weapons.containers.bmp-1"
-ENUMS.Storage.weapons.bombs.GBU_31="weapons.bombs.GBU_31"
-ENUMS.Storage.weapons.containers.aaq_28LEFT_litening="weapons.containers.aaq-28LEFT litening"
-ENUMS.Storage.weapons.missiles.Kh_66_Grom="weapons.missiles.Kh-66_Grom"
-ENUMS.Storage.weapons.containers.MIG21_SMOKE_RED="weapons.containers.{MIG21_SMOKE_RED}"
-ENUMS.Storage.weapons.containers.U22="weapons.containers.U22"
-ENUMS.Storage.weapons.bombs.IFV_BMD_1_Skid_17930lb="weapons.bombs.IFV BMD-1 Skid [17930lb]"
-ENUMS.Storage.weapons.droptanks.Bidon="weapons.droptanks.Bidon"
-ENUMS.Storage.weapons.bombs.GBU_31_V_2B="weapons.bombs.GBU_31_V_2B"
-ENUMS.Storage.weapons.bombs.Mk_82Y="weapons.bombs.Mk_82Y"
-ENUMS.Storage.weapons.containers.pl5eii="weapons.containers.pl5eii"
-ENUMS.Storage.weapons.bombs.RBK_500U_OAB_2_5RT="weapons.bombs.RBK_500U_OAB_2_5RT"
-ENUMS.Storage.weapons.bombs.British_GP_500LB_Bomb_Mk5="weapons.bombs.British_GP_500LB_Bomb_Mk5"
-ENUMS.Storage.weapons.containers.Eclair="weapons.containers.{Eclair}"
-ENUMS.Storage.weapons.nurs.S5MO_HEFRAG_FFAR="weapons.nurs.S5MO_HEFRAG_FFAR"
-ENUMS.Storage.weapons.bombs.BETAB_500M="weapons.bombs.BETAB-500M"
-ENUMS.Storage.weapons.bombs.Transport_M818_16000lb="weapons.bombs.Transport M818 [16000lb]"
-ENUMS.Storage.weapons.bombs.British_MC_250LB_Bomb_Mk1="weapons.bombs.British_MC_250LB_Bomb_Mk1"
-ENUMS.Storage.weapons.nurs.SNEB_TYPE251_H1="weapons.nurs.SNEB_TYPE251_H1"
-ENUMS.Storage.weapons.bombs.TYPE_200A="weapons.bombs.TYPE-200A"
-ENUMS.Storage.weapons.nurs.HYDRA_70_M151="weapons.nurs.HYDRA_70_M151"
-ENUMS.Storage.weapons.bombs.IFV_BMP_3_32912lb="weapons.bombs.IFV BMP-3 [32912lb]"
-ENUMS.Storage.weapons.bombs.APC_MTLB_Air_26400lb="weapons.bombs.APC MTLB Air [26400lb]"
-ENUMS.Storage.weapons.nurs.HYDRA_70_M229="weapons.nurs.HYDRA_70_M229"
-ENUMS.Storage.weapons.bombs.BDU_45="weapons.bombs.BDU_45"
-ENUMS.Storage.weapons.bombs.OFAB_100_120TU="weapons.bombs.OFAB-100-120TU"
-ENUMS.Storage.weapons.missiles.AIM_9J="weapons.missiles.AIM-9J"
-ENUMS.Storage.weapons.nurs.ARF8M3API="weapons.nurs.ARF8M3API"
-ENUMS.Storage.weapons.bombs.BetAB_500ShP="weapons.bombs.BetAB_500ShP"
-ENUMS.Storage.weapons.nurs.C_8OFP2="weapons.nurs.C_8OFP2"
-ENUMS.Storage.weapons.bombs.GBU_10="weapons.bombs.GBU_10"
-ENUMS.Storage.weapons.bombs.APC_MTLB_Skid_26290lb="weapons.bombs.APC MTLB Skid [26290lb]"
-ENUMS.Storage.weapons.nurs.SNEB_TYPE254_F1B_RED="weapons.nurs.SNEB_TYPE254_F1B_RED"
-ENUMS.Storage.weapons.missiles.X_65="weapons.missiles.X_65"
-ENUMS.Storage.weapons.missiles.R_550_M1="weapons.missiles.R_550_M1"
-ENUMS.Storage.weapons.missiles.AGM_65K="weapons.missiles.AGM_65K"
-ENUMS.Storage.weapons.nurs.SNEB_TYPE254_F1B_YELLOW="weapons.nurs.SNEB_TYPE254_F1B_YELLOW"
-ENUMS.Storage.weapons.missiles.AGM_88="weapons.missiles.AGM_88"
-ENUMS.Storage.weapons.nurs.C_8OM="weapons.nurs.C_8OM"
-ENUMS.Storage.weapons.bombs.SAM_ROLAND_LN_34720b="weapons.bombs.SAM ROLAND LN [34720b]"
-ENUMS.Storage.weapons.missiles.AIM_120="weapons.missiles.AIM_120"
-ENUMS.Storage.weapons.missiles.HOT3_MBDA="weapons.missiles.HOT3_MBDA"
-ENUMS.Storage.weapons.missiles.R_13M="weapons.missiles.R-13M"
-ENUMS.Storage.weapons.missiles.AIM_54C_Mk60="weapons.missiles.AIM_54C_Mk60"
-ENUMS.Storage.weapons.bombs.AAA_GEPARD_34720lb="weapons.bombs.AAA GEPARD [34720lb]"
-ENUMS.Storage.weapons.missiles.R_13M1="weapons.missiles.R-13M1"
-ENUMS.Storage.weapons.bombs.APC_Cobra_Air_10912lb="weapons.bombs.APC Cobra Air [10912lb]"
-ENUMS.Storage.weapons.bombs.RBK_250="weapons.bombs.RBK_250"
-ENUMS.Storage.weapons.bombs.SC_500_J="weapons.bombs.SC_500_J"
-ENUMS.Storage.weapons.missiles.AGM_114K="weapons.missiles.AGM_114K"
-ENUMS.Storage.weapons.missiles.ALARM="weapons.missiles.ALARM"
-ENUMS.Storage.weapons.bombs.Mk_83="weapons.bombs.Mk_83"
-ENUMS.Storage.weapons.missiles.AGM_65B="weapons.missiles.AGM_65B"
-ENUMS.Storage.weapons.bombs.MK_82SNAKEYE="weapons.bombs.MK_82SNAKEYE"
-ENUMS.Storage.weapons.nurs.HYDRA_70_MK1="weapons.nurs.HYDRA_70_MK1"
-ENUMS.Storage.weapons.bombs.BLG66_BELOUGA="weapons.bombs.BLG66_BELOUGA"
-ENUMS.Storage.weapons.containers.EclairM_51="weapons.containers.{EclairM_51}"
-ENUMS.Storage.weapons.missiles.AIM_54A_Mk60="weapons.missiles.AIM_54A_Mk60"
-ENUMS.Storage.weapons.droptanks.DFT_300_GAL_A4E="weapons.droptanks.DFT_300_GAL_A4E"
-ENUMS.Storage.weapons.bombs.ATGM_M1134_Stryker_30337lb="weapons.bombs.ATGM M1134 Stryker [30337lb]"
-ENUMS.Storage.weapons.bombs.BAT_120="weapons.bombs.BAT-120"
-ENUMS.Storage.weapons.missiles.DWS39_MJ1_MJ2="weapons.missiles.DWS39_MJ1_MJ2"
-ENUMS.Storage.weapons.containers.SPRD="weapons.containers.SPRD"
-ENUMS.Storage.weapons.bombs.BR_500="weapons.bombs.BR_500"
-ENUMS.Storage.weapons.bombs.British_GP_500LB_Bomb_Mk1="weapons.bombs.British_GP_500LB_Bomb_Mk1"
-ENUMS.Storage.weapons.bombs.BDU_50HD="weapons.bombs.BDU_50HD"
-ENUMS.Storage.weapons.missiles.RS2US="weapons.missiles.RS2US"
-ENUMS.Storage.weapons.bombs.IFV_BMP_2_25168lb="weapons.bombs.IFV BMP-2 [25168lb]"
-ENUMS.Storage.weapons.bombs.SAMP400HD="weapons.bombs.SAMP400HD"
-ENUMS.Storage.weapons.containers.Hercules_Battle_Station="weapons.containers.Hercules_Battle_Station"
-ENUMS.Storage.weapons.bombs.AN_M64="weapons.bombs.AN_M64"
-ENUMS.Storage.weapons.containers.rearCargoSeats="weapons.containers.rearCargoSeats"
-ENUMS.Storage.weapons.bombs.Mk_82="weapons.bombs.Mk_82"
-ENUMS.Storage.weapons.missiles.AKD_10="weapons.missiles.AKD-10"
-ENUMS.Storage.weapons.bombs.BDU_50LGB="weapons.bombs.BDU_50LGB"
-ENUMS.Storage.weapons.missiles.SD_10="weapons.missiles.SD-10"
-ENUMS.Storage.weapons.containers.IRDeflector="weapons.containers.IRDeflector"
-ENUMS.Storage.weapons.bombs.FAB_500="weapons.bombs.FAB_500"
-ENUMS.Storage.weapons.bombs.KAB_500="weapons.bombs.KAB_500"
-ENUMS.Storage.weapons.nurs.S_5M="weapons.nurs.S-5M"
-ENUMS.Storage.weapons.missiles.MICA_R="weapons.missiles.MICA_R"
-ENUMS.Storage.weapons.missiles.X_59M="weapons.missiles.X_59M"
-ENUMS.Storage.weapons.nurs.UG_90MM="weapons.nurs.UG_90MM"
-ENUMS.Storage.weapons.bombs.LYSBOMB="weapons.bombs.LYSBOMB"
-ENUMS.Storage.weapons.nurs.R4M="weapons.nurs.R4M"
-ENUMS.Storage.weapons.containers.dlpod_akg="weapons.containers.dlpod_akg"
-ENUMS.Storage.weapons.missiles.LD_10="weapons.missiles.LD-10"
-ENUMS.Storage.weapons.bombs.SC_50="weapons.bombs.SC_50"
-ENUMS.Storage.weapons.nurs.HYDRA_70_MK5="weapons.nurs.HYDRA_70_MK5"
-ENUMS.Storage.weapons.bombs.FAB_100M="weapons.bombs.FAB_100M"
-ENUMS.Storage.weapons.missiles.Rb_24="weapons.missiles.Rb 24"
-ENUMS.Storage.weapons.bombs.BDU_45B="weapons.bombs.BDU_45B"
-ENUMS.Storage.weapons.missiles.GB_6_HE="weapons.missiles.GB-6-HE"
-ENUMS.Storage.weapons.missiles.KD_63B="weapons.missiles.KD-63B"
-ENUMS.Storage.weapons.missiles.P_27PE="weapons.missiles.P_27PE"
-ENUMS.Storage.weapons.droptanks.PTB300_MIG15="weapons.droptanks.PTB300_MIG15"
-ENUMS.Storage.weapons.bombs.Two50_3="weapons.bombs.250-3"
-ENUMS.Storage.weapons.bombs.SC_500_L2="weapons.bombs.SC_500_L2"
-ENUMS.Storage.weapons.containers.HMMWV_M1045="weapons.containers.HMMWV_M1045"
-ENUMS.Storage.weapons.bombs.FAB_500M54TU="weapons.bombs.FAB-500M54TU"
-ENUMS.Storage.weapons.containers.US_M10_SMOKE_TANK_YELLOW="weapons.containers.{US_M10_SMOKE_TANK_YELLOW}"
-ENUMS.Storage.weapons.containers.EclairM_60="weapons.containers.{EclairM_60}"
-ENUMS.Storage.weapons.bombs.SAB_250_200="weapons.bombs.SAB_250_200"
-ENUMS.Storage.weapons.bombs.FAB_100="weapons.bombs.FAB_100"
-ENUMS.Storage.weapons.bombs.KAB_500S="weapons.bombs.KAB_500S"
-ENUMS.Storage.weapons.missiles.AGM_45A="weapons.missiles.AGM_45A"
-ENUMS.Storage.weapons.missiles.Kh25MP_PRGS1VP="weapons.missiles.Kh25MP_PRGS1VP"
-ENUMS.Storage.weapons.nurs.S5M1_HEFRAG_FFAR="weapons.nurs.S5M1_HEFRAG_FFAR"
-ENUMS.Storage.weapons.containers.kg600="weapons.containers.kg600"
-ENUMS.Storage.weapons.bombs.AN_M65="weapons.bombs.AN_M65"
-ENUMS.Storage.weapons.bombs.AN_M57="weapons.bombs.AN_M57"
-ENUMS.Storage.weapons.bombs.BLU_3B_GROUP="weapons.bombs.BLU_3B_GROUP"
-ENUMS.Storage.weapons.bombs.BAP_100="weapons.bombs.BAP-100"
-ENUMS.Storage.weapons.containers.HEMTT="weapons.containers.HEMTT"
-ENUMS.Storage.weapons.bombs.British_MC_500LB_Bomb_Mk1_Short="weapons.bombs.British_MC_500LB_Bomb_Mk1_Short"
-ENUMS.Storage.weapons.nurs.ARAKM70BAP="weapons.nurs.ARAKM70BAP"
-ENUMS.Storage.weapons.missiles.AGM_119="weapons.missiles.AGM_119"
-ENUMS.Storage.weapons.missiles.MMagicII="weapons.missiles.MMagicII"
-ENUMS.Storage.weapons.bombs.AB_500_1_SD_10A="weapons.bombs.AB_500_1_SD_10A"
-ENUMS.Storage.weapons.nurs.HYDRA_70_M282="weapons.nurs.HYDRA_70_M282"
-ENUMS.Storage.weapons.droptanks.DFT_400_GAL_A4E="weapons.droptanks.DFT_400_GAL_A4E"
-ENUMS.Storage.weapons.nurs.HYDRA_70_M257="weapons.nurs.HYDRA_70_M257"
-ENUMS.Storage.weapons.droptanks.AV8BNA_AERO1D="weapons.droptanks.AV8BNA_AERO1D"
-ENUMS.Storage.weapons.containers.US_M10_SMOKE_TANK_BLUE="weapons.containers.{US_M10_SMOKE_TANK_BLUE}"
-ENUMS.Storage.weapons.nurs.ARF8M3HEI="weapons.nurs.ARF8M3HEI"
-ENUMS.Storage.weapons.bombs.RN_28="weapons.bombs.RN-28"
-ENUMS.Storage.weapons.bombs.Squad_30_x_Soldier_7950lb="weapons.bombs.Squad 30 x Soldier [7950lb]"
-ENUMS.Storage.weapons.containers.uaz_469="weapons.containers.uaz-469"
-ENUMS.Storage.weapons.containers.Otokar_Cobra="weapons.containers.Otokar_Cobra"
-ENUMS.Storage.weapons.bombs.APC_BTR_82A_Air_24998lb="weapons.bombs.APC BTR-82A Air [24998lb]"
-ENUMS.Storage.weapons.nurs.HYDRA_70_M274="weapons.nurs.HYDRA_70_M274"
-ENUMS.Storage.weapons.missiles.P_24R="weapons.missiles.P_24R"
-ENUMS.Storage.weapons.nurs.HYDRA_70_MK61="weapons.nurs.HYDRA_70_MK61"
-ENUMS.Storage.weapons.missiles.Igla_1E="weapons.missiles.Igla_1E"
-ENUMS.Storage.weapons.missiles.C_802AK="weapons.missiles.C-802AK"
-ENUMS.Storage.weapons.nurs.C_24="weapons.nurs.C_24"
-ENUMS.Storage.weapons.droptanks.M2KC_08_RPL541="weapons.droptanks.M2KC_08_RPL541"
-ENUMS.Storage.weapons.nurs.C_13="weapons.nurs.C_13"
-ENUMS.Storage.weapons.droptanks.droptank_110_gal="weapons.droptanks.droptank_110_gal"
-ENUMS.Storage.weapons.bombs.Mk_84="weapons.bombs.Mk_84"
-ENUMS.Storage.weapons.missiles.Sea_Eagle="weapons.missiles.Sea_Eagle"
-ENUMS.Storage.weapons.droptanks.PTB_1200_F1="weapons.droptanks.PTB_1200_F1"
-ENUMS.Storage.weapons.nurs.SNEB_TYPE256_H1="weapons.nurs.SNEB_TYPE256_H1"
-ENUMS.Storage.weapons.containers.MATRA_PHIMAT="weapons.containers.MATRA-PHIMAT"
-ENUMS.Storage.weapons.containers.smoke_pod="weapons.containers.smoke_pod"
-ENUMS.Storage.weapons.containers.F_15E_AAQ_14_LANTIRN="weapons.containers.F-15E_AAQ-14_LANTIRN"
-ENUMS.Storage.weapons.containers.EclairM_24="weapons.containers.{EclairM_24}"
-ENUMS.Storage.weapons.bombs.GBU_16="weapons.bombs.GBU_16"
-ENUMS.Storage.weapons.nurs.HYDRA_70_M156="weapons.nurs.HYDRA_70_M156"
-ENUMS.Storage.weapons.missiles.R_60="weapons.missiles.R-60"
-ENUMS.Storage.weapons.containers.zsu_23_4="weapons.containers.zsu-23-4"
-ENUMS.Storage.weapons.missiles.RB75="weapons.missiles.RB75"
-ENUMS.Storage.weapons.missiles.Mistral="weapons.missiles.Mistral"
-ENUMS.Storage.weapons.droptanks.MB339_TT500_L="weapons.droptanks.MB339_TT500_L"
-ENUMS.Storage.weapons.bombs.SAM_SA_13_STRELA_21624lb="weapons.bombs.SAM SA-13 STRELA [21624lb]"
-ENUMS.Storage.weapons.bombs.SAM_Avenger_M1097_Air_7200lb="weapons.bombs.SAM Avenger M1097 Air [7200lb]"
-ENUMS.Storage.weapons.droptanks.Eleven00L_Tank_Empty="weapons.droptanks.1100L Tank Empty"
-ENUMS.Storage.weapons.bombs.AN_M88="weapons.bombs.AN-M88"
-ENUMS.Storage.weapons.missiles.S_25L="weapons.missiles.S_25L"
-ENUMS.Storage.weapons.nurs.British_AP_25LBNo1_3INCHNo1="weapons.nurs.British_AP_25LBNo1_3INCHNo1"
-ENUMS.Storage.weapons.bombs.BDU_50LD="weapons.bombs.BDU_50LD"
-ENUMS.Storage.weapons.bombs.AGM_62="weapons.bombs.AGM_62"
-ENUMS.Storage.weapons.containers.US_M10_SMOKE_TANK_WHITE="weapons.containers.{US_M10_SMOKE_TANK_WHITE}"
-ENUMS.Storage.weapons.missiles.MICA_T="weapons.missiles.MICA_T"
-ENUMS.Storage.weapons.containers.HVAR_rocket="weapons.containers.HVAR_rocket"
-do
-FIFO={
-ClassName="FIFO",
-lid="",
-version="0.0.5",
-counter=0,
-pointer=0,
-stackbypointer={},
-stackbyid={}
-}
-function FIFO:New()
-local self=BASE:Inherit(self,BASE:New())
-self.pointer=0
-self.counter=0
-self.stackbypointer={}
-self.stackbyid={}
-self.uniquecounter=0
-self.lid=string.format("%s (%s) | ","FiFo",self.version)
-self:T(self.lid.."Created.")
-return self
-end
-function FIFO:Clear()
-self:T(self.lid.."Clear")
-self.pointer=0
-self.counter=0
-self.stackbypointer=nil
-self.stackbyid=nil
-self.stackbypointer={}
-self.stackbyid={}
-self.uniquecounter=0
-return self
-end
-function FIFO:Push(Object,UniqueID)
-self:T(self.lid.."Push")
-self:T({Object,UniqueID})
-self.pointer=self.pointer+1
-self.counter=self.counter+1
-local uniID=UniqueID
-if not UniqueID then
-self.uniquecounter=self.uniquecounter+1
-uniID=self.uniquecounter
-end
-self.stackbyid[uniID]={pointer=self.pointer,data=Object,uniqueID=uniID}
-self.stackbypointer[self.pointer]={pointer=self.pointer,data=Object,uniqueID=uniID}
-return self
-end
-function FIFO:Pull()
-self:T(self.lid.."Pull")
-if self.counter==0 then return nil end
-local object=self.stackbypointer[1].data
-self.stackbypointer[1]=nil
-self.counter=self.counter-1
-self:Flatten()
-return object
-end
-function FIFO:PullByPointer(Pointer)
-self:T(self.lid.."PullByPointer "..tostring(Pointer))
-if self.counter==0 then return nil end
-local object=self.stackbypointer[Pointer]
-self.stackbypointer[Pointer]=nil
-if object then self.stackbyid[object.uniqueID]=nil end
-self.counter=self.counter-1
-self:Flatten()
-if object then
-return object.data
-else
-return nil
-end
-end
-function FIFO:ReadByPointer(Pointer)
-self:T(self.lid.."ReadByPointer "..tostring(Pointer))
-if self.counter==0 or not Pointer or not self.stackbypointer[Pointer]then return nil end
-local object=self.stackbypointer[Pointer]
-if object then
-return object.data
-else
-return nil
-end
-end
-function FIFO:ReadByID(UniqueID)
-self:T(self.lid.."ReadByID "..tostring(UniqueID))
-if self.counter==0 or not UniqueID or not self.stackbyid[UniqueID]then return nil end
-local object=self.stackbyid[UniqueID]
-if object then
-return object.data
-else
-return nil
-end
-end
-function FIFO:PullByID(UniqueID)
-self:T(self.lid.."PullByID "..tostring(UniqueID))
-if self.counter==0 then return nil end
-local object=self.stackbyid[UniqueID]
-if object then
-return self:PullByPointer(object.pointer)
-else
-return nil
-end
-end
-function FIFO:Flatten()
-self:T(self.lid.."Flatten")
-local pointerstack={}
-local idstack={}
-local counter=0
-for _ID,_entry in pairs(self.stackbypointer)do
-counter=counter+1
-pointerstack[counter]={pointer=counter,data=_entry.data,uniqueID=_entry.uniqueID}
-end
-for _ID,_entry in pairs(pointerstack)do
-idstack[_entry.uniqueID]={pointer=_entry.pointer,data=_entry.data,uniqueID=_entry.uniqueID}
-end
-self.stackbypointer=nil
-self.stackbypointer=pointerstack
-self.stackbyid=nil
-self.stackbyid=idstack
-self.counter=counter
-self.pointer=counter
-return self
-end
-function FIFO:IsEmpty()
-self:T(self.lid.."IsEmpty")
-return self.counter==0 and true or false
-end
-function FIFO:GetSize()
-self:T(self.lid.."GetSize")
-return self.counter
-end
-function FIFO:Count()
-self:T(self.lid.."Count")
-return self.counter
-end
-function FIFO:IsNotEmpty()
-self:T(self.lid.."IsNotEmpty")
-return not self:IsEmpty()
-end
-function FIFO:GetPointerStack()
-self:T(self.lid.."GetPointerStack")
-return self.stackbypointer
-end
-function FIFO:HasUniqueID(UniqueID)
-self:T(self.lid.."HasUniqueID")
-if self.stackbyid[UniqueID]~=nil then
-return true
-else
-return false
-end
-end
-function FIFO:GetIDStack()
-self:T(self.lid.."GetIDStack")
-return self.stackbyid
-end
-function FIFO:GetIDStackSorted()
-self:T(self.lid.."GetIDStackSorted")
-local stack=self:GetIDStack()
-local idstack={}
-for _id,_entry in pairs(stack)do
-idstack[#idstack+1]=_id
-self:T({"pre",_id})
-end
-local function sortID(a,b)
-return a0 then
-BASE:ScheduleOnce(Delay,PROFILER.Start,0,Duration)
-else
-PROFILER.TstartGame=timer.getTime()
-PROFILER.TstartOS=os.clock()
-world.addEventHandler(PROFILER.eventHandler)
-env.info('############################ Profiler Started ############################')
-if Duration then
-env.info(string.format("- Will be running for %d seconds",Duration))
-else
-env.info(string.format("- Will be stopped when mission ends"))
-end
-env.info(string.format("- Calls per second threshold %.3f/sec",PROFILER.ThreshCPS))
-env.info(string.format("- Total function time threshold %.3f sec",PROFILER.ThreshTtot))
-env.info(string.format("- Output file \"%s\" in your DCS log file folder",PROFILER.getfilename(PROFILER.fileNameSuffix)))
-env.info(string.format("- Output file \"%s\" in CSV format",PROFILER.getfilename("csv")))
-env.info('###############################################################################')
-local duration=Duration or 600
-trigger.action.outText("### Profiler running ###",duration)
-debug.sethook(PROFILER.hook,"cr")
-if Duration then
-PROFILER.Stop(Duration)
-end
-end
-end
-function PROFILER.Stop(Delay)
-if Delay and Delay>0 then
-BASE:ScheduleOnce(Delay,PROFILER.Stop)
-end
-end
-function PROFILER.Stop(Delay)
-if Delay and Delay>0 then
-BASE:ScheduleOnce(Delay,PROFILER.Stop)
-else
-debug.sethook()
-local runTimeGame=timer.getTime()-PROFILER.TstartGame
-local runTimeOS=os.clock()-PROFILER.TstartOS
-PROFILER.showInfo(runTimeGame,runTimeOS)
-end
-end
-function PROFILER.eventHandler:onEvent(event)
-if event.id==world.event.S_EVENT_MISSION_END then
-PROFILER.Stop()
-end
-end
-function PROFILER.hook(event)
-local f=debug.getinfo(2,"f").func
-if event=='call'then
-if PROFILER.Counters[f]==nil then
-PROFILER.Counters[f]=1
-PROFILER.dInfo[f]=debug.getinfo(2,"Sn")
-if PROFILER.fTimeTotal[f]==nil then
-PROFILER.fTimeTotal[f]=0
-end
-else
-PROFILER.Counters[f]=PROFILER.Counters[f]+1
-end
-if PROFILER.fTime[f]==nil then
-PROFILER.fTime[f]=os.clock()
-end
-elseif(event=='return')then
-if PROFILER.fTime[f]~=nil then
-PROFILER.fTimeTotal[f]=PROFILER.fTimeTotal[f]+(os.clock()-PROFILER.fTime[f])
-PROFILER.fTime[f]=nil
-end
-end
-end
-function PROFILER.getData(func)
-local n=PROFILER.dInfo[func]
-if n.what=="C"then
-return n.name,"?","?",PROFILER.fTimeTotal[func]
-end
-return n.name,n.short_src,n.linedefined,PROFILER.fTimeTotal[func]
-end
-function PROFILER._flog(f,txt)
-f:write(txt.."\r\n")
-end
-function PROFILER.showTable(data,f,runTimeGame)
-for i=1,#data do
-local t=data[i]
-local cps=t.count/runTimeGame
-local threshCPS=cps>=PROFILER.ThreshCPS
-local threshTot=t.tm>=PROFILER.ThreshTtot
-if threshCPS and threshTot then
-local text=string.format("%30s: %8d calls %8.1f/sec - Time Total %8.3f sec (%.3f %%) %5.3f sec/call %s line %s",t.func,t.count,cps,t.tm,t.tm/runTimeGame*100,t.tm/t.count,tostring(t.src),tostring(t.line))
-PROFILER._flog(f,text)
-end
-end
-end
-function PROFILER.printCSV(data,runTimeGame)
-local file=PROFILER.getfilename("csv")
-local g=io.open(file,'w')
-local text="Function,Total Calls,Calls per Sec,Total Time,Total in %,Sec per Call,Source File;Line Number,"
-g:write(text.."\r\n")
-for i=1,#data do
-local t=data[i]
-local cps=t.count/runTimeGame
-local txt=string.format("%s,%d,%.1f,%.3f,%.3f,%.3f,%s,%s,",t.func,t.count,cps,t.tm,t.tm/runTimeGame*100,t.tm/t.count,tostring(t.src),tostring(t.line))
-g:write(txt.."\r\n")
-end
-g:close()
-end
-function PROFILER.getfilename(ext)
-local dir=lfs.writedir()..[[Logs\]]
-ext=ext or PROFILER.fileNameSuffix
-local file=dir..PROFILER.fileNamePrefix.."."..ext
-if not UTILS.FileExists(file)then
-return file
-end
-for i=1,999 do
-local file=string.format("%s%s-%03d.%s",dir,PROFILER.fileNamePrefix,i,ext)
-if not UTILS.FileExists(file)then
-return file
-end
-end
-end
-function PROFILER.showInfo(runTimeGame,runTimeOS)
-local file=PROFILER.getfilename(PROFILER.fileNameSuffix)
-local f=io.open(file,'w')
-local Ttot=0
-local Calls=0
-local t={}
-local tcopy=nil
-local tserialize=nil
-local tforgen=nil
-local tpairs=nil
-for func,count in pairs(PROFILER.Counters)do
-local s,src,line,tm=PROFILER.getData(func)
-if PROFILER.logUnknown==true then
-if s==nil then s=""end
-end
-if s~=nil then
-local T=
-{func=s,
-src=src,
-line=line,
-count=count,
-tm=tm,
-}
-if s=="_copy"then
-if tcopy==nil then
-tcopy=T
-else
-tcopy.count=tcopy.count+T.count
-tcopy.tm=tcopy.tm+T.tm
-end
-elseif s=="_Serialize"then
-if tserialize==nil then
-tserialize=T
-else
-tserialize.count=tserialize.count+T.count
-tserialize.tm=tserialize.tm+T.tm
-end
-elseif s=="(for generator)"then
-if tforgen==nil then
-tforgen=T
-else
-tforgen.count=tforgen.count+T.count
-tforgen.tm=tforgen.tm+T.tm
-end
-elseif s=="pairs"then
-if tpairs==nil then
-tpairs=T
-else
-tpairs.count=tpairs.count+T.count
-tpairs.tm=tpairs.tm+T.tm
-end
-else
-table.insert(t,T)
-end
-Ttot=Ttot+tm
-Calls=Calls+count
-end
-end
-if tcopy then
-table.insert(t,tcopy)
-end
-if tserialize then
-table.insert(t,tserialize)
-end
-if tforgen then
-table.insert(t,tforgen)
-end
-if tpairs then
-table.insert(t,tpairs)
-end
-env.info('############################ Profiler Stopped ############################')
-env.info(string.format("* Runtime Game : %s = %d sec",UTILS.SecondsToClock(runTimeGame,true),runTimeGame))
-env.info(string.format("* Runtime Real : %s = %d sec",UTILS.SecondsToClock(runTimeOS,true),runTimeOS))
-env.info(string.format("* Function time : %s = %.1f sec (%.1f percent of runtime game)",UTILS.SecondsToClock(Ttot,true),Ttot,Ttot/runTimeGame*100))
-env.info(string.format("* Total functions : %d",#t))
-env.info(string.format("* Total func calls : %d",Calls))
-env.info(string.format("* Writing to file : \"%s\"",file))
-env.info(string.format("* Writing to file : \"%s\"",PROFILER.getfilename("csv")))
-env.info("##############################################################################")
-table.sort(t,function(a,b)return a.tm>b.tm end)
-PROFILER._flog(f,"")
-PROFILER._flog(f,"************************************************************************************************************************")
-PROFILER._flog(f,"************************************************************************************************************************")
-PROFILER._flog(f,"************************************************************************************************************************")
-PROFILER._flog(f,"")
-PROFILER._flog(f,"-------------------------")
-PROFILER._flog(f,"---- Profiler Report ----")
-PROFILER._flog(f,"-------------------------")
-PROFILER._flog(f,"")
-PROFILER._flog(f,string.format("* Runtime Game : %s = %.1f sec",UTILS.SecondsToClock(runTimeGame,true),runTimeGame))
-PROFILER._flog(f,string.format("* Runtime Real : %s = %.1f sec",UTILS.SecondsToClock(runTimeOS,true),runTimeOS))
-PROFILER._flog(f,string.format("* Function time : %s = %.1f sec (%.1f %% of runtime game)",UTILS.SecondsToClock(Ttot,true),Ttot,Ttot/runTimeGame*100))
-PROFILER._flog(f,"")
-PROFILER._flog(f,string.format("* Total functions = %d",#t))
-PROFILER._flog(f,string.format("* Total func calls = %d",Calls))
-PROFILER._flog(f,"")
-PROFILER._flog(f,string.format("* Calls per second threshold = %.3f/sec",PROFILER.ThreshCPS))
-PROFILER._flog(f,string.format("* Total func time threshold = %.3f sec",PROFILER.ThreshTtot))
-PROFILER._flog(f,"")
-PROFILER._flog(f,"************************************************************************************************************************")
-PROFILER._flog(f,"")
-PROFILER.showTable(t,f,runTimeGame)
-table.sort(t,function(a,b)return a.tm/a.count>b.tm/b.count end)
-PROFILER._flog(f,"")
-PROFILER._flog(f,"************************************************************************************************************************")
-PROFILER._flog(f,"")
-PROFILER._flog(f,"--------------------------------------")
-PROFILER._flog(f,"---- Data Sorted by Time per Call ----")
-PROFILER._flog(f,"--------------------------------------")
-PROFILER._flog(f,"")
-PROFILER.showTable(t,f,runTimeGame)
-table.sort(t,function(a,b)return a.count>b.count end)
-PROFILER._flog(f,"")
-PROFILER._flog(f,"************************************************************************************************************************")
-PROFILER._flog(f,"")
-PROFILER._flog(f,"------------------------------------")
-PROFILER._flog(f,"---- Data Sorted by Total Calls ----")
-PROFILER._flog(f,"------------------------------------")
-PROFILER._flog(f,"")
-PROFILER.showTable(t,f,runTimeGame)
-PROFILER._flog(f,"")
-PROFILER._flog(f,"************************************************************************************************************************")
-PROFILER._flog(f,"************************************************************************************************************************")
-PROFILER._flog(f,"************************************************************************************************************************")
-f:close()
-PROFILER.printCSV(t,runTimeGame)
-end
-SOCKET={
-ClassName="SOCKET",
-verbose=0,
-lid=nil,
-}
-SOCKET.DataType={
-TEXT="moose_text",
-BOMBRESULT="moose_bomb_result",
-STRAFERESULT="moose_strafe_result",
-LSOGRADE="moose_lso_grade",
-TTS="moose_text2speech"
-}
-SOCKET.version="0.3.0"
-function SOCKET:New(Port,Host)
-local self=BASE:Inherit(self,FSM:New())
-package.path=package.path..";.\\LuaSocket\\?.lua;"
-package.cpath=package.cpath..";.\\LuaSocket\\?.dll;"
-self.socket=require("socket")
-self.port=Port or 10042
-self.host=Host or"127.0.0.1"
-self.json=loadfile("Scripts\\JSON.lua")()
-self.UDPSendSocket=self.socket.udp()
-self.UDPSendSocket:settimeout(0)
-return self
-end
-function SOCKET:SetPort(Port)
-self.port=Port or 10042
-end
-function SOCKET:SetHost(Host)
-self.host=Host or"127.0.0.1"
-end
-function SOCKET:SendTable(Table)
-Table.server_name=BASE.ServerName or"Unknown"
-local json=self.json:encode(Table)
-self:T("Json table:")
-self:T(json)
-self.socket.try(self.UDPSendSocket:sendto(json,self.host,self.port))
-return self
-end
-function SOCKET:SendText(Text)
-local message={}
-message.command=SOCKET.DataType.TEXT
-message.text=Text
-self:SendTable(message)
-return self
-end
-function SOCKET:SendTextToSpeech(Text,Provider,Voice,Culture,Gender,Volume)
-Text=Text or"Hello World!"
-local message={}
-message.command=SOCKET.DataType.TTS
-message.text=Text
-message.provider=Provider
-message.voice=Voice
-message.culture=Culture
-message.gender=Gender
-message.volume=Volume
-self:SendTable(message)
-return self
-end
-STTS={
-ClassName="STTS",
-DIRECTORY="",
-SRS_PORT=5002,
-GOOGLE_CREDENTIALS="C:\\Users\\Ciaran\\Downloads\\googletts.json",
-EXECUTABLE="DCS-SR-ExternalAudio.exe"
-}
-STTS.DIRECTORY="D:/DCS/_SRS"
-STTS.SRS_PORT=5002
-STTS.GOOGLE_CREDENTIALS="C:\\Users\\Ciaran\\Downloads\\googletts.json"
-STTS.EXECUTABLE="DCS-SR-ExternalAudio.exe"
-function STTS.uuid()
-local random=math.random
-local template='yxxx-xxxxxxxxxxxx'
-return string.gsub(template,'[xy]',function(c)
-local v=(c=='x')and random(0,0xf)or random(8,0xb)
-return string.format('%x',v)
-end)
-end
-function STTS.round(x,n)
-n=math.pow(10,n or 0)
-x=x*n
-if x>=0 then
-x=math.floor(x+0.5)
-else
-x=math.ceil(x-0.5)
-end
-return x/n
-end
-function STTS.getSpeechTime(length,speed,isGoogle)
-local maxRateRatio=3
-speed=speed or 1.0
-isGoogle=isGoogle or false
-local speedFactor=1.0
-if isGoogle then
-speedFactor=speed
-else
-if speed~=0 then
-speedFactor=math.abs(speed)*(maxRateRatio-1)/10+1
-end
-if speed<0 then
-speedFactor=1/speedFactor
-end
-end
-local wpm=math.ceil(100*speedFactor)
-local cps=math.floor((wpm*5)/60)
-if type(length)=="string"then
-length=string.len(length)
-end
-return length/cps
-end
-function STTS.TextToSpeech(message,freqs,modulations,volume,name,coalition,point,speed,gender,culture,voice,googleTTS)
-if os==nil or io==nil then
-env.info("[DCS-STTS] LUA modules os or io are sanitized. skipping. ")
-return
-end
-speed=speed or 1
-gender=gender or"female"
-culture=culture or""
-voice=voice or""
-coalition=coalition or"0"
-name=name or"ROBOT"
-volume=1
-speed=1
-message=message:gsub("\"","\\\"")
-local cmd=string.format("start /min \"\" /d \"%s\" /b \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -h",STTS.DIRECTORY,STTS.EXECUTABLE,freqs or"305",modulations or"AM",coalition,STTS.SRS_PORT,name)
-if voice~=""then
-cmd=cmd..string.format(" -V \"%s\"",voice)
-else
-if culture~=""then
-cmd=cmd..string.format(" -l %s",culture)
-end
-if gender~=""then
-cmd=cmd..string.format(" -g %s",gender)
-end
-end
-if googleTTS==true then
-cmd=cmd..string.format(" -G \"%s\"",STTS.GOOGLE_CREDENTIALS)
-end
-if speed~=1 then
-cmd=cmd..string.format(" -s %s",speed)
-end
-if volume~=1.0 then
-cmd=cmd..string.format(" -v %s",volume)
-end
-if point and type(point)=="table"and point.x then
-local lat,lon,alt=coord.LOtoLL(point)
-lat=STTS.round(lat,4)
-lon=STTS.round(lon,4)
-alt=math.floor(alt)
-cmd=cmd..string.format(" -L %s -O %s -A %s",lat,lon,alt)
-end
-cmd=cmd..string.format(" -t \"%s\"",message)
-if string.len(cmd)>255 then
-local filename=os.getenv('TMP').."\\DCS_STTS-"..STTS.uuid()..".bat"
-local script=io.open(filename,"w+")
-script:write(cmd.." && exit")
-script:close()
-cmd=string.format("\"%s\"",filename)
-timer.scheduleFunction(os.remove,filename,timer.getTime()+1)
-end
-if string.len(cmd)>255 then
-env.info("[DCS-STTS] - cmd string too long")
-env.info("[DCS-STTS] TextToSpeech Command :\n"..cmd.."\n")
-end
-os.execute(cmd)
-return STTS.getSpeechTime(message,speed,googleTTS)
-end
-function STTS.PlayMP3(pathToMP3,freqs,modulations,volume,name,coalition,point)
-local cmd=string.format("start \"\" /d \"%s\" /b /min \"%s\" -i \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -v %s -h",STTS.DIRECTORY,STTS.EXECUTABLE,pathToMP3,freqs or"305",modulations or"AM",coalition or"0",STTS.SRS_PORT,name or"ROBOT",volume or"1")
-if point and type(point)=="table"and point.x then
-local lat,lon,alt=coord.LOtoLL(point)
-lat=STTS.round(lat,4)
-lon=STTS.round(lon,4)
-alt=math.floor(alt)
-cmd=cmd..string.format(" -L %s -O %s -A %s",lat,lon,alt)
-end
-env.info("[DCS-STTS] MP3/OGG Command :\n"..cmd.."\n")
-os.execute(cmd)
-end
-TEMPLATE={
-ClassName="TEMPLATE",
-Ground={},
-Naval={},
-Airplane={},
-Helicopter={},
-}
-TEMPLATE.TypeGround={
-InfantryAK="Infantry AK",
-ParatrooperAKS74="Paratrooper AKS-74",
-ParatrooperRPG16="Paratrooper RPG-16",
-SoldierWWIIUS="soldier_wwii_us",
-InfantryM248="Infantry M249",
-SoldierM4="Soldier M4",
-}
-TEMPLATE.TypeNaval={
-Ticonderoga="TICONDEROG",
-}
-TEMPLATE.TypeAirplane={
-A10C="A-10C",
-}
-TEMPLATE.TypeHelicopter={
-AH1W="AH-1W",
-}
-function TEMPLATE.GetGround(TypeName,GroupName,CountryID,Vec3,Nunits,Radius)
-TypeName=TypeName or TEMPLATE.TypeGround.SoldierM4
-GroupName=GroupName or"Ground-1"
-CountryID=CountryID or country.id.USA
-Vec3=Vec3 or{x=0,y=0,z=0}
-Nunits=Nunits or 1
-Radius=Radius or 50
-local template=UTILS.DeepCopy(TEMPLATE.GenericGround)
-template.name=GroupName
-template.CountryID=CountryID
-template.CoalitionID=coalition.getCountryCoalition(template.CountryID)
-template.CategoryID=Unit.Category.GROUND_UNIT
-template.units[1].type=TypeName
-template.units[1].name=GroupName.."-1"
-if Vec3 then
-TEMPLATE.SetPositionFromVec3(template,Vec3)
-end
-TEMPLATE.SetUnits(template,Nunits,COORDINATE:NewFromVec3(Vec3),Radius)
-return template
-end
-function TEMPLATE.GetNaval(TypeName,GroupName,CountryID,Vec3,Nunits,Radius)
-TypeName=TypeName or TEMPLATE.TypeNaval.Ticonderoga
-GroupName=GroupName or"Naval-1"
-CountryID=CountryID or country.id.USA
-Vec3=Vec3 or{x=0,y=0,z=0}
-Nunits=Nunits or 1
-Radius=Radius or 500
-local template=UTILS.DeepCopy(TEMPLATE.GenericNaval)
-template.name=GroupName
-template.CountryID=CountryID
-template.CoalitionID=coalition.getCountryCoalition(template.CountryID)
-template.CategoryID=Unit.Category.SHIP
-template.units[1].type=TypeName
-template.units[1].name=GroupName.."-1"
-if Vec3 then
-TEMPLATE.SetPositionFromVec3(template,Vec3)
-end
-TEMPLATE.SetUnits(template,Nunits,COORDINATE:NewFromVec3(Vec3),Radius)
-return template
-end
-function TEMPLATE.GetAirplane(TypeName,GroupName,CountryID,Vec3,Nunits,Radius)
-TypeName=TypeName or TEMPLATE.TypeAirplane.A10C
-GroupName=GroupName or"Airplane-1"
-CountryID=CountryID or country.id.USA
-Vec3=Vec3 or{x=0,y=1000,z=0}
-Nunits=Nunits or 1
-Radius=Radius or 100
-local template=TEMPLATE._GetAircraft(true,TypeName,GroupName,CountryID,Vec3,Nunits,Radius)
-return template
-end
-function TEMPLATE.GetHelicopter(TypeName,GroupName,CountryID,Vec3,Nunits,Radius)
-TypeName=TypeName or TEMPLATE.TypeHelicopter.AH1W
-GroupName=GroupName or"Helicopter-1"
-CountryID=CountryID or country.id.USA
-Vec3=Vec3 or{x=0,y=500,z=0}
-Nunits=Nunits or 1
-Radius=Radius or 100
-Nunits=math.min(Nunits,4)
-local template=TEMPLATE._GetAircraft(false,TypeName,GroupName,CountryID,Vec3,Nunits,Radius)
-return template
-end
-function TEMPLATE._GetAircraft(Airplane,TypeName,GroupName,CountryID,Vec3,Nunits,Radius)
-TypeName=TypeName
-GroupName=GroupName or"Aircraft-1"
-CountryID=CountryID or country.id.USA
-Vec3=Vec3 or{x=0,y=0,z=0}
-Nunits=Nunits or 1
-Radius=Radius or 100
-local template=UTILS.DeepCopy(TEMPLATE.GenericAircraft)
-template.name=GroupName
-template.CountryID=CountryID
-template.CoalitionID=coalition.getCountryCoalition(template.CountryID)
-if Airplane then
-template.CategoryID=Unit.Category.AIRPLANE
-else
-template.CategoryID=Unit.Category.HELICOPTER
-end
-template.units[1].type=TypeName
-template.units[1].name=GroupName.."-1"
-if Vec3 then
-TEMPLATE.SetPositionFromVec3(template,Vec3)
-end
-TEMPLATE.SetUnits(template,Nunits,COORDINATE:NewFromVec3(Vec3),Radius)
-return template
-end
-function TEMPLATE.SetPositionFromVec2(Template,Vec2)
-Template.x=Vec2.x
-Template.y=Vec2.y
-for _,unit in pairs(Template.units)do
-unit.x=Vec2.x
-unit.y=Vec2.y
-end
-Template.route.points[1].x=Vec2.x
-Template.route.points[1].y=Vec2.y
-Template.route.points[1].alt=0
-end
-function TEMPLATE.SetPositionFromVec3(Template,Vec3)
-local Vec2={x=Vec3.x,y=Vec3.z}
-TEMPLATE.SetPositionFromVec2(Template,Vec2)
-end
-function TEMPLATE.SetUnits(Template,N,Coordinate,Radius)
-local units=Template.units
-local unit1=units[1]
-local Vec3=Coordinate:GetVec3()
-unit1.x=Vec3.x
-unit1.y=Vec3.z
-unit1.alt=Vec3.y
-for i=2,N do
-units[i]=UTILS.DeepCopy(unit1)
-end
-for i=1,N do
-local unit=units[i]
-unit.name=string.format("%s-%d",Template.name,i)
-if i>1 then
-local vec2=Coordinate:GetRandomCoordinateInRadius(Radius,5):GetVec2()
-unit.x=vec2.x
-unit.y=vec2.y
-unit.alt=unit1.alt
-end
-end
-end
-function TEMPLATE.SetAirbase(Template,AirBase,ParkingSpots,EngineOn)
-local AirbaseID=AirBase:GetID()
-local point=Template.route.points[1]
-if AirBase:IsAirdrome()then
-point.airdromeId=AirbaseID
-else
-point.helipadId=AirbaseID
-point.linkUnit=AirbaseID
-end
-if EngineOn then
-point.action=COORDINATE.WaypointAction.FromParkingAreaHot
-point.type=COORDINATE.WaypointType.TakeOffParkingHot
-else
-point.action=COORDINATE.WaypointAction.FromParkingArea
-point.type=COORDINATE.WaypointType.TakeOffParking
-end
-for i,unit in ipairs(Template.units)do
-unit.parking_id=ParkingSpots[i]
-end
-end
-function TEMPLATE.AddWaypoint(Template,Waypoint)
-table.insert(Template.route.points,Waypoint)
-end
-TEMPLATE.GenericGround=
-{
-["visible"]=false,
-["tasks"]={},
-["uncontrollable"]=false,
-["task"]="Ground Nothing",
-["route"]=
-{
-["spans"]={},
-["points"]=
-{
-[1]=
-{
-["alt"]=0,
-["type"]="Turning Point",
-["ETA"]=0,
-["alt_type"]="BARO",
-["formation_template"]="",
-["y"]=0,
-["x"]=0,
-["ETA_locked"]=true,
-["speed"]=0,
-["action"]="Off Road",
-["task"]=
-{
-["id"]="ComboTask",
-["params"]=
-{
-["tasks"]=
-{
-},
-},
-},
-["speed_locked"]=true,
-},
-},
-},
-["groupId"]=nil,
-["hidden"]=false,
-["units"]=
-{
-[1]=
-{
-["transportable"]=
-{
-["randomTransportable"]=false,
-},
-["skill"]="Average",
-["type"]="Infantry AK",
-["unitId"]=nil,
-["y"]=0,
-["x"]=0,
-["name"]="Infantry AK-47 Rus",
-["heading"]=0,
-["playerCanDrive"]=false,
-},
-},
-["y"]=0,
-["x"]=0,
-["name"]="Infantry AK-47 Rus",
-["start_time"]=0,
-}
-TEMPLATE.GenericNaval=
-{
-["visible"]=false,
-["tasks"]={},
-["uncontrollable"]=false,
-["route"]=
-{
-["points"]=
-{
-[1]=
-{
-["alt"]=0,
-["type"]="Turning Point",
-["ETA"]=0,
-["alt_type"]="BARO",
-["formation_template"]="",
-["y"]=0,
-["x"]=0,
-["ETA_locked"]=true,
-["speed"]=0,
-["action"]="Turning Point",
-["task"]=
-{
-["id"]="ComboTask",
-["params"]=
-{
-["tasks"]=
-{
-},
-},
-},
-["speed_locked"]=true,
-},
-},
-},
-["groupId"]=nil,
-["hidden"]=false,
-["units"]=
-{
-[1]=
-{
-["transportable"]=
-{
-["randomTransportable"]=false,
-},
-["skill"]="Average",
-["type"]="TICONDEROG",
-["unitId"]=nil,
-["y"]=0,
-["x"]=0,
-["name"]="Naval-1-1",
-["heading"]=0,
-["modulation"]=0,
-["frequency"]=127500000,
-},
-},
-["y"]=0,
-["x"]=0,
-["name"]="Naval-1",
-["start_time"]=0,
-}
-TEMPLATE.GenericAircraft=
-{
-["groupId"]=nil,
-["name"]="Rotary-1",
-["uncontrolled"]=false,
-["hidden"]=false,
-["task"]="Nothing",
-["y"]=0,
-["x"]=0,
-["start_time"]=0,
-["communication"]=true,
-["radioSet"]=false,
-["frequency"]=127.5,
-["modulation"]=0,
-["taskSelected"]=true,
-["tasks"]={},
-["route"]=
-{
-["points"]=
-{
-[1]=
-{
-["y"]=0,
-["x"]=0,
-["alt"]=1000,
-["alt_type"]="BARO",
-["action"]="Turning Point",
-["type"]="Turning Point",
-["airdromeId"]=nil,
-["task"]=
-{
-["id"]="ComboTask",
-["params"]=
-{
-["tasks"]={},
-},
-},
-["ETA"]=0,
-["ETA_locked"]=true,
-["speed"]=100,
-["speed_locked"]=true,
-["formation_template"]="",
-},
-},
-},
-["units"]=
-{
-[1]=
-{
-["name"]="Rotary-1-1",
-["unitId"]=nil,
-["type"]="AH-1W",
-["onboard_num"]="050",
-["livery_id"]="USA X Black",
-["skill"]="High",
-["ropeLength"]=15,
-["speed"]=0,
-["x"]=0,
-["y"]=0,
-["alt"]=10,
-["alt_type"]="BARO",
-["heading"]=0,
-["psi"]=0,
-["parking"]=nil,
-["parking_id"]=nil,
-["payload"]=
-{
-["pylons"]={},
-["fuel"]="1250.0",
-["flare"]=30,
-["chaff"]=30,
-["gun"]=100,
-},
-["callsign"]=
-{
-[1]=2,
-[2]=1,
-[3]=1,
-["name"]="Springfield11",
-},
-},
-},
-}
-SMOKECOLOR=trigger.smokeColor
-FLARECOLOR=trigger.flareColor
-BIGSMOKEPRESET={
-SmallSmokeAndFire=1,
-MediumSmokeAndFire=2,
-LargeSmokeAndFire=3,
-HugeSmokeAndFire=4,
-SmallSmoke=5,
-MediumSmoke=6,
-LargeSmoke=7,
-HugeSmoke=8,
-}
-DCSMAP={
-Caucasus="Caucasus",
-NTTR="Nevada",
-Normandy="Normandy",
-PersianGulf="PersianGulf",
-TheChannel="TheChannel",
-Syria="Syria",
-MarianaIslands="MarianaIslands",
-Falklands="Falklands",
-Sinai="SinaiMap"
-}
-CALLSIGN={
-Aircraft={
-Enfield=1,
-Springfield=2,
-Uzi=3,
-Colt=4,
-Dodge=5,
-Ford=6,
-Chevy=7,
-Pontiac=8,
-Hawg=9,
-Boar=10,
-Pig=11,
-Tusk=12,
-},
-AWACS={
-Overlord=1,
-Magic=2,
-Wizard=3,
-Focus=4,
-Darkstar=5,
-},
-Tanker={
-Texaco=1,
-Arco=2,
-Shell=3,
-Navy_One=4,
-Mauler=5,
-Bloodhound=6,
-},
-JTAC={
-Axeman=1,
-Darknight=2,
-Warrior=3,
-Pointer=4,
-Eyeball=5,
-Moonbeam=6,
-Whiplash=7,
-Finger=8,
-Pinpoint=9,
-Ferret=10,
-Shaba=11,
-Playboy=12,
-Hammer=13,
-Jaguar=14,
-Deathstar=15,
-Anvil=16,
-Firefly=17,
-Mantis=18,
-Badger=19,
-},
-FARP={
-London=1,
-Dallas=2,
-Paris=3,
-Moscow=4,
-Berlin=5,
-Rome=6,
-Madrid=7,
-Warsaw=8,
-Dublin=9,
-Perth=10,
-},
-F16={
-Viper=9,
-Venom=10,
-Lobo=11,
-Cowboy=12,
-Python=13,
-Rattler=14,
-Panther=15,
-Wolf=16,
-Weasel=17,
-Wild=18,
-Ninja=19,
-Jedi=20,
-},
-F18={
-Hornet=9,
-Squid=10,
-Ragin=11,
-Roman=12,
-Sting=13,
-Jury=14,
-Jokey=15,
-Ram=16,
-Hawk=17,
-Devil=18,
-Check=19,
-Snake=20,
-},
-F15E={
-Dude=9,
-Thud=10,
-Gunny=11,
-Trek=12,
-Sniper=13,
-Sled=14,
-Best=15,
-Jazz=16,
-Rage=17,
-Tahoe=18,
-},
-B1B={
-Bone=9,
-Dark=10,
-Vader=11
-},
-B52={
-Buff=9,
-Dump=10,
-Kenworth=11,
-},
-TransportAircraft={
-Heavy=9,
-Trash=10,
-Cargo=11,
-Ascot=12,
-},
-}
-UTILS={
-_MarkID=1
-}
-UTILS.IsInstanceOf=function(object,className)
-if type(className)~='string'then
-if type(className)=='table'and className.IsInstanceOf~=nil then
-className=className.ClassName
-else
-local err_str='className parameter should be a string; parameter received: '..type(className)
-return false
-end
-end
-if type(object)=='table'and object.IsInstanceOf~=nil then
-return object:IsInstanceOf(className)
-else
-local basicDataTypes={'string','number','function','boolean','nil','table'}
-for _,basicDataType in ipairs(basicDataTypes)do
-if className==basicDataType then
-return type(object)==basicDataType
-end
-end
-end
-return false
-end
-UTILS.DeepCopy=function(object)
-local lookup_table={}
-local function _copy(object)
-if type(object)~="table"then
-return object
-elseif lookup_table[object]then
-return lookup_table[object]
-end
-local new_table={}
-lookup_table[object]=new_table
-for index,value in pairs(object)do
-new_table[_copy(index)]=_copy(value)
-end
-return setmetatable(new_table,getmetatable(object))
-end
-local objectreturn=_copy(object)
-return objectreturn
-end
-UTILS.OneLineSerialize=function(tbl)
-lookup_table={}
-local function _Serialize(tbl)
-if type(tbl)=='table'then
-if lookup_table[tbl]then
-return lookup_table[object]
-end
-local tbl_str={}
-lookup_table[tbl]=tbl_str
-tbl_str[#tbl_str+1]='{'
-for ind,val in pairs(tbl)do
-local ind_str={}
-if type(ind)=="number"then
-ind_str[#ind_str+1]='['
-ind_str[#ind_str+1]=tostring(ind)
-ind_str[#ind_str+1]=']='
-else
-ind_str[#ind_str+1]='['
-ind_str[#ind_str+1]=UTILS.BasicSerialize(ind)
-ind_str[#ind_str+1]=']='
-end
-local val_str={}
-if((type(val)=='number')or(type(val)=='boolean'))then
-val_str[#val_str+1]=tostring(val)
-val_str[#val_str+1]=','
-tbl_str[#tbl_str+1]=table.concat(ind_str)
-tbl_str[#tbl_str+1]=table.concat(val_str)
-elseif type(val)=='string'then
-val_str[#val_str+1]=UTILS.BasicSerialize(val)
-val_str[#val_str+1]=','
-tbl_str[#tbl_str+1]=table.concat(ind_str)
-tbl_str[#tbl_str+1]=table.concat(val_str)
-elseif type(val)=='nil'then
-val_str[#val_str+1]='nil,'
-tbl_str[#tbl_str+1]=table.concat(ind_str)
-tbl_str[#tbl_str+1]=table.concat(val_str)
-elseif type(val)=='table'then
-if ind=="__index"then
-else
-val_str[#val_str+1]=_Serialize(val)
-val_str[#val_str+1]=','
-tbl_str[#tbl_str+1]=table.concat(ind_str)
-tbl_str[#tbl_str+1]=table.concat(val_str)
-end
-elseif type(val)=='function'then
-tbl_str[#tbl_str+1]="f() "..tostring(ind)
-tbl_str[#tbl_str+1]=','
-else
-env.info('unable to serialize value type '..UTILS.BasicSerialize(type(val))..' at index '..tostring(ind))
-env.info(debug.traceback())
-end
-end
-tbl_str[#tbl_str+1]='}'
-return table.concat(tbl_str)
-else
-return tostring(tbl)
-end
-end
-local objectreturn=_Serialize(tbl)
-return objectreturn
-end
-function UTILS._OneLineSerialize(tbl)
-if type(tbl)=='table'then
-local tbl_str={}
-tbl_str[#tbl_str+1]='{ '
-for ind,val in pairs(tbl)do
-if type(ind)=="number"then
-tbl_str[#tbl_str+1]='['
-tbl_str[#tbl_str+1]=tostring(ind)
-tbl_str[#tbl_str+1]='] = '
-else
-tbl_str[#tbl_str+1]='['
-tbl_str[#tbl_str+1]=UTILS.BasicSerialize(ind)
-tbl_str[#tbl_str+1]='] = '
-end
-if((type(val)=='number')or(type(val)=='boolean'))then
-tbl_str[#tbl_str+1]=tostring(val)
-tbl_str[#tbl_str+1]=', '
-elseif type(val)=='string'then
-tbl_str[#tbl_str+1]=UTILS.BasicSerialize(val)
-tbl_str[#tbl_str+1]=', '
-elseif type(val)=='nil'then
-tbl_str[#tbl_str+1]='nil, '
-elseif type(val)=='table'then
-else
-end
-end
-tbl_str[#tbl_str+1]='}'
-return table.concat(tbl_str)
-else
-return UTILS.BasicSerialize(tbl)
-end
-end
-UTILS.BasicSerialize=function(s)
-if s==nil then
-return"\"\""
-else
-if((type(s)=='number')or(type(s)=='boolean')or(type(s)=='function')or(type(s)=='userdata'))then
-return tostring(s)
-elseif type(s)=="table"then
-return UTILS._OneLineSerialize(s)
-elseif type(s)=='string'then
-s=string.format('(%s)',s)
-return s
-end
-end
-end
-function UTILS.PrintTableToLog(table,indent)
-local text="\n"
-if not table then
-env.warning("No table passed!")
-return nil
-end
-if not indent then indent=0 end
-for k,v in pairs(table)do
-if string.find(k," ")then k='"'..k..'"'end
-if type(v)=="table"then
-env.info(string.rep(" ",indent)..tostring(k).." = {")
-text=text..string.rep(" ",indent)..tostring(k).." = {\n"
-text=text..tostring(UTILS.PrintTableToLog(v,indent+1)).."\n"
-env.info(string.rep(" ",indent).."},")
-text=text..string.rep(" ",indent).."},\n"
-else
-local value
-if tostring(v)=="true"or tostring(v)=="false"or tonumber(v)~=nil then
-value=v
-else
-value='"'..tostring(v)..'"'
-end
-env.info(string.rep(" ",indent)..tostring(k).." = "..tostring(value)..",\n")
-text=text..string.rep(" ",indent)..tostring(k).." = "..tostring(value)..",\n"
-end
-end
-return text
-end
-function UTILS.TableShow(tbl,loc,indent,tableshow_tbls)
-tableshow_tbls=tableshow_tbls or{}
-loc=loc or""
-indent=indent or""
-if type(tbl)=='table'then
-tableshow_tbls[tbl]=loc
-local tbl_str={}
-tbl_str[#tbl_str+1]=indent..'{\n'
-for ind,val in pairs(tbl)do
-if type(ind)=="number"then
-tbl_str[#tbl_str+1]=indent
-tbl_str[#tbl_str+1]=loc..'['
-tbl_str[#tbl_str+1]=tostring(ind)
-tbl_str[#tbl_str+1]='] = '
-else
-tbl_str[#tbl_str+1]=indent
-tbl_str[#tbl_str+1]=loc..'['
-tbl_str[#tbl_str+1]=UTILS.BasicSerialize(ind)
-tbl_str[#tbl_str+1]='] = '
-end
-if((type(val)=='number')or(type(val)=='boolean'))then
-tbl_str[#tbl_str+1]=tostring(val)
-tbl_str[#tbl_str+1]=',\n'
-elseif type(val)=='string'then
-tbl_str[#tbl_str+1]=UTILS.BasicSerialize(val)
-tbl_str[#tbl_str+1]=',\n'
-elseif type(val)=='nil'then
-tbl_str[#tbl_str+1]='nil,\n'
-elseif type(val)=='table'then
-if tableshow_tbls[val]then
-tbl_str[#tbl_str+1]=tostring(val)..' already defined: '..tableshow_tbls[val]..',\n'
-else
-tableshow_tbls[val]=loc..'['..UTILS.BasicSerialize(ind)..']'
-tbl_str[#tbl_str+1]=tostring(val)..' '
-tbl_str[#tbl_str+1]=UTILS.TableShow(val,loc..'['..UTILS.BasicSerialize(ind)..']',indent..' ',tableshow_tbls)
-tbl_str[#tbl_str+1]=',\n'
-end
-elseif type(val)=='function'then
-if debug and debug.getinfo then
-local fcnname=tostring(val)
-local info=debug.getinfo(val,"S")
-if info.what=="C"then
-tbl_str[#tbl_str+1]=string.format('%q',fcnname..', C function')..',\n'
-else
-if(string.sub(info.source,1,2)==[[./]])then
-tbl_str[#tbl_str+1]=string.format('%q',fcnname..', defined in ('..info.linedefined..'-'..info.lastlinedefined..')'..info.source)..',\n'
-else
-tbl_str[#tbl_str+1]=string.format('%q',fcnname..', defined in ('..info.linedefined..'-'..info.lastlinedefined..')')..',\n'
-end
-end
-else
-tbl_str[#tbl_str+1]='a function,\n'
-end
-else
-tbl_str[#tbl_str+1]='unable to serialize value type '..UTILS.BasicSerialize(type(val))..' at index '..tostring(ind)
-end
-end
-tbl_str[#tbl_str+1]=indent..'}'
-return table.concat(tbl_str)
-end
-end
-function UTILS.Gdump(fname)
-if lfs and io then
-local fdir=lfs.writedir()..[[Logs\]]..fname
-local f=io.open(fdir,'w')
-f:write(UTILS.TableShow(_G))
-f:close()
-env.info(string.format('Wrote debug data to $1',fdir))
-else
-env.error("WARNING: lfs and/or io not de-sanitized - cannot dump _G!")
-end
-end
-function UTILS.DoString(s)
-local f,err=loadstring(s)
-if f then
-return true,f()
-else
-return false,err
-end
-end
-UTILS.ToDegree=function(angle)
-return angle*180/math.pi
-end
-UTILS.ToRadian=function(angle)
-return angle*math.pi/180
-end
-UTILS.MetersToNM=function(meters)
-return meters/1852
-end
-UTILS.KiloMetersToNM=function(kilometers)
-return kilometers/1852*1000
-end
-UTILS.MetersToSM=function(meters)
-return meters/1609.34
-end
-UTILS.KiloMetersToSM=function(kilometers)
-return kilometers/1609.34*1000
-end
-UTILS.MetersToFeet=function(meters)
-return meters/0.3048
-end
-UTILS.KiloMetersToFeet=function(kilometers)
-return kilometers/0.3048*1000
-end
-UTILS.NMToMeters=function(NM)
-return NM*1852
-end
-UTILS.NMToKiloMeters=function(NM)
-return NM*1852/1000
-end
-UTILS.FeetToMeters=function(feet)
-return feet*0.3048
-end
-UTILS.KnotsToKmph=function(knots)
-return knots*1.852
-end
-UTILS.KmphToKnots=function(knots)
-return knots/1.852
-end
-UTILS.KmphToMps=function(kmph)
-return kmph/3.6
-end
-UTILS.MpsToKmph=function(mps)
-return mps*3.6
-end
-UTILS.MiphToMps=function(miph)
-return miph*0.44704
-end
-UTILS.MpsToMiph=function(mps)
-return mps/0.44704
-end
-UTILS.MpsToKnots=function(mps)
-return mps*1.94384
-end
-UTILS.KnotsToMps=function(knots)
-if type(knots)=="number"then
-return knots/1.94384
-else
-return 0
-end
-end
-UTILS.CelsiusToFahrenheit=function(Celcius)
-return Celcius*9/5+32
-end
-UTILS.hPa2inHg=function(hPa)
-return hPa*0.0295299830714
-end
-UTILS.IasToTas=function(ias,altitude,oatcorr)
-oatcorr=oatcorr or 0.017
-local tas=ias+(ias*oatcorr*UTILS.MetersToFeet(altitude)/1000)
-return tas
-end
-UTILS.TasToIas=function(tas,altitude,oatcorr)
-oatcorr=oatcorr or 0.017
-local ias=tas/(1+oatcorr*UTILS.MetersToFeet(altitude)/1000)
-return ias
-end
-UTILS.KnotsToAltKIAS=function(knots,altitude)
-return(knots*0.018*(altitude/1000))+knots
-end
-UTILS.hPa2mmHg=function(hPa)
-return hPa*0.7500615613030
-end
-UTILS.kg2lbs=function(kg)
-return kg*2.20462
-end
-UTILS.tostringLL=function(lat,lon,acc,DMS)
-local latHemi,lonHemi
-if lat>0 then
-latHemi='N'
-else
-latHemi='S'
-end
-if lon>0 then
-lonHemi='E'
-else
-lonHemi='W'
-end
-lat=math.abs(lat)
-lon=math.abs(lon)
-local latDeg=math.floor(lat)
-local latMin=(lat-latDeg)*60
-local lonDeg=math.floor(lon)
-local lonMin=(lon-lonDeg)*60
-if DMS then
-local oldLatMin=latMin
-latMin=math.floor(latMin)
-local latSec=UTILS.Round((oldLatMin-latMin)*60,acc)
-local oldLonMin=lonMin
-lonMin=math.floor(lonMin)
-local lonSec=UTILS.Round((oldLonMin-lonMin)*60,acc)
-if latSec==60 then
-latSec=0
-latMin=latMin+1
-end
-if lonSec==60 then
-lonSec=0
-lonMin=lonMin+1
-end
-local secFrmtStr
-secFrmtStr='%02d'
-if acc<=0 then
-secFrmtStr='%02d'
-else
-local width=3+acc
-secFrmtStr='%0'..width..'.'..acc..'f'
-end
-return string.format('%03d°',latDeg)..string.format('%02d',latMin)..'\''..string.format(secFrmtStr,latSec)..'"'..latHemi..' '
-..string.format('%03d°',lonDeg)..string.format('%02d',lonMin)..'\''..string.format(secFrmtStr,lonSec)..'"'..lonHemi
-else
-latMin=UTILS.Round(latMin,acc)
-lonMin=UTILS.Round(lonMin,acc)
-if latMin==60 then
-latMin=0
-latDeg=latDeg+1
-end
-if lonMin==60 then
-lonMin=0
-lonDeg=lonDeg+1
-end
-local minFrmtStr
-if acc<=0 then
-minFrmtStr='%02d'
-else
-local width=3+acc
-minFrmtStr='%0'..width..'.'..acc..'f'
-end
-return string.format('%03d°',latDeg)..' '..string.format(minFrmtStr,latMin)..'\''..latHemi..' '
-..string.format('%03d°',lonDeg)..' '..string.format(minFrmtStr,lonMin)..'\''..lonHemi
-end
-end
-UTILS.tostringMGRS=function(MGRS,acc)
-if acc<=0 then
-return MGRS.UTMZone..' '..MGRS.MGRSDigraph
-else
-if acc>5 then acc=5 end
-local Easting=tostring(MGRS.Easting)
-local Northing=tostring(MGRS.Northing)
-local nE=5-string.len(Easting)
-local nN=5-string.len(Northing)
-for i=1,nE do Easting="0"..Easting end
-for i=1,nN do Northing="0"..Northing end
-return string.format("%s %s %s %s",MGRS.UTMZone,MGRS.MGRSDigraph,string.sub(Easting,1,acc),string.sub(Northing,1,acc))
-end
-end
-function UTILS.Round(num,idp)
-local mult=10^(idp or 0)
-return math.floor(num*mult+0.5)/mult
-end
-function UTILS.DoString(s)
-local f,err=loadstring(s)
-if f then
-return true,f()
-else
-return false,err
-end
-end
-function UTILS.spairs(t,order)
-local keys={}
-for k in pairs(t)do keys[#keys+1]=k end
-if order then
-table.sort(keys,function(a,b)return order(t,a,b)end)
-else
-table.sort(keys)
-end
-local i=0
-return function()
-i=i+1
-if keys[i]then
-return keys[i],t[keys[i]]
-end
-end
-end
-function UTILS.kpairs(t,getkey,order)
-local keys={}
-local keyso={}
-for k,o in pairs(t)do keys[#keys+1]=k keyso[#keyso+1]=getkey(o)end
-if order then
-table.sort(keys,function(a,b)return order(t,a,b)end)
-else
-table.sort(keys)
-end
-local i=0
-return function()
-i=i+1
-if keys[i]then
-return keyso[i],t[keys[i]]
-end
-end
-end
-function UTILS.rpairs(t)
-local keys={}
-for k in pairs(t)do keys[#keys+1]=k end
-local random={}
-local j=#keys
-for i=1,j do
-local k=math.random(1,#keys)
-random[i]=keys[k]
-table.remove(keys,k)
-end
-local i=0
-return function()
-i=i+1
-if random[i]then
-return random[i],t[random[i]]
-end
-end
-end
-function UTILS.GetMarkID()
-UTILS._MarkID=UTILS._MarkID+1
-return UTILS._MarkID
-end
-function UTILS.RemoveMark(MarkID,Delay)
-if Delay and Delay>0 then
-TIMER:New(UTILS.RemoveMark,MarkID):Start(Delay)
-else
-if MarkID then
-trigger.action.removeMark(MarkID)
-end
-end
-end
-function UTILS.IsInRadius(InVec2,Vec2,Radius)
-local InRadius=((InVec2.x-Vec2.x)^2+(InVec2.y-Vec2.y)^2)^0.5<=Radius
-return InRadius
-end
-function UTILS.IsInSphere(InVec3,Vec3,Radius)
-local InSphere=((InVec3.x-Vec3.x)^2+(InVec3.y-Vec3.y)^2+(InVec3.z-Vec3.z)^2)^0.5<=Radius
-return InSphere
-end
-function UTILS.BeaufortScale(speed)
-local bn=nil
-local bd=nil
-if speed<0.51 then
-bn=0
-bd="Calm"
-elseif speed<2.06 then
-bn=1
-bd="Light Air"
-elseif speed<3.60 then
-bn=2
-bd="Light Breeze"
-elseif speed<5.66 then
-bn=3
-bd="Gentle Breeze"
-elseif speed<8.23 then
-bn=4
-bd="Moderate Breeze"
-elseif speed<11.32 then
-bn=5
-bd="Fresh Breeze"
-elseif speed<14.40 then
-bn=6
-bd="Strong Breeze"
-elseif speed<17.49 then
-bn=7
-bd="Moderate Gale"
-elseif speed<21.09 then
-bn=8
-bd="Fresh Gale"
-elseif speed<24.69 then
-bn=9
-bd="Strong Gale"
-elseif speed<28.81 then
-bn=10
-bd="Storm"
-elseif speed<32.92 then
-bn=11
-bd="Violent Storm"
-else
-bn=12
-bd="Hurricane"
-end
-return bn,bd
-end
-function UTILS.Split(str,sep)
-local result={}
-local regex=("([^%s]+)"):format(sep)
-for each in str:gmatch(regex)do
-table.insert(result,each)
-end
-return result
-end
-function UTILS.GetCharacters(str)
-local chars={}
-for i=1,#str do
-local c=str:sub(i,i)
-table.insert(chars,c)
-end
-return chars
-end
-function UTILS.SecondsToClock(seconds,short)
-if seconds==nil then
-return nil
-end
-local seconds=tonumber(seconds)
-local _seconds=seconds%(60*60*24)
-if seconds<0 then
-return nil
-else
-local hours=string.format("%02.f",math.floor(_seconds/3600))
-local mins=string.format("%02.f",math.floor(_seconds/60-(hours*60)))
-local secs=string.format("%02.f",math.floor(_seconds-hours*3600-mins*60))
-local days=string.format("%d",seconds/(60*60*24))
-local clock=hours..":"..mins..":"..secs.."+"..days
-if short then
-if hours=="00"then
-clock=hours..":"..mins..":"..secs
-else
-clock=hours..":"..mins..":"..secs
-end
-end
-return clock
-end
-end
-function UTILS.SecondsOfToday()
-local time=timer.getAbsTime()
-local clock=UTILS.SecondsToClock(time,true)
-return UTILS.ClockToSeconds(clock)
-end
-function UTILS.SecondsToMidnight()
-return 24*60*60-UTILS.SecondsOfToday()
-end
-function UTILS.ClockToSeconds(clock)
-if clock==nil then
-return nil
-end
-local seconds=0
-local dsplit=UTILS.Split(clock,"+")
-if#dsplit>1 then
-seconds=seconds+tonumber(dsplit[2])*60*60*24
-end
-local tsplit=UTILS.Split(dsplit[1],":")
-local i=1
-for _,time in ipairs(tsplit)do
-if i==1 then
-seconds=seconds+tonumber(time)*60*60
-elseif i==2 then
-seconds=seconds+tonumber(time)*60
-elseif i==3 then
-seconds=seconds+tonumber(time)
-end
-i=i+1
-end
-return seconds
-end
-function UTILS.DisplayMissionTime(duration)
-duration=duration or 5
-local Tnow=timer.getAbsTime()
-local mission_time=Tnow-timer.getTime0()
-local mission_time_minutes=mission_time/60
-local mission_time_seconds=mission_time%60
-local local_time=UTILS.SecondsToClock(Tnow)
-local text=string.format("Time: %s - %02d:%02d",local_time,mission_time_minutes,mission_time_seconds)
-MESSAGE:New(text,duration):ToAll()
-end
-function UTILS.ReplaceIllegalCharacters(Text,ReplaceBy)
-ReplaceBy=ReplaceBy or"_"
-local text=Text:gsub("[<>|/?*:\\]",ReplaceBy)
-return text
-end
-function UTILS.RandomGaussian(x0,sigma,xmin,xmax,imax)
-sigma=sigma or 10
-imax=imax or 100
-local r
-local gotit=false
-local i=0
-while not gotit do
-local x1=math.random()
-local x2=math.random()
-r=math.sqrt(-2*sigma*sigma*math.log(x1))*math.cos(2*math.pi*x2)+x0
-i=i+1
-if(r>=xmin and r<=xmax)or i>imax then
-gotit=true
-end
-end
-return r
-end
-function UTILS.Randomize(value,fac,lower,upper)
-local min
-if lower then
-min=math.max(value-value*fac,lower)
-else
-min=value-value*fac
-end
-local max
-if upper then
-max=math.min(value+value*fac,upper)
-else
-max=value+value*fac
-end
-local r=math.random(min,max)
-return r
-end
-function UTILS.VecDot(a,b)
-return a.x*b.x+a.y*b.y+a.z*b.z
-end
-function UTILS.Vec2Dot(a,b)
-return a.x*b.x+a.y*b.y
-end
-function UTILS.VecNorm(a)
-return math.sqrt(UTILS.VecDot(a,a))
-end
-function UTILS.Vec2Norm(a)
-return math.sqrt(UTILS.Vec2Dot(a,a))
-end
-function UTILS.VecDist2D(a,b)
-local d=math.huge
-if(not a)or(not b)then return d end
-local c={x=b.x-a.x,y=b.y-a.y}
-d=math.sqrt(c.x*c.x+c.y*c.y)
-return d
-end
-function UTILS.VecDist3D(a,b)
-local d=math.huge
-if(not a)or(not b)then return d end
-local c={x=b.x-a.x,y=b.y-a.y,z=b.z-a.z}
-d=math.sqrt(UTILS.VecDot(c,c))
-return d
-end
-function UTILS.VecCross(a,b)
-return{x=a.y*b.z-a.z*b.y,y=a.z*b.x-a.x*b.z,z=a.x*b.y-a.y*b.x}
-end
-function UTILS.VecSubstract(a,b)
-return{x=a.x-b.x,y=a.y-b.y,z=a.z-b.z}
-end
-function UTILS.VecSubtract(a,b)
-return UTILS.VecSubstract(a,b)
-end
-function UTILS.Vec2Substract(a,b)
-return{x=a.x-b.x,y=a.y-b.y}
-end
-function UTILS.Vec2Subtract(a,b)
-return UTILS.Vec2Substract(a,b)
-end
-function UTILS.VecAdd(a,b)
-return{x=a.x+b.x,y=a.y+b.y,z=a.z+b.z}
-end
-function UTILS.Vec2Add(a,b)
-return{x=a.x+b.x,y=a.y+b.y}
-end
-function UTILS.VecAngle(a,b)
-local cosalpha=UTILS.VecDot(a,b)/(UTILS.VecNorm(a)*UTILS.VecNorm(b))
-local alpha=0
-if cosalpha>=0.9999999999 then
-alpha=0
-elseif cosalpha<=-0.999999999 then
-alpha=math.pi
-else
-alpha=math.acos(cosalpha)
-end
-return math.deg(alpha)
-end
-function UTILS.VecHdg(a)
-local h=math.deg(math.atan2(a.z,a.x))
-if h<0 then
-h=h+360
-end
-return h
-end
-function UTILS.Vec2Hdg(a)
-local h=math.deg(math.atan2(a.y,a.x))
-if h<0 then
-h=h+360
-end
-return h
-end
-function UTILS.HdgDiff(h1,h2)
-local alpha=math.rad(tonumber(h1))
-local beta=math.rad(tonumber(h2))
-local v1={x=math.cos(alpha),y=0,z=math.sin(alpha)}
-local v2={x=math.cos(beta),y=0,z=math.sin(beta)}
-local delta=UTILS.VecAngle(v1,v2)
-return math.abs(delta)
-end
-function UTILS.HdgTo(a,b)
-local dz=b.z-a.z
-local dx=b.x-a.x
-local heading=math.deg(math.atan2(dz,dx))
-if heading<0 then
-heading=360+heading
-end
-return heading
-end
-function UTILS.VecTranslate(a,distance,angle)
-local SX=a.x
-local SY=a.z
-local Radians=math.rad(angle or 0)
-local TX=distance*math.cos(Radians)+SX
-local TY=distance*math.sin(Radians)+SY
-return{x=TX,y=a.y,z=TY}
-end
-function UTILS.Vec2Translate(a,distance,angle)
-local SX=a.x
-local SY=a.y
-local Radians=math.rad(angle or 0)
-local TX=distance*math.cos(Radians)+SX
-local TY=distance*math.sin(Radians)+SY
-return{x=TX,y=TY}
-end
-function UTILS.Rotate2D(a,angle)
-local phi=math.rad(angle)
-local x=a.z
-local y=a.x
-local Z=x*math.cos(phi)-y*math.sin(phi)
-local X=x*math.sin(phi)+y*math.cos(phi)
-local Y=a.y
-local A={x=X,y=Y,z=Z}
-return A
-end
-function UTILS.Vec2Rotate2D(a,angle)
-local phi=math.rad(angle)
-local x=a.x
-local y=a.y
-local X=x*math.cos(phi)-y*math.sin(phi)
-local Y=x*math.sin(phi)+y*math.cos(phi)
-local A={x=X,y=Y}
-return A
-end
-function UTILS.TACANToFrequency(TACANChannel,TACANMode)
-if type(TACANChannel)~="number"then
-return nil
-end
-if TACANMode~="X"and TACANMode~="Y"then
-return nil
-end
-local A=1151
-local B=64
-if TACANChannel<64 then
-B=1
-end
-if TACANMode=='Y'then
-A=1025
-if TACANChannel<64 then
-A=1088
-end
-else
-if TACANChannel<64 then
-A=962
-end
-end
-return(A+TACANChannel-B)*1000000
-end
-function UTILS.GetDCSMap()
-return env.mission.theatre
-end
-function UTILS.GetDCSMissionDate()
-local year=tostring(env.mission.date.Year)
-local month=tostring(env.mission.date.Month)
-local day=tostring(env.mission.date.Day)
-return string.format("%s/%s/%s",year,month,day),tonumber(year),tonumber(month),tonumber(day)
-end
-function UTILS.GetMissionDay(Time)
-Time=Time or timer.getAbsTime()
-local clock=UTILS.SecondsToClock(Time,false)
-local x=tonumber(UTILS.Split(clock,"+")[2])
-return x
-end
-function UTILS.GetMissionDayOfYear(Time)
-local Date,Year,Month,Day=UTILS.GetDCSMissionDate()
-local d=UTILS.GetMissionDay(Time)
-return UTILS.GetDayOfYear(Year,Month,Day)+d
-end
-function UTILS.GetMagneticDeclination(map)
-map=map or UTILS.GetDCSMap()
-local declination=0
-if map==DCSMAP.Caucasus then
-declination=6
-elseif map==DCSMAP.NTTR then
-declination=12
-elseif map==DCSMAP.Normandy then
-declination=-10
-elseif map==DCSMAP.PersianGulf then
-declination=2
-elseif map==DCSMAP.TheChannel then
-declination=-10
-elseif map==DCSMAP.Syria then
-declination=5
-elseif map==DCSMAP.MarianaIslands then
-declination=2
-elseif map==DCSMAP.Falklands then
-declination=12
-elseif map==DCSMAP.Sinai then
-declination=4.8
-else
-declination=0
-end
-return declination
-end
-function UTILS.FileExists(file)
-if io then
-local f=io.open(file,"r")
-if f~=nil then
-io.close(f)
-return true
-else
-return false
-end
-else
-return nil
-end
-end
-function UTILS.CheckMemory(output)
-local time=timer.getTime()
-local clock=UTILS.SecondsToClock(time)
-local mem=collectgarbage("count")
-if output then
-env.info(string.format("T=%s Memory usage %d kByte = %.2f MByte",clock,mem,mem/1024))
-end
-return mem
-end
-function UTILS.GetCoalitionName(Coalition)
-if Coalition then
-if Coalition==coalition.side.BLUE then
-return"Blue"
-elseif Coalition==coalition.side.RED then
-return"Red"
-elseif Coalition==coalition.side.NEUTRAL then
-return"Neutral"
-else
-return"Unknown"
-end
-else
-return"Unknown"
-end
-end
-function UTILS.GetCoalitionEnemy(Coalition,Neutral)
-local Coalitions={}
-if Coalition then
-if Coalition==coalition.side.RED then
-Coalitions={coalition.side.BLUE}
-elseif Coalition==coalition.side.BLUE then
-Coalitions={coalition.side.RED}
-elseif Coalition==coalition.side.NEUTRAL then
-Coalitions={coalition.side.RED,coalition.side.BLUE}
-end
-end
-if Neutral then
-table.insert(Coalitions,coalition.side.NEUTRAL)
-end
-return Coalitions
-end
-function UTILS.GetModulationName(Modulation)
-if Modulation then
-if Modulation==0 then
-return"AM"
-elseif Modulation==1 then
-return"FM"
-else
-return"Unknown"
-end
-else
-return"Unknown"
-end
-end
-function UTILS.GetReportingName(Typename)
-local typename=string.lower(Typename)
-for name,value in pairs(ENUMS.ReportingName.NATO)do
-local svalue=string.lower(value)
-if string.find(typename,svalue,1,true)then
-return name
-end
-end
-return"Bogey"
-end
-function UTILS.GetCallsignName(Callsign)
-for name,value in pairs(CALLSIGN.Aircraft)do
-if value==Callsign then
-return name
-end
-end
-for name,value in pairs(CALLSIGN.AWACS)do
-if value==Callsign then
-return name
-end
-end
-for name,value in pairs(CALLSIGN.JTAC)do
-if value==Callsign then
-return name
-end
-end
-for name,value in pairs(CALLSIGN.Tanker)do
-if value==Callsign then
-return name
-end
-end
-for name,value in pairs(CALLSIGN.B1B)do
-if value==Callsign then
-return name
-end
-end
-for name,value in pairs(CALLSIGN.B52)do
-if value==Callsign then
-return name
-end
-end
-for name,value in pairs(CALLSIGN.F15E)do
-if value==Callsign then
-return name
-end
-end
-for name,value in pairs(CALLSIGN.F16)do
-if value==Callsign then
-return name
-end
-end
-for name,value in pairs(CALLSIGN.F18)do
-if value==Callsign then
-return name
-end
-end
-for name,value in pairs(CALLSIGN.FARP)do
-if value==Callsign then
-return name
-end
-end
-for name,value in pairs(CALLSIGN.TransportAircraft)do
-if value==Callsign then
-return name
-end
-end
-return"Ghostrider"
-end
-function UTILS.GMTToLocalTimeDifference()
-local theatre=UTILS.GetDCSMap()
-if theatre==DCSMAP.Caucasus then
-return 4
-elseif theatre==DCSMAP.PersianGulf then
-return 4
-elseif theatre==DCSMAP.NTTR then
-return-8
-elseif theatre==DCSMAP.Normandy then
-return 0
-elseif theatre==DCSMAP.TheChannel then
-return 2
-elseif theatre==DCSMAP.Syria then
-return 3
-elseif theatre==DCSMAP.MarianaIslands then
-return 10
-elseif theatre==DCSMAP.Falklands then
-return-3
-elseif theatre==DCSMAP.Sinai then
-return 2
-else
-BASE:E(string.format("ERROR: Unknown Map %s in UTILS.GMTToLocal function. Returning 0",tostring(theatre)))
-return 0
-end
-end
-function UTILS.GetDayOfYear(Year,Month,Day)
-local floor=math.floor
-local n1=floor(275*Month/9)
-local n2=floor((Month+9)/12)
-local n3=(1+floor((Year-4*floor(Year/4)+2)/3))
-return n1-(n2*n3)+Day-30
-end
-function UTILS.GetSunRiseAndSet(DayOfYear,Latitude,Longitude,Rising,Tlocal)
-local zenith=90.83
-local latitude=Latitude
-local longitude=Longitude
-local rising=Rising
-local n=DayOfYear
-Tlocal=Tlocal or 0
-local rad=math.rad
-local deg=math.deg
-local floor=math.floor
-local frac=function(n)return n-floor(n)end
-local cos=function(d)return math.cos(rad(d))end
-local acos=function(d)return deg(math.acos(d))end
-local sin=function(d)return math.sin(rad(d))end
-local asin=function(d)return deg(math.asin(d))end
-local tan=function(d)return math.tan(rad(d))end
-local atan=function(d)return deg(math.atan(d))end
-local function fit_into_range(val,min,max)
-local range=max-min
-local count
-if val=max then
-count=floor((val-max)/range)+1
-return val-count*range
-else
-return val
-end
-end
-local lng_hour=longitude/15
-local t
-if rising then
-t=n+((6-lng_hour)/24)
-else
-t=n+((18-lng_hour)/24)
-end
-local M=(0.9856*t)-3.289
-local L=fit_into_range(M+(1.916*sin(M))+(0.020*sin(2*M))+282.634,0,360)
-local RA=fit_into_range(atan(0.91764*tan(L)),0,360)
-local Lquadrant=floor(L/90)*90
-local RAquadrant=floor(RA/90)*90
-RA=RA+Lquadrant-RAquadrant
-RA=RA/15
-local sinDec=0.39782*sin(L)
-local cosDec=cos(asin(sinDec))
-local cosH=(cos(zenith)-(sinDec*sin(latitude)))/(cosDec*cos(latitude))
-if rising and cosH>1 then
-return"N/R"
-elseif cosH<-1 then
-return"N/S"
-end
-local H
-if rising then
-H=360-acos(cosH)
-else
-H=acos(cosH)
-end
-H=H/15
-local T=H+RA-(0.06571*t)-6.622
-local UT=fit_into_range(T-lng_hour+Tlocal,0,24)
-return floor(UT)*60*60+frac(UT)*60*60
-end
-function UTILS.GetSunrise(Day,Month,Year,Latitude,Longitude,Tlocal)
-local DayOfYear=UTILS.GetDayOfYear(Year,Month,Day)
-return UTILS.GetSunRiseAndSet(DayOfYear,Latitude,Longitude,true,Tlocal)
-end
-function UTILS.GetSunset(Day,Month,Year,Latitude,Longitude,Tlocal)
-local DayOfYear=UTILS.GetDayOfYear(Year,Month,Day)
-return UTILS.GetSunRiseAndSet(DayOfYear,Latitude,Longitude,false,Tlocal)
-end
-function UTILS.GetOSTime()
-if os then
-local ts=0
-local t=os.date("*t")
-local s=t.sec
-local m=t.min*60
-local h=t.hour*3600
-ts=s+m+h
-return ts
-else
-return nil
-end
-end
-function UTILS.ShuffleTable(t)
-if t==nil or type(t)~="table"then
-BASE:I("Error in ShuffleTable: Missing or wrong type of Argument")
-return
-end
-math.random()
-math.random()
-math.random()
-local TempTable={}
-for i=1,#t do
-local r=math.random(1,#t)
-TempTable[i]=t[r]
-table.remove(t,r)
-end
-return TempTable
-end
-function UTILS.GetRandomTableElement(t,replace)
-if t==nil or type(t)~="table"then
-BASE:I("Error in ShuffleTable: Missing or wrong type of Argument")
-return
-end
-math.random()
-math.random()
-math.random()
-local r=math.random(#t)
-local element=t[r]
-if not replace then
-table.remove(t,r)
-end
-return element
-end
-function UTILS.IsLoadingDoorOpen(unit_name)
-local unit=Unit.getByName(unit_name)
-if unit~=nil then
-local type_name=unit:getTypeName()
-BASE:T("TypeName = "..type_name)
-if type_name=="Mi-8MT"and(unit:getDrawArgumentValue(38)==1 or unit:getDrawArgumentValue(86)==1 or unit:getDrawArgumentValue(250)<0)then
-BASE:T(unit_name.." Cargo doors are open or cargo door not present")
-return true
-end
-if type_name=="Mi-24P"and(unit:getDrawArgumentValue(38)==1 or unit:getDrawArgumentValue(86)==1)then
-BASE:T(unit_name.." a side door is open")
-return true
-end
-if type_name=="UH-1H"and(unit:getDrawArgumentValue(43)==1 or unit:getDrawArgumentValue(44)==1)then
-BASE:T(unit_name.." a side door is open ")
-return true
-end
-if string.find(type_name,"SA342")and(unit:getDrawArgumentValue(34)==1)then
-BASE:T(unit_name.." front door(s) are open or doors removed")
-return true
-end
-if string.find(type_name,"Hercules")and(unit:getDrawArgumentValue(1215)==1 and unit:getDrawArgumentValue(1216)==1)then
-BASE:T(unit_name.." rear doors are open")
-return true
-end
-if string.find(type_name,"Hercules")and(unit:getDrawArgumentValue(1220)==1 or unit:getDrawArgumentValue(1221)==1)then
-BASE:T(unit_name.." para doors are open")
-return true
-end
-if string.find(type_name,"Hercules")and(unit:getDrawArgumentValue(1217)==1)then
-BASE:T(unit_name.." side door is open")
-return true
-end
-if type_name=="Bell-47"then
-BASE:T(unit_name.." door is open")
-return true
-end
-if type_name=="UH-60L"and(unit:getDrawArgumentValue(401)==1 or unit:getDrawArgumentValue(402)==1)then
-BASE:T(unit_name.." cargo door is open")
-return true
-end
-if type_name=="UH-60L"and(unit:getDrawArgumentValue(38)>0 or unit:getDrawArgumentValue(400)==1)then
-BASE:T(unit_name.." front door(s) are open")
-return true
-end
-if type_name=="AH-64D_BLK_II"then
-BASE:T(unit_name.." front door(s) are open")
-return true
-end
-if type_name=="Bronco-OV-10A"then
-BASE:T(unit_name.." front door(s) are open")
-return true
-end
-return false
-end
-return nil
-end
-function UTILS.GenerateFMFrequencies()
-local FreeFMFrequencies={}
-for _first=3,7 do
-for _second=0,5 do
-for _third=0,9 do
-local _frequency=((100*_first)+(10*_second)+_third)*100000
-table.insert(FreeFMFrequencies,_frequency)
-end
-end
-end
-return FreeFMFrequencies
-end
-function UTILS.GenerateVHFrequencies()
-local _skipFrequencies={
-214,274,291.5,295,297.5,
-300.5,304,305,307,309.5,311,312,312.5,316,
-320,324,328,329,330,332,336,337,
-342,343,348,351,352,353,358,
-363,365,368,372.5,374,
-380,381,384,385,389,395,396,
-414,420,430,432,435,440,450,455,462,470,485,
-507,515,520,525,528,540,550,560,570,577,580,
-602,625,641,662,670,680,682,690,
-705,720,722,730,735,740,745,750,770,795,
-822,830,862,866,
-905,907,920,935,942,950,995,
-1000,1025,1030,1050,1065,1116,1175,1182,1210,1215
-}
-local FreeVHFFrequencies={}
-local _start=200000
-while _start<400000 do
-local _found=false
-for _,value in pairs(_skipFrequencies)do
-if value*1000==_start then
-_found=true
-break
-end
-end
-if _found==false then
-table.insert(FreeVHFFrequencies,_start)
-end
-_start=_start+10000
-end
-_start=400000
-while _start<850000 do
-local _found=false
-for _,value in pairs(_skipFrequencies)do
-if value*1000==_start then
-_found=true
-break
-end
-end
-if _found==false then
-table.insert(FreeVHFFrequencies,_start)
-end
-_start=_start+10000
-end
-_start=850000
-while _start<=999000 do
-local _found=false
-for _,value in pairs(_skipFrequencies)do
-if value*1000==_start then
-_found=true
-break
-end
-end
-if _found==false then
-table.insert(FreeVHFFrequencies,_start)
-end
-_start=_start+50000
-end
-return FreeVHFFrequencies
-end
-function UTILS.GenerateUHFrequencies()
-local FreeUHFFrequencies={}
-local _start=220000000
-while _start<399000000 do
-if _start~=243000000 then
-table.insert(FreeUHFFrequencies,_start)
-end
-_start=_start+500000
-end
-return FreeUHFFrequencies
-end
-function UTILS.GenerateLaserCodes()
-local jtacGeneratedLaserCodes={}
-local function ContainsDigit(_number,_numberToFind)
-local _thisNumber=_number
-local _thisDigit=0
-while _thisNumber~=0 do
-_thisDigit=_thisNumber%10
-_thisNumber=math.floor(_thisNumber/10)
-if _thisDigit==_numberToFind then
-return true
-end
-end
-return false
-end
-local _code=1111
-local _count=1
-while _code<1777 and _count<30 do
-while true do
-_code=_code+1
-if not ContainsDigit(_code,8)
-and not ContainsDigit(_code,9)
-and not ContainsDigit(_code,0)then
-table.insert(jtacGeneratedLaserCodes,_code)
-break
-end
-end
-_count=_count+1
-end
-return jtacGeneratedLaserCodes
-end
-function UTILS.EnsureTable(Object,ReturnNil)
-if Object then
-if type(Object)~="table"then
-Object={Object}
-end
-else
-if ReturnNil then
-return nil
-else
-Object={}
-end
-end
-return Object
-end
-function UTILS.SaveToFile(Path,Filename,Data)
-if not io then
-BASE:E("ERROR: io not desanitized. Can't save current file.")
-return false
-end
-if Path==nil and not lfs then
-BASE:E("WARNING: lfs not desanitized. File will be saved in DCS installation root directory rather than your \"Saved Games\\DCS\" folder.")
-end
-local path=nil
-if lfs then
-path=Path or lfs.writedir()
-end
-local filename=Filename
-if path~=nil then
-filename=path.."\\"..filename
-end
-local f=assert(io.open(filename,"wb"))
-f:write(Data)
-f:close()
-return true
-end
-function UTILS.LoadFromFile(Path,Filename)
-if not io then
-BASE:E("ERROR: io not desanitized. Can't save current state.")
-return false
-end
-if Path==nil and not lfs then
-BASE:E("WARNING: lfs not desanitized. Loading will look into your DCS installation root directory rather than your \"Saved Games\\DCS\" folder.")
-end
-local path=nil
-if lfs then
-path=Path or lfs.writedir()
-end
-local filename=Filename
-if path~=nil then
-filename=path.."\\"..filename
-end
-local exists=UTILS.CheckFileExists(Path,Filename)
-if not exists then
-BASE:I(string.format("ERROR: File %s does not exist!",filename))
-return false
-end
-local file=assert(io.open(filename,"rb"))
-local loadeddata={}
-for line in file:lines()do
-loadeddata[#loadeddata+1]=line
-end
-file:close()
-return true,loadeddata
-end
-function UTILS.CheckFileExists(Path,Filename)
-local function _fileexists(name)
-local f=io.open(name,"r")
-if f~=nil then
-io.close(f)
-return true
-else
-return false
-end
-end
-if not io then
-BASE:E("ERROR: io not desanitized.")
-return false
-end
-if Path==nil and not lfs then
-BASE:E("WARNING: lfs not desanitized. Loading will look into your DCS installation root directory rather than your \"Saved Games\\DCS\" folder.")
-end
-local path=nil
-if lfs then
-path=Path or lfs.writedir()
-end
-local filename=Filename
-if path~=nil then
-filename=path.."\\"..filename
-end
-local exists=_fileexists(filename)
-if not exists then
-BASE:E(string.format("ERROR: File %s does not exist!",filename))
-return false
-else
-return true
-end
-end
-function UTILS.GetCountPerTypeName(Group)
-local units=Group:GetUnits()
-local TypeNameTable={}
-for _,_unt in pairs(units)do
-local unit=_unt
-local typen=unit:GetTypeName()
-if not TypeNameTable[typen]then
-TypeNameTable[typen]=1
-else
-TypeNameTable[typen]=TypeNameTable[typen]+1
-end
-end
-return TypeNameTable
-end
-function UTILS.SaveStationaryListOfGroups(List,Path,Filename,Structured)
-local filename=Filename or"StateListofGroups"
-local data="--Save Stationary List of Groups: "..Filename.."\n"
-for _,_group in pairs(List)do
-local group=GROUP:FindByName(_group)
-if group and group:IsAlive()then
-local units=group:CountAliveUnits()
-local position=group:GetVec3()
-if Structured then
-local structure=UTILS.GetCountPerTypeName(group)
-local strucdata=""
-for typen,anzahl in pairs(structure)do
-strucdata=strucdata..typen.."=="..anzahl..";"
-end
-data=string.format("%s%s,%d,%d,%d,%d,%s\n",data,_group,units,position.x,position.y,position.z,strucdata)
-else
-data=string.format("%s%s,%d,%d,%d,%d\n",data,_group,units,position.x,position.y,position.z)
-end
-else
-data=string.format("%s%s,0,0,0,0\n",data,_group)
-end
-end
-local outcome=UTILS.SaveToFile(Path,Filename,data)
-return outcome
-end
-function UTILS.SaveSetOfGroups(Set,Path,Filename,Structured)
-local filename=Filename or"SetOfGroups"
-local data="--Save SET of groups: "..Filename.."\n"
-local List=Set:GetSetObjects()
-for _,_group in pairs(List)do
-local group=_group
-if group and group:IsAlive()then
-local name=group:GetName()
-local template=string.gsub(name,"-(.+)$","")
-if string.find(template,"#")then
-template=string.gsub(name,"#(%d+)$","")
-end
-local units=group:CountAliveUnits()
-local position=group:GetVec3()
-if Structured then
-local structure=UTILS.GetCountPerTypeName(group)
-local strucdata=""
-for typen,anzahl in pairs(structure)do
-strucdata=strucdata..typen.."=="..anzahl..";"
-end
-data=string.format("%s%s,%s,%d,%d,%d,%d,%s\n",data,name,template,units,position.x,position.y,position.z,strucdata)
-else
-data=string.format("%s%s,%s,%d,%d,%d,%d\n",data,name,template,units,position.x,position.y,position.z)
-end
-end
-end
-local outcome=UTILS.SaveToFile(Path,Filename,data)
-return outcome
-end
-function UTILS.SaveSetOfStatics(Set,Path,Filename)
-local filename=Filename or"SetOfStatics"
-local data="--Save SET of statics: "..Filename.."\n"
-local List=Set:GetSetObjects()
-for _,_group in pairs(List)do
-local group=_group
-if group and group:IsAlive()then
-local name=group:GetName()
-local position=group:GetVec3()
-data=string.format("%s%s,%d,%d,%d\n",data,name,position.x,position.y,position.z)
-end
-end
-local outcome=UTILS.SaveToFile(Path,Filename,data)
-return outcome
-end
-function UTILS.SaveStationaryListOfStatics(List,Path,Filename)
-local filename=Filename or"StateListofStatics"
-local data="--Save Stationary List of Statics: "..Filename.."\n"
-for _,_group in pairs(List)do
-local group=STATIC:FindByName(_group,false)
-if group and group:IsAlive()then
-local position=group:GetVec3()
-data=string.format("%s%s,1,%d,%d,%d\n",data,_group,position.x,position.y,position.z)
-else
-data=string.format("%s%s,0,0,0,0\n",data,_group)
-end
-end
-local outcome=UTILS.SaveToFile(Path,Filename,data)
-return outcome
-end
-function UTILS.LoadStationaryListOfGroups(Path,Filename,Reduce,Structured,Cinematic,Effect,Density)
-local fires={}
-local function Smokers(name,coord,effect,density)
-local eff=math.random(8)
-if type(effect)=="number"then eff=effect end
-coord:BigSmokeAndFire(eff,density,name)
-table.insert(fires,name)
-end
-local function Cruncher(group,typename,anzahl)
-local units=group:GetUnits()
-local reduced=0
-for _,_unit in pairs(units)do
-local typo=_unit:GetTypeName()
-if typename==typo then
-if Cinematic then
-local coordinate=_unit:GetCoordinate()
-local name=_unit:GetName()
-Smokers(name,coordinate,Effect,Density)
-end
-_unit:Destroy(false)
-reduced=reduced+1
-if reduced==anzahl then break end
-end
-end
-end
-local reduce=true
-if Reduce==false then reduce=false end
-local filename=Filename or"StateListofGroups"
-local datatable={}
-if UTILS.CheckFileExists(Path,filename)then
-local outcome,loadeddata=UTILS.LoadFromFile(Path,Filename)
-table.remove(loadeddata,1)
-for _id,_entry in pairs(loadeddata)do
-local dataset=UTILS.Split(_entry,",")
-local groupname=dataset[1]
-local size=tonumber(dataset[2])
-local posx=tonumber(dataset[3])
-local posy=tonumber(dataset[4])
-local posz=tonumber(dataset[5])
-local structure=dataset[6]
-local coordinate=COORDINATE:NewFromVec3({x=posx,y=posy,z=posz})
-local data={groupname=groupname,size=size,coordinate=coordinate,group=GROUP:FindByName(groupname)}
-if reduce then
-local actualgroup=GROUP:FindByName(groupname)
-if actualgroup and actualgroup:IsAlive()and actualgroup:CountAliveUnits()>size then
-if Structured and structure then
-local loadedstructure={}
-local strcset=UTILS.Split(structure,";")
-for _,_data in pairs(strcset)do
-local datasplit=UTILS.Split(_data,"==")
-loadedstructure[datasplit[1]]=tonumber(datasplit[2])
-end
-local originalstructure=UTILS.GetCountPerTypeName(actualgroup)
-for _name,_number in pairs(originalstructure)do
-local loadednumber=0
-if loadedstructure[_name]then
-loadednumber=loadedstructure[_name]
-end
-local reduce=false
-if loadednumber<_number then reduce=true end
-if reduce then
-Cruncher(actualgroup,_name,_number-loadednumber)
-end
-end
-else
-local reduction=actualgroup:CountAliveUnits()-size
-local units=actualgroup:GetUnits()
-local units2=UTILS.ShuffleTable(units)
-for i=1,reduction do
-units2[i]:Destroy(false)
-end
-end
-end
-end
-table.insert(datatable,data)
-end
-else
-return nil
-end
-return datatable,fires
-end
-function UTILS.LoadSetOfGroups(Path,Filename,Spawn,Structured,Cinematic,Effect,Density)
-local fires={}
-local usedtemplates={}
-local spawn=true
-if Spawn==false then spawn=false end
-local filename=Filename or"SetOfGroups"
-local setdata=SET_GROUP:New()
-local datatable={}
-local function Smokers(name,coord,effect,density)
-local eff=math.random(8)
-if type(effect)=="number"then eff=effect end
-coord:BigSmokeAndFire(eff,density,name)
-table.insert(fires,name)
-end
-local function Cruncher(group,typename,anzahl)
-local units=group:GetUnits()
-local reduced=0
-for _,_unit in pairs(units)do
-local typo=_unit:GetTypeName()
-if typename==typo then
-if Cinematic then
-local coordinate=_unit:GetCoordinate()
-local name=_unit:GetName()
-Smokers(name,coordinate,Effect,Density)
-end
-_unit:Destroy(false)
-reduced=reduced+1
-if reduced==anzahl then break end
-end
-end
-end
-local function PostSpawn(args)
-local spwndgrp=args[1]
-local size=args[2]
-local structure=args[3]
-setdata:AddObject(spwndgrp)
-local actualsize=spwndgrp:CountAliveUnits()
-if actualsize>size then
-if Structured and structure then
-local loadedstructure={}
-local strcset=UTILS.Split(structure,";")
-for _,_data in pairs(strcset)do
-local datasplit=UTILS.Split(_data,"==")
-loadedstructure[datasplit[1]]=tonumber(datasplit[2])
-end
-local originalstructure=UTILS.GetCountPerTypeName(spwndgrp)
-for _name,_number in pairs(originalstructure)do
-local loadednumber=0
-if loadedstructure[_name]then
-loadednumber=loadedstructure[_name]
-end
-local reduce=false
-if loadednumber<_number then reduce=true end
-if reduce then
-Cruncher(spwndgrp,_name,_number-loadednumber)
-end
-end
-else
-local reduction=actualsize-size
-local units=spwndgrp:GetUnits()
-local units2=UTILS.ShuffleTable(units)
-for i=1,reduction do
-units2[i]:Destroy(false)
-end
-end
-end
-end
-local function MultiUse(Data)
-local template=Data.template
-if template and usedtemplates[template]and usedtemplates[template].used and usedtemplates[template].used>1 then
-if not usedtemplates[template].done then
-local spwnd=0
-local spawngrp=SPAWN:New(template)
-spawngrp:InitLimit(0,usedtemplates[template].used)
-for _,_entry in pairs(usedtemplates[template].data)do
-spwnd=spwnd+1
-local sgrp=spawngrp:SpawnFromCoordinate(_entry.coordinate,spwnd)
-BASE:ScheduleOnce(0.5,PostSpawn,{sgrp,_entry.size,_entry.structure})
-end
-usedtemplates[template].done=true
-end
-return true
-else
-return false
-end
-end
-if UTILS.CheckFileExists(Path,filename)then
-local outcome,loadeddata=UTILS.LoadFromFile(Path,Filename)
-table.remove(loadeddata,1)
-for _id,_entry in pairs(loadeddata)do
-local dataset=UTILS.Split(_entry,",")
-local groupname=dataset[1]
-local template=dataset[2]
-local size=tonumber(dataset[3])
-local posx=tonumber(dataset[4])
-local posy=tonumber(dataset[5])
-local posz=tonumber(dataset[6])
-local structure=dataset[7]
-local coordinate=COORDINATE:NewFromVec3({x=posx,y=posy,z=posz})
-local group=nil
-if size>0 then
-local data={groupname=groupname,size=size,coordinate=coordinate,template=template,structure=structure}
-table.insert(datatable,data)
-if usedtemplates[template]then
-usedtemplates[template].used=usedtemplates[template].used+1
-table.insert(usedtemplates[template].data,data)
-else
-usedtemplates[template]={
-data={},
-used=1,
-done=false,
-}
-table.insert(usedtemplates[template].data,data)
-end
-end
-end
-for _id,_entry in pairs(datatable)do
-if spawn and not MultiUse(_entry)and _entry.size>0 then
-local group=SPAWN:New(_entry.template)
-local sgrp=group:SpawnFromCoordinate(_entry.coordinate)
-BASE:ScheduleOnce(0.5,PostSpawn,{sgrp,_entry.size,_entry.structure})
-end
-end
-else
-return nil
-end
-if spawn then
-return setdata,fires
-else
-return datatable
-end
-end
-function UTILS.LoadSetOfStatics(Path,Filename)
-local filename=Filename or"SetOfStatics"
-local datatable=SET_STATIC:New()
-if UTILS.CheckFileExists(Path,filename)then
-local outcome,loadeddata=UTILS.LoadFromFile(Path,Filename)
-table.remove(loadeddata,1)
-for _id,_entry in pairs(loadeddata)do
-local dataset=UTILS.Split(_entry,",")
-local staticname=dataset[1]
-local StaticObject=STATIC:FindByName(staticname,false)
-if StaticObject then
-datatable:AddObject(StaticObject)
-end
-end
-else
-return nil
-end
-return datatable
-end
-function UTILS.LoadStationaryListOfStatics(Path,Filename,Reduce,Dead,Cinematic,Effect,Density)
-local fires={}
-local reduce=true
-if Reduce==false then reduce=false end
-local filename=Filename or"StateListofStatics"
-local datatable={}
-if UTILS.CheckFileExists(Path,filename)then
-local outcome,loadeddata=UTILS.LoadFromFile(Path,Filename)
-table.remove(loadeddata,1)
-for _id,_entry in pairs(loadeddata)do
-local dataset=UTILS.Split(_entry,",")
-local staticname=dataset[1]
-local size=tonumber(dataset[2])
-local posx=tonumber(dataset[3])
-local posy=tonumber(dataset[4])
-local posz=tonumber(dataset[5])
-local coordinate=COORDINATE:NewFromVec3({x=posx,y=posy,z=posz})
-local data={staticname=staticname,size=size,coordinate=coordinate,static=STATIC:FindByName(staticname,false)}
-table.insert(datatable,data)
-if size==0 and reduce then
-local static=STATIC:FindByName(staticname,false)
-if static then
-if Dead then
-local deadobject=SPAWNSTATIC:NewFromStatic(staticname,static:GetCountry())
-deadobject:InitDead(true)
-local heading=static:GetHeading()
-local coord=static:GetCoordinate()
-static:Destroy(false)
-deadobject:SpawnFromCoordinate(coord,heading,staticname)
-if Cinematic then
-local effect=math.random(8)
-if type(Effect)=="number"then
-effect=Effect
-end
-coord:BigSmokeAndFire(effect,Density,staticname)
-table.insert(fires,staticname)
-end
-else
-static:Destroy(false)
-end
-end
-end
-end
-else
-return nil
-end
-return datatable,fires
-end
-function UTILS.BearingToCardinal(Heading)
-if Heading>=0 and Heading<=22 then return"North"
-elseif Heading>=23 and Heading<=66 then return"North-East"
-elseif Heading>=67 and Heading<=101 then return"East"
-elseif Heading>=102 and Heading<=146 then return"South-East"
-elseif Heading>=147 and Heading<=201 then return"South"
-elseif Heading>=202 and Heading<=246 then return"South-West"
-elseif Heading>=247 and Heading<=291 then return"West"
-elseif Heading>=292 and Heading<=338 then return"North-West"
-elseif Heading>=339 then return"North"
-end
-end
-function UTILS.ToStringBRAANATO(FromGrp,ToGrp)
-local BRAANATO="Merged."
-local GroupNumber=ToGrp:GetSize()
-local GroupWords="Singleton"
-if GroupNumber==2 then GroupWords="Two-Ship"
-elseif GroupNumber>=3 then GroupWords="Heavy"
-end
-local grpLeadUnit=ToGrp:GetUnit(1)
-local tgtCoord=grpLeadUnit:GetCoordinate()
-local currentCoord=FromGrp:GetCoordinate()
-local hdg=UTILS.Round(ToGrp:GetHeading()/100,1)*100
-local bearing=UTILS.Round(currentCoord:HeadingTo(tgtCoord),0)
-local rangeMetres=tgtCoord:Get2DDistance(currentCoord)
-local rangeNM=UTILS.Round(UTILS.MetersToNM(rangeMetres),0)
-local aspect=tgtCoord:ToStringAspect(currentCoord)
-local alt=UTILS.Round(UTILS.MetersToFeet(grpLeadUnit:GetAltitude())/1000,0)
-local track=UTILS.BearingToCardinal(hdg)
-if rangeNM>3 then
-if aspect==""then
-BRAANATO=string.format("%s, BRA, %03d, %d miles, Angels %d, Track %s",GroupWords,bearing,rangeNM,alt,track)
-else
-BRAANATO=string.format("%s, BRAA, %03d, %d miles, Angels %d, %s, Track %s",GroupWords,bearing,rangeNM,alt,aspect,track)
-end
-end
-return BRAANATO
-end
-function UTILS.IsInTable(Table,Object,Key)
-for key,object in pairs(Table)do
-if Key then
-if Object[Key]==object[Key]then
-return true
-end
-else
-if object==Object then
-return true
-end
-end
-end
-return false
-end
-function UTILS.IsAnyInTable(Table,Objects,Key)
-for _,Object in pairs(UTILS.EnsureTable(Objects))do
-for key,object in pairs(Table)do
-if Key then
-if Object[Key]==object[Key]then
-return true
-end
-else
-if object==Object then
-return true
-end
-end
-end
-end
-return false
-end
-function UTILS.PlotRacetrack(Coordinate,Altitude,Speed,Heading,Leg,Coalition,Color,Alpha,LineType,ReadOnly)
-local fix_coordinate=Coordinate
-local altitude=Altitude
-local speed=Speed or 350
-local heading=Heading or 270
-local leg_distance=Leg or 10
-local coalition=Coalition or-1
-local color=Color or{1,0,0}
-local alpha=Alpha or 1
-local lineType=LineType or 1
-speed=UTILS.IasToTas(speed,UTILS.FeetToMeters(altitude),oatcorr)
-local turn_radius=0.0211*speed-3.01
-local point_two=fix_coordinate:Translate(UTILS.NMToMeters(leg_distance),heading,true,false)
-local point_three=point_two:Translate(UTILS.NMToMeters(turn_radius)*2,heading-90,true,false)
-local point_four=fix_coordinate:Translate(UTILS.NMToMeters(turn_radius)*2,heading-90,true,false)
-local circle_center_fix_four=point_two:Translate(UTILS.NMToMeters(turn_radius),heading-90,true,false)
-local circle_center_two_three=fix_coordinate:Translate(UTILS.NMToMeters(turn_radius),heading-90,true,false)
-fix_coordinate:LineToAll(point_two,coalition,color,alpha,lineType)
-point_four:LineToAll(point_three,coalition,color,alpha,lineType)
-circle_center_fix_four:CircleToAll(UTILS.NMToMeters(turn_radius),coalition,color,alpha,nil,0,lineType)
-circle_center_two_three:CircleToAll(UTILS.NMToMeters(turn_radius),coalition,color,alpha,nil,0,lineType)
-end
-function UTILS.TimeNow()
-return UTILS.SecondsToClock(timer.getAbsTime(),false,false)
-end
-function UTILS.TimeDifferenceInSeconds(start_time,end_time)
-return UTILS.ClockToSeconds(end_time)-UTILS.ClockToSeconds(start_time)
-end
-function UTILS.TimeLaterThan(time_string)
-if timer.getAbsTime()>UTILS.ClockToSeconds(time_string)then
-return true
-end
-return false
-end
-function UTILS.TimeBefore(time_string)
-if timer.getAbsTime()max then value=max end
-return value
-end
-function UTILS.ClampAngle(value)
-if value>360 then return value-360 end
-if value<0 then return value+360 end
-return value
-end
-function UTILS.RemapValue(value,old_min,old_max,new_min,new_max)
-new_min=new_min or 0
-new_max=new_max or 100
-local old_range=old_max-old_min
-local new_range=new_max-new_min
-local percentage=(value-old_min)/old_range
-return(new_range*percentage)+new_min
-end
-function UTILS.RandomPointInTriangle(pt1,pt2,pt3)
-local pt={math.random(),math.random()}
-table.sort(pt)
-local s=pt[1]
-local t=pt[2]-pt[1]
-local u=1-pt[2]
-return{x=s*pt1.x+t*pt2.x+u*pt3.x,
-y=s*pt1.y+t*pt2.y+u*pt3.y}
-end
-function UTILS.AngleBetween(angle,min,max)
-angle=(360+(angle%360))%360
-min=(360+min%360)%360
-max=(360+max%360)%360
-if min0 then
-for _,property in pairs(zone["properties"])do
-return_table[property["key"]]=property["value"]
-end
-return return_table
-else
-BASE:I(string.format("%s doesn't have any properties",zone_name))
-return{}
-end
-end
-end
-end
-function UTILS.RotatePointAroundPivot(point,pivot,angle)
-local radians=math.rad(angle)
-local x=point.x-pivot.x
-local y=point.y-pivot.y
-local rotated_x=x*math.cos(radians)-y*math.sin(radians)
-local rotatex_y=x*math.sin(radians)+y*math.cos(radians)
-local original_x=rotated_x+pivot.x
-local original_y=rotatex_y+pivot.y
-return{x=original_x,y=original_y}
-end
-function UTILS.UniqueName(base)
-base=base or""
-local ran=tostring(math.random(0,1000000))
-if base==""then
-return ran
-end
-return base.."_"..ran
-end
-function string.startswith(str,value)
-return string.sub(str,1,string.len(value))==value
-end
-function string.endswith(str,value)
-return value==""or str:sub(-#value)==value
-end
-function string.split(input,separator)
-local parts={}
-for part in input:gmatch("[^"..separator.."]+")do
-table.insert(parts,part)
-end
-return parts
-end
-function string.contains(str,value)
-return string.match(str,value)
-end
-function table.contains(tbl,element)
-if element==nil or tbl==nil then return false end
-local index=1
-while tbl[index]do
-if tbl[index]==element then
-return true
-end
-index=index+1
-end
-return false
-end
-function table.contains_key(tbl,key)
-if tbl[key]~=nil then return true else return false end
-end
-function table.insert_unique(tbl,element)
-if element==nil or tbl==nil then return end
-if not table.contains(tbl,element)then
-table.insert(tbl,element)
-end
-end
-function table.remove_by_value(tbl,element)
-local indices_to_remove={}
-local index=1
-for _,value in pairs(tbl)do
-if value==element then
-table.insert(indices_to_remove,index)
-end
-index=index+1
-end
-for _,idx in pairs(indices_to_remove)do
-table.remove(tbl,idx)
-end
-end
-function table.remove_key(table,key)
-local element=table[key]
-table[key]=nil
-return element
-end
-function table.index_of(table,element)
-for i,v in ipairs(table)do
-if v==element then
-return i
-end
-end
-return nil
-end
-function table.length(T)
-local count=0
-for _ in pairs(T)do count=count+1 end
-return count
-end
-function table.slice(tbl,first,last)
-local sliced={}
-local start=first or 1
-local stop=last or table.length(tbl)
-local count=1
-for key,value in pairs(tbl)do
-if count>=start and count<=stop then
-sliced[key]=value
-end
-count=count+1
-end
-return sliced
-end
-function table.count_value(tbl,value)
-local count=0
-for _,item in pairs(tbl)do
-if item==value then count=count+1 end
-end
-return count
-end
-function table.combine(t1,t2)
-if t1==nil and t2==nil then
-BASE:E("Both tables were empty!")
-end
-if t1==nil then return t2 end
-if t2==nil then return t1 end
-for i=1,#t2 do
-t1[#t1+1]=t2[i]
-end
-return t1
-end
-function table.merge(t1,t2)
-for k,v in pairs(t2)do
-if(type(v)=="table")and(type(t1[k]or false)=="table")then
-table.merge(t1[k],t2[k])
-else
-t1[k]=v
-end
-end
-return t1
-end
-function table.add(tbl,item)
-tbl[#tbl+1]=item
-end
-function table.shuffle(tbl)
-local new_table={}
-for _,value in ipairs(tbl)do
-local pos=math.random(1,#new_table+1)
-table.insert(new_table,pos,value)
-end
-return new_table
-end
-function table.find_key_value_pair(tbl,key,value)
-for k,v in pairs(tbl)do
-if type(v)=="table"then
-local result=table.find_key_value_pair(v,key,value)
-if result~=nil then
-return result
-end
-elseif k==key and v==value then
-return tbl
-end
-end
-return nil
-end
-function UTILS.DecimalToOctal(Number)
-if Number<8 then return Number end
-local number=tonumber(Number)
-local octal=""
-local n=1
-while number>7 do
-local number1=number%8
-octal=string.format("%d",number1)..octal
-local number2=math.abs(number/8)
-if number2<8 then
-octal=string.format("%d",number2)..octal
-end
-number=number2
-n=n+1
-end
-return tonumber(octal)
-end
-function UTILS.OctalToDecimal(Number)
-return tonumber(Number,8)
-end
-local _TraceOnOff=true
-local _TraceLevel=1
-local _TraceAll=false
-local _TraceClass={}
-local _TraceClassMethod={}
-local _ClassID=0
-BASE={
-ClassName="BASE",
-ClassID=0,
-Events={},
-States={},
-Debug=debug,
-Scheduler=nil,
-}
-BASE.__={}
-BASE._={
-Schedules={},
-}
-FORMATION={
-Cone="Cone",
-Vee="Vee",
-}
-function BASE:New()
-local self=UTILS.DeepCopy(self)
-_ClassID=_ClassID+1
-self.ClassID=_ClassID
-return self
-end
-function BASE:Inherit(Child,Parent)
-local Child=UTILS.DeepCopy(Child)
-if Child~=nil then
-if rawget(Child,"__")then
-setmetatable(Child,{__index=Child.__})
-setmetatable(Child.__,{__index=Parent})
-else
-setmetatable(Child,{__index=Parent})
-end
-end
-return Child
-end
-local function getParent(Child)
-local Parent=nil
-if Child.ClassName=='BASE'then
-Parent=nil
-else
-if rawget(Child,"__")then
-Parent=getmetatable(Child.__).__index
-else
-Parent=getmetatable(Child).__index
-end
-end
-return Parent
-end
-function BASE:GetParent(Child,FromClass)
-local Parent
-if Child.ClassName=='BASE'then
-Parent=nil
-else
-if FromClass then
-while(Child.ClassName~="BASE"and Child.ClassName~=FromClass.ClassName)do
-Child=getParent(Child)
-end
-end
-if Child.ClassName=='BASE'then
-Parent=nil
-else
-Parent=getParent(Child)
-end
-end
-return Parent
-end
-function BASE:IsInstanceOf(ClassName)
-if type(ClassName)~='string'then
-if type(ClassName)=='table'and ClassName.ClassName~=nil then
-ClassName=ClassName.ClassName
-else
-local err_str='className parameter should be a string; parameter received: '..type(ClassName)
-self:E(err_str)
-return false
-end
-end
-ClassName=string.upper(ClassName)
-if string.upper(self.ClassName)==ClassName then
-return true
-end
-local Parent=getParent(self)
-while Parent do
-if string.upper(Parent.ClassName)==ClassName then
-return true
-end
-Parent=getParent(Parent)
-end
-return false
-end
-function BASE:GetClassNameAndID()
-return string.format('%s#%09d',self.ClassName,self.ClassID)
-end
-function BASE:GetClassName()
-return self.ClassName
-end
-function BASE:GetClassID()
-return self.ClassID
-end
-do
-function BASE:EventDispatcher()
-return _EVENTDISPATCHER
-end
-function BASE:GetEventPriority()
-return self._.EventPriority or 5
-end
-function BASE:SetEventPriority(EventPriority)
-self._.EventPriority=EventPriority
-end
-function BASE:EventRemoveAll()
-self:EventDispatcher():RemoveAll(self)
-return self
-end
-function BASE:HandleEvent(EventID,EventFunction)
-self:EventDispatcher():OnEventGeneric(EventFunction,self,EventID)
-return self
-end
-function BASE:UnHandleEvent(EventID)
-self:EventDispatcher():RemoveEvent(self,EventID)
-return self
-end
-end
-function BASE:CreateEventBirth(EventTime,Initiator,IniUnitName,place,subplace)
-self:F({EventTime,Initiator,IniUnitName,place,subplace})
-local Event={
-id=world.event.S_EVENT_BIRTH,
-time=EventTime,
-initiator=Initiator,
-IniUnitName=IniUnitName,
-place=place,
-subplace=subplace,
-}
-world.onEvent(Event)
-end
-function BASE:CreateEventCrash(EventTime,Initiator,IniObjectCategory)
-self:F({EventTime,Initiator})
-local Event={
-id=world.event.S_EVENT_CRASH,
-time=EventTime,
-initiator=Initiator,
-IniObjectCategory=IniObjectCategory,
-}
-world.onEvent(Event)
-end
-function BASE:CreateEventUnitLost(EventTime,Initiator)
-self:F({EventTime,Initiator})
-local Event={
-id=world.event.S_EVENT_UNIT_LOST,
-time=EventTime,
-initiator=Initiator,
-}
-world.onEvent(Event)
-end
-function BASE:CreateEventDead(EventTime,Initiator,IniObjectCategory)
-self:F({EventTime,Initiator,IniObjectCategory})
-local Event={
-id=world.event.S_EVENT_DEAD,
-time=EventTime,
-initiator=Initiator,
-IniObjectCategory=IniObjectCategory,
-}
-world.onEvent(Event)
-end
-function BASE:CreateEventRemoveUnit(EventTime,Initiator)
-self:F({EventTime,Initiator})
-local Event={
-id=EVENTS.RemoveUnit,
-time=EventTime,
-initiator=Initiator,
-}
-world.onEvent(Event)
-end
-function BASE:CreateEventTakeoff(EventTime,Initiator)
-self:F({EventTime,Initiator})
-local Event={
-id=world.event.S_EVENT_TAKEOFF,
-time=EventTime,
-initiator=Initiator,
-}
-world.onEvent(Event)
-end
-function BASE:CreateEventPlayerEnterAircraft(PlayerUnit)
-self:F({PlayerUnit})
-local Event={
-id=EVENTS.PlayerEnterAircraft,
-time=timer.getTime(),
-initiator=PlayerUnit:GetDCSObject()
-}
-world.onEvent(Event)
-end
-function BASE:onEvent(event)
-if self then
-for EventID,EventObject in pairs(self.Events)do
-if EventObject.EventEnabled then
-if event.id==EventObject.Event then
-if self==EventObject.Self then
-if event.initiator and event.initiator:isExist()then
-event.IniUnitName=event.initiator:getName()
-end
-if event.target and event.target:isExist()then
-event.TgtUnitName=event.target:getName()
-end
-end
-end
-end
-end
-end
-end
-do
-function BASE:ScheduleOnce(Start,SchedulerFunction,...)
-local ObjectName="-"
-ObjectName=self.ClassName..self.ClassID
-self:F3({"ScheduleOnce: ",ObjectName,Start})
-if not self.Scheduler then
-self.Scheduler=SCHEDULER:New(self)
-end
-local ScheduleID=self.Scheduler:Schedule(nil,SchedulerFunction,{...},Start)
-self._.Schedules[#self._.Schedules+1]=ScheduleID
-return self._.Schedules[#self._.Schedules]
-end
-function BASE:ScheduleRepeat(Start,Repeat,RandomizeFactor,Stop,SchedulerFunction,...)
-self:F2({Start})
-self:T3({...})
-local ObjectName="-"
-ObjectName=self.ClassName..self.ClassID
-self:F3({"ScheduleRepeat: ",ObjectName,Start,Repeat,RandomizeFactor,Stop})
-if not self.Scheduler then
-self.Scheduler=SCHEDULER:New(self)
-end
-local ScheduleID=self.Scheduler:Schedule(
-nil,
-SchedulerFunction,
-{...},
-Start,
-Repeat,
-RandomizeFactor,
-Stop,
-4
-)
-self._.Schedules[#self._.Schedules+1]=ScheduleID
-return self._.Schedules[#self._.Schedules]
-end
-function BASE:ScheduleStop(SchedulerID)
-self:F3({"ScheduleStop:"})
-if self.Scheduler then
-_SCHEDULEDISPATCHER:Stop(self.Scheduler,SchedulerID)
-end
-end
-end
-function BASE:SetState(Object,Key,Value)
-local ClassNameAndID=Object:GetClassNameAndID()
-self.States[ClassNameAndID]=self.States[ClassNameAndID]or{}
-self.States[ClassNameAndID][Key]=Value
-return self.States[ClassNameAndID][Key]
-end
-function BASE:GetState(Object,Key)
-local ClassNameAndID=Object:GetClassNameAndID()
-if self.States[ClassNameAndID]then
-local Value=self.States[ClassNameAndID][Key]or false
-return Value
-end
-return nil
-end
-function BASE:ClearState(Object,StateName)
-local ClassNameAndID=Object:GetClassNameAndID()
-if self.States[ClassNameAndID]then
-self.States[ClassNameAndID][StateName]=nil
-end
-end
-function BASE:TraceOn()
-self:TraceOnOff(true)
-end
-function BASE:TraceOff()
-self:TraceOnOff(false)
-end
-function BASE:TraceOnOff(TraceOnOff)
-if TraceOnOff==false then
-self:I("Tracing in MOOSE is OFF")
-_TraceOnOff=false
-else
-self:I("Tracing in MOOSE is ON")
-_TraceOnOff=true
-end
-end
-function BASE:IsTrace()
-if BASE.Debug and(_TraceAll==true)or(_TraceClass[self.ClassName]or _TraceClassMethod[self.ClassName])then
-return true
-else
-return false
-end
-end
-function BASE:TraceLevel(Level)
-_TraceLevel=Level or 1
-self:I("Tracing level ".._TraceLevel)
-end
-function BASE:TraceAll(TraceAll)
-if TraceAll==false then
-_TraceAll=false
-else
-_TraceAll=true
-end
-if _TraceAll then
-self:I("Tracing all methods in MOOSE ")
-else
-self:I("Switched off tracing all methods in MOOSE")
-end
-end
-function BASE:TraceClass(Class)
-_TraceClass[Class]=true
-_TraceClassMethod[Class]={}
-self:I("Tracing class "..Class)
-end
-function BASE:TraceClassMethod(Class,Method)
-if not _TraceClassMethod[Class]then
-_TraceClassMethod[Class]={}
-_TraceClassMethod[Class].Method={}
-end
-_TraceClassMethod[Class].Method[Method]=true
-self:I("Tracing method "..Method.." of class "..Class)
-end
-function BASE:_F(Arguments,DebugInfoCurrentParam,DebugInfoFromParam)
-if BASE.Debug and(_TraceAll==true)or(_TraceClass[self.ClassName]or _TraceClassMethod[self.ClassName])then
-local DebugInfoCurrent=DebugInfoCurrentParam and DebugInfoCurrentParam or BASE.Debug.getinfo(2,"nl")
-local DebugInfoFrom=DebugInfoFromParam and DebugInfoFromParam or BASE.Debug.getinfo(3,"l")
-local Function="function"
-if DebugInfoCurrent.name then
-Function=DebugInfoCurrent.name
-end
-if _TraceAll==true or _TraceClass[self.ClassName]or _TraceClassMethod[self.ClassName].Method[Function]then
-local LineCurrent=0
-if DebugInfoCurrent.currentline then
-LineCurrent=DebugInfoCurrent.currentline
-end
-local LineFrom=0
-if DebugInfoFrom then
-LineFrom=DebugInfoFrom.currentline
-end
-env.info(string.format("%6d(%6d)/%1s:%30s%05d.%s(%s)",LineCurrent,LineFrom,"F",self.ClassName,self.ClassID,Function,UTILS.BasicSerialize(Arguments)))
-end
-end
-end
-function BASE:F(Arguments)
-if BASE.Debug and _TraceOnOff then
-local DebugInfoCurrent=BASE.Debug.getinfo(2,"nl")
-local DebugInfoFrom=BASE.Debug.getinfo(3,"l")
-if _TraceLevel>=1 then
-self:_F(Arguments,DebugInfoCurrent,DebugInfoFrom)
-end
-end
-end
-function BASE:F2(Arguments)
-if BASE.Debug and _TraceOnOff then
-local DebugInfoCurrent=BASE.Debug.getinfo(2,"nl")
-local DebugInfoFrom=BASE.Debug.getinfo(3,"l")
-if _TraceLevel>=2 then
-self:_F(Arguments,DebugInfoCurrent,DebugInfoFrom)
-end
-end
-end
-function BASE:F3(Arguments)
-if BASE.Debug and _TraceOnOff then
-local DebugInfoCurrent=BASE.Debug.getinfo(2,"nl")
-local DebugInfoFrom=BASE.Debug.getinfo(3,"l")
-if _TraceLevel>=3 then
-self:_F(Arguments,DebugInfoCurrent,DebugInfoFrom)
-end
-end
-end
-function BASE:_T(Arguments,DebugInfoCurrentParam,DebugInfoFromParam)
-if BASE.Debug and(_TraceAll==true)or(_TraceClass[self.ClassName]or _TraceClassMethod[self.ClassName])then
-local DebugInfoCurrent=DebugInfoCurrentParam and DebugInfoCurrentParam or BASE.Debug.getinfo(2,"nl")
-local DebugInfoFrom=DebugInfoFromParam and DebugInfoFromParam or BASE.Debug.getinfo(3,"l")
-local Function="function"
-if DebugInfoCurrent.name then
-Function=DebugInfoCurrent.name
-end
-if _TraceAll==true or _TraceClass[self.ClassName]or _TraceClassMethod[self.ClassName].Method[Function]then
-local LineCurrent=0
-if DebugInfoCurrent.currentline then
-LineCurrent=DebugInfoCurrent.currentline
-end
-local LineFrom=0
-if DebugInfoFrom then
-LineFrom=DebugInfoFrom.currentline
-end
-env.info(string.format("%6d(%6d)/%1s:%30s%05d.%s",LineCurrent,LineFrom,"T",self.ClassName,self.ClassID,UTILS.BasicSerialize(Arguments)))
-end
-end
-end
-function BASE:T(Arguments)
-if BASE.Debug and _TraceOnOff then
-local DebugInfoCurrent=BASE.Debug.getinfo(2,"nl")
-local DebugInfoFrom=BASE.Debug.getinfo(3,"l")
-if _TraceLevel>=1 then
-self:_T(Arguments,DebugInfoCurrent,DebugInfoFrom)
-end
-end
-end
-function BASE:T2(Arguments)
-if BASE.Debug and _TraceOnOff then
-local DebugInfoCurrent=BASE.Debug.getinfo(2,"nl")
-local DebugInfoFrom=BASE.Debug.getinfo(3,"l")
-if _TraceLevel>=2 then
-self:_T(Arguments,DebugInfoCurrent,DebugInfoFrom)
-end
-end
-end
-function BASE:T3(Arguments)
-if BASE.Debug and _TraceOnOff then
-local DebugInfoCurrent=BASE.Debug.getinfo(2,"nl")
-local DebugInfoFrom=BASE.Debug.getinfo(3,"l")
-if _TraceLevel>=3 then
-self:_T(Arguments,DebugInfoCurrent,DebugInfoFrom)
-end
-end
-end
-function BASE:E(Arguments)
-if BASE.Debug then
-local DebugInfoCurrent=BASE.Debug.getinfo(2,"nl")
-local DebugInfoFrom=BASE.Debug.getinfo(3,"l")
-local Function="function"
-if DebugInfoCurrent.name then
-Function=DebugInfoCurrent.name
-end
-local LineCurrent=DebugInfoCurrent.currentline
-local LineFrom=-1
-if DebugInfoFrom then
-LineFrom=DebugInfoFrom.currentline
-end
-env.info(string.format("%6d(%6d)/%1s:%30s%05d.%s(%s)",LineCurrent,LineFrom,"E",self.ClassName,self.ClassID,Function,UTILS.BasicSerialize(Arguments)))
-else
-env.info(string.format("%1s:%30s%05d(%s)","E",self.ClassName,self.ClassID,UTILS.BasicSerialize(Arguments)))
-end
-end
-function BASE:I(Arguments)
-if BASE.Debug then
-local DebugInfoCurrent=BASE.Debug.getinfo(2,"nl")
-local DebugInfoFrom=BASE.Debug.getinfo(3,"l")
-local Function="function"
-if DebugInfoCurrent.name then
-Function=DebugInfoCurrent.name
-end
-local LineCurrent=DebugInfoCurrent.currentline
-local LineFrom=-1
-if DebugInfoFrom then
-LineFrom=DebugInfoFrom.currentline
-end
-env.info(string.format("%6d(%6d)/%1s:%30s%05d.%s(%s)",LineCurrent,LineFrom,"I",self.ClassName,self.ClassID,Function,UTILS.BasicSerialize(Arguments)))
-else
-env.info(string.format("%1s:%30s%05d(%s)","I",self.ClassName,self.ClassID,UTILS.BasicSerialize(Arguments)))
-end
-end
-ASTAR={
-ClassName="ASTAR",
-Debug=nil,
-lid=nil,
-nodes={},
-counter=1,
-Nnodes=0,
-ncost=0,
-ncostcache=0,
-nvalid=0,
-nvalidcache=0,
-}
-ASTAR.INF=1/0
-ASTAR.version="0.4.0"
-function ASTAR:New()
-local self=BASE:Inherit(self,BASE:New())
-self.lid="ASTAR | "
-return self
-end
-function ASTAR:SetStartCoordinate(Coordinate)
-self.startCoord=Coordinate
-return self
-end
-function ASTAR:SetEndCoordinate(Coordinate)
-self.endCoord=Coordinate
-return self
-end
-function ASTAR:GetNodeFromCoordinate(Coordinate)
-local node={}
-node.coordinate=Coordinate
-node.surfacetype=Coordinate:GetSurfaceType()
-node.id=self.counter
-node.valid={}
-node.cost={}
-self.counter=self.counter+1
-return node
-end
-function ASTAR:AddNode(Node)
-self.nodes[Node.id]=Node
-self.Nnodes=self.Nnodes+1
-return self
-end
-function ASTAR:AddNodeFromCoordinate(Coordinate)
-local node=self:GetNodeFromCoordinate(Coordinate)
-self:AddNode(node)
-return node
-end
-function ASTAR:CheckValidSurfaceType(Node,SurfaceTypes)
-if SurfaceTypes then
-if type(SurfaceTypes)~="table"then
-SurfaceTypes={SurfaceTypes}
-end
-for _,surface in pairs(SurfaceTypes)do
-if surface==Node.surfacetype then
-return true
-end
-end
-return false
-else
-return true
-end
-end
-function ASTAR:SetValidNeighbourFunction(NeighbourFunction,...)
-self.ValidNeighbourFunc=NeighbourFunction
-self.ValidNeighbourArg={}
-if arg then
-self.ValidNeighbourArg=arg
-end
-return self
-end
-function ASTAR:SetValidNeighbourLoS(CorridorWidth)
-self:SetValidNeighbourFunction(ASTAR.LoS,CorridorWidth)
-return self
-end
-function ASTAR:SetValidNeighbourDistance(MaxDistance)
-self:SetValidNeighbourFunction(ASTAR.DistMax,MaxDistance)
-return self
-end
-function ASTAR:SetValidNeighbourRoad(MaxDistance)
-self:SetValidNeighbourFunction(ASTAR.Road,MaxDistance)
-return self
-end
-function ASTAR:SetCostFunction(CostFunction,...)
-self.CostFunc=CostFunction
-self.CostArg={}
-if arg then
-self.CostArg=arg
-end
-return self
-end
-function ASTAR:SetCostDist2D()
-self:SetCostFunction(ASTAR.Dist2D)
-return self
-end
-function ASTAR:SetCostDist3D()
-self:SetCostFunction(ASTAR.Dist3D)
-return self
-end
-function ASTAR:SetCostRoad()
-self:SetCostFunction(ASTAR)
-return self
-end
-function ASTAR:CreateGrid(ValidSurfaceTypes,BoxHY,SpaceX,deltaX,deltaY,MarkGrid)
-local Dz=SpaceX or 10000
-local Dx=BoxHY and BoxHY/2 or 20000
-local dz=deltaX or 2000
-local dx=deltaY or dz
-local angle=self.startCoord:HeadingTo(self.endCoord)
-local dist=self.startCoord:Get2DDistance(self.endCoord)+2*Dz
-local co=COORDINATE:New(0,0,0)
-local do1=co:Get2DDistance(self.startCoord)
-local ho1=co:HeadingTo(self.startCoord)
-local xmin=-Dx
-local zmin=-Dz
-local nz=dist/dz+1
-local nx=2*Dx/dx+1
-local text=string.format("Building grid with nx=%d ny=%d => total=%d nodes",nx,nz,nx*nz)
-self:T(self.lid..text)
-for i=1,nx do
-local x=xmin+dx*(i-1)
-for j=1,nz do
-local z=zmin+dz*(j-1)
-local vec3=UTILS.Rotate2D({x=x,y=0,z=z},angle)
-local c=COORDINATE:New(vec3.z,vec3.y,vec3.x):Translate(do1,ho1,true)
-local node=self:GetNodeFromCoordinate(c)
-if self:CheckValidSurfaceType(node,ValidSurfaceTypes)then
-if MarkGrid then
-c:MarkToAll(string.format("i=%d, j=%d surface=%d",i,j,node.surfacetype))
-end
-self:AddNode(node)
-end
-end
-end
-local text=string.format("Done building grid!")
-self:T2(self.lid..text)
-return self
-end
-function ASTAR.LoS(nodeA,nodeB,corridor)
-local offset=1
-local dx=corridor and corridor/2 or nil
-local dy=dx
-local cA=nodeA.coordinate:GetVec3()
-local cB=nodeB.coordinate:GetVec3()
-cA.y=offset
-cB.y=offset
-local los=land.isVisible(cA,cB)
-if los and corridor then
-local heading=nodeA.coordinate:HeadingTo(nodeB.coordinate)
-local Ap=UTILS.VecTranslate(cA,dx,heading+90)
-local Bp=UTILS.VecTranslate(cB,dx,heading+90)
-los=land.isVisible(Ap,Bp)
-if los then
-local Am=UTILS.VecTranslate(cA,dx,heading-90)
-local Bm=UTILS.VecTranslate(cB,dx,heading-90)
-los=land.isVisible(Am,Bm)
-end
-end
-return los
-end
-function ASTAR.Road(nodeA,nodeB)
-local path=land.findPathOnRoads("roads",nodeA.coordinate.x,nodeA.coordinate.z,nodeB.coordinate.x,nodeB.coordinate.z)
-if path then
-return true
-else
-return false
-end
-end
-function ASTAR.DistMax(nodeA,nodeB,distmax)
-distmax=distmax or 2000
-local dist=nodeA.coordinate:Get2DDistance(nodeB.coordinate)
-return dist<=distmax
-end
-function ASTAR.Dist2D(nodeA,nodeB)
-local dist=nodeA.coordinate:Get2DDistance(nodeB)
-return dist
-end
-function ASTAR.Dist3D(nodeA,nodeB)
-local dist=nodeA.coordinate:Get3DDistance(nodeB.coordinate)
-return dist
-end
-function ASTAR.DistRoad(nodeA,nodeB)
-local path=land.findPathOnRoads("roads",nodeA.coordinate.x,nodeA.coordinate.z,nodeB.coordinate.x,nodeB.coordinate.z)
-if path then
-local dist=0
-for i=2,#path do
-local b=path[i]
-local a=path[i-1]
-dist=dist+UTILS.VecDist2D(a,b)
-end
-return dist
-end
-return math.huge
-end
-function ASTAR:FindClosestNode(Coordinate)
-local distMin=math.huge
-local closeNode=nil
-for _,_node in pairs(self.nodes)do
-local node=_node
-local dist=node.coordinate:Get2DDistance(Coordinate)
-if dist1000 then
-self:T(self.lid.."Adding start node to node grid!")
-self:AddNode(node)
-end
-return self
-end
-function ASTAR:FindEndNode()
-local node,dist=self:FindClosestNode(self.endCoord)
-self.endNode=node
-if dist>1000 then
-self:T(self.lid.."Adding end node to node grid!")
-self:AddNode(node)
-end
-return self
-end
-function ASTAR:GetPath(ExcludeStartNode,ExcludeEndNode)
-self:FindStartNode()
-self:FindEndNode()
-local nodes=self.nodes
-local start=self.startNode
-local goal=self.endNode
-local openset={}
-local closedset={}
-local came_from={}
-local g_score={}
-local f_score={}
-openset[start.id]=true
-local Nopen=1
-g_score[start.id]=0
-f_score[start.id]=g_score[start.id]+self:_HeuristicCost(start,goal)
-local T0=timer.getAbsTime()
-local text=string.format("Starting A* pathfinding with %d Nodes",self.Nnodes)
-self:T(self.lid..text)
-local Tstart=UTILS.GetOSTime()
-while Nopen>0 do
-local current=self:_LowestFscore(openset,f_score)
-if current.id==goal.id then
-local path=self:_UnwindPath({},came_from,goal)
-if not ExcludeEndNode then
-table.insert(path,goal)
-end
-if ExcludeStartNode then
-table.remove(path,1)
-end
-local Tstop=UTILS.GetOSTime()
-local dT=nil
-if Tstart and Tstop then
-dT=Tstop-Tstart
-end
-local text=string.format("Found path with %d nodes (%d total)",#path,self.Nnodes)
-if dT then
-text=text..string.format(", OS Time %.6f sec",dT)
-end
-text=text..string.format(", Nvalid=%d [%d cached]",self.nvalid,self.nvalidcache)
-text=text..string.format(", Ncost=%d [%d cached]",self.ncost,self.ncostcache)
-self:T(self.lid..text)
-return path
-end
-openset[current.id]=nil
-Nopen=Nopen-1
-closedset[current.id]=true
-local neighbors=self:_NeighbourNodes(current,nodes)
-for _,neighbor in pairs(neighbors)do
-if self:_NotIn(closedset,neighbor.id)then
-local tentative_g_score=g_score[current.id]+self:_DistNodes(current,neighbor)
-if self:_NotIn(openset,neighbor.id)or tentative_g_score result=%s",tostring(isGen),tostring(isAny),tostring(isAll),tostring(self.negateResult),tostring(result)))
-return result
-end
-function CONDITION:_EvalConditionsAll(functions)
-local gotone=false
-for _,_condition in pairs(functions or{})do
-local condition=_condition
-gotone=true
-local istrue=condition.func(unpack(condition.arg))
-if not istrue then
-return false
-end
-end
-return true
-end
-function CONDITION:_EvalConditionsAny(functions)
-local gotone=false
-for _,_condition in pairs(functions or{})do
-local condition=_condition
-gotone=true
-local istrue=condition.func(unpack(condition.arg))
-if istrue then
-return true
-end
-end
-if gotone then
-return false
-else
-return true
-end
-end
-function CONDITION:_CreateCondition(Ftype,Function,...)
-self.functionCounter=self.functionCounter+1
-local condition={}
-condition.uid=self.functionCounter
-condition.type=Ftype or 0
-condition.persistence=self.defaultPersist
-condition.func=Function
-condition.arg={}
-if arg then
-condition.arg=arg
-end
-return condition
-end
-function CONDITION.IsTimeGreater(Time,Absolute)
-local Tnow=nil
-if Absolute then
-Tnow=timer.getAbsTime()
-else
-Tnow=timer.getTime()
-end
-if Tnow>Time then
-return true
-else
-return false
-end
-return nil
-end
-function CONDITION.IsRandomSuccess(Probability)
-Probability=Probability or 50
-math.random()
-math.random()
-math.random()
-local N=math.random()*100
-if N0 then
-self:RemoveGenericSubEntries(Entry)
-end
-local depth=#Entry.path
-local uuid=Entry.UUID
-local tbl=UTILS.DeepCopy(self.menutree)
-if tbl[depth]then
-for i=depth,#tbl do
-for _id,_uuid in pairs(tbl[i])do
-self:T(_uuid)
-if string.find(_uuid,uuid,1,true)or _uuid==uuid then
-self.menutree[i][_id]=nil
-self.flattree[_uuid]=nil
-end
-end
-end
-end
-return self
-end
-function CLIENTMENUMANAGER:RemoveGenericSubEntries(Entry)
-self:T(self.lid.."RemoveGenericSubEntries")
-local depth=#Entry.path+1
-local uuid=Entry.UUID
-local tbl=UTILS.DeepCopy(self.menutree)
-if tbl[depth]then
-for i=depth,#tbl do
-self:T("Level = "..i)
-for _id,_uuid in pairs(tbl[i])do
-self:T(_uuid)
-if string.find(_uuid,uuid,1,true)then
-self:T("Match for ".._uuid)
-self.menutree[i][_id]=nil
-self.flattree[_uuid]=nil
-end
-end
-end
-end
-return self
-end
-function CLIENTMENUMANAGER:RemoveF10SubEntries(Entry,Client)
-self:T(self.lid.."RemoveSubEntries")
-local Set=self.clientset.Set
-if Client then
-Set={Client}
-end
-for _,_client in pairs(Set)do
-if _client and _client:IsAlive()then
-local playername=_client:GetPlayerName()
-if self.playertree[playername]then
-local centry=self.playertree[playername][Entry.UUID]
-centry:RemoveSubEntries()
-end
-end
-end
-return self
-end
-DATABASE={
-ClassName="DATABASE",
-Templates={
-Units={},
-Groups={},
-Statics={},
-ClientsByName={},
-ClientsByID={},
-},
-UNITS={},
-UNITS_Index={},
-STATICS={},
-GROUPS={},
-PLAYERS={},
-PLAYERSJOINED={},
-PLAYERUNITS={},
-CLIENTS={},
-CARGOS={},
-AIRBASES={},
-COUNTRY_ID={},
-COUNTRY_NAME={},
-NavPoints={},
-PLAYERSETTINGS={},
-ZONENAMES={},
-HITS={},
-DESTROYS={},
-ZONES={},
-ZONES_GOAL={},
-WAREHOUSES={},
-FLIGHTGROUPS={},
-FLIGHTCONTROLS={},
-OPSZONES={},
-PATHLINES={},
-STORAGES={},
-}
-local _DATABASECoalition=
-{
-[1]="Red",
-[2]="Blue",
-[3]="Neutral",
-}
-local _DATABASECategory=
-{
-["plane"]=Unit.Category.AIRPLANE,
-["helicopter"]=Unit.Category.HELICOPTER,
-["vehicle"]=Unit.Category.GROUND_UNIT,
-["ship"]=Unit.Category.SHIP,
-["static"]=Unit.Category.STRUCTURE,
-}
-function DATABASE:New()
-local self=BASE:Inherit(self,BASE:New())
-self:SetEventPriority(1)
-self:HandleEvent(EVENTS.Birth,self._EventOnBirth)
-self:HandleEvent(EVENTS.PlayerEnterUnit,self._EventOnPlayerEnterUnit)
-self:HandleEvent(EVENTS.Dead,self._EventOnDeadOrCrash)
-self:HandleEvent(EVENTS.Crash,self._EventOnDeadOrCrash)
-self:HandleEvent(EVENTS.RemoveUnit,self._EventOnDeadOrCrash)
-self:HandleEvent(EVENTS.Hit,self.AccountHits)
-self:HandleEvent(EVENTS.NewCargo)
-self:HandleEvent(EVENTS.DeleteCargo)
-self:HandleEvent(EVENTS.NewZone)
-self:HandleEvent(EVENTS.DeleteZone)
-self:HandleEvent(EVENTS.PlayerLeaveUnit,self._EventOnPlayerLeaveUnit)
-self:_RegisterTemplates()
-self:_RegisterGroupsAndUnits()
-self:_RegisterClients()
-self:_RegisterStatics()
-self.UNITS_Position=0
-return self
-end
-function DATABASE:FindUnit(UnitName)
-local UnitFound=self.UNITS[UnitName]
-return UnitFound
-end
-function DATABASE:AddUnit(DCSUnitName)
-if not self.UNITS[DCSUnitName]then
-self:T({"Add UNIT:",DCSUnitName})
-self.UNITS[DCSUnitName]=UNIT:Register(DCSUnitName)
-end
-return self.UNITS[DCSUnitName]
-end
-function DATABASE:DeleteUnit(DCSUnitName)
-self.UNITS[DCSUnitName]=nil
-end
-function DATABASE:AddStatic(DCSStaticName)
-if not self.STATICS[DCSStaticName]then
-self.STATICS[DCSStaticName]=STATIC:Register(DCSStaticName)
-return self.STATICS[DCSStaticName]
-end
-return nil
-end
-function DATABASE:DeleteStatic(DCSStaticName)
-self.STATICS[DCSStaticName]=nil
-end
-function DATABASE:FindStatic(StaticName)
-local StaticFound=self.STATICS[StaticName]
-return StaticFound
-end
-function DATABASE:AddAirbase(AirbaseName)
-if not self.AIRBASES[AirbaseName]then
-self.AIRBASES[AirbaseName]=AIRBASE:Register(AirbaseName)
-end
-return self.AIRBASES[AirbaseName]
-end
-function DATABASE:DeleteAirbase(AirbaseName)
-self.AIRBASES[AirbaseName]=nil
-end
-function DATABASE:FindAirbase(AirbaseName)
-local AirbaseFound=self.AIRBASES[AirbaseName]
-return AirbaseFound
-end
-function DATABASE:AddStorage(AirbaseName)
-if not self.STORAGES[AirbaseName]then
-self.STORAGES[AirbaseName]=STORAGE:New(AirbaseName)
-end
-return self.STORAGES[AirbaseName]
-end
-function DATABASE:DeleteStorage(AirbaseName)
-self.STORAGES[AirbaseName]=nil
-end
-function DATABASE:FindStorage(AirbaseName)
-local storage=self.STORAGES[AirbaseName]
-return storage
-end
-do
-function DATABASE:FindZone(ZoneName)
-local ZoneFound=self.ZONES[ZoneName]
-return ZoneFound
-end
-function DATABASE:AddZone(ZoneName,Zone)
-if not self.ZONES[ZoneName]then
-self.ZONES[ZoneName]=Zone
-end
-end
-function DATABASE:DeleteZone(ZoneName)
-self.ZONES[ZoneName]=nil
-end
-function DATABASE:AddPathline(PathlineName,Pathline)
-if not self.PATHLINES[PathlineName]then
-self.PATHLINES[PathlineName]=Pathline
-end
-end
-function DATABASE:FindPathline(PathlineName)
-local pathline=self.PATHLINES[PathlineName]
-return pathline
-end
-function DATABASE:DeletePathline(PathlineName)
-self.PATHLINES[PathlineName]=nil
-return self
-end
-function DATABASE:_RegisterZones()
-for ZoneID,ZoneData in pairs(env.mission.triggers.zones)do
-local ZoneName=ZoneData.name
-local color=ZoneData.color or{1,0,0,0.15}
-local Zone=nil
-if ZoneData.type==0 then
-self:I(string.format("Register ZONE: %s (Circular)",ZoneName))
-Zone=ZONE:New(ZoneName)
-else
-self:I(string.format("Register ZONE: %s (Polygon, Quad)",ZoneName))
-Zone=ZONE_POLYGON:NewFromPointsArray(ZoneName,ZoneData.verticies)
-end
-if Zone then
-Zone.Color=color
-Zone.ZoneID=ZoneData.zoneId
-local ZoneProperties=ZoneData.properties or nil
-Zone.Properties={}
-if ZoneName and ZoneProperties then
-for _,ZoneProp in ipairs(ZoneProperties)do
-if ZoneProp.key then
-Zone.Properties[ZoneProp.key]=ZoneProp.value
-end
-end
-end
-self.ZONENAMES[ZoneName]=ZoneName
-self:AddZone(ZoneName,Zone)
-end
-end
-for ZoneGroupName,ZoneGroup in pairs(self.GROUPS)do
-if ZoneGroupName:match("#ZONE_POLYGON")then
-local ZoneName1=ZoneGroupName:match("(.*)#ZONE_POLYGON")
-local ZoneName2=ZoneGroupName:match(".*#ZONE_POLYGON(.*)")
-local ZoneName=ZoneName1..(ZoneName2 or"")
-self:I(string.format("Register ZONE: %s (Polygon)",ZoneName))
-local Zone_Polygon=ZONE_POLYGON:New(ZoneName,ZoneGroup)
-Zone_Polygon:SetColor({1,0,0},0.15)
-self.ZONENAMES[ZoneName]=ZoneName
-self:AddZone(ZoneName,Zone_Polygon)
-end
-end
-if env.mission.drawings and env.mission.drawings.layers then
-for layerID,layerData in pairs(env.mission.drawings.layers or{})do
-for objectID,objectData in pairs(layerData.objects or{})do
-if objectData.polygonMode and(objectData.polygonMode=="free")and objectData.points and#objectData.points>=4 then
-local ZoneName=objectData.name or"Unknown free Polygon Drawing"
-local vec2={x=objectData.mapX,y=objectData.mapY}
-local points=UTILS.DeepCopy(objectData.points)
-for i,_point in pairs(points)do
-local point=_point
-points[i]=UTILS.Vec2Add(point,vec2)
-end
-table.remove(points,#points)
-self:I(string.format("Register ZONE: %s (Polygon (free) drawing with %d vertices)",ZoneName,#points))
-local Zone=ZONE_POLYGON:NewFromPointsArray(ZoneName,points)
-Zone:SetColor({1,0,0},0.15)
-Zone:SetFillColor({1,0,0},0.15)
-if objectData.colorString then
-local color=string.gsub(objectData.colorString,"^0x","")
-local r=tonumber(string.sub(color,1,2),16)/255
-local g=tonumber(string.sub(color,3,4),16)/255
-local b=tonumber(string.sub(color,5,6),16)/255
-local a=tonumber(string.sub(color,7,8),16)/255
-Zone:SetColor({r,g,b},a)
-end
-if objectData.fillColorString then
-local color=string.gsub(objectData.colorString,"^0x","")
-local r=tonumber(string.sub(color,1,2),16)/255
-local g=tonumber(string.sub(color,3,4),16)/255
-local b=tonumber(string.sub(color,5,6),16)/255
-local a=tonumber(string.sub(color,7,8),16)/255
-Zone:SetFillColor({r,g,b},a)
-end
-self.ZONENAMES[ZoneName]=ZoneName
-self:AddZone(ZoneName,Zone)
-elseif objectData.polygonMode and objectData.polygonMode=="rect"then
-local ZoneName=objectData.name or"Unknown rect Polygon Drawing"
-local vec2={x=objectData.mapX,y=objectData.mapY}
-local w=objectData.width
-local h=objectData.height
-local points={}
-points[1]={x=vec2.x-h/2,y=vec2.y+w/2}
-points[2]={x=vec2.x+h/2,y=vec2.y+w/2}
-points[3]={x=vec2.x+h/2,y=vec2.y-w/2}
-points[4]={x=vec2.x-h/2,y=vec2.y-w/2}
-self:I(string.format("Register ZONE: %s (Polygon (rect) drawing with %d vertices)",ZoneName,#points))
-local Zone=ZONE_POLYGON:NewFromPointsArray(ZoneName,points)
-Zone:SetColor({1,0,0},0.15)
-if objectData.colorString then
-local color=string.gsub(objectData.colorString,"^0x","")
-local r=tonumber(string.sub(color,1,2),16)/255
-local g=tonumber(string.sub(color,3,4),16)/255
-local b=tonumber(string.sub(color,5,6),16)/255
-local a=tonumber(string.sub(color,7,8),16)/255
-Zone:SetColor({r,g,b},a)
-end
-if objectData.fillColorString then
-local color=string.gsub(objectData.colorString,"^0x","")
-local r=tonumber(string.sub(color,1,2),16)/255
-local g=tonumber(string.sub(color,3,4),16)/255
-local b=tonumber(string.sub(color,5,6),16)/255
-local a=tonumber(string.sub(color,7,8),16)/255
-Zone:SetFillColor({r,g,b},a)
-end
-self.ZONENAMES[ZoneName]=ZoneName
-self:AddZone(ZoneName,Zone)
-elseif objectData.lineMode and(objectData.lineMode=="segments"or objectData.lineMode=="segment"or objectData.lineMode=="free")and objectData.points and#objectData.points>=2 then
-local Name=objectData.name or"Unknown Line Drawing"
-local vec2={x=objectData.mapX,y=objectData.mapY}
-local points=UTILS.DeepCopy(objectData.points)
-for i,_point in pairs(points)do
-local point=_point
-points[i]=UTILS.Vec2Add(point,vec2)
-end
-self:I(string.format("Register PATHLINE: %s (Line drawing with %d points)",Name,#points))
-local Pathline=PATHLINE:NewFromVec2Array(Name,points)
-self:AddPathline(Name,Pathline)
-end
-end
-end
-end
-end
-end
-do
-function DATABASE:FindZoneGoal(ZoneName)
-local ZoneFound=self.ZONES_GOAL[ZoneName]
-return ZoneFound
-end
-function DATABASE:AddZoneGoal(ZoneName,Zone)
-if not self.ZONES_GOAL[ZoneName]then
-self.ZONES_GOAL[ZoneName]=Zone
-end
-end
-function DATABASE:DeleteZoneGoal(ZoneName)
-self.ZONES_GOAL[ZoneName]=nil
-end
-end
-do
-function DATABASE:FindOpsZone(ZoneName)
-local ZoneFound=self.OPSZONES[ZoneName]
-return ZoneFound
-end
-function DATABASE:AddOpsZone(OpsZone)
-if OpsZone then
-local ZoneName=OpsZone:GetName()
-if not self.OPSZONES[ZoneName]then
-self.OPSZONES[ZoneName]=OpsZone
-end
-end
-end
-function DATABASE:DeleteOpsZone(ZoneName)
-self.OPSZONES[ZoneName]=nil
-end
-end
-do
-function DATABASE:AddCargo(Cargo)
-if not self.CARGOS[Cargo.Name]then
-self.CARGOS[Cargo.Name]=Cargo
-end
-end
-function DATABASE:DeleteCargo(CargoName)
-self.CARGOS[CargoName]=nil
-end
-function DATABASE:FindCargo(CargoName)
-local CargoFound=self.CARGOS[CargoName]
-return CargoFound
-end
-function DATABASE:IsCargo(TemplateName)
-TemplateName=env.getValueDictByKey(TemplateName)
-local Cargo=TemplateName:match("#(CARGO)")
-return Cargo and Cargo=="CARGO"
-end
-function DATABASE:_RegisterCargos()
-local Groups=UTILS.DeepCopy(self.GROUPS)
-for CargoGroupName,CargoGroup in pairs(Groups)do
-if self:IsCargo(CargoGroupName)then
-local CargoInfo=CargoGroupName:match("#CARGO(.*)")
-local CargoParam=CargoInfo and CargoInfo:match("%((.*)%)")
-local CargoName1=CargoGroupName:match("(.*)#CARGO%(.*%)")
-local CargoName2=CargoGroupName:match(".*#CARGO%(.*%)(.*)")
-local CargoName=CargoName1..(CargoName2 or"")
-local Type=CargoParam and CargoParam:match("T=([%a%d ]+),?")
-local Name=CargoParam and CargoParam:match("N=([%a%d]+),?")or CargoName
-local LoadRadius=CargoParam and tonumber(CargoParam:match("RR=([%a%d]+),?"))
-local NearRadius=CargoParam and tonumber(CargoParam:match("NR=([%a%d]+),?"))
-self:I({"Register CargoGroup:",Type=Type,Name=Name,LoadRadius=LoadRadius,NearRadius=NearRadius})
-CARGO_GROUP:New(CargoGroup,Type,Name,LoadRadius,NearRadius)
-end
-end
-for CargoStaticName,CargoStatic in pairs(self.STATICS)do
-if self:IsCargo(CargoStaticName)then
-local CargoInfo=CargoStaticName:match("#CARGO(.*)")
-local CargoParam=CargoInfo and CargoInfo:match("%((.*)%)")
-local CargoName=CargoStaticName:match("(.*)#CARGO")
-local Type=CargoParam and CargoParam:match("T=([%a%d ]+),?")
-local Category=CargoParam and CargoParam:match("C=([%a%d ]+),?")
-local Name=CargoParam and CargoParam:match("N=([%a%d]+),?")or CargoName
-local LoadRadius=CargoParam and tonumber(CargoParam:match("RR=([%a%d]+),?"))
-local NearRadius=CargoParam and tonumber(CargoParam:match("NR=([%a%d]+),?"))
-if Category=="SLING"then
-self:I({"Register CargoSlingload:",Type=Type,Name=Name,LoadRadius=LoadRadius,NearRadius=NearRadius})
-CARGO_SLINGLOAD:New(CargoStatic,Type,Name,LoadRadius,NearRadius)
-else
-if Category=="CRATE"then
-self:I({"Register CargoCrate:",Type=Type,Name=Name,LoadRadius=LoadRadius,NearRadius=NearRadius})
-CARGO_CRATE:New(CargoStatic,Type,Name,LoadRadius,NearRadius)
-end
-end
-end
-end
-end
-end
-function DATABASE:FindClient(ClientName)
-local ClientFound=self.CLIENTS[ClientName]
-return ClientFound
-end
-function DATABASE:AddClient(ClientName)
-if not self.CLIENTS[ClientName]then
-self.CLIENTS[ClientName]=CLIENT:Register(ClientName)
-end
-return self.CLIENTS[ClientName]
-end
-function DATABASE:FindGroup(GroupName)
-local GroupFound=self.GROUPS[GroupName]
-return GroupFound
-end
-function DATABASE:AddGroup(GroupName)
-if not self.GROUPS[GroupName]then
-self:T({"Add GROUP:",GroupName})
-self.GROUPS[GroupName]=GROUP:Register(GroupName)
-end
-return self.GROUPS[GroupName]
-end
-function DATABASE:AddPlayer(UnitName,PlayerName)
-if PlayerName then
-self:T({"Add player for unit:",UnitName,PlayerName})
-self.PLAYERS[PlayerName]=UnitName
-self.PLAYERUNITS[PlayerName]=self:FindUnit(UnitName)
-self.PLAYERSJOINED[PlayerName]=PlayerName
-end
-end
-function DATABASE:DeletePlayer(UnitName,PlayerName)
-if PlayerName then
-self:T({"Clean player:",PlayerName})
-self.PLAYERS[PlayerName]=nil
-self.PLAYERUNITS[PlayerName]=nil
-end
-end
-function DATABASE:GetPlayers()
-return self.PLAYERS
-end
-function DATABASE:GetPlayerUnits()
-return self.PLAYERUNITS
-end
-function DATABASE:GetPlayersJoined()
-return self.PLAYERSJOINED
-end
-function DATABASE:Spawn(SpawnTemplate)
-self:F(SpawnTemplate.name)
-self:T({SpawnTemplate.SpawnCountryID,SpawnTemplate.SpawnCategoryID})
-local SpawnCoalitionID=SpawnTemplate.CoalitionID
-local SpawnCountryID=SpawnTemplate.CountryID
-local SpawnCategoryID=SpawnTemplate.CategoryID
-SpawnTemplate.CoalitionID=nil
-SpawnTemplate.CountryID=nil
-SpawnTemplate.CategoryID=nil
-self:_RegisterGroupTemplate(SpawnTemplate,SpawnCoalitionID,SpawnCategoryID,SpawnCountryID)
-self:T3(SpawnTemplate)
-coalition.addGroup(SpawnCountryID,SpawnCategoryID,SpawnTemplate)
-SpawnTemplate.CoalitionID=SpawnCoalitionID
-SpawnTemplate.CountryID=SpawnCountryID
-SpawnTemplate.CategoryID=SpawnCategoryID
-local SpawnGroup=self:AddGroup(SpawnTemplate.name)
-for UnitID,UnitData in pairs(SpawnTemplate.units)do
-self:AddUnit(UnitData.name)
-end
-return SpawnGroup
-end
-function DATABASE:SetStatusGroup(GroupName,Status)
-self:F2(Status)
-self.Templates.Groups[GroupName].Status=Status
-end
-function DATABASE:GetStatusGroup(GroupName)
-self:F2(GroupName)
-if self.Templates.Groups[GroupName]then
-return self.Templates.Groups[GroupName].Status
-else
-return""
-end
-end
-function DATABASE:_RegisterGroupTemplate(GroupTemplate,CoalitionSide,CategoryID,CountryID,GroupName)
-local GroupTemplateName=GroupName or env.getValueDictByKey(GroupTemplate.name)
-if not self.Templates.Groups[GroupTemplateName]then
-self.Templates.Groups[GroupTemplateName]={}
-self.Templates.Groups[GroupTemplateName].Status=nil
-end
-if GroupTemplate.route and GroupTemplate.route.spans then
-GroupTemplate.route.spans=nil
-end
-GroupTemplate.CategoryID=CategoryID
-GroupTemplate.CoalitionID=CoalitionSide
-GroupTemplate.CountryID=CountryID
-self.Templates.Groups[GroupTemplateName].GroupName=GroupTemplateName
-self.Templates.Groups[GroupTemplateName].Template=GroupTemplate
-self.Templates.Groups[GroupTemplateName].groupId=GroupTemplate.groupId
-self.Templates.Groups[GroupTemplateName].UnitCount=#GroupTemplate.units
-self.Templates.Groups[GroupTemplateName].Units=GroupTemplate.units
-self.Templates.Groups[GroupTemplateName].CategoryID=CategoryID
-self.Templates.Groups[GroupTemplateName].CoalitionID=CoalitionSide
-self.Templates.Groups[GroupTemplateName].CountryID=CountryID
-local UnitNames={}
-for unit_num,UnitTemplate in pairs(GroupTemplate.units)do
-UnitTemplate.name=env.getValueDictByKey(UnitTemplate.name)
-self.Templates.Units[UnitTemplate.name]={}
-self.Templates.Units[UnitTemplate.name].UnitName=UnitTemplate.name
-self.Templates.Units[UnitTemplate.name].Template=UnitTemplate
-self.Templates.Units[UnitTemplate.name].GroupName=GroupTemplateName
-self.Templates.Units[UnitTemplate.name].GroupTemplate=GroupTemplate
-self.Templates.Units[UnitTemplate.name].GroupId=GroupTemplate.groupId
-self.Templates.Units[UnitTemplate.name].CategoryID=CategoryID
-self.Templates.Units[UnitTemplate.name].CoalitionID=CoalitionSide
-self.Templates.Units[UnitTemplate.name].CountryID=CountryID
-if UnitTemplate.skill and(UnitTemplate.skill=="Client"or UnitTemplate.skill=="Player")then
-self.Templates.ClientsByName[UnitTemplate.name]=UnitTemplate
-self.Templates.ClientsByName[UnitTemplate.name].CategoryID=CategoryID
-self.Templates.ClientsByName[UnitTemplate.name].CoalitionID=CoalitionSide
-self.Templates.ClientsByName[UnitTemplate.name].CountryID=CountryID
-self.Templates.ClientsByID[UnitTemplate.unitId]=UnitTemplate
-end
-UnitNames[#UnitNames+1]=self.Templates.Units[UnitTemplate.name].UnitName
-end
-self:T({Group=self.Templates.Groups[GroupTemplateName].GroupName,
-Coalition=self.Templates.Groups[GroupTemplateName].CoalitionID,
-Category=self.Templates.Groups[GroupTemplateName].CategoryID,
-Country=self.Templates.Groups[GroupTemplateName].CountryID,
-Units=UnitNames
-}
-)
-end
-function DATABASE:GetGroupTemplate(GroupName)
-local GroupTemplate=self.Templates.Groups[GroupName].Template
-GroupTemplate.SpawnCoalitionID=self.Templates.Groups[GroupName].CoalitionID
-GroupTemplate.SpawnCategoryID=self.Templates.Groups[GroupName].CategoryID
-GroupTemplate.SpawnCountryID=self.Templates.Groups[GroupName].CountryID
-return GroupTemplate
-end
-function DATABASE:_RegisterStaticTemplate(StaticTemplate,CoalitionID,CategoryID,CountryID)
-local StaticTemplate=UTILS.DeepCopy(StaticTemplate)
-local StaticTemplateGroupName=env.getValueDictByKey(StaticTemplate.name)
-local StaticTemplateName=StaticTemplate.units[1].name
-self.Templates.Statics[StaticTemplateName]=self.Templates.Statics[StaticTemplateName]or{}
-StaticTemplate.CategoryID=CategoryID
-StaticTemplate.CoalitionID=CoalitionID
-StaticTemplate.CountryID=CountryID
-self.Templates.Statics[StaticTemplateName].StaticName=StaticTemplateGroupName
-self.Templates.Statics[StaticTemplateName].GroupTemplate=StaticTemplate
-self.Templates.Statics[StaticTemplateName].UnitTemplate=StaticTemplate.units[1]
-self.Templates.Statics[StaticTemplateName].CategoryID=CategoryID
-self.Templates.Statics[StaticTemplateName].CoalitionID=CoalitionID
-self.Templates.Statics[StaticTemplateName].CountryID=CountryID
-self:T({Static=self.Templates.Statics[StaticTemplateName].StaticName,
-Coalition=self.Templates.Statics[StaticTemplateName].CoalitionID,
-Category=self.Templates.Statics[StaticTemplateName].CategoryID,
-Country=self.Templates.Statics[StaticTemplateName].CountryID
-}
-)
-self:AddStatic(StaticTemplateName)
-return self
-end
-function DATABASE:GetStaticGroupTemplate(StaticName)
-if self.Templates.Statics[StaticName]then
-local StaticTemplate=self.Templates.Statics[StaticName].GroupTemplate
-return StaticTemplate,self.Templates.Statics[StaticName].CoalitionID,self.Templates.Statics[StaticName].CategoryID,self.Templates.Statics[StaticName].CountryID
-else
-self:E("ERROR: Static group template does NOT exist for static "..tostring(StaticName))
-return nil
-end
-end
-function DATABASE:GetStaticUnitTemplate(StaticName)
-if self.Templates.Statics[StaticName]then
-local UnitTemplate=self.Templates.Statics[StaticName].UnitTemplate
-return UnitTemplate,self.Templates.Statics[StaticName].CoalitionID,self.Templates.Statics[StaticName].CategoryID,self.Templates.Statics[StaticName].CountryID
-else
-self:E("ERROR: Static unit template does NOT exist for static "..tostring(StaticName))
-return nil
-end
-end
-function DATABASE:GetGroupNameFromUnitName(UnitName)
-if self.Templates.Units[UnitName]then
-return self.Templates.Units[UnitName].GroupName
-else
-self:E("ERROR: Unit template does not exist for unit "..tostring(UnitName))
-return nil
-end
-end
-function DATABASE:GetGroupTemplateFromUnitName(UnitName)
-if self.Templates.Units[UnitName]then
-return self.Templates.Units[UnitName].GroupTemplate
-else
-self:E("ERROR: Unit template does not exist for unit "..tostring(UnitName))
-return nil
-end
-end
-function DATABASE:GetUnitTemplateFromUnitName(UnitName)
-if self.Templates.Units[UnitName]then
-return self.Templates.Units[UnitName]
-else
-self:E("ERROR: Unit template does not exist for unit "..tostring(UnitName))
-return nil
-end
-end
-function DATABASE:GetCoalitionFromClientTemplate(ClientName)
-return self.Templates.ClientsByName[ClientName].CoalitionID
-end
-function DATABASE:GetCategoryFromClientTemplate(ClientName)
-return self.Templates.ClientsByName[ClientName].CategoryID
-end
-function DATABASE:GetCountryFromClientTemplate(ClientName)
-return self.Templates.ClientsByName[ClientName].CountryID
-end
-function DATABASE:GetCoalitionFromAirbase(AirbaseName)
-return self.AIRBASES[AirbaseName]:GetCoalition()
-end
-function DATABASE:GetCategoryFromAirbase(AirbaseName)
-return self.AIRBASES[AirbaseName]:GetAirbaseCategory()
-end
-function DATABASE:_RegisterPlayers()
-local CoalitionsData={AlivePlayersRed=coalition.getPlayers(coalition.side.RED),AlivePlayersBlue=coalition.getPlayers(coalition.side.BLUE),AlivePlayersNeutral=coalition.getPlayers(coalition.side.NEUTRAL)}
-for CoalitionId,CoalitionData in pairs(CoalitionsData)do
-for UnitId,UnitData in pairs(CoalitionData)do
-self:T3({"UnitData:",UnitData})
-if UnitData and UnitData:isExist()then
-local UnitName=UnitData:getName()
-local PlayerName=UnitData:getPlayerName()
-if not self.PLAYERS[PlayerName]then
-self:I({"Add player for unit:",UnitName,PlayerName})
-self:AddPlayer(UnitName,PlayerName)
-end
-end
-end
-end
-return self
-end
-function DATABASE:_RegisterGroupsAndUnits()
-local CoalitionsData={GroupsRed=coalition.getGroups(coalition.side.RED),GroupsBlue=coalition.getGroups(coalition.side.BLUE),GroupsNeutral=coalition.getGroups(coalition.side.NEUTRAL)}
-for CoalitionId,CoalitionData in pairs(CoalitionsData)do
-for DCSGroupId,DCSGroup in pairs(CoalitionData)do
-if DCSGroup:isExist()then
-local DCSGroupName=DCSGroup:getName()
-self:I(string.format("Register Group: %s",tostring(DCSGroupName)))
-self:AddGroup(DCSGroupName)
-for DCSUnitId,DCSUnit in pairs(DCSGroup:getUnits())do
-local DCSUnitName=DCSUnit:getName()
-self:I(string.format("Register Unit: %s",tostring(DCSUnitName)))
-self:AddUnit(DCSUnitName)
-end
-else
-self:E({"Group does not exist: ",DCSGroup})
-end
-end
-end
-return self
-end
-function DATABASE:_RegisterClients()
-for ClientName,ClientTemplate in pairs(self.Templates.ClientsByName)do
-self:I(string.format("Register Client: %s",tostring(ClientName)))
-local client=self:AddClient(ClientName)
-client.SpawnCoord=COORDINATE:New(ClientTemplate.x,ClientTemplate.alt,ClientTemplate.y)
-end
-return self
-end
-function DATABASE:_RegisterStatics()
-local CoalitionsData={GroupsRed=coalition.getStaticObjects(coalition.side.RED),GroupsBlue=coalition.getStaticObjects(coalition.side.BLUE),GroupsNeutral=coalition.getStaticObjects(coalition.side.NEUTRAL)}
-for CoalitionId,CoalitionData in pairs(CoalitionsData)do
-for DCSStaticId,DCSStatic in pairs(CoalitionData)do
-if DCSStatic:isExist()then
-local DCSStaticName=DCSStatic:getName()
-self:I(string.format("Register Static: %s",tostring(DCSStaticName)))
-self:AddStatic(DCSStaticName)
-else
-self:E({"Static does not exist: ",DCSStatic})
-end
-end
-end
-return self
-end
-function DATABASE:_RegisterAirbases()
-for DCSAirbaseId,DCSAirbase in pairs(world.getAirbases())do
-self:_RegisterAirbase(DCSAirbase)
-end
-return self
-end
-function DATABASE:_RegisterAirbase(airbase)
-if airbase then
-local DCSAirbaseName=airbase:getName()
-local airbaseID=airbase:getID()
-local airbase=self:AddAirbase(DCSAirbaseName)
-local airbaseUID=airbase:GetID(true)
-local text=string.format("Register %s: %s (UID=%d), Runways=%d, Parking=%d [",AIRBASE.CategoryName[airbase.category],tostring(DCSAirbaseName),airbaseUID,#airbase.runways,airbase.NparkingTotal)
-for _,terminalType in pairs(AIRBASE.TerminalType)do
-if airbase.NparkingTerminal and airbase.NparkingTerminal[terminalType]then
-text=text..string.format("%d=%d ",terminalType,airbase.NparkingTerminal[terminalType])
-end
-end
-text=text.."]"
-self:I(text)
-end
-return self
-end
-function DATABASE:_EventOnBirth(Event)
-self:F({Event})
-if Event.IniDCSUnit then
-if Event.IniObjectCategory==Object.Category.STATIC then
-self:AddStatic(Event.IniDCSUnitName)
-else
-if Event.IniObjectCategory==Object.Category.UNIT then
-self:AddUnit(Event.IniDCSUnitName)
-self:AddGroup(Event.IniDCSGroupName)
-local DCSAirbase=Airbase.getByName(Event.IniDCSUnitName)
-if DCSAirbase then
-self:I(string.format("Adding airbase %s",tostring(Event.IniDCSUnitName)))
-self:AddAirbase(Event.IniDCSUnitName)
-end
-end
-end
-if Event.IniObjectCategory==Object.Category.UNIT then
-Event.IniUnit=self:FindUnit(Event.IniDCSUnitName)
-Event.IniGroup=self:FindGroup(Event.IniDCSGroupName)
-local client=self.CLIENTS[Event.IniDCSUnitName]
-if client then
-end
-local PlayerName=Event.IniUnit:GetPlayerName()
-if PlayerName then
-self:I(string.format("Player '%s' joined unit '%s' of group '%s'",tostring(PlayerName),tostring(Event.IniDCSUnitName),tostring(Event.IniDCSGroupName)))
-if not client then
-client=self:AddClient(Event.IniDCSUnitName)
-end
-client:AddPlayer(PlayerName)
-if not self.PLAYERS[PlayerName]then
-self:AddPlayer(Event.IniUnitName,PlayerName)
-end
-local Settings=SETTINGS:Set(PlayerName)
-Settings:SetPlayerMenu(Event.IniUnit)
-self:CreateEventPlayerEnterAircraft(Event.IniUnit)
-end
-end
-end
-end
-function DATABASE:_EventOnDeadOrCrash(Event)
-if Event.IniDCSUnit then
-local name=Event.IniDCSUnitName
-if Event.IniObjectCategory==3 then
-if self.STATICS[Event.IniDCSUnitName]then
-self:DeleteStatic(Event.IniDCSUnitName)
-end
-if self.UNITS[Event.IniDCSUnitName]then
-self:T("STATIC Event for UNIT "..tostring(Event.IniDCSUnitName))
-local DCSUnit=_DATABASE:FindUnit(Event.IniDCSUnitName)
-self:T({DCSUnit})
-if DCSUnit then
-return
-end
-end
-else
-if Event.IniObjectCategory==1 then
-if self.UNITS[Event.IniDCSUnitName]then
-self:DeleteUnit(Event.IniDCSUnitName)
-end
-local client=self.CLIENTS[name]
-if client then
-client:RemovePlayers()
-end
-end
-end
-local airbase=self.AIRBASES[Event.IniDCSUnitName]
-if airbase and(airbase:IsHelipad()or airbase:IsShip())then
-self:DeleteAirbase(Event.IniDCSUnitName)
-end
-end
-self:AccountDestroys(Event)
-end
-function DATABASE:_EventOnPlayerEnterUnit(Event)
-self:F2({Event})
-if Event.IniDCSUnit then
-if Event.IniObjectCategory==1 and Event.IniGroup and Event.IniGroup:IsGround()then
-local IsPlayer=Event.IniDCSUnit:getPlayerName()
-if IsPlayer then
-self:I(string.format("Player '%s' joined GROUND unit '%s' of group '%s'",tostring(Event.IniPlayerName),tostring(Event.IniDCSUnitName),tostring(Event.IniDCSGroupName)))
-local client=self.CLIENTS[Event.IniDCSUnitName]
-if not client then
-client=self:AddClient(Event.IniDCSUnitName)
-end
-client:AddPlayer(Event.IniPlayerName)
-if not self.PLAYERS[Event.IniPlayerName]then
-self:AddPlayer(Event.IniUnitName,Event.IniPlayerName)
-end
-local Settings=SETTINGS:Set(Event.IniPlayerName)
-Settings:SetPlayerMenu(Event.IniUnit)
-end
-end
-end
-end
-function DATABASE:_EventOnPlayerLeaveUnit(Event)
-self:F2({Event})
-local function FindPlayerName(UnitName)
-local playername=nil
-for _name,_unitname in pairs(self.PLAYERS)do
-if _unitname==UnitName then
-playername=_name
-break
-end
-end
-return playername
-end
-if Event.IniUnit then
-if Event.IniObjectCategory==1 then
-local PlayerName=Event.IniUnit:GetPlayerName()or FindPlayerName(Event.IniUnitName)
-if PlayerName then
-self:I(string.format("Player '%s' left unit %s",tostring(PlayerName),tostring(Event.IniUnitName)))
-local Settings=SETTINGS:Set(PlayerName)
-Settings:RemovePlayerMenu(Event.IniUnit)
-self:DeletePlayer(Event.IniUnit,PlayerName)
-local client=self.CLIENTS[Event.IniDCSUnitName]
-if client then
-client:RemovePlayer(PlayerName)
-end
-end
-end
-end
-end
-function DATABASE:ForEach(IteratorFunction,FinalizeFunction,arg,Set)
-self:F2(arg)
-local function CoRoutine()
-local Count=0
-for ObjectID,Object in pairs(Set)do
-self:T2(Object)
-IteratorFunction(Object,unpack(arg))
-Count=Count+1
-end
-return true
-end
-local co=CoRoutine
-local function Schedule()
-local status,res=co()
-self:T3({status,res})
-if status==false then
-error(res)
-end
-if res==false then
-return true
-end
-if FinalizeFunction then
-FinalizeFunction(unpack(arg))
-end
-return false
-end
-Schedule()
-return self
-end
-function DATABASE:ForEachStatic(IteratorFunction,FinalizeFunction,...)
-self:F2(arg)
-self:ForEach(IteratorFunction,FinalizeFunction,arg,self.STATICS)
-return self
-end
-function DATABASE:ForEachUnit(IteratorFunction,FinalizeFunction,...)
-self:F2(arg)
-self:ForEach(IteratorFunction,FinalizeFunction,arg,self.UNITS)
-return self
-end
-function DATABASE:ForEachGroup(IteratorFunction,FinalizeFunction,...)
-self:F2(arg)
-self:ForEach(IteratorFunction,FinalizeFunction,arg,self.GROUPS)
-return self
-end
-function DATABASE:ForEachPlayer(IteratorFunction,FinalizeFunction,...)
-self:F2(arg)
-self:ForEach(IteratorFunction,FinalizeFunction,arg,self.PLAYERS)
-return self
-end
-function DATABASE:ForEachPlayerJoined(IteratorFunction,FinalizeFunction,...)
-self:F2(arg)
-self:ForEach(IteratorFunction,FinalizeFunction,arg,self.PLAYERSJOINED)
-return self
-end
-function DATABASE:ForEachPlayerUnit(IteratorFunction,FinalizeFunction,...)
-self:F2(arg)
-self:ForEach(IteratorFunction,FinalizeFunction,arg,self.PLAYERUNITS)
-return self
-end
-function DATABASE:ForEachClient(IteratorFunction,FinalizeFunction,...)
-self:F2(arg)
-self:ForEach(IteratorFunction,FinalizeFunction,arg,self.CLIENTS)
-return self
-end
-function DATABASE:ForEachCargo(IteratorFunction,FinalizeFunction,...)
-self:F2(arg)
-self:ForEach(IteratorFunction,FinalizeFunction,arg,self.CARGOS)
-return self
-end
-function DATABASE:OnEventNewCargo(EventData)
-self:F2({EventData})
-if EventData.Cargo then
-self:AddCargo(EventData.Cargo)
-end
-end
-function DATABASE:OnEventDeleteCargo(EventData)
-self:F2({EventData})
-if EventData.Cargo then
-self:DeleteCargo(EventData.Cargo.Name)
-end
-end
-function DATABASE:OnEventNewZone(EventData)
-self:F2({EventData})
-if EventData.Zone then
-self:AddZone(EventData.Zone.ZoneName,EventData.Zone)
-end
-end
-function DATABASE:OnEventDeleteZone(EventData)
-self:F2({EventData})
-if EventData.Zone then
-self:DeleteZone(EventData.Zone.ZoneName)
-end
-end
-function DATABASE:GetPlayerSettings(PlayerName)
-self:F2({PlayerName})
-return self.PLAYERSETTINGS[PlayerName]
-end
-function DATABASE:SetPlayerSettings(PlayerName,Settings)
-self:F2({PlayerName,Settings})
-self.PLAYERSETTINGS[PlayerName]=Settings
-end
-function DATABASE:AddOpsGroup(opsgroup)
-self.FLIGHTGROUPS[opsgroup.groupname]=opsgroup
-end
-function DATABASE:GetOpsGroup(groupname)
-if type(groupname)=="string"then
-else
-groupname=groupname:GetName()
-end
-return self.FLIGHTGROUPS[groupname]
-end
-function DATABASE:FindOpsGroup(groupname)
-if type(groupname)=="string"then
-else
-groupname=groupname:GetName()
-end
-return self.FLIGHTGROUPS[groupname]
-end
-function DATABASE:FindOpsGroupFromUnit(unitname)
-local unit=nil
-local groupname
-if type(unitname)=="string"then
-unit=UNIT:FindByName(unitname)
-else
-unit=unitname
-end
-if unit then
-groupname=unit:GetGroup():GetName()
-end
-if groupname then
-return self.FLIGHTGROUPS[groupname]
-else
-return nil
-end
-end
-function DATABASE:AddFlightControl(flightcontrol)
-self:F2({flightcontrol})
-self.FLIGHTCONTROLS[flightcontrol.airbasename]=flightcontrol
-end
-function DATABASE:GetFlightControl(airbasename)
-return self.FLIGHTCONTROLS[airbasename]
-end
-function DATABASE:_RegisterTemplates()
-self:F2()
-self.Navpoints={}
-self.UNITS={}
-for CoalitionName,coa_data in pairs(env.mission.coalition)do
-self:T({CoalitionName=CoalitionName})
-if(CoalitionName=='red'or CoalitionName=='blue'or CoalitionName=='neutrals')and type(coa_data)=='table'then
-local CoalitionSide=coalition.side[string.upper(CoalitionName)]
-if CoalitionName=="red"then
-CoalitionSide=coalition.side.RED
-elseif CoalitionName=="blue"then
-CoalitionSide=coalition.side.BLUE
-else
-CoalitionSide=coalition.side.NEUTRAL
-end
-self.Navpoints[CoalitionName]={}
-if coa_data.nav_points then
-for nav_ind,nav_data in pairs(coa_data.nav_points)do
-if type(nav_data)=='table'then
-self.Navpoints[CoalitionName][nav_ind]=UTILS.DeepCopy(nav_data)
-self.Navpoints[CoalitionName][nav_ind]['name']=nav_data.callsignStr
-self.Navpoints[CoalitionName][nav_ind]['point']={}
-self.Navpoints[CoalitionName][nav_ind]['point']['x']=nav_data.x
-self.Navpoints[CoalitionName][nav_ind]['point']['y']=0
-self.Navpoints[CoalitionName][nav_ind]['point']['z']=nav_data.y
-end
-end
-end
-if coa_data.country then
-for cntry_id,cntry_data in pairs(coa_data.country)do
-local CountryName=string.upper(cntry_data.name)
-local CountryID=cntry_data.id
-self.COUNTRY_ID[CountryName]=CountryID
-self.COUNTRY_NAME[CountryID]=CountryName
-if type(cntry_data)=='table'then
-for obj_type_name,obj_type_data in pairs(cntry_data)do
-if obj_type_name=="helicopter"or obj_type_name=="ship"or obj_type_name=="plane"or obj_type_name=="vehicle"or obj_type_name=="static"then
-local CategoryName=obj_type_name
-if((type(obj_type_data)=='table')and obj_type_data.group and(type(obj_type_data.group)=='table')and(#obj_type_data.group>0))then
-for group_num,Template in pairs(obj_type_data.group)do
-if obj_type_name~="static"and Template and Template.units and type(Template.units)=='table'then
-self:_RegisterGroupTemplate(Template,CoalitionSide,_DATABASECategory[string.lower(CategoryName)],CountryID)
-else
-self:_RegisterStaticTemplate(Template,CoalitionSide,_DATABASECategory[string.lower(CategoryName)],CountryID)
-end
-end
-end
-end
-end
-end
-end
-end
-end
-end
-return self
-end
-function DATABASE:AccountHits(Event)
-self:F({Event})
-if Event.IniPlayerName~=nil then
-self:T("Hitting Something")
-if Event.TgtCategory then
-self.HITS[Event.TgtUnitName]=self.HITS[Event.TgtUnitName]or{}
-local Hit=self.HITS[Event.TgtUnitName]
-Hit.Players=Hit.Players or{}
-Hit.Players[Event.IniPlayerName]=true
-end
-end
-if Event.WeaponPlayerName~=nil then
-self:T("Hitting Scenery")
-if Event.TgtCategory then
-if Event.WeaponCoalition then
-self.HITS[Event.TgtUnitName]=self.HITS[Event.TgtUnitName]or{}
-local Hit=self.HITS[Event.TgtUnitName]
-Hit.Players=Hit.Players or{}
-Hit.Players[Event.WeaponPlayerName]=true
-else
-end
-end
-end
-end
-function DATABASE:AccountDestroys(Event)
-self:F({Event})
-local TargetUnit=nil
-local TargetGroup=nil
-local TargetUnitName=""
-local TargetGroupName=""
-local TargetPlayerName=""
-local TargetCoalition=nil
-local TargetCategory=nil
-local TargetType=nil
-local TargetUnitCoalition=nil
-local TargetUnitCategory=nil
-local TargetUnitType=nil
-if Event.IniDCSUnit then
-TargetUnit=Event.IniUnit
-TargetUnitName=Event.IniDCSUnitName
-TargetGroup=Event.IniDCSGroup
-TargetGroupName=Event.IniDCSGroupName
-TargetPlayerName=Event.IniPlayerName
-TargetCoalition=Event.IniCoalition
-TargetCategory=Event.IniCategory
-TargetType=Event.IniTypeName
-TargetUnitType=TargetType
-self:T({TargetUnitName,TargetGroupName,TargetPlayerName,TargetCoalition,TargetCategory,TargetType})
-end
-local Destroyed=false
-if self.HITS[Event.IniUnitName]then
-self.DESTROYS[Event.IniUnitName]=self.DESTROYS[Event.IniUnitName]or{}
-self.DESTROYS[Event.IniUnitName]=true
-end
-end
-EVENT={
-ClassName="EVENT",
-ClassID=0,
-MissionEnd=false,
-}
-world.event.S_EVENT_NEW_CARGO=world.event.S_EVENT_MAX+1000
-world.event.S_EVENT_DELETE_CARGO=world.event.S_EVENT_MAX+1001
-world.event.S_EVENT_NEW_ZONE=world.event.S_EVENT_MAX+1002
-world.event.S_EVENT_DELETE_ZONE=world.event.S_EVENT_MAX+1003
-world.event.S_EVENT_NEW_ZONE_GOAL=world.event.S_EVENT_MAX+1004
-world.event.S_EVENT_DELETE_ZONE_GOAL=world.event.S_EVENT_MAX+1005
-world.event.S_EVENT_REMOVE_UNIT=world.event.S_EVENT_MAX+1006
-world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT=world.event.S_EVENT_MAX+1007
-EVENTS={
-Shot=world.event.S_EVENT_SHOT,
-Hit=world.event.S_EVENT_HIT,
-Takeoff=world.event.S_EVENT_TAKEOFF,
-Land=world.event.S_EVENT_LAND,
-Crash=world.event.S_EVENT_CRASH,
-Ejection=world.event.S_EVENT_EJECTION,
-Refueling=world.event.S_EVENT_REFUELING,
-Dead=world.event.S_EVENT_DEAD,
-PilotDead=world.event.S_EVENT_PILOT_DEAD,
-BaseCaptured=world.event.S_EVENT_BASE_CAPTURED,
-MissionStart=world.event.S_EVENT_MISSION_START,
-MissionEnd=world.event.S_EVENT_MISSION_END,
-TookControl=world.event.S_EVENT_TOOK_CONTROL,
-RefuelingStop=world.event.S_EVENT_REFUELING_STOP,
-Birth=world.event.S_EVENT_BIRTH,
-HumanFailure=world.event.S_EVENT_HUMAN_FAILURE,
-EngineStartup=world.event.S_EVENT_ENGINE_STARTUP,
-EngineShutdown=world.event.S_EVENT_ENGINE_SHUTDOWN,
-PlayerEnterUnit=world.event.S_EVENT_PLAYER_ENTER_UNIT,
-PlayerLeaveUnit=world.event.S_EVENT_PLAYER_LEAVE_UNIT,
-PlayerComment=world.event.S_EVENT_PLAYER_COMMENT,
-ShootingStart=world.event.S_EVENT_SHOOTING_START,
-ShootingEnd=world.event.S_EVENT_SHOOTING_END,
-MarkAdded=world.event.S_EVENT_MARK_ADDED,
-MarkChange=world.event.S_EVENT_MARK_CHANGE,
-MarkRemoved=world.event.S_EVENT_MARK_REMOVED,
-NewCargo=world.event.S_EVENT_NEW_CARGO,
-DeleteCargo=world.event.S_EVENT_DELETE_CARGO,
-NewZone=world.event.S_EVENT_NEW_ZONE,
-DeleteZone=world.event.S_EVENT_DELETE_ZONE,
-NewZoneGoal=world.event.S_EVENT_NEW_ZONE_GOAL,
-DeleteZoneGoal=world.event.S_EVENT_DELETE_ZONE_GOAL,
-RemoveUnit=world.event.S_EVENT_REMOVE_UNIT,
-PlayerEnterAircraft=world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT,
-DetailedFailure=world.event.S_EVENT_DETAILED_FAILURE or-1,
-Kill=world.event.S_EVENT_KILL or-1,
-Score=world.event.S_EVENT_SCORE or-1,
-UnitLost=world.event.S_EVENT_UNIT_LOST or-1,
-LandingAfterEjection=world.event.S_EVENT_LANDING_AFTER_EJECTION or-1,
-ParatrooperLanding=world.event.S_EVENT_PARATROOPER_LENDING or-1,
-DiscardChairAfterEjection=world.event.S_EVENT_DISCARD_CHAIR_AFTER_EJECTION or-1,
-WeaponAdd=world.event.S_EVENT_WEAPON_ADD or-1,
-TriggerZone=world.event.S_EVENT_TRIGGER_ZONE or-1,
-LandingQualityMark=world.event.S_EVENT_LANDING_QUALITY_MARK or-1,
-BDA=world.event.S_EVENT_BDA or-1,
-AIAbortMission=world.event.S_EVENT_AI_ABORT_MISSION or-1,
-DayNight=world.event.S_EVENT_DAYNIGHT or-1,
-FlightTime=world.event.S_EVENT_FLIGHT_TIME or-1,
-SelfKillPilot=world.event.S_EVENT_PLAYER_SELF_KILL_PILOT or-1,
-PlayerCaptureAirfield=world.event.S_EVENT_PLAYER_CAPTURE_AIRFIELD or-1,
-EmergencyLanding=world.event.S_EVENT_EMERGENCY_LANDING or-1,
-UnitCreateTask=world.event.S_EVENT_UNIT_CREATE_TASK or-1,
-UnitDeleteTask=world.event.S_EVENT_UNIT_DELETE_TASK or-1,
-SimulationStart=world.event.S_EVENT_SIMULATION_START or-1,
-WeaponRearm=world.event.S_EVENT_WEAPON_REARM or-1,
-WeaponDrop=world.event.S_EVENT_WEAPON_DROP or-1,
-UnitTaskTimeout=world.event.S_EVENT_UNIT_TASK_TIMEOUT or-1,
-UnitTaskStage=world.event.S_EVENT_UNIT_TASK_STAGE or-1,
-MacSubtaskScore=world.event.S_EVENT_MAC_SUBTASK_SCORE or-1,
-MacExtraScore=world.event.S_EVENT_MAC_EXTRA_SCORE or-1,
-MissionRestart=world.event.S_EVENT_MISSION_RESTART or-1,
-MissionWinner=world.event.S_EVENT_MISSION_WINNER or-1,
-PostponedTakeoff=world.event.S_EVENT_POSTPONED_TAKEOFF or-1,
-PostponedLand=world.event.S_EVENT_POSTPONED_LAND or-1,
-}
-local _EVENTMETA={
-[world.event.S_EVENT_SHOT]={
-Order=1,
-Side="I",
-Event="OnEventShot",
-Text="S_EVENT_SHOT"
-},
-[world.event.S_EVENT_HIT]={
-Order=1,
-Side="T",
-Event="OnEventHit",
-Text="S_EVENT_HIT"
-},
-[world.event.S_EVENT_TAKEOFF]={
-Order=1,
-Side="I",
-Event="OnEventTakeoff",
-Text="S_EVENT_TAKEOFF"
-},
-[world.event.S_EVENT_LAND]={
-Order=1,
-Side="I",
-Event="OnEventLand",
-Text="S_EVENT_LAND"
-},
-[world.event.S_EVENT_CRASH]={
-Order=-1,
-Side="I",
-Event="OnEventCrash",
-Text="S_EVENT_CRASH"
-},
-[world.event.S_EVENT_EJECTION]={
-Order=1,
-Side="I",
-Event="OnEventEjection",
-Text="S_EVENT_EJECTION"
-},
-[world.event.S_EVENT_REFUELING]={
-Order=1,
-Side="I",
-Event="OnEventRefueling",
-Text="S_EVENT_REFUELING"
-},
-[world.event.S_EVENT_DEAD]={
-Order=-1,
-Side="I",
-Event="OnEventDead",
-Text="S_EVENT_DEAD"
-},
-[world.event.S_EVENT_PILOT_DEAD]={
-Order=1,
-Side="I",
-Event="OnEventPilotDead",
-Text="S_EVENT_PILOT_DEAD"
-},
-[world.event.S_EVENT_BASE_CAPTURED]={
-Order=1,
-Side="I",
-Event="OnEventBaseCaptured",
-Text="S_EVENT_BASE_CAPTURED"
-},
-[world.event.S_EVENT_MISSION_START]={
-Order=1,
-Side="N",
-Event="OnEventMissionStart",
-Text="S_EVENT_MISSION_START"
-},
-[world.event.S_EVENT_MISSION_END]={
-Order=1,
-Side="N",
-Event="OnEventMissionEnd",
-Text="S_EVENT_MISSION_END"
-},
-[world.event.S_EVENT_TOOK_CONTROL]={
-Order=1,
-Side="N",
-Event="OnEventTookControl",
-Text="S_EVENT_TOOK_CONTROL"
-},
-[world.event.S_EVENT_REFUELING_STOP]={
-Order=1,
-Side="I",
-Event="OnEventRefuelingStop",
-Text="S_EVENT_REFUELING_STOP"
-},
-[world.event.S_EVENT_BIRTH]={
-Order=1,
-Side="I",
-Event="OnEventBirth",
-Text="S_EVENT_BIRTH"
-},
-[world.event.S_EVENT_HUMAN_FAILURE]={
-Order=1,
-Side="I",
-Event="OnEventHumanFailure",
-Text="S_EVENT_HUMAN_FAILURE"
-},
-[world.event.S_EVENT_ENGINE_STARTUP]={
-Order=1,
-Side="I",
-Event="OnEventEngineStartup",
-Text="S_EVENT_ENGINE_STARTUP"
-},
-[world.event.S_EVENT_ENGINE_SHUTDOWN]={
-Order=1,
-Side="I",
-Event="OnEventEngineShutdown",
-Text="S_EVENT_ENGINE_SHUTDOWN"
-},
-[world.event.S_EVENT_PLAYER_ENTER_UNIT]={
-Order=1,
-Side="I",
-Event="OnEventPlayerEnterUnit",
-Text="S_EVENT_PLAYER_ENTER_UNIT"
-},
-[world.event.S_EVENT_PLAYER_LEAVE_UNIT]={
-Order=-1,
-Side="I",
-Event="OnEventPlayerLeaveUnit",
-Text="S_EVENT_PLAYER_LEAVE_UNIT"
-},
-[world.event.S_EVENT_PLAYER_COMMENT]={
-Order=1,
-Side="I",
-Event="OnEventPlayerComment",
-Text="S_EVENT_PLAYER_COMMENT"
-},
-[world.event.S_EVENT_SHOOTING_START]={
-Order=1,
-Side="I",
-Event="OnEventShootingStart",
-Text="S_EVENT_SHOOTING_START"
-},
-[world.event.S_EVENT_SHOOTING_END]={
-Order=1,
-Side="I",
-Event="OnEventShootingEnd",
-Text="S_EVENT_SHOOTING_END"
-},
-[world.event.S_EVENT_MARK_ADDED]={
-Order=1,
-Side="I",
-Event="OnEventMarkAdded",
-Text="S_EVENT_MARK_ADDED"
-},
-[world.event.S_EVENT_MARK_CHANGE]={
-Order=1,
-Side="I",
-Event="OnEventMarkChange",
-Text="S_EVENT_MARK_CHANGE"
-},
-[world.event.S_EVENT_MARK_REMOVED]={
-Order=1,
-Side="I",
-Event="OnEventMarkRemoved",
-Text="S_EVENT_MARK_REMOVED"
-},
-[EVENTS.NewCargo]={
-Order=1,
-Event="OnEventNewCargo",
-Text="S_EVENT_NEW_CARGO"
-},
-[EVENTS.DeleteCargo]={
-Order=1,
-Event="OnEventDeleteCargo",
-Text="S_EVENT_DELETE_CARGO"
-},
-[EVENTS.NewZone]={
-Order=1,
-Event="OnEventNewZone",
-Text="S_EVENT_NEW_ZONE"
-},
-[EVENTS.DeleteZone]={
-Order=1,
-Event="OnEventDeleteZone",
-Text="S_EVENT_DELETE_ZONE"
-},
-[EVENTS.NewZoneGoal]={
-Order=1,
-Event="OnEventNewZoneGoal",
-Text="S_EVENT_NEW_ZONE_GOAL"
-},
-[EVENTS.DeleteZoneGoal]={
-Order=1,
-Event="OnEventDeleteZoneGoal",
-Text="S_EVENT_DELETE_ZONE_GOAL"
-},
-[EVENTS.RemoveUnit]={
-Order=-1,
-Event="OnEventRemoveUnit",
-Text="S_EVENT_REMOVE_UNIT"
-},
-[EVENTS.PlayerEnterAircraft]={
-Order=1,
-Event="OnEventPlayerEnterAircraft",
-Text="S_EVENT_PLAYER_ENTER_AIRCRAFT"
-},
-[EVENTS.DetailedFailure]={
-Order=1,
-Event="OnEventDetailedFailure",
-Text="S_EVENT_DETAILED_FAILURE"
-},
-[EVENTS.Kill]={
-Order=1,
-Event="OnEventKill",
-Text="S_EVENT_KILL"
-},
-[EVENTS.Score]={
-Order=1,
-Event="OnEventScore",
-Text="S_EVENT_SCORE"
-},
-[EVENTS.UnitLost]={
-Order=1,
-Event="OnEventUnitLost",
-Text="S_EVENT_UNIT_LOST"
-},
-[EVENTS.LandingAfterEjection]={
-Order=1,
-Event="OnEventLandingAfterEjection",
-Text="S_EVENT_LANDING_AFTER_EJECTION"
-},
-[EVENTS.ParatrooperLanding]={
-Order=1,
-Event="OnEventParatrooperLanding",
-Text="S_EVENT_PARATROOPER_LENDING"
-},
-[EVENTS.DiscardChairAfterEjection]={
-Order=1,
-Event="OnEventDiscardChairAfterEjection",
-Text="S_EVENT_DISCARD_CHAIR_AFTER_EJECTION"
-},
-[EVENTS.WeaponAdd]={
-Order=1,
-Event="OnEventWeaponAdd",
-Text="S_EVENT_WEAPON_ADD"
-},
-[EVENTS.TriggerZone]={
-Order=1,
-Event="OnEventTriggerZone",
-Text="S_EVENT_TRIGGER_ZONE"
-},
-[EVENTS.LandingQualityMark]={
-Order=1,
-Event="OnEventLandingQualityMark",
-Text="S_EVENT_LANDING_QUALITYMARK"
-},
-[EVENTS.BDA]={
-Order=1,
-Event="OnEventBDA",
-Text="S_EVENT_BDA"
-},
-[EVENTS.AIAbortMission]={
-Order=1,
-Side="I",
-Event="OnEventAIAbortMission",
-Text="S_EVENT_AI_ABORT_MISSION"
-},
-[EVENTS.DayNight]={
-Order=1,
-Event="OnEventDayNight",
-Text="S_EVENT_DAYNIGHT"
-},
-[EVENTS.FlightTime]={
-Order=1,
-Event="OnEventFlightTime",
-Text="S_EVENT_FLIGHT_TIME"
-},
-[EVENTS.SelfKillPilot]={
-Order=1,
-Side="I",
-Event="OnEventSelfKillPilot",
-Text="S_EVENT_PLAYER_SELF_KILL_PILOT"
-},
-[EVENTS.PlayerCaptureAirfield]={
-Order=1,
-Event="OnEventPlayerCaptureAirfield",
-Text="S_EVENT_PLAYER_CAPTURE_AIRFIELD"
-},
-[EVENTS.EmergencyLanding]={
-Order=1,
-Side="I",
-Event="OnEventEmergencyLanding",
-Text="S_EVENT_EMERGENCY_LANDING"
-},
-[EVENTS.UnitCreateTask]={
-Order=1,
-Event="OnEventUnitCreateTask",
-Text="S_EVENT_UNIT_CREATE_TASK"
-},
-[EVENTS.UnitDeleteTask]={
-Order=1,
-Event="OnEventUnitDeleteTask",
-Text="S_EVENT_UNIT_DELETE_TASK"
-},
-[EVENTS.SimulationStart]={
-Order=1,
-Event="OnEventSimulationStart",
-Text="S_EVENT_SIMULATION_START"
-},
-[EVENTS.WeaponRearm]={
-Order=1,
-Side="I",
-Event="OnEventWeaponRearm",
-Text="S_EVENT_WEAPON_REARM"
-},
-[EVENTS.WeaponDrop]={
-Order=1,
-Side="I",
-Event="OnEventWeaponDrop",
-Text="S_EVENT_WEAPON_DROP"
-},
-[EVENTS.UnitTaskTimeout]={
-Order=1,
-Side="I",
-Event="OnEventUnitTaskTimeout",
-Text="S_EVENT_UNIT_TASK_TIMEOUT "
-},
-[EVENTS.UnitTaskStage]={
-Order=1,
-Side="I",
-Event="OnEventUnitTaskStage",
-Text="S_EVENT_UNIT_TASK_STAGE "
-},
-[EVENTS.MacSubtaskScore]={
-Order=1,
-Side="I",
-Event="OnEventMacSubtaskScore",
-Text="S_EVENT_MAC_SUBTASK_SCORE"
-},
-[EVENTS.MacExtraScore]={
-Order=1,
-Side="I",
-Event="OnEventMacExtraScore",
-Text="S_EVENT_MAC_EXTRA_SCOREP"
-},
-[EVENTS.MissionRestart]={
-Order=1,
-Side="I",
-Event="OnEventMissionRestart",
-Text="S_EVENT_MISSION_RESTART"
-},
-[EVENTS.MissionWinner]={
-Order=1,
-Side="I",
-Event="OnEventMissionWinner",
-Text="S_EVENT_MISSION_WINNER"
-},
-[EVENTS.PostponedTakeoff]={
-Order=1,
-Side="I",
-Event="OnEventPostponedTakeoff",
-Text="S_EVENT_POSTPONED_TAKEOFF"
-},
-[EVENTS.PostponedLand]={
-Order=1,
-Side="I",
-Event="OnEventPostponedLand",
-Text="S_EVENT_POSTPONED_LAND"
-},
-}
-function EVENT:New()
-local self=BASE:Inherit(self,BASE:New())
-self.EventHandler=world.addEventHandler(self)
-return self
-end
-function EVENT:Init(EventID,EventClass)
-self:F3({_EVENTMETA[EventID].Text,EventClass})
-if not self.Events[EventID]then
-self.Events[EventID]={}
-end
-local EventPriority=EventClass:GetEventPriority()
-if not self.Events[EventID][EventPriority]then
-self.Events[EventID][EventPriority]=setmetatable({},{__mode="k"})
-end
-if not self.Events[EventID][EventPriority][EventClass]then
-self.Events[EventID][EventPriority][EventClass]={}
-end
-return self.Events[EventID][EventPriority][EventClass]
-end
-function EVENT:RemoveEvent(EventClass,EventID)
-self:F2({"Removing subscription for class: ",EventClass:GetClassNameAndID()})
-local EventPriority=EventClass:GetEventPriority()
-self.Events=self.Events or{}
-self.Events[EventID]=self.Events[EventID]or{}
-self.Events[EventID][EventPriority]=self.Events[EventID][EventPriority]or{}
-self.Events[EventID][EventPriority][EventClass]=nil
-return self
-end
-function EVENT:Reset(EventObject)
-self:F({"Resetting subscriptions for class: ",EventObject:GetClassNameAndID()})
-local EventPriority=EventObject:GetEventPriority()
-for EventID,EventData in pairs(self.Events)do
-if self.EventsDead then
-if self.EventsDead[EventID]then
-if self.EventsDead[EventID][EventPriority]then
-if self.EventsDead[EventID][EventPriority][EventObject]then
-self.Events[EventID][EventPriority][EventObject]=self.EventsDead[EventID][EventPriority][EventObject]
-end
-end
-end
-end
-end
-end
-function EVENT:RemoveAll(EventClass)
-local EventClassName=EventClass:GetClassNameAndID()
-local EventPriority=EventClass:GetEventPriority()
-for EventID,EventData in pairs(self.Events)do
-self.Events[EventID][EventPriority][EventClass]=nil
-end
-return self
-end
-function EVENT:OnEventForTemplate(EventTemplate,EventFunction,EventClass,EventID)
-self:F2(EventTemplate.name)
-for EventUnitID,EventUnit in pairs(EventTemplate.units)do
-self:OnEventForUnit(EventUnit.name,EventFunction,EventClass,EventID)
-end
-return self
-end
-function EVENT:OnEventGeneric(EventFunction,EventClass,EventID)
-self:F2({EventID,EventClass,EventFunction})
-local EventData=self:Init(EventID,EventClass)
-EventData.EventFunction=EventFunction
-return self
-end
-function EVENT:OnEventForUnit(UnitName,EventFunction,EventClass,EventID)
-self:F2(UnitName)
-local EventData=self:Init(EventID,EventClass)
-EventData.EventUnit=true
-EventData.EventFunction=EventFunction
-return self
-end
-function EVENT:OnEventForGroup(GroupName,EventFunction,EventClass,EventID,...)
-local Event=self:Init(EventID,EventClass)
-Event.EventGroup=true
-Event.EventFunction=EventFunction
-Event.Params=arg
-return self
-end
-do
-function EVENT:OnBirthForTemplate(EventTemplate,EventFunction,EventClass)
-self:F2(EventTemplate.name)
-self:OnEventForTemplate(EventTemplate,EventFunction,EventClass,EVENTS.Birth)
-return self
-end
-end
-do
-function EVENT:OnCrashForTemplate(EventTemplate,EventFunction,EventClass)
-self:F2(EventTemplate.name)
-self:OnEventForTemplate(EventTemplate,EventFunction,EventClass,EVENTS.Crash)
-return self
-end
-end
-do
-function EVENT:OnDeadForTemplate(EventTemplate,EventFunction,EventClass)
-self:F2(EventTemplate.name)
-self:OnEventForTemplate(EventTemplate,EventFunction,EventClass,EVENTS.Dead)
-return self
-end
-end
-do
-function EVENT:OnLandForTemplate(EventTemplate,EventFunction,EventClass)
-self:F2(EventTemplate.name)
-self:OnEventForTemplate(EventTemplate,EventFunction,EventClass,EVENTS.Land)
-return self
-end
-end
-do
-function EVENT:OnTakeOffForTemplate(EventTemplate,EventFunction,EventClass)
-self:F2(EventTemplate.name)
-self:OnEventForTemplate(EventTemplate,EventFunction,EventClass,EVENTS.Takeoff)
-return self
-end
-end
-do
-function EVENT:OnEngineShutDownForTemplate(EventTemplate,EventFunction,EventClass)
-self:F2(EventTemplate.name)
-self:OnEventForTemplate(EventTemplate,EventFunction,EventClass,EVENTS.EngineShutdown)
-return self
-end
-end
-do
-function EVENT:CreateEventNewCargo(Cargo)
-self:F({Cargo})
-local Event={
-id=EVENTS.NewCargo,
-time=timer.getTime(),
-cargo=Cargo,
-}
-world.onEvent(Event)
-end
-function EVENT:CreateEventDeleteCargo(Cargo)
-self:F({Cargo})
-local Event={
-id=EVENTS.DeleteCargo,
-time=timer.getTime(),
-cargo=Cargo,
-}
-world.onEvent(Event)
-end
-function EVENT:CreateEventNewZone(Zone)
-self:F({Zone})
-local Event={
-id=EVENTS.NewZone,
-time=timer.getTime(),
-zone=Zone,
-}
-world.onEvent(Event)
-end
-function EVENT:CreateEventDeleteZone(Zone)
-self:F({Zone})
-local Event={
-id=EVENTS.DeleteZone,
-time=timer.getTime(),
-zone=Zone,
-}
-world.onEvent(Event)
-end
-function EVENT:CreateEventNewZoneGoal(ZoneGoal)
-self:F({ZoneGoal})
-local Event={
-id=EVENTS.NewZoneGoal,
-time=timer.getTime(),
-ZoneGoal=ZoneGoal,
-}
-world.onEvent(Event)
-end
-function EVENT:CreateEventDeleteZoneGoal(ZoneGoal)
-self:F({ZoneGoal})
-local Event={
-id=EVENTS.DeleteZoneGoal,
-time=timer.getTime(),
-ZoneGoal=ZoneGoal,
-}
-world.onEvent(Event)
-end
-function EVENT:CreateEventPlayerEnterUnit(PlayerUnit)
-self:F({PlayerUnit})
-local Event={
-id=EVENTS.PlayerEnterUnit,
-time=timer.getTime(),
-initiator=PlayerUnit:GetDCSObject()
-}
-world.onEvent(Event)
-end
-function EVENT:CreateEventPlayerEnterAircraft(PlayerUnit)
-self:F({PlayerUnit})
-local Event={
-id=EVENTS.PlayerEnterAircraft,
-time=timer.getTime(),
-initiator=PlayerUnit:GetDCSObject()
-}
-world.onEvent(Event)
-end
-end
-function EVENT:onEvent(Event)
-local ErrorHandler=function(errmsg)
-env.info("Error in SCHEDULER function:"..errmsg)
-if BASE.Debug~=nil then
-env.info(debug.traceback())
-end
-return errmsg
-end
-local EventMeta=_EVENTMETA[Event.id]
-if EventMeta then
-if self and self.Events and self.Events[Event.id]and self.MissionEnd==false and(Event.initiator~=nil or(Event.initiator==nil and Event.id~=EVENTS.PlayerLeaveUnit))then
-if Event.id and Event.id==EVENTS.MissionEnd then
-self.MissionEnd=true
-end
-if Event.initiator then
-Event.IniObjectCategory=Object.getCategory(Event.initiator)
-if Event.IniObjectCategory==Object.Category.STATIC then
-if Event.id==31 then
-Event.IniDCSUnit=Event.initiator
-local ID=Event.initiator.id_
-Event.IniDCSUnitName=string.format("Ejected Pilot ID %s",tostring(ID))
-Event.IniUnitName=Event.IniDCSUnitName
-Event.IniCoalition=0
-Event.IniCategory=0
-Event.IniTypeName="Ejected Pilot"
-elseif Event.id==33 then
-Event.IniDCSUnit=Event.initiator
-local ID=Event.initiator.id_
-Event.IniDCSUnitName=string.format("Ejection Seat ID %s",tostring(ID))
-Event.IniUnitName=Event.IniDCSUnitName
-Event.IniCoalition=0
-Event.IniCategory=0
-Event.IniTypeName="Ejection Seat"
-else
-Event.IniDCSUnit=Event.initiator
-Event.IniDCSUnitName=Event.IniDCSUnit:getName()
-Event.IniUnitName=Event.IniDCSUnitName
-Event.IniUnit=STATIC:FindByName(Event.IniDCSUnitName,false)
-Event.IniCoalition=Event.IniDCSUnit:getCoalition()
-Event.IniCategory=Event.IniDCSUnit:getDesc().category
-Event.IniTypeName=Event.IniDCSUnit:getTypeName()
-end
-local Unit=UNIT:FindByName(Event.IniDCSUnitName)
-if Unit then
-Event.IniObjectCategory=Object.Category.UNIT
-end
-elseif Event.IniObjectCategory==Object.Category.UNIT then
-Event.IniDCSUnit=Event.initiator
-Event.IniDCSUnitName=Event.IniDCSUnit:getName()
-Event.IniUnitName=Event.IniDCSUnitName
-Event.IniDCSGroup=Event.IniDCSUnit:getGroup()
-Event.IniUnit=UNIT:FindByName(Event.IniDCSUnitName)
-if not Event.IniUnit then
-Event.IniUnit=CLIENT:FindByName(Event.IniDCSUnitName,'',true)
-end
-Event.IniDCSGroupName=Event.IniUnit and Event.IniUnit.GroupName or""
-if Event.IniDCSGroup and Event.IniDCSGroup:isExist()then
-Event.IniDCSGroupName=Event.IniDCSGroup:getName()
-Event.IniGroup=GROUP:FindByName(Event.IniDCSGroupName)
-Event.IniGroupName=Event.IniDCSGroupName
-end
-Event.IniPlayerName=Event.IniDCSUnit:getPlayerName()
-if Event.IniPlayerName then
-local PID=NET.GetPlayerIDByName(nil,Event.IniPlayerName)
-if PID then
-Event.IniPlayerUCID=net.get_player_info(tonumber(PID),'ucid')
-end
-end
-Event.IniCoalition=Event.IniDCSUnit:getCoalition()
-Event.IniTypeName=Event.IniDCSUnit:getTypeName()
-Event.IniCategory=Event.IniDCSUnit:getDesc().category
-elseif Event.IniObjectCategory==Object.Category.CARGO then
-Event.IniDCSUnit=Event.initiator
-Event.IniDCSUnitName=Event.IniDCSUnit:getName()
-Event.IniUnitName=Event.IniDCSUnitName
-Event.IniUnit=CARGO:FindByName(Event.IniDCSUnitName)
-Event.IniCoalition=Event.IniDCSUnit:getCoalition()
-Event.IniCategory=Event.IniDCSUnit:getDesc().category
-Event.IniTypeName=Event.IniDCSUnit:getTypeName()
-elseif Event.IniObjectCategory==Object.Category.SCENERY then
-Event.IniDCSUnit=Event.initiator
-Event.IniDCSUnitName=Event.IniDCSUnit:getName()
-Event.IniUnitName=Event.IniDCSUnitName
-Event.IniUnit=SCENERY:Register(Event.IniDCSUnitName,Event.initiator)
-Event.IniCategory=Event.IniDCSUnit:getDesc().category
-Event.IniTypeName=Event.initiator:isExist()and Event.IniDCSUnit:getTypeName()or"SCENERY"
-elseif Event.IniObjectCategory==Object.Category.BASE then
-Event.IniDCSUnit=Event.initiator
-Event.IniDCSUnitName=Event.IniDCSUnit:getName()
-Event.IniUnitName=Event.IniDCSUnitName
-Event.IniUnit=AIRBASE:FindByName(Event.IniDCSUnitName)
-Event.IniCoalition=Event.IniDCSUnit:getCoalition()
-Event.IniCategory=Event.IniDCSUnit:getDesc().category
-Event.IniTypeName=Event.IniDCSUnit:getTypeName()
-if not Event.IniUnit then
-_DATABASE:_RegisterAirbase(Event.initiator)
-Event.IniUnit=AIRBASE:FindByName(Event.IniDCSUnitName)
-end
-end
-end
-if Event.target then
-Event.TgtObjectCategory=Object.getCategory(Event.target)
-if Event.TgtObjectCategory==Object.Category.UNIT then
-Event.TgtDCSUnit=Event.target
-Event.TgtDCSGroup=Event.TgtDCSUnit:getGroup()
-Event.TgtDCSUnitName=Event.TgtDCSUnit:getName()
-Event.TgtUnitName=Event.TgtDCSUnitName
-Event.TgtUnit=UNIT:FindByName(Event.TgtDCSUnitName)
-Event.TgtDCSGroupName=""
-if Event.TgtDCSGroup and Event.TgtDCSGroup:isExist()then
-Event.TgtDCSGroupName=Event.TgtDCSGroup:getName()
-Event.TgtGroup=GROUP:FindByName(Event.TgtDCSGroupName)
-Event.TgtGroupName=Event.TgtDCSGroupName
-end
-Event.TgtPlayerName=Event.TgtDCSUnit:getPlayerName()
-if Event.TgtPlayerName then
-local PID=NET.GetPlayerIDByName(nil,Event.TgtPlayerName)
-if PID then
-Event.TgtPlayerUCID=net.get_player_info(tonumber(PID),'ucid')
-end
-end
-Event.TgtCoalition=Event.TgtDCSUnit:getCoalition()
-Event.TgtCategory=Event.TgtDCSUnit:getDesc().category
-Event.TgtTypeName=Event.TgtDCSUnit:getTypeName()
-elseif Event.TgtObjectCategory==Object.Category.STATIC then
-Event.TgtDCSUnit=Event.target
-if Event.target:isExist()and Event.id~=33 then
-Event.TgtDCSUnitName=Event.TgtDCSUnit:getName()
-if Event.TgtDCSUnitName and Event.TgtDCSUnitName~=""then
-Event.TgtUnitName=Event.TgtDCSUnitName
-Event.TgtUnit=STATIC:FindByName(Event.TgtDCSUnitName,false)
-Event.TgtCoalition=Event.TgtDCSUnit:getCoalition()
-Event.TgtCategory=Event.TgtDCSUnit:getDesc().category
-Event.TgtTypeName=Event.TgtDCSUnit:getTypeName()
-end
-else
-Event.TgtDCSUnitName=string.format("No target object for Event ID %s",tostring(Event.id))
-Event.TgtUnitName=Event.TgtDCSUnitName
-Event.TgtUnit=nil
-Event.TgtCoalition=0
-Event.TgtCategory=0
-if Event.id==6 then
-Event.TgtTypeName="Ejected Pilot"
-Event.TgtDCSUnitName=string.format("Ejected Pilot ID %s",tostring(Event.IniDCSUnitName))
-Event.TgtUnitName=Event.TgtDCSUnitName
-elseif Event.id==33 then
-Event.TgtTypeName="Ejection Seat"
-Event.TgtDCSUnitName=string.format("Ejection Seat ID %s",tostring(Event.IniDCSUnitName))
-Event.TgtUnitName=Event.TgtDCSUnitName
-else
-Event.TgtTypeName="Static"
-end
-end
-elseif Event.TgtObjectCategory==Object.Category.SCENERY then
-Event.TgtDCSUnit=Event.target
-Event.TgtDCSUnitName=Event.TgtDCSUnit:getName()
-Event.TgtUnitName=Event.TgtDCSUnitName
-Event.TgtUnit=SCENERY:Register(Event.TgtDCSUnitName,Event.target)
-Event.TgtCategory=Event.TgtDCSUnit:getDesc().category
-Event.TgtTypeName=Event.TgtDCSUnit:getTypeName()
-end
-end
-if Event.weapon then
-Event.Weapon=Event.weapon
-Event.WeaponName=Event.Weapon:getTypeName()
-Event.WeaponUNIT=CLIENT:Find(Event.Weapon,'',true)
-Event.WeaponPlayerName=Event.WeaponUNIT and Event.Weapon.getPlayerName and Event.Weapon:getPlayerName()
-Event.WeaponCoalition=Event.WeaponUNIT and Event.Weapon:getCoalition()
-Event.WeaponCategory=Event.WeaponUNIT and Event.Weapon:getDesc().category
-Event.WeaponTypeName=Event.WeaponUNIT and Event.Weapon:getTypeName()
-end
-if Event.place then
-if Event.id==EVENTS.LandingAfterEjection then
-else
-if Event.place:isExist()and Object.getCategory(Event.place)~=Object.Category.SCENERY then
-Event.Place=AIRBASE:Find(Event.place)
-Event.PlaceName=Event.Place:GetName()
-end
-end
-end
-if Event.idx then
-Event.MarkID=Event.idx
-Event.MarkVec3=Event.pos
-Event.MarkCoordinate=COORDINATE:NewFromVec3(Event.pos)
-Event.MarkText=Event.text
-Event.MarkCoalition=Event.coalition
-Event.MarkGroupID=Event.groupID
-end
-if Event.cargo then
-Event.Cargo=Event.cargo
-Event.CargoName=Event.cargo.Name
-end
-if Event.zone then
-Event.Zone=Event.zone
-Event.ZoneName=Event.zone.ZoneName
-end
-local PriorityOrder=EventMeta.Order
-local PriorityBegin=PriorityOrder==-1 and 5 or 1
-local PriorityEnd=PriorityOrder==-1 and 1 or 5
-for EventPriority=PriorityBegin,PriorityEnd,PriorityOrder do
-if self.Events[Event.id][EventPriority]then
-for EventClass,EventData in pairs(self.Events[Event.id][EventPriority])do
-Event.IniGroup=Event.IniGroup or GROUP:FindByName(Event.IniDCSGroupName)
-Event.TgtGroup=Event.TgtGroup or GROUP:FindByName(Event.TgtDCSGroupName)
-if EventData.EventUnit then
-if EventClass:IsAlive()or
-Event.id==EVENTS.PlayerEnterUnit or
-Event.id==EVENTS.Crash or
-Event.id==EVENTS.Dead or
-Event.id==EVENTS.RemoveUnit or
-Event.id==EVENTS.UnitLost then
-local UnitName=EventClass:GetName()
-if(EventMeta.Side=="I"and UnitName==Event.IniDCSUnitName)or
-(EventMeta.Side=="T"and UnitName==Event.TgtDCSUnitName)then
-if EventData.EventFunction then
-local Result,Value=xpcall(
-function()
-return EventData.EventFunction(EventClass,Event)
-end,ErrorHandler)
-else
-local EventFunction=EventClass[EventMeta.Event]
-if EventFunction and type(EventFunction)=="function"then
-local Result,Value=xpcall(
-function()
-return EventFunction(EventClass,Event)
-end,ErrorHandler)
-end
-end
-end
-else
-self:RemoveEvent(EventClass,Event.id)
-end
-else
-if EventData.EventGroup then
-if EventClass:IsAlive()or
-Event.id==EVENTS.PlayerEnterUnit or
-Event.id==EVENTS.Crash or
-Event.id==EVENTS.Dead or
-Event.id==EVENTS.RemoveUnit or
-Event.id==EVENTS.UnitLost then
-local GroupName=EventClass:GetName()
-if(EventMeta.Side=="I"and GroupName==Event.IniDCSGroupName)or
-(EventMeta.Side=="T"and GroupName==Event.TgtDCSGroupName)then
-if EventData.EventFunction then
-local Result,Value=xpcall(
-function()
-return EventData.EventFunction(EventClass,Event,unpack(EventData.Params))
-end,ErrorHandler)
-else
-local EventFunction=EventClass[EventMeta.Event]
-if EventFunction and type(EventFunction)=="function"then
-local Result,Value=xpcall(
-function()
-return EventFunction(EventClass,Event,unpack(EventData.Params))
-end,ErrorHandler)
-end
-end
-end
-else
-end
-else
-if not EventData.EventUnit then
-if EventData.EventFunction then
-local Result,Value=xpcall(
-function()
-return EventData.EventFunction(EventClass,Event)
-end,ErrorHandler)
-else
-local EventFunction=EventClass[EventMeta.Event]
-if EventFunction and type(EventFunction)=="function"then
-local Result,Value=xpcall(
-function()
-local Result,Value=EventFunction(EventClass,Event)
-return Result,Value
-end,ErrorHandler)
-end
-end
-end
-end
-end
-end
-end
-end
-if Event.id==EVENTS.DeleteCargo then
-Event.Cargo.NoDestroy=nil
-end
-else
-self:T({EventMeta.Text,Event})
-end
-else
-self:E(string.format("WARNING: Could not get EVENTMETA data for event ID=%d! Is this an unknown/new DCS event?",tostring(Event.id)))
-end
-Event=nil
-end
-EVENTHANDLER={
-ClassName="EVENTHANDLER",
-ClassID=0,
-}
-function EVENTHANDLER:New()
-self=BASE:Inherit(self,BASE:New())
-return self
-end
-do
-FSM={
-ClassName="FSM",
-}
-function FSM:New()
-self=BASE:Inherit(self,BASE:New())
-self.options=options or{}
-self.options.subs=self.options.subs or{}
-self.current=self.options.initial or'none'
-self.Events={}
-self.subs={}
-self.endstates={}
-self.Scores={}
-self._StartState="none"
-self._Transitions={}
-self._Processes={}
-self._EndStates={}
-self._Scores={}
-self._EventSchedules={}
-self.CallScheduler=SCHEDULER:New(self)
-return self
-end
-function FSM:SetStartState(State)
-self._StartState=State
-self.current=State
-end
-function FSM:GetStartState()
-return self._StartState or{}
-end
-function FSM:AddTransition(From,Event,To)
-local Transition={}
-Transition.From=From
-Transition.Event=Event
-Transition.To=To
-self._Transitions[Transition]=Transition
-self:_eventmap(self.Events,Transition)
-end
-function FSM:GetTransitions()
-return self._Transitions or{}
-end
-function FSM:AddProcess(From,Event,Process,ReturnEvents)
-local Sub={}
-Sub.From=From
-Sub.Event=Event
-Sub.fsm=Process
-Sub.StartEvent="Start"
-Sub.ReturnEvents=ReturnEvents
-self._Processes[Sub]=Sub
-self:_submap(self.subs,Sub,nil)
-self:AddTransition(From,Event,From)
-return Process
-end
-function FSM:GetProcesses()
-self:F({Processes=self._Processes})
-return self._Processes or{}
-end
-function FSM:GetProcess(From,Event)
-for ProcessID,Process in pairs(self:GetProcesses())do
-if Process.From==From and Process.Event==Event then
-return Process.fsm
-end
-end
-error("Sub-Process from state "..From.." with event "..Event.." not found!")
-end
-function FSM:SetProcess(From,Event,Fsm)
-for ProcessID,Process in pairs(self:GetProcesses())do
-if Process.From==From and Process.Event==Event then
-Process.fsm=Fsm
-return true
-end
-end
-error("Sub-Process from state "..From.." with event "..Event.." not found!")
-end
-function FSM:AddEndState(State)
-self._EndStates[State]=State
-self.endstates[State]=State
-end
-function FSM:GetEndStates()
-return self._EndStates or{}
-end
-function FSM:AddScore(State,ScoreText,Score)
-self:F({State,ScoreText,Score})
-self._Scores[State]=self._Scores[State]or{}
-self._Scores[State].ScoreText=ScoreText
-self._Scores[State].Score=Score
-return self
-end
-function FSM:AddScoreProcess(From,Event,State,ScoreText,Score)
-self:F({From,Event,State,ScoreText,Score})
-local Process=self:GetProcess(From,Event)
-Process._Scores[State]=Process._Scores[State]or{}
-Process._Scores[State].ScoreText=ScoreText
-Process._Scores[State].Score=Score
-return Process
-end
-function FSM:GetScores()
-return self._Scores or{}
-end
-function FSM:GetSubs()
-return self.options.subs
-end
-function FSM:LoadCallBacks(CallBackTable)
-for name,callback in pairs(CallBackTable or{})do
-self[name]=callback
-end
-end
-function FSM:_eventmap(Events,EventStructure)
-local Event=EventStructure.Event
-local __Event="__"..EventStructure.Event
-self[Event]=self[Event]or self:_create_transition(Event)
-self[__Event]=self[__Event]or self:_delayed_transition(Event)
-Events[Event]=self.Events[Event]or{map={}}
-self:_add_to_map(Events[Event].map,EventStructure)
-end
-function FSM:_submap(subs,sub,name)
-subs[sub.From]=subs[sub.From]or{}
-subs[sub.From][sub.Event]=subs[sub.From][sub.Event]or{}
-subs[sub.From][sub.Event][sub]={}
-subs[sub.From][sub.Event][sub].fsm=sub.fsm
-subs[sub.From][sub.Event][sub].StartEvent=sub.StartEvent
-subs[sub.From][sub.Event][sub].ReturnEvents=sub.ReturnEvents or{}
-subs[sub.From][sub.Event][sub].name=name
-subs[sub.From][sub.Event][sub].fsmparent=self
-end
-function FSM:_call_handler(step,trigger,params,EventName)
-local handler=step..trigger
-if self[handler]then
-self._EventSchedules[EventName]=nil
-local ErrorHandler=function(errmsg)
-env.info("Error in SCHEDULER function:"..errmsg)
-if BASE.Debug~=nil then
-env.info(BASE.Debug.traceback())
-end
-return errmsg
-end
-local Result,Value=xpcall(function()
-return self[handler](self,unpack(params))
-end,ErrorHandler)
-return Value
-end
-end
-function FSM._handler(self,EventName,...)
-local Can,To=self:can(EventName)
-if To=="*"then
-To=self.current
-end
-if Can then
-local From=self.current
-local Params={From,EventName,To,...}
-if self["onleave"..From]or
-self["OnLeave"..From]or
-self["onbefore"..EventName]or
-self["OnBefore"..EventName]or
-self["onafter"..EventName]or
-self["OnAfter"..EventName]or
-self["onenter"..To]or
-self["OnEnter"..To]then
-if self:_call_handler("onbefore",EventName,Params,EventName)==false then
-self:T("*** FSM *** Cancel".." *** "..self.current.." --> "..EventName.." --> "..To.." *** onbefore"..EventName)
-return false
-else
-if self:_call_handler("OnBefore",EventName,Params,EventName)==false then
-self:T("*** FSM *** Cancel".." *** "..self.current.." --> "..EventName.." --> "..To.." *** OnBefore"..EventName)
-return false
-else
-if self:_call_handler("onleave",From,Params,EventName)==false then
-self:T("*** FSM *** Cancel".." *** "..self.current.." --> "..EventName.." --> "..To.." *** onleave"..From)
-return false
-else
-if self:_call_handler("OnLeave",From,Params,EventName)==false then
-self:T("*** FSM *** Cancel".." *** "..self.current.." --> "..EventName.." --> "..To.." *** OnLeave"..From)
-return false
-end
-end
-end
-end
-else
-local ClassName=self:GetClassName()
-if ClassName=="FSM"then
-self:T("*** FSM *** Transit *** "..self.current.." --> "..EventName.." --> "..To)
-end
-if ClassName=="FSM_TASK"then
-self:T("*** FSM *** Transit *** "..self.current.." --> "..EventName.." --> "..To.." *** Task: "..self.TaskName)
-end
-if ClassName=="FSM_CONTROLLABLE"then
-self:T("*** FSM *** Transit *** "..self.current.." --> "..EventName.." --> "..To.." *** TaskUnit: "..self.Controllable.ControllableName.." *** ")
-end
-if ClassName=="FSM_PROCESS"then
-self:T("*** FSM *** Transit *** "..self.current.." --> "..EventName.." --> "..To.." *** Task: "..self.Task:GetName()..", TaskUnit: "..self.Controllable.ControllableName.." *** ")
-end
-end
-self.current=To
-local execute=true
-local subtable=self:_gosub(From,EventName)
-for _,sub in pairs(subtable)do
-self:T("*** FSM *** Sub *** "..sub.StartEvent)
-sub.fsm.fsmparent=self
-sub.fsm.ReturnEvents=sub.ReturnEvents
-sub.fsm[sub.StartEvent](sub.fsm)
-execute=false
-end
-local fsmparent,Event=self:_isendstate(To)
-if fsmparent and Event then
-self:T("*** FSM *** End *** "..Event)
-self:_call_handler("onenter",To,Params,EventName)
-self:_call_handler("OnEnter",To,Params,EventName)
-self:_call_handler("onafter",EventName,Params,EventName)
-self:_call_handler("OnAfter",EventName,Params,EventName)
-self:_call_handler("onstate","change",Params,EventName)
-fsmparent[Event](fsmparent)
-execute=false
-end
-if execute then
-self:_call_handler("onafter",EventName,Params,EventName)
-self:_call_handler("OnAfter",EventName,Params,EventName)
-self:_call_handler("onenter",To,Params,EventName)
-self:_call_handler("OnEnter",To,Params,EventName)
-self:_call_handler("onstate","change",Params,EventName)
-end
-else
-self:T("*** FSM *** NO Transition *** "..self.current.." --> "..EventName.." --> ? ")
-end
-return nil
-end
-function FSM:_delayed_transition(EventName)
-return function(self,DelaySeconds,...)
-self:T3("Delayed Event: "..EventName)
-local CallID=0
-if DelaySeconds~=nil then
-if DelaySeconds<0 then
-DelaySeconds=math.abs(DelaySeconds)
-if not self._EventSchedules[EventName]then
-CallID=self.CallScheduler:Schedule(self,self._handler,{EventName,...},DelaySeconds or 1,nil,nil,nil,4,true)
-self._EventSchedules[EventName]=CallID
-self:T2(string.format("NEGATIVE Event %s delayed by %.3f sec SCHEDULED with CallID=%s",EventName,DelaySeconds,tostring(CallID)))
-else
-self:T2(string.format("NEGATIVE Event %s delayed by %.3f sec CANCELLED as we already have such an event in the queue.",EventName,DelaySeconds))
-end
-else
-CallID=self.CallScheduler:Schedule(self,self._handler,{EventName,...},DelaySeconds or 1,nil,nil,nil,4,true)
-self:T2(string.format("Event %s delayed by %.3f sec SCHEDULED with CallID=%s",EventName,DelaySeconds,tostring(CallID)))
-end
-else
-error("FSM: An asynchronous event trigger requires a DelaySeconds parameter!!! This can be positive or negative! Sorry, but will not process this.")
-end
-end
-end
-function FSM:_create_transition(EventName)
-return function(self,...)
-return self._handler(self,EventName,...)
-end
-end
-function FSM:_gosub(ParentFrom,ParentEvent)
-local fsmtable={}
-if self.subs[ParentFrom]and self.subs[ParentFrom][ParentEvent]then
-return self.subs[ParentFrom][ParentEvent]
-else
-return{}
-end
-end
-function FSM:_isendstate(Current)
-local FSMParent=self.fsmparent
-if FSMParent and self.endstates[Current]then
-FSMParent.current=Current
-local ParentFrom=FSMParent.current
-local Event=self.ReturnEvents[Current]
-if Event then
-return FSMParent,Event
-else
-end
-end
-return nil
-end
-function FSM:_add_to_map(Map,Event)
-self:F3({Map,Event})
-if type(Event.From)=='string'then
-Map[Event.From]=Event.To
-else
-for _,From in ipairs(Event.From)do
-Map[From]=Event.To
-end
-end
-end
-function FSM:GetState()
-return self.current
-end
-function FSM:GetCurrentState()
-return self.current
-end
-function FSM:Is(State)
-return self.current==State
-end
-function FSM:is(state)
-return self.current==state
-end
-function FSM:can(e)
-local Event=self.Events[e]
-local To=Event and Event.map[self.current]or Event.map['*']
-return To~=nil,To
-end
-function FSM:cannot(e)
-return not self:can(e)
-end
-end
-do
-FSM_CONTROLLABLE={
-ClassName="FSM_CONTROLLABLE",
-}
-function FSM_CONTROLLABLE:New(Controllable)
-local self=BASE:Inherit(self,FSM:New())
-if Controllable then
-self:SetControllable(Controllable)
-end
-self:AddTransition("*","Stop","Stopped")
-return self
-end
-function FSM_CONTROLLABLE:OnAfterStop(Controllable,From,Event,To)
-self.CallScheduler:Clear()
-end
-function FSM_CONTROLLABLE:SetControllable(FSMControllable)
-self.Controllable=FSMControllable
-end
-function FSM_CONTROLLABLE:GetControllable()
-return self.Controllable
-end
-function FSM_CONTROLLABLE:_call_handler(step,trigger,params,EventName)
-local handler=step..trigger
-local ErrorHandler=function(errmsg)
-env.info("Error in SCHEDULER function:"..errmsg)
-if BASE.Debug~=nil then
-env.info(BASE.Debug.traceback())
-end
-return errmsg
-end
-if self[handler]then
-self:T("*** FSM *** "..step.." *** "..params[1].." --> "..params[2].." --> "..params[3].." *** TaskUnit: "..self.Controllable:GetName())
-self._EventSchedules[EventName]=nil
-local Result,Value=xpcall(function()
-return self[handler](self,self.Controllable,unpack(params))
-end,ErrorHandler)
-return Value
-end
-end
-end
-do
-FSM_PROCESS={ClassName="FSM_PROCESS"}
-function FSM_PROCESS:New(Controllable,Task)
-local self=BASE:Inherit(self,FSM_CONTROLLABLE:New())
-self:Assign(Controllable,Task)
-return self
-end
-function FSM_PROCESS:Init(FsmProcess)
-self:T("No Initialisation")
-end
-function FSM_PROCESS:_call_handler(step,trigger,params,EventName)
-local handler=step..trigger
-local ErrorHandler=function(errmsg)
-env.info("Error in FSM_PROCESS call handler:"..errmsg)
-if BASE.Debug~=nil then
-env.info(BASE.Debug.traceback())
-end
-return errmsg
-end
-if self[handler]then
-if handler~="onstatechange"then
-self:T("*** FSM *** "..step.." *** "..params[1].." --> "..params[2].." --> "..params[3].." *** Task: "..self.Task:GetName()..", TaskUnit: "..self.Controllable:GetName())
-end
-self._EventSchedules[EventName]=nil
-local Result,Value
-if self.Controllable and self.Controllable:IsAlive()==true then
-Result,Value=xpcall(function()
-return self[handler](self,self.Controllable,self.Task,unpack(params))
-end,ErrorHandler)
-end
-return Value
-end
-end
-function FSM_PROCESS:Copy(Controllable,Task)
-local NewFsm=self:New(Controllable,Task)
-NewFsm:Assign(Controllable,Task)
-NewFsm:Init(self)
-NewFsm:SetStartState(self:GetStartState())
-for TransitionID,Transition in pairs(self:GetTransitions())do
-NewFsm:AddTransition(Transition.From,Transition.Event,Transition.To)
-end
-for ProcessID,Process in pairs(self:GetProcesses())do
-local FsmProcess=NewFsm:AddProcess(Process.From,Process.Event,Process.fsm:Copy(Controllable,Task),Process.ReturnEvents)
-end
-for EndStateID,EndState in pairs(self:GetEndStates())do
-NewFsm:AddEndState(EndState)
-end
-for ScoreID,Score in pairs(self:GetScores())do
-NewFsm:AddScore(ScoreID,Score.ScoreText,Score.Score)
-end
-return NewFsm
-end
-function FSM_PROCESS:Remove()
-self:F({self:GetClassNameAndID()})
-self:F("Clearing Schedules")
-self.CallScheduler:Clear()
-for ProcessID,Process in pairs(self:GetProcesses())do
-if Process.fsm then
-Process.fsm:Remove()
-Process.fsm=nil
-end
-end
-return self
-end
-function FSM_PROCESS:SetTask(Task)
-self.Task=Task
-return self
-end
-function FSM_PROCESS:GetTask()
-return self.Task
-end
-function FSM_PROCESS:GetMission()
-return self.Task.Mission
-end
-function FSM_PROCESS:GetCommandCenter()
-return self:GetTask():GetMission():GetCommandCenter()
-end
-function FSM_PROCESS:Message(Message)
-self:F({Message=Message})
-local CC=self:GetCommandCenter()
-local TaskGroup=self.Controllable:GetGroup()
-local PlayerName=self.Controllable:GetPlayerName()
-PlayerName=PlayerName and" ("..PlayerName..")"or""
-local Callsign=self.Controllable:GetCallsign()
-local Prefix=Callsign and" @ "..Callsign..PlayerName or""
-Message=Prefix..": "..Message
-CC:MessageToGroup(Message,TaskGroup)
-end
-function FSM_PROCESS:Assign(ProcessUnit,Task)
-self:SetControllable(ProcessUnit)
-self:SetTask(Task)
-return self
-end
-function FSM_PROCESS:onenterFailed(ProcessUnit,Task,From,Event,To)
-self:T("*** FSM *** Failed *** "..Task:GetName().."/"..ProcessUnit:GetName().." *** "..From.." --> "..Event.." --> "..To)
-self.Task:Fail()
-end
-function FSM_PROCESS:onstatechange(ProcessUnit,Task,From,Event,To)
-if From~=To then
-self:T("*** FSM *** Change *** "..Task:GetName().."/"..ProcessUnit:GetName().." *** "..From.." --> "..Event.." --> "..To)
-end
-if self._Scores[To]then
-local Task=self.Task
-local Scoring=Task:GetScoring()
-if Scoring then
-Scoring:_AddMissionTaskScore(Task.Mission,ProcessUnit,self._Scores[To].ScoreText,self._Scores[To].Score)
-end
-end
-end
-end
-do
-FSM_TASK={
-ClassName="FSM_TASK",
-}
-function FSM_TASK:New(TaskName)
-local self=BASE:Inherit(self,FSM_CONTROLLABLE:New())
-self["onstatechange"]=self.OnStateChange
-self.TaskName=TaskName
-return self
-end
-function FSM_TASK:_call_handler(step,trigger,params,EventName)
-local handler=step..trigger
-local ErrorHandler=function(errmsg)
-env.info("Error in SCHEDULER function:"..errmsg)
-if BASE.Debug~=nil then
-env.info(BASE.Debug.traceback())
-end
-return errmsg
-end
-if self[handler]then
-self:T("*** FSM *** "..step.." *** "..params[1].." --> "..params[2].." --> "..params[3].." *** Task: "..self.TaskName)
-self._EventSchedules[EventName]=nil
-local Result,Value=xpcall(function()
-return self[handler](self,unpack(params))
-end,ErrorHandler)
-return Value
-end
-end
-end
-do
-FSM_SET={
-ClassName="FSM_SET",
-}
-function FSM_SET:New(FSMSet)
-self=BASE:Inherit(self,FSM:New())
-if FSMSet then
-self:Set(FSMSet)
-end
-return self
-end
-function FSM_SET:Set(FSMSet)
-self:F(FSMSet)
-self.Set=FSMSet
-end
-function FSM_SET:Get()
-return self.Set
-end
-function FSM_SET:_call_handler(step,trigger,params,EventName)
-local handler=step..trigger
-if self[handler]then
-self:T("*** FSM *** "..step.." *** "..params[1].." --> "..params[2].." --> "..params[3])
-self._EventSchedules[EventName]=nil
-return self[handler](self,self.Set,unpack(params))
-end
-end
-end
-do
-GOAL={
-ClassName="GOAL",
-}
-GOAL.Players={}
-GOAL.TotalContributions=0
-function GOAL:New()
-local self=BASE:Inherit(self,FSM:New())
-self:F({})
-self:SetStartState("Pending")
-self:AddTransition("*","Achieved","Achieved")
-self:SetEventPriority(5)
-return self
-end
-function GOAL:AddPlayerContribution(PlayerName)
-self:F({PlayerName})
-self.Players[PlayerName]=self.Players[PlayerName]or 0
-self.Players[PlayerName]=self.Players[PlayerName]+1
-self.TotalContributions=self.TotalContributions+1
-end
-function GOAL:GetPlayerContribution(PlayerName)
-return self.Players[PlayerName]or 0
-end
-function GOAL:GetPlayerContributions()
-return self.Players or{}
-end
-function GOAL:GetTotalContributions()
-return self.TotalContributions or 0
-end
-function GOAL:IsAchieved()
-return self:Is("Achieved")
-end
-end
-MARKEROPS_BASE={
-ClassName="MARKEROPS",
-Tag="mytag",
-Keywords={},
-version="0.1.1",
-debug=false,
-Casesensitive=true,
-}
-function MARKEROPS_BASE:New(Tagname,Keywords,Casesensitive)
-local self=BASE:Inherit(self,FSM:New())
-self.lid=string.format("MARKEROPS_BASE %s | ",tostring(self.version))
-self.Tag=Tagname or"mytag"
-self.Keywords=Keywords or{}
-self.debug=false
-self.Casesensitive=true
-if Casesensitive and Casesensitive==false then
-self.Casesensitive=false
-end
-self:SetStartState("Stopped")
-self:AddTransition("Stopped","Start","Running")
-self:AddTransition("*","MarkAdded","*")
-self:AddTransition("*","MarkChanged","*")
-self:AddTransition("*","MarkDeleted","*")
-self:AddTransition("Running","Stop","Stopped")
-self:HandleEvent(EVENTS.MarkAdded,self.OnEventMark)
-self:HandleEvent(EVENTS.MarkChange,self.OnEventMark)
-self:HandleEvent(EVENTS.MarkRemoved,self.OnEventMark)
-self:I(self.lid..string.format("started for %s",self.Tag))
-self:__Start(1)
-return self
-end
-function MARKEROPS_BASE:OnEventMark(Event)
-self:T({Event})
-if Event==nil or Event.idx==nil then
-self:E("Skipping onEvent. Event or Event.idx unknown.")
-return true
-end
-local vec3={y=Event.pos.y,x=Event.pos.x,z=Event.pos.z}
-local coord=COORDINATE:NewFromVec3(vec3)
-if self.debug then
-local coordtext=coord:ToStringLLDDM()
-local text=tostring(Event.text)
-local m=MESSAGE:New(string.format("Mark added at %s with text: %s",coordtext,text),10,"Info",false):ToAll()
-end
-if Event.id==world.event.S_EVENT_MARK_ADDED then
-self:T({event="S_EVENT_MARK_ADDED",carrier=self.groupname,vec3=Event.pos})
-local Eventtext=tostring(Event.text)
-if Eventtext~=nil then
-if self:_MatchTag(Eventtext)then
-local matchtable=self:_MatchKeywords(Eventtext)
-self:MarkAdded(Eventtext,matchtable,coord)
-end
-end
-elseif Event.id==world.event.S_EVENT_MARK_CHANGE then
-self:T({event="S_EVENT_MARK_CHANGE",carrier=self.groupname,vec3=Event.pos})
-local Eventtext=tostring(Event.text)
-if Eventtext~=nil then
-if self:_MatchTag(Eventtext)then
-local matchtable=self:_MatchKeywords(Eventtext)
-self:MarkChanged(Eventtext,matchtable,coord,Event.idx)
-end
-end
-elseif Event.id==world.event.S_EVENT_MARK_REMOVED then
-self:T({event="S_EVENT_MARK_REMOVED",carrier=self.groupname,vec3=Event.pos})
-local Eventtext=tostring(Event.text)
-if Eventtext~=nil then
-if self:_MatchTag(Eventtext)then
-self:MarkDeleted()
-end
-end
-end
-end
-function MARKEROPS_BASE:_MatchTag(Eventtext)
-local matches=false
-if not self.Casesensitive then
-local type=string.lower(self.Tag)
-if string.find(string.lower(Eventtext),type)then
-matches=true
-end
-else
-local type=self.Tag
-if string.find(Eventtext,type)then
-matches=true
-end
-end
-return matches
-end
-function MARKEROPS_BASE:_MatchKeywords(Eventtext)
-local matchtable={}
-local keytable=self.Keywords
-for _index,_word in pairs(keytable)do
-if string.find(string.lower(Eventtext),string.lower(_word))then
-table.insert(matchtable,_word)
-end
-end
-return matchtable
-end
-function MARKEROPS_BASE:onbeforeMarkAdded(From,Event,To,Text,Keywords,Coord)
-self:T({self.lid,From,Event,To,Text,Keywords,Coord:ToStringLLDDM()})
-end
-function MARKEROPS_BASE:onbeforeMarkChanged(From,Event,To,Text,Keywords,Coord)
-self:T({self.lid,From,Event,To,Text,Keywords,Coord:ToStringLLDDM()})
-end
-function MARKEROPS_BASE:onbeforeMarkDeleted(From,Event,To)
-self:T({self.lid,From,Event,To})
-end
-function MARKEROPS_BASE:onenterStopped(From,Event,To)
-self:T({self.lid,From,Event,To})
-self:UnHandleEvent(EVENTS.MarkAdded)
-self:UnHandleEvent(EVENTS.MarkChange)
-self:UnHandleEvent(EVENTS.MarkRemoved)
-end
-MENU_INDEX={}
-MENU_INDEX.MenuMission={}
-MENU_INDEX.MenuMission.Menus={}
-MENU_INDEX.Coalition={}
-MENU_INDEX.Coalition[coalition.side.BLUE]={}
-MENU_INDEX.Coalition[coalition.side.BLUE].Menus={}
-MENU_INDEX.Coalition[coalition.side.RED]={}
-MENU_INDEX.Coalition[coalition.side.RED].Menus={}
-MENU_INDEX.Group={}
-function MENU_INDEX:ParentPath(ParentMenu,MenuText)
-local Path=ParentMenu and"@"..table.concat(ParentMenu.MenuPath or{},"@")or""
-if ParentMenu then
-if ParentMenu:IsInstanceOf("MENU_GROUP")or ParentMenu:IsInstanceOf("MENU_GROUP_COMMAND")then
-local GroupName=ParentMenu.Group:GetName()
-if not self.Group[GroupName].Menus[Path]then
-BASE:E({Path=Path,GroupName=GroupName})
-error("Parent path not found in menu index for group menu")
-return nil
-end
-elseif ParentMenu:IsInstanceOf("MENU_COALITION")or ParentMenu:IsInstanceOf("MENU_COALITION_COMMAND")then
-local Coalition=ParentMenu.Coalition
-if not self.Coalition[Coalition].Menus[Path]then
-BASE:E({Path=Path,Coalition=Coalition})
-error("Parent path not found in menu index for coalition menu")
-return nil
-end
-elseif ParentMenu:IsInstanceOf("MENU_MISSION")or ParentMenu:IsInstanceOf("MENU_MISSION_COMMAND")then
-if not self.MenuMission.Menus[Path]then
-BASE:E({Path=Path})
-error("Parent path not found in menu index for mission menu")
-return nil
-end
-end
-end
-Path=Path.."@"..MenuText
-return Path
-end
-function MENU_INDEX:PrepareMission()
-self.MenuMission.Menus=self.MenuMission.Menus or{}
-end
-function MENU_INDEX:PrepareCoalition(CoalitionSide)
-self.Coalition[CoalitionSide]=self.Coalition[CoalitionSide]or{}
-self.Coalition[CoalitionSide].Menus=self.Coalition[CoalitionSide].Menus or{}
-end
-function MENU_INDEX:PrepareGroup(Group)
-if Group and Group:IsAlive()~=nil then
-local GroupName=Group:GetName()
-self.Group[GroupName]=self.Group[GroupName]or{}
-self.Group[GroupName].Menus=self.Group[GroupName].Menus or{}
-end
-end
-function MENU_INDEX:HasMissionMenu(Path)
-return self.MenuMission.Menus[Path]
-end
-function MENU_INDEX:SetMissionMenu(Path,Menu)
-self.MenuMission.Menus[Path]=Menu
-end
-function MENU_INDEX:ClearMissionMenu(Path)
-self.MenuMission.Menus[Path]=nil
-end
-function MENU_INDEX:HasCoalitionMenu(Coalition,Path)
-return self.Coalition[Coalition].Menus[Path]
-end
-function MENU_INDEX:SetCoalitionMenu(Coalition,Path,Menu)
-self.Coalition[Coalition].Menus[Path]=Menu
-end
-function MENU_INDEX:ClearCoalitionMenu(Coalition,Path)
-self.Coalition[Coalition].Menus[Path]=nil
-end
-function MENU_INDEX:HasGroupMenu(Group,Path)
-if Group and Group:IsAlive()then
-local MenuGroupName=Group:GetName()
-return self.Group[MenuGroupName].Menus[Path]
-end
-return nil
-end
-function MENU_INDEX:SetGroupMenu(Group,Path,Menu)
-local MenuGroupName=Group:GetName()
-Group:F({MenuGroupName=MenuGroupName,Path=Path})
-self.Group[MenuGroupName].Menus[Path]=Menu
-end
-function MENU_INDEX:ClearGroupMenu(Group,Path)
-local MenuGroupName=Group:GetName()
-self.Group[MenuGroupName].Menus[Path]=nil
-end
-function MENU_INDEX:Refresh(Group)
-for MenuID,Menu in pairs(self.MenuMission.Menus)do
-Menu:Refresh()
-end
-for MenuID,Menu in pairs(self.Coalition[coalition.side.BLUE].Menus)do
-Menu:Refresh()
-end
-for MenuID,Menu in pairs(self.Coalition[coalition.side.RED].Menus)do
-Menu:Refresh()
-end
-local GroupName=Group:GetName()
-for MenuID,Menu in pairs(self.Group[GroupName].Menus)do
-Menu:Refresh()
-end
-return self
-end
-do
-MENU_BASE={
-ClassName="MENU_BASE",
-MenuPath=nil,
-MenuText="",
-MenuParentPath=nil,
-}
-function MENU_BASE:New(MenuText,ParentMenu)
-local MenuParentPath={}
-if ParentMenu~=nil then
-MenuParentPath=ParentMenu.MenuPath
-end
-local self=BASE:Inherit(self,BASE:New())
-self.MenuPath=nil
-self.MenuText=MenuText
-self.ParentMenu=ParentMenu
-self.MenuParentPath=MenuParentPath
-self.Path=(self.ParentMenu and"@"..table.concat(self.MenuParentPath or{},"@")or"").."@"..self.MenuText
-self.Menus={}
-self.MenuCount=0
-self.MenuStamp=timer.getTime()
-self.MenuRemoveParent=false
-if self.ParentMenu then
-self.ParentMenu.Menus=self.ParentMenu.Menus or{}
-self.ParentMenu.Menus[MenuText]=self
-end
-return self
-end
-function MENU_BASE:SetParentMenu(MenuText,Menu)
-if self.ParentMenu then
-self.ParentMenu.Menus=self.ParentMenu.Menus or{}
-self.ParentMenu.Menus[MenuText]=Menu
-self.ParentMenu.MenuCount=self.ParentMenu.MenuCount+1
-end
-end
-function MENU_BASE:ClearParentMenu(MenuText)
-if self.ParentMenu and self.ParentMenu.Menus[MenuText]then
-self.ParentMenu.Menus[MenuText]=nil
-self.ParentMenu.MenuCount=self.ParentMenu.MenuCount-1
-if self.ParentMenu.MenuCount==0 then
-end
-end
-end
-function MENU_BASE:SetRemoveParent(RemoveParent)
-self.MenuRemoveParent=RemoveParent
-return self
-end
-function MENU_BASE:GetMenu(MenuText)
-return self.Menus[MenuText]
-end
-function MENU_BASE:SetStamp(MenuStamp)
-self.MenuStamp=MenuStamp
-return self
-end
-function MENU_BASE:GetStamp()
-return timer.getTime()
-end
-function MENU_BASE:SetTime(MenuStamp)
-self.MenuStamp=MenuStamp
-return self
-end
-function MENU_BASE:SetTag(MenuTag)
-self.MenuTag=MenuTag
-return self
-end
-end
-do
-MENU_COMMAND_BASE={
-ClassName="MENU_COMMAND_BASE",
-CommandMenuFunction=nil,
-CommandMenuArgument=nil,
-MenuCallHandler=nil,
-}
-function MENU_COMMAND_BASE:New(MenuText,ParentMenu,CommandMenuFunction,CommandMenuArguments)
-local self=BASE:Inherit(self,MENU_BASE:New(MenuText,ParentMenu))
-local ErrorHandler=function(errmsg)
-env.info("MOOSE error in MENU COMMAND function: "..errmsg)
-if BASE.Debug~=nil then
-env.info(BASE.Debug.traceback())
-end
-return errmsg
-end
-self:SetCommandMenuFunction(CommandMenuFunction)
-self:SetCommandMenuArguments(CommandMenuArguments)
-self.MenuCallHandler=function()
-local function MenuFunction()
-return self.CommandMenuFunction(unpack(self.CommandMenuArguments))
-end
-local Status,Result=xpcall(MenuFunction,ErrorHandler)
-end
-return self
-end
-function MENU_COMMAND_BASE:SetCommandMenuFunction(CommandMenuFunction)
-self.CommandMenuFunction=CommandMenuFunction
-return self
-end
-function MENU_COMMAND_BASE:SetCommandMenuArguments(CommandMenuArguments)
-self.CommandMenuArguments=CommandMenuArguments
-return self
-end
-end
-do
-MENU_MISSION={
-ClassName="MENU_MISSION",
-}
-function MENU_MISSION:New(MenuText,ParentMenu)
-MENU_INDEX:PrepareMission()
-local Path=MENU_INDEX:ParentPath(ParentMenu,MenuText)
-local MissionMenu=MENU_INDEX:HasMissionMenu(Path)
-if MissionMenu then
-return MissionMenu
-else
-local self=BASE:Inherit(self,MENU_BASE:New(MenuText,ParentMenu))
-MENU_INDEX:SetMissionMenu(Path,self)
-self.MenuPath=missionCommands.addSubMenu(self.MenuText,self.MenuParentPath)
-self:SetParentMenu(self.MenuText,self)
-return self
-end
-end
-function MENU_MISSION:Refresh()
-do
-missionCommands.removeItem(self.MenuPath)
-self.MenuPath=missionCommands.addSubMenu(self.MenuText,self.MenuParentPath)
-end
-return self
-end
-function MENU_MISSION:RemoveSubMenus()
-for MenuID,Menu in pairs(self.Menus or{})do
-Menu:Remove()
-end
-self.Menus=nil
-end
-function MENU_MISSION:Remove(MenuStamp,MenuTag)
-MENU_INDEX:PrepareMission()
-local Path=MENU_INDEX:ParentPath(self.ParentMenu,self.MenuText)
-local MissionMenu=MENU_INDEX:HasMissionMenu(Path)
-if MissionMenu==self then
-self:RemoveSubMenus()
-if not MenuStamp or self.MenuStamp~=MenuStamp then
-if(not MenuTag)or(MenuTag and self.MenuTag and MenuTag==self.MenuTag)then
-self:F({Text=self.MenuText,Path=self.MenuPath})
-if self.MenuPath~=nil then
-missionCommands.removeItem(self.MenuPath)
-end
-MENU_INDEX:ClearMissionMenu(self.Path)
-self:ClearParentMenu(self.MenuText)
-return nil
-end
-end
-else
-BASE:E({"Cannot Remove MENU_MISSION",Path=Path,ParentMenu=self.ParentMenu,MenuText=self.MenuText})
-end
-return self
-end
-end
-do
-MENU_MISSION_COMMAND={
-ClassName="MENU_MISSION_COMMAND",
-}
-function MENU_MISSION_COMMAND:New(MenuText,ParentMenu,CommandMenuFunction,...)
-MENU_INDEX:PrepareMission()
-local Path=MENU_INDEX:ParentPath(ParentMenu,MenuText)
-local MissionMenu=MENU_INDEX:HasMissionMenu(Path)
-if MissionMenu then
-MissionMenu:SetCommandMenuFunction(CommandMenuFunction)
-MissionMenu:SetCommandMenuArguments(arg)
-return MissionMenu
-else
-local self=BASE:Inherit(self,MENU_COMMAND_BASE:New(MenuText,ParentMenu,CommandMenuFunction,arg))
-MENU_INDEX:SetMissionMenu(Path,self)
-self.MenuPath=missionCommands.addCommand(MenuText,self.MenuParentPath,self.MenuCallHandler)
-self:SetParentMenu(self.MenuText,self)
-return self
-end
-end
-function MENU_MISSION_COMMAND:Refresh()
-do
-missionCommands.removeItem(self.MenuPath)
-missionCommands.addCommand(self.MenuText,self.MenuParentPath,self.MenuCallHandler)
-end
-return self
-end
-function MENU_MISSION_COMMAND:Remove()
-MENU_INDEX:PrepareMission()
-local Path=MENU_INDEX:ParentPath(self.ParentMenu,self.MenuText)
-local MissionMenu=MENU_INDEX:HasMissionMenu(Path)
-if MissionMenu==self then
-if not MenuStamp or self.MenuStamp~=MenuStamp then
-if(not MenuTag)or(MenuTag and self.MenuTag and MenuTag==self.MenuTag)then
-self:F({Text=self.MenuText,Path=self.MenuPath})
-if self.MenuPath~=nil then
-missionCommands.removeItem(self.MenuPath)
-end
-MENU_INDEX:ClearMissionMenu(self.Path)
-self:ClearParentMenu(self.MenuText)
-return nil
-end
-end
-else
-BASE:E({"Cannot Remove MENU_MISSION_COMMAND",Path=Path,ParentMenu=self.ParentMenu,MenuText=self.MenuText})
-end
-return self
-end
-end
-do
-MENU_COALITION={
-ClassName="MENU_COALITION"
-}
-function MENU_COALITION:New(Coalition,MenuText,ParentMenu)
-MENU_INDEX:PrepareCoalition(Coalition)
-local Path=MENU_INDEX:ParentPath(ParentMenu,MenuText)
-local CoalitionMenu=MENU_INDEX:HasCoalitionMenu(Coalition,Path)
-if CoalitionMenu then
-return CoalitionMenu
-else
-local self=BASE:Inherit(self,MENU_BASE:New(MenuText,ParentMenu))
-MENU_INDEX:SetCoalitionMenu(Coalition,Path,self)
-self.Coalition=Coalition
-self.MenuPath=missionCommands.addSubMenuForCoalition(Coalition,MenuText,self.MenuParentPath)
-self:SetParentMenu(self.MenuText,self)
-return self
-end
-end
-function MENU_COALITION:Refresh()
-do
-missionCommands.removeItemForCoalition(self.Coalition,self.MenuPath)
-missionCommands.addSubMenuForCoalition(self.Coalition,self.MenuText,self.MenuParentPath)
-end
-return self
-end
-function MENU_COALITION:RemoveSubMenus()
-for MenuID,Menu in pairs(self.Menus or{})do
-Menu:Remove()
-end
-self.Menus=nil
-end
-function MENU_COALITION:Remove(MenuStamp,MenuTag)
-MENU_INDEX:PrepareCoalition(self.Coalition)
-local Path=MENU_INDEX:ParentPath(self.ParentMenu,self.MenuText)
-local CoalitionMenu=MENU_INDEX:HasCoalitionMenu(self.Coalition,Path)
-if CoalitionMenu==self then
-self:RemoveSubMenus()
-if not MenuStamp or self.MenuStamp~=MenuStamp then
-if(not MenuTag)or(MenuTag and self.MenuTag and MenuTag==self.MenuTag)then
-self:F({Coalition=self.Coalition,Text=self.MenuText,Path=self.MenuPath})
-if self.MenuPath~=nil then
-missionCommands.removeItemForCoalition(self.Coalition,self.MenuPath)
-end
-MENU_INDEX:ClearCoalitionMenu(self.Coalition,Path)
-self:ClearParentMenu(self.MenuText)
-return nil
-end
-end
-else
-BASE:E({"Cannot Remove MENU_COALITION",Path=Path,ParentMenu=self.ParentMenu,MenuText=self.MenuText,Coalition=self.Coalition})
-end
-return self
-end
-end
-do
-MENU_COALITION_COMMAND={
-ClassName="MENU_COALITION_COMMAND"
-}
-function MENU_COALITION_COMMAND:New(Coalition,MenuText,ParentMenu,CommandMenuFunction,...)
-MENU_INDEX:PrepareCoalition(Coalition)
-local Path=MENU_INDEX:ParentPath(ParentMenu,MenuText)
-local CoalitionMenu=MENU_INDEX:HasCoalitionMenu(Coalition,Path)
-if CoalitionMenu then
-CoalitionMenu:SetCommandMenuFunction(CommandMenuFunction)
-CoalitionMenu:SetCommandMenuArguments(arg)
-return CoalitionMenu
-else
-local self=BASE:Inherit(self,MENU_COMMAND_BASE:New(MenuText,ParentMenu,CommandMenuFunction,arg))
-MENU_INDEX:SetCoalitionMenu(Coalition,Path,self)
-self.Coalition=Coalition
-self.MenuPath=missionCommands.addCommandForCoalition(self.Coalition,MenuText,self.MenuParentPath,self.MenuCallHandler)
-self:SetParentMenu(self.MenuText,self)
-return self
-end
-end
-function MENU_COALITION_COMMAND:Refresh()
-do
-missionCommands.removeItemForCoalition(self.Coalition,self.MenuPath)
-missionCommands.addCommandForCoalition(self.Coalition,self.MenuText,self.MenuParentPath,self.MenuCallHandler)
-end
-return self
-end
-function MENU_COALITION_COMMAND:Remove(MenuStamp,MenuTag)
-MENU_INDEX:PrepareCoalition(self.Coalition)
-local Path=MENU_INDEX:ParentPath(self.ParentMenu,self.MenuText)
-local CoalitionMenu=MENU_INDEX:HasCoalitionMenu(self.Coalition,Path)
-if CoalitionMenu==self then
-if not MenuStamp or self.MenuStamp~=MenuStamp then
-if(not MenuTag)or(MenuTag and self.MenuTag and MenuTag==self.MenuTag)then
-self:F({Coalition=self.Coalition,Text=self.MenuText,Path=self.MenuPath})
-if self.MenuPath~=nil then
-missionCommands.removeItemForCoalition(self.Coalition,self.MenuPath)
-end
-MENU_INDEX:ClearCoalitionMenu(self.Coalition,Path)
-self:ClearParentMenu(self.MenuText)
-return nil
-end
-end
-else
-BASE:E({"Cannot Remove MENU_COALITION_COMMAND",Path=Path,ParentMenu=self.ParentMenu,MenuText=self.MenuText,Coalition=self.Coalition})
-end
-return self
-end
-end
-do
-local _MENUGROUPS={}
-MENU_GROUP={
-ClassName="MENU_GROUP"
-}
-function MENU_GROUP:New(Group,MenuText,ParentMenu)
-MENU_INDEX:PrepareGroup(Group)
-local Path=MENU_INDEX:ParentPath(ParentMenu,MenuText)
-local GroupMenu=MENU_INDEX:HasGroupMenu(Group,Path)
-if GroupMenu then
-return GroupMenu
-else
-self=BASE:Inherit(self,MENU_BASE:New(MenuText,ParentMenu))
-MENU_INDEX:SetGroupMenu(Group,Path,self)
-self.Group=Group
-self.GroupID=Group:GetID()
-self.MenuPath=missionCommands.addSubMenuForGroup(self.GroupID,MenuText,self.MenuParentPath)
-self:SetParentMenu(self.MenuText,self)
-return self
-end
-end
-function MENU_GROUP:Refresh()
-do
-missionCommands.removeItemForGroup(self.GroupID,self.MenuPath)
-missionCommands.addSubMenuForGroup(self.GroupID,self.MenuText,self.MenuParentPath)
-for MenuText,Menu in pairs(self.Menus or{})do
-Menu:Refresh()
-end
-end
-return self
-end
-function MENU_GROUP:RefreshAndOrderByTag()
-do
-missionCommands.removeItemForGroup(self.GroupID,self.MenuPath)
-missionCommands.addSubMenuForGroup(self.GroupID,self.MenuText,self.MenuParentPath)
-local MenuTable={}
-for MenuText,Menu in pairs(self.Menus or{})do
-local tag=Menu.MenuTag or math.random(1,10000)
-MenuTable[#MenuTable+1]={Tag=tag,Enty=Menu}
-end
-table.sort(MenuTable,function(k1,k2)return k1.tag=self.x and z-Precision<=self.z and z+Precision>=self.z
-end
-function COORDINATE:ScanObjects(radius,scanunits,scanstatics,scanscenery)
-self:F(string.format("Scanning in radius %.1f m.",radius or 100))
-local SphereSearch={
-id=world.VolumeType.SPHERE,
-params={
-point=self:GetVec3(),
-radius=radius,
-}
-}
-radius=radius or 100
-if scanunits==nil then
-scanunits=true
-end
-if scanstatics==nil then
-scanstatics=true
-end
-if scanscenery==nil then
-scanscenery=false
-end
-local scanobjects={}
-if scanunits then
-table.insert(scanobjects,Object.Category.UNIT)
-end
-if scanstatics then
-table.insert(scanobjects,Object.Category.STATIC)
-end
-if scanscenery then
-table.insert(scanobjects,Object.Category.SCENERY)
-end
-local Units={}
-local Statics={}
-local Scenery={}
-local gotstatics=false
-local gotunits=false
-local gotscenery=false
-local function EvaluateZone(ZoneObject)
-if ZoneObject then
-local ObjectCategory=Object.getCategory(ZoneObject)
-if ObjectCategory==Object.Category.UNIT and ZoneObject:isExist()then
-table.insert(Units,UNIT:Find(ZoneObject))
-gotunits=true
-elseif ObjectCategory==Object.Category.STATIC and ZoneObject:isExist()then
-table.insert(Statics,ZoneObject)
-gotstatics=true
-elseif ObjectCategory==Object.Category.SCENERY then
-table.insert(Scenery,ZoneObject)
-gotscenery=true
-end
-end
-return true
-end
-world.searchObjects(scanobjects,SphereSearch,EvaluateZone)
-for _,unit in pairs(Units)do
-self:T(string.format("Scan found unit %s",unit:GetName()))
-end
-for _,static in pairs(Statics)do
-self:T(string.format("Scan found static %s",static:getName()))
-_DATABASE:AddStatic(static:getName())
-end
-for _,scenery in pairs(Scenery)do
-self:T(string.format("Scan found scenery %s typename=%s",scenery:getName(),scenery:getTypeName()))
-end
-return gotunits,gotstatics,gotscenery,Units,Statics,Scenery
-end
-function COORDINATE:ScanUnits(radius)
-local _,_,_,units=self:ScanObjects(radius,true,false,false)
-local set=SET_UNIT:New()
-for _,unit in pairs(units)do
-set:AddUnit(unit)
-end
-return set
-end
-function COORDINATE:ScanStatics(radius)
-local _,_,_,_,statics=self:ScanObjects(radius,false,true,false)
-local set=SET_STATIC:New()
-for _,stat in pairs(statics)do
-set:AddStatic(STATIC:Find(stat))
-end
-return set
-end
-function COORDINATE:FindClosestStatic(radius)
-local units=self:ScanStatics(radius)
-local umin=nil
-local dmin=math.huge
-for _,_unit in pairs(units.Set)do
-local unit=_unit
-local coordinate=unit:GetCoordinate()
-local d=self:Get2DDistance(coordinate)
-if d1 then
-Radials=2-Radials
-end
-local RadialMultiplier
-if InnerRadius and InnerRadius<=OuterRadius then
-RadialMultiplier=(OuterRadius-InnerRadius)*Radials+InnerRadius
-else
-RadialMultiplier=OuterRadius*Radials
-end
-local RandomVec2
-if OuterRadius>0 then
-RandomVec2={x=math.cos(Theta)*RadialMultiplier+self.x,y=math.sin(Theta)*RadialMultiplier+self.z}
-else
-RandomVec2={x=self.x,y=self.z}
-end
-return RandomVec2
-end
-function COORDINATE:GetRandomCoordinateInRadius(OuterRadius,InnerRadius)
-self:F2({OuterRadius,InnerRadius})
-local coord=COORDINATE:NewFromVec2(self:GetRandomVec2InRadius(OuterRadius,InnerRadius))
-return coord
-end
-function COORDINATE:GetRandomVec3InRadius(OuterRadius,InnerRadius)
-local RandomVec2=self:GetRandomVec2InRadius(OuterRadius,InnerRadius)
-local y=self.y+math.random(InnerRadius,OuterRadius)
-local RandomVec3={x=RandomVec2.x,y=y,z=RandomVec2.y}
-return RandomVec3
-end
-function COORDINATE:GetLandHeight()
-local Vec2={x=self.x,y=self.z}
-return land.getHeight(Vec2)
-end
-function COORDINATE:SetHeading(Heading)
-self.Heading=Heading
-end
-function COORDINATE:GetHeading()
-return self.Heading
-end
-function COORDINATE:SetVelocity(Velocity)
-self.Velocity=Velocity
-end
-function COORDINATE:GetVelocity()
-local Velocity=self.Velocity
-return Velocity or 0
-end
-function COORDINATE:GetName()
-local name=self:ToStringMGRS()
-return name
-end
-function COORDINATE:GetMovingText(Settings)
-return self:GetVelocityText(Settings)..", "..self:GetHeadingText(Settings)
-end
-function COORDINATE:GetDirectionVec3(TargetCoordinate)
-if TargetCoordinate then
-return{x=TargetCoordinate.x-self.x,y=TargetCoordinate.y-self.y,z=TargetCoordinate.z-self.z}
-else
-return{x=0,y=0,z=0}
-end
-end
-function COORDINATE:GetNorthCorrectionRadians()
-local TargetVec3=self:GetVec3()
-local lat,lon=coord.LOtoLL(TargetVec3)
-local north_posit=coord.LLtoLO(lat+1,lon)
-return math.atan2(north_posit.z-TargetVec3.z,north_posit.x-TargetVec3.x)
-end
-function COORDINATE:GetAngleRadians(DirectionVec3)
-local DirectionRadians=math.atan2(DirectionVec3.z,DirectionVec3.x)
-if DirectionRadians<0 then
-DirectionRadians=DirectionRadians+2*math.pi
-end
-return DirectionRadians
-end
-function COORDINATE:GetAngleDegrees(DirectionVec3)
-local AngleRadians=self:GetAngleRadians(DirectionVec3)
-local Angle=UTILS.ToDegree(AngleRadians)
-return Angle
-end
-function COORDINATE:GetIntermediateCoordinate(ToCoordinate,Fraction)
-local f=Fraction or 0.5
-local vec=UTILS.VecSubstract(ToCoordinate,self)
-if f>1 then
-local norm=UTILS.VecNorm(vec)
-f=Fraction/norm
-end
-vec.x=f*vec.x
-vec.y=f*vec.y
-vec.z=f*vec.z
-vec=UTILS.VecAdd(self,vec)
-local coord=COORDINATE:New(vec.x,vec.y,vec.z)
-return coord
-end
-function COORDINATE:Get2DDistance(TargetCoordinate)
-if not TargetCoordinate then return 1000000 end
-local a={x=TargetCoordinate.x-self.x,y=0,z=TargetCoordinate.z-self.z}
-local norm=UTILS.VecNorm(a)
-return norm
-end
-function COORDINATE:GetTemperature(height)
-self:F2(height)
-local y=height or self.y
-local point={x=self.x,y=height or self.y,z=self.z}
-local T,P=atmosphere.getTemperatureAndPressure(point)
-return T-273.15
-end
-function COORDINATE:GetTemperatureText(height,Settings)
-local DegreesCelcius=self:GetTemperature(height)
-local Settings=Settings or _SETTINGS
-if DegreesCelcius then
-if Settings:IsMetric()then
-return string.format(" %-2.2f °C",DegreesCelcius)
-else
-return string.format(" %-2.2f °F",UTILS.CelsiusToFahrenheit(DegreesCelcius))
-end
-else
-return" no temperature"
-end
-return nil
-end
-function COORDINATE:GetPressure(height)
-local point={x=self.x,y=height or self.y,z=self.z}
-local T,P=atmosphere.getTemperatureAndPressure(point)
-return P/100
-end
-function COORDINATE:GetPressureText(height,Settings)
-local Pressure_hPa=self:GetPressure(height)
-local Pressure_mmHg=Pressure_hPa*0.7500615613030
-local Pressure_inHg=Pressure_hPa*0.0295299830714
-local Settings=Settings or _SETTINGS
-if Pressure_hPa then
-if Settings:IsMetric()then
-return string.format(" %4.1f hPa (%3.1f mmHg)",Pressure_hPa,Pressure_mmHg)
-else
-return string.format(" %4.1f hPa (%3.2f inHg)",Pressure_hPa,Pressure_inHg)
-end
-else
-return" no pressure"
-end
-return nil
-end
-function COORDINATE:HeadingTo(ToCoordinate)
-local dz=ToCoordinate.z-self.z
-local dx=ToCoordinate.x-self.x
-local heading=math.deg(math.atan2(dz,dx))
-if heading<0 then
-heading=360+heading
-end
-return heading
-end
-function COORDINATE:GetWindVec3(height,turbulence)
-local landheight=self:GetLandHeight()+0.1
-local point={x=self.x,y=math.max(height or self.y,landheight),z=self.z}
-local wind=nil
-if turbulence then
-wind=atmosphere.getWindWithTurbulence(point)
-else
-wind=atmosphere.getWind(point)
-end
-return wind
-end
-function COORDINATE:GetWind(height,turbulence)
-local wind=self:GetWindVec3(height,turbulence)
-local direction=UTILS.VecHdg(wind)
-if direction>180 then
-direction=direction-180
-else
-direction=direction+180
-end
-local strength=UTILS.VecNorm(wind)
-return direction,strength
-end
-function COORDINATE:GetWindWithTurbulenceVec3(height)
-local landheight=self:GetLandHeight()+0.1
-local point={x=self.x,y=math.max(height or self.y,landheight),z=self.z}
-local vec3=atmosphere.getWindWithTurbulence(point)
-return vec3
-end
-function COORDINATE:GetWindText(height,Settings)
-local Direction,Strength=self:GetWind(height)
-local Settings=Settings or _SETTINGS
-if Direction and Strength then
-if Settings:IsMetric()then
-return string.format(" %d ° at %3.2f mps",Direction,UTILS.MpsToKmph(Strength))
-else
-return string.format(" %d ° at %3.2f kps",Direction,UTILS.MpsToKnots(Strength))
-end
-else
-return" no wind"
-end
-return nil
-end
-function COORDINATE:Get3DDistance(TargetCoordinate)
-local TargetVec3={x=TargetCoordinate.x,y=TargetCoordinate.y,z=TargetCoordinate.z}
-local SourceVec3=self:GetVec3()
-local dist=UTILS.VecDist3D(TargetVec3,SourceVec3)
-return dist
-end
-function COORDINATE:GetBearingText(AngleRadians,Precision,Settings,MagVar)
-local Settings=Settings or _SETTINGS
-local AngleDegrees=UTILS.Round(UTILS.ToDegree(AngleRadians),Precision)
-local s=string.format('%03d°',AngleDegrees)
-if MagVar then
-local variation=UTILS.GetMagneticDeclination()or 0
-local AngleMagnetic=AngleDegrees-variation
-if AngleMagnetic<0 then AngleMagnetic=360-AngleMagnetic end
-s=string.format('%03d°M|%03d°',AngleMagnetic,AngleDegrees)
-end
-return s
-end
-function COORDINATE:GetDistanceText(Distance,Settings,Language,Precision)
-local Settings=Settings or _SETTINGS
-local Language=Language or Settings.Locale or _SETTINGS.Locale or"EN"
-Language=string.lower(Language)
-local Precision=Precision or 0
-local DistanceText
-if Settings:IsMetric()then
-if Language=="en"then
-DistanceText=" for "..UTILS.Round(Distance/1000,Precision).." km"
-elseif Language=="ru"then
-DistanceText=" за "..UTILS.Round(Distance/1000,Precision).." километров"
-end
-else
-if Language=="en"then
-DistanceText=" for "..UTILS.Round(UTILS.MetersToNM(Distance),Precision).." miles"
-elseif Language=="ru"then
-DistanceText=" за "..UTILS.Round(UTILS.MetersToNM(Distance),Precision).." миль"
-end
-end
-return DistanceText
-end
-function COORDINATE:GetAltitudeText(Settings,Language)
-local Altitude=self.y
-local Settings=Settings or _SETTINGS
-local Language=Language or Settings.Locale or _SETTINGS.Locale or"EN"
-Language=string.lower(Language)
-if Altitude~=0 then
-if Settings:IsMetric()then
-if Language=="en"then
-return" at "..UTILS.Round(self.y,-3).." meters"
-elseif Language=="ru"then
-return" в "..UTILS.Round(self.y,-3).." метры"
-end
-else
-if Language=="en"then
-return" at "..UTILS.Round(UTILS.MetersToFeet(self.y),-3).." feet"
-elseif Language=="ru"then
-return" в "..UTILS.Round(self.y,-3).." ноги"
-end
-end
-else
-return""
-end
-end
-function COORDINATE:GetVelocityText(Settings)
-local Velocity=self:GetVelocity()
-local Settings=Settings or _SETTINGS
-if Velocity then
-if Settings:IsMetric()then
-return string.format(" moving at %d km/h",UTILS.MpsToKmph(Velocity))
-else
-return string.format(" moving at %d mi/h",UTILS.MpsToKmph(Velocity)/1.852)
-end
-else
-return" stationary"
-end
-end
-function COORDINATE:GetHeadingText(Settings)
-local Heading=self:GetHeading()
-if Heading then
-return string.format(" bearing %3d°",Heading)
-else
-return" bearing unknown"
-end
-end
-function COORDINATE:GetBRText(AngleRadians,Distance,Settings,Language,MagVar)
-local Settings=Settings or _SETTINGS
-local BearingText=self:GetBearingText(AngleRadians,0,Settings,MagVar)
-local DistanceText=self:GetDistanceText(Distance,Settings,Language,0)
-local BRText=BearingText..DistanceText
-return BRText
-end
-function COORDINATE:GetBRAText(AngleRadians,Distance,Settings,Language,MagVar)
-local Settings=Settings or _SETTINGS
-local BearingText=self:GetBearingText(AngleRadians,0,Settings,MagVar)
-local DistanceText=self:GetDistanceText(Distance,Settings,Language,0)
-local AltitudeText=self:GetAltitudeText(Settings,Language)
-local BRAText=BearingText..DistanceText..AltitudeText
-return BRAText
-end
-function COORDINATE:SetAltitude(altitude,asl)
-local alt=altitude
-if asl then
-alt=altitude
-else
-alt=self:GetLandHeight()+altitude
-end
-self.y=alt
-return self
-end
-function COORDINATE:SetAtLandheight()
-local alt=self:GetLandHeight()
-self.y=alt
-return self
-end
-function COORDINATE:WaypointAir(AltType,Type,Action,Speed,SpeedLocked,airbase,DCSTasks,description,timeReFuAr)
-self:F2({AltType,Type,Action,Speed,SpeedLocked})
-AltType=AltType or"RADIO"
-if SpeedLocked==nil then
-SpeedLocked=true
-end
-Speed=Speed or 500
-local RoutePoint={}
-RoutePoint.x=self.x
-RoutePoint.y=self.z
-RoutePoint.alt=self.y
-RoutePoint.alt_type=AltType
-RoutePoint.type=Type or nil
-RoutePoint.action=Action or nil
-RoutePoint.speed=Speed/3.6
-RoutePoint.speed_locked=SpeedLocked
-RoutePoint.ETA=0
-RoutePoint.ETA_locked=false
-RoutePoint.name=description
-if airbase then
-local AirbaseID=airbase:GetID()
-local AirbaseCategory=airbase:GetAirbaseCategory()
-if AirbaseCategory==Airbase.Category.SHIP or AirbaseCategory==Airbase.Category.HELIPAD then
-RoutePoint.linkUnit=AirbaseID
-RoutePoint.helipadId=AirbaseID
-elseif AirbaseCategory==Airbase.Category.AIRDROME then
-RoutePoint.airdromeId=AirbaseID
-else
-self:E("ERROR: Unknown airbase category in COORDINATE:WaypointAir()!")
-end
-end
-if Type==COORDINATE.WaypointType.LandingReFuAr then
-RoutePoint.timeReFuAr=timeReFuAr or 10
-end
-RoutePoint.task={}
-RoutePoint.task.id="ComboTask"
-RoutePoint.task.params={}
-RoutePoint.task.params.tasks=DCSTasks or{}
-self:T({RoutePoint=RoutePoint})
-return RoutePoint
-end
-function COORDINATE:WaypointAirTurningPoint(AltType,Speed,DCSTasks,description)
-return self:WaypointAir(AltType,COORDINATE.WaypointType.TurningPoint,COORDINATE.WaypointAction.TurningPoint,Speed,true,nil,DCSTasks,description)
-end
-function COORDINATE:WaypointAirFlyOverPoint(AltType,Speed)
-return self:WaypointAir(AltType,COORDINATE.WaypointType.TurningPoint,COORDINATE.WaypointAction.FlyoverPoint,Speed)
-end
-function COORDINATE:WaypointAirTakeOffParkingHot(AltType,Speed)
-return self:WaypointAir(AltType,COORDINATE.WaypointType.TakeOffParkingHot,COORDINATE.WaypointAction.FromParkingAreaHot,Speed)
-end
-function COORDINATE:WaypointAirTakeOffParking(AltType,Speed)
-return self:WaypointAir(AltType,COORDINATE.WaypointType.TakeOffParking,COORDINATE.WaypointAction.FromParkingArea,Speed)
-end
-function COORDINATE:WaypointAirTakeOffRunway(AltType,Speed)
-return self:WaypointAir(AltType,COORDINATE.WaypointType.TakeOff,COORDINATE.WaypointAction.FromRunway,Speed)
-end
-function COORDINATE:WaypointAirLanding(Speed,airbase,DCSTasks,description)
-return self:WaypointAir(nil,COORDINATE.WaypointType.Land,COORDINATE.WaypointAction.Landing,Speed,false,airbase,DCSTasks,description)
-end
-function COORDINATE:WaypointAirLandingReFu(Speed,airbase,timeReFuAr,DCSTasks,description)
-return self:WaypointAir(nil,COORDINATE.WaypointType.LandingReFuAr,COORDINATE.WaypointAction.LandingReFuAr,Speed,false,airbase,DCSTasks,description,timeReFuAr or 10)
-end
-function COORDINATE:WaypointGround(Speed,Formation,DCSTasks)
-self:F2({Speed,Formation,DCSTasks})
-local RoutePoint={}
-RoutePoint.x=self.x
-RoutePoint.y=self.z
-RoutePoint.alt=self:GetLandHeight()+1
-RoutePoint.alt_type=COORDINATE.WaypointAltType.BARO
-RoutePoint.type="Turning Point"
-RoutePoint.action=Formation or"Off Road"
-RoutePoint.formation_template=""
-RoutePoint.ETA=0
-RoutePoint.ETA_locked=false
-RoutePoint.speed=(Speed or 20)/3.6
-RoutePoint.speed_locked=true
-RoutePoint.task={}
-RoutePoint.task.id="ComboTask"
-RoutePoint.task.params={}
-RoutePoint.task.params.tasks=DCSTasks or{}
-return RoutePoint
-end
-function COORDINATE:WaypointNaval(Speed,Depth,DCSTasks)
-self:F2({Speed,Depth,DCSTasks})
-local RoutePoint={}
-RoutePoint.x=self.x
-RoutePoint.y=self.z
-RoutePoint.alt=Depth or self.y
-RoutePoint.alt_type="BARO"
-RoutePoint.type="Turning Point"
-RoutePoint.action="Turning Point"
-RoutePoint.formation_template=""
-RoutePoint.ETA=0
-RoutePoint.ETA_locked=false
-RoutePoint.speed=(Speed or 20)/3.6
-RoutePoint.speed_locked=true
-RoutePoint.task={}
-RoutePoint.task.id="ComboTask"
-RoutePoint.task.params={}
-RoutePoint.task.params.tasks=DCSTasks or{}
-return RoutePoint
-end
-function COORDINATE:GetClosestAirbase(Category,Coalition)
-local airbases=AIRBASE.GetAllAirbases(Coalition)
-local closest=nil
-local distmin=nil
-for _,_airbase in pairs(airbases)do
-local airbase=_airbase
-if airbase then
-local category=airbase:GetAirbaseCategory()
-if Category and Category==category or Category==nil then
-local dist=self:Get2DDistance(airbase:GetCoordinate())
-if closest==nil then
-distmin=dist
-closest=airbase
-else
-if dist=2 then
-for i=1,#Path-1 do
-Way=Way+Path[i+1]:Get2DDistance(Path[i])
-end
-else
-return nil,nil,false
-end
-return Path,Way,GotPath
-end
-function COORDINATE:GetSurfaceType()
-local vec2=self:GetVec2()
-local surface=land.getSurfaceType(vec2)
-return surface
-end
-function COORDINATE:IsSurfaceTypeLand()
-return self:GetSurfaceType()==land.SurfaceType.LAND
-end
-function COORDINATE:IsSurfaceTypeLand()
-return self:GetSurfaceType()==land.SurfaceType.LAND
-end
-function COORDINATE:IsSurfaceTypeRoad()
-return self:GetSurfaceType()==land.SurfaceType.ROAD
-end
-function COORDINATE:IsSurfaceTypeRunway()
-return self:GetSurfaceType()==land.SurfaceType.RUNWAY
-end
-function COORDINATE:IsSurfaceTypeShallowWater()
-return self:GetSurfaceType()==land.SurfaceType.SHALLOW_WATER
-end
-function COORDINATE:IsSurfaceTypeWater()
-return self:GetSurfaceType()==land.SurfaceType.WATER
-end
-function COORDINATE:Explosion(ExplosionIntensity,Delay)
-ExplosionIntensity=ExplosionIntensity or 100
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,self.Explosion,self,ExplosionIntensity)
-else
-trigger.action.explosion(self:GetVec3(),ExplosionIntensity)
-end
-return self
-end
-function COORDINATE:IlluminationBomb(Power,Delay)
-Power=Power or 1000
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,self.IlluminationBomb,self,Power)
-else
-trigger.action.illuminationBomb(self:GetVec3(),Power)
-end
-return self
-end
-function COORDINATE:Smoke(SmokeColor)
-self:F2({SmokeColor})
-trigger.action.smoke(self:GetVec3(),SmokeColor)
-end
-function COORDINATE:SmokeGreen()
-self:F2()
-self:Smoke(SMOKECOLOR.Green)
-end
-function COORDINATE:SmokeRed()
-self:F2()
-self:Smoke(SMOKECOLOR.Red)
-end
-function COORDINATE:SmokeWhite()
-self:F2()
-self:Smoke(SMOKECOLOR.White)
-end
-function COORDINATE:SmokeOrange()
-self:F2()
-self:Smoke(SMOKECOLOR.Orange)
-end
-function COORDINATE:SmokeBlue()
-self:F2()
-self:Smoke(SMOKECOLOR.Blue)
-end
-function COORDINATE:BigSmokeAndFire(preset,density,name)
-self:F2({preset=preset,density=density})
-density=density or 0.5
-self.firename=name or"Fire-"..math.random(1,10000)
-trigger.action.effectSmokeBig(self:GetVec3(),preset,density,self.firename)
-end
-function COORDINATE:StopBigSmokeAndFire(name)
-name=name or self.firename
-trigger.action.effectSmokeStop(name)
-end
-function COORDINATE:BigSmokeAndFireSmall(density,name)
-self:F2({density=density})
-density=density or 0.5
-self:BigSmokeAndFire(BIGSMOKEPRESET.SmallSmokeAndFire,density,name)
-end
-function COORDINATE:BigSmokeAndFireMedium(density,name)
-self:F2({density=density})
-density=density or 0.5
-self:BigSmokeAndFire(BIGSMOKEPRESET.MediumSmokeAndFire,density,name)
-end
-function COORDINATE:BigSmokeAndFireLarge(density,name)
-self:F2({density=density})
-density=density or 0.5
-self:BigSmokeAndFire(BIGSMOKEPRESET.LargeSmokeAndFire,density,name)
-end
-function COORDINATE:BigSmokeAndFireHuge(density,name)
-self:F2({density=density})
-density=density or 0.5
-self:BigSmokeAndFire(BIGSMOKEPRESET.HugeSmokeAndFire,density,name)
-end
-function COORDINATE:BigSmokeSmall(density,name)
-self:F2({density=density})
-density=density or 0.5
-self:BigSmokeAndFire(BIGSMOKEPRESET.SmallSmoke,density,name)
-end
-function COORDINATE:BigSmokeMedium(density,name)
-self:F2({density=density})
-density=density or 0.5
-self:BigSmokeAndFire(BIGSMOKEPRESET.MediumSmoke,density,name)
-end
-function COORDINATE:BigSmokeLarge(density,name)
-self:F2({density=density})
-density=density or 0.5
-self:BigSmokeAndFire(BIGSMOKEPRESET.LargeSmoke,density,name)
-end
-function COORDINATE:BigSmokeHuge(density,name)
-self:F2({density=density})
-density=density or 0.5
-self:BigSmokeAndFire(BIGSMOKEPRESET.HugeSmoke,density,name)
-end
-function COORDINATE:Flare(FlareColor,Azimuth)
-self:F2({FlareColor})
-trigger.action.signalFlare(self:GetVec3(),FlareColor,Azimuth and Azimuth or 0)
-end
-function COORDINATE:FlareWhite(Azimuth)
-self:F2(Azimuth)
-self:Flare(FLARECOLOR.White,Azimuth)
-end
-function COORDINATE:FlareYellow(Azimuth)
-self:F2(Azimuth)
-self:Flare(FLARECOLOR.Yellow,Azimuth)
-end
-function COORDINATE:FlareGreen(Azimuth)
-self:F2(Azimuth)
-self:Flare(FLARECOLOR.Green,Azimuth)
-end
-function COORDINATE:FlareRed(Azimuth)
-self:F2(Azimuth)
-self:Flare(FLARECOLOR.Red,Azimuth)
-end
-do
-function COORDINATE:MarkToAll(MarkText,ReadOnly,Text)
-local MarkID=UTILS.GetMarkID()
-if ReadOnly==nil then
-ReadOnly=false
-end
-local text=Text or""
-trigger.action.markToAll(MarkID,MarkText,self:GetVec3(),ReadOnly,text)
-return MarkID
-end
-function COORDINATE:MarkToCoalition(MarkText,Coalition,ReadOnly,Text)
-local MarkID=UTILS.GetMarkID()
-if ReadOnly==nil then
-ReadOnly=false
-end
-local text=Text or""
-trigger.action.markToCoalition(MarkID,MarkText,self:GetVec3(),Coalition,ReadOnly,text)
-return MarkID
-end
-function COORDINATE:MarkToCoalitionRed(MarkText,ReadOnly,Text)
-return self:MarkToCoalition(MarkText,coalition.side.RED,ReadOnly,Text)
-end
-function COORDINATE:MarkToCoalitionBlue(MarkText,ReadOnly,Text)
-return self:MarkToCoalition(MarkText,coalition.side.BLUE,ReadOnly,Text)
-end
-function COORDINATE:MarkToGroup(MarkText,MarkGroup,ReadOnly,Text)
-local MarkID=UTILS.GetMarkID()
-if ReadOnly==nil then
-ReadOnly=false
-end
-local text=Text or""
-trigger.action.markToGroup(MarkID,MarkText,self:GetVec3(),MarkGroup:GetID(),ReadOnly,text)
-return MarkID
-end
-function COORDINATE:RemoveMark(MarkID)
-trigger.action.removeMark(MarkID)
-end
-function COORDINATE:LineToAll(Endpoint,Coalition,Color,Alpha,LineType,ReadOnly,Text)
-local MarkID=UTILS.GetMarkID()
-if ReadOnly==nil then
-ReadOnly=false
-end
-local vec3=Endpoint:GetVec3()
-Coalition=Coalition or-1
-Color=Color or{1,0,0}
-Color[4]=Alpha or 1.0
-LineType=LineType or 1
-trigger.action.lineToAll(Coalition,MarkID,self:GetVec3(),vec3,Color,LineType,ReadOnly,Text or"")
-return MarkID
-end
-function COORDINATE:CircleToAll(Radius,Coalition,Color,Alpha,FillColor,FillAlpha,LineType,ReadOnly,Text)
-local MarkID=UTILS.GetMarkID()
-if ReadOnly==nil then
-ReadOnly=false
-end
-local vec3=self:GetVec3()
-Radius=Radius or 1000
-Coalition=Coalition or-1
-Color=Color or{1,0,0}
-Color[4]=Alpha or 1.0
-LineType=LineType or 1
-FillColor=FillColor or UTILS.DeepCopy(Color)
-FillColor[4]=FillAlpha or 0.15
-trigger.action.circleToAll(Coalition,MarkID,vec3,Radius,Color,FillColor,LineType,ReadOnly,Text or"")
-return MarkID
-end
-end
-function COORDINATE:RectToAll(Endpoint,Coalition,Color,Alpha,FillColor,FillAlpha,LineType,ReadOnly,Text)
-local MarkID=UTILS.GetMarkID()
-if ReadOnly==nil then
-ReadOnly=false
-end
-local vec3=Endpoint:GetVec3()
-Coalition=Coalition or-1
-Color=Color or{1,0,0}
-Color[4]=Alpha or 1.0
-LineType=LineType or 1
-FillColor=FillColor or UTILS.DeepCopy(Color)
-FillColor[4]=FillAlpha or 0.15
-trigger.action.rectToAll(Coalition,MarkID,self:GetVec3(),vec3,Color,FillColor,LineType,ReadOnly,Text or"")
-return MarkID
-end
-function COORDINATE:QuadToAll(Coord2,Coord3,Coord4,Coalition,Color,Alpha,FillColor,FillAlpha,LineType,ReadOnly,Text)
-local MarkID=UTILS.GetMarkID()
-if ReadOnly==nil then
-ReadOnly=false
-end
-local point1=self:GetVec3()
-local point2=Coord2:GetVec3()
-local point3=Coord3:GetVec3()
-local point4=Coord4:GetVec3()
-Coalition=Coalition or-1
-Color=Color or{1,0,0}
-Color[4]=Alpha or 1.0
-LineType=LineType or 1
-FillColor=FillColor or UTILS.DeepCopy(Color)
-FillColor[4]=FillAlpha or 0.15
-trigger.action.quadToAll(Coalition,MarkID,point1,point2,point3,point4,Color,FillColor,LineType,ReadOnly,Text or"")
-return MarkID
-end
-function COORDINATE:MarkupToAllFreeForm(Coordinates,Coalition,Color,Alpha,FillColor,FillAlpha,LineType,ReadOnly,Text)
-local MarkID=UTILS.GetMarkID()
-if ReadOnly==nil then
-ReadOnly=false
-end
-Coalition=Coalition or-1
-Color=Color or{1,0,0}
-Color[4]=Alpha or 1.0
-LineType=LineType or 1
-FillColor=FillColor or UTILS.DeepCopy(Color)
-FillColor[4]=FillAlpha or 0.15
-local vecs={}
-vecs[1]=self:GetVec3()
-for i,coord in ipairs(Coordinates)do
-vecs[i+1]=coord:GetVec3()
-end
-if#vecs<3 then
-self:E("ERROR: A free form polygon needs at least three points!")
-elseif#vecs==3 then
-trigger.action.markupToAll(7,Coalition,MarkID,vecs[1],vecs[2],vecs[3],Color,FillColor,LineType,ReadOnly,Text or"")
-elseif#vecs==4 then
-trigger.action.markupToAll(7,Coalition,MarkID,vecs[1],vecs[2],vecs[3],vecs[4],Color,FillColor,LineType,ReadOnly,Text or"")
-elseif#vecs==5 then
-trigger.action.markupToAll(7,Coalition,MarkID,vecs[1],vecs[2],vecs[3],vecs[4],vecs[5],Color,FillColor,LineType,ReadOnly,Text or"")
-elseif#vecs==6 then
-trigger.action.markupToAll(7,Coalition,MarkID,vecs[1],vecs[2],vecs[3],vecs[4],vecs[5],vecs[6],Color,FillColor,LineType,ReadOnly,Text or"")
-elseif#vecs==7 then
-trigger.action.markupToAll(7,Coalition,MarkID,vecs[1],vecs[2],vecs[3],vecs[4],vecs[5],vecs[6],vecs[7],Color,FillColor,LineType,ReadOnly,Text or"")
-elseif#vecs==8 then
-trigger.action.markupToAll(7,Coalition,MarkID,vecs[1],vecs[2],vecs[3],vecs[4],vecs[5],vecs[6],vecs[7],vecs[8],Color,FillColor,LineType,ReadOnly,Text or"")
-elseif#vecs==9 then
-trigger.action.markupToAll(7,Coalition,MarkID,vecs[1],vecs[2],vecs[3],vecs[4],vecs[5],vecs[6],vecs[7],vecs[8],vecs[9],Color,FillColor,LineType,ReadOnly,Text or"")
-elseif#vecs==10 then
-trigger.action.markupToAll(7,Coalition,MarkID,vecs[1],vecs[2],vecs[3],vecs[4],vecs[5],vecs[6],vecs[7],vecs[8],vecs[9],vecs[10],Color,FillColor,LineType,ReadOnly,Text or"")
-elseif#vecs==11 then
-trigger.action.markupToAll(7,Coalition,MarkID,vecs[1],vecs[2],vecs[3],vecs[4],vecs[5],vecs[6],vecs[7],vecs[8],vecs[9],vecs[10],
-vecs[11],
-Color,FillColor,LineType,ReadOnly,Text or"")
-elseif#vecs==12 then
-trigger.action.markupToAll(7,Coalition,MarkID,vecs[1],vecs[2],vecs[3],vecs[4],vecs[5],vecs[6],vecs[7],vecs[8],vecs[9],vecs[10],
-vecs[11],vecs[12],
-Color,FillColor,LineType,ReadOnly,Text or"")
-elseif#vecs==13 then
-trigger.action.markupToAll(7,Coalition,MarkID,vecs[1],vecs[2],vecs[3],vecs[4],vecs[5],vecs[6],vecs[7],vecs[8],vecs[9],vecs[10],
-vecs[11],vecs[12],vecs[13],
-Color,FillColor,LineType,ReadOnly,Text or"")
-elseif#vecs==14 then
-trigger.action.markupToAll(7,Coalition,MarkID,vecs[1],vecs[2],vecs[3],vecs[4],vecs[5],vecs[6],vecs[7],vecs[8],vecs[9],vecs[10],
-vecs[11],vecs[12],vecs[13],vecs[14],
-Color,FillColor,LineType,ReadOnly,Text or"")
-elseif#vecs==15 then
-trigger.action.markupToAll(7,Coalition,MarkID,vecs[1],vecs[2],vecs[3],vecs[4],vecs[5],vecs[6],vecs[7],vecs[8],vecs[9],vecs[10],
-vecs[11],vecs[12],vecs[13],vecs[14],vecs[15],
-Color,FillColor,LineType,ReadOnly,Text or"")
-else
-local s=string.format("trigger.action.markupToAll(7, %d, %d,",Coalition,MarkID)
-for _,vec in pairs(vecs)do
-s=s..string.format("%s,",UTILS._OneLineSerialize(vec))
-end
-s=s..string.format("%s, %s, %s, %s",UTILS._OneLineSerialize(Color),UTILS._OneLineSerialize(FillColor),tostring(LineType),tostring(ReadOnly))
-if Text and Text~=""then
-s=s..string.format(", \"%s\"",Text)
-end
-s=s..")"
-local success=UTILS.DoString(s)
-if not success then
-self:E("ERROR: Could not draw polygon")
-env.info(s)
-end
-end
-return MarkID
-end
-function COORDINATE:TextToAll(Text,Coalition,Color,Alpha,FillColor,FillAlpha,FontSize,ReadOnly)
-local MarkID=UTILS.GetMarkID()
-if ReadOnly==nil then
-ReadOnly=false
-end
-Coalition=Coalition or-1
-Color=Color or{1,0,0}
-Color[4]=Alpha or 1.0
-FillColor=FillColor or UTILS.DeepCopy(Color)
-FillColor[4]=FillAlpha or 0.3
-FontSize=FontSize or 14
-trigger.action.textToAll(Coalition,MarkID,self:GetVec3(),Color,FillColor,FontSize,ReadOnly,Text or"Hello World")
-return MarkID
-end
-function COORDINATE:ArrowToAll(Endpoint,Coalition,Color,Alpha,FillColor,FillAlpha,LineType,ReadOnly,Text)
-local MarkID=UTILS.GetMarkID()
-if ReadOnly==nil then
-ReadOnly=false
-end
-local vec3=Endpoint:GetVec3()
-Coalition=Coalition or-1
-Color=Color or{1,0,0}
-Color[4]=Alpha or 1.0
-LineType=LineType or 1
-FillColor=FillColor or UTILS.DeepCopy(Color)
-FillColor[4]=FillAlpha or 0.15
-trigger.action.arrowToAll(Coalition,MarkID,vec3,self:GetVec3(),Color,FillColor,LineType,ReadOnly,Text or"")
-return MarkID
-end
-function COORDINATE:IsLOS(ToCoordinate,Offset)
-Offset=Offset or 2
-local FromVec3=self:GetVec3()
-FromVec3.y=FromVec3.y+Offset
-local ToVec3=ToCoordinate:GetVec3()
-ToVec3.y=ToVec3.y+Offset
-local IsLOS=land.isVisible(FromVec3,ToVec3)
-return IsLOS
-end
-function COORDINATE:IsInRadius(Coordinate,Radius)
-local InVec2=self:GetVec2()
-local Vec2=Coordinate:GetVec2()
-local InRadius=UTILS.IsInRadius(InVec2,Vec2,Radius)
-return InRadius
-end
-function COORDINATE:IsInSphere(Coordinate,Radius)
-local InVec3=self:GetVec3()
-local Vec3=Coordinate:GetVec3()
-local InSphere=UTILS.IsInSphere(InVec3,Vec3,Radius)
-return InSphere
-end
-function COORDINATE:GetSunriseAtDate(Day,Month,Year,InSeconds)
-local DayOfYear=UTILS.GetDayOfYear(Year,Month,Day)
-local Latitude,Longitude=self:GetLLDDM()
-local Tdiff=UTILS.GMTToLocalTimeDifference()
-local sunrise=UTILS.GetSunRiseAndSet(DayOfYear,Latitude,Longitude,true,Tdiff)
-if InSeconds then
-return sunrise
-else
-return UTILS.SecondsToClock(sunrise,true)
-end
-end
-function COORDINATE:GetSunriseAtDayOfYear(DayOfYear,InSeconds)
-local Latitude,Longitude=self:GetLLDDM()
-local Tdiff=UTILS.GMTToLocalTimeDifference()
-local sunrise=UTILS.GetSunRiseAndSet(DayOfYear,Latitude,Longitude,true,Tdiff)
-if InSeconds then
-return sunrise
-else
-return UTILS.SecondsToClock(sunrise,true)
-end
-end
-function COORDINATE:GetSunrise(InSeconds)
-local DayOfYear=UTILS.GetMissionDayOfYear()
-local Latitude,Longitude=self:GetLLDDM()
-local Tdiff=UTILS.GMTToLocalTimeDifference()
-local sunrise=UTILS.GetSunRiseAndSet(DayOfYear,Latitude,Longitude,true,Tdiff)
-local date=UTILS.GetDCSMissionDate()
-if InSeconds then
-return sunrise
-else
-return UTILS.SecondsToClock(sunrise,true)
-end
-end
-function COORDINATE:GetMinutesToSunrise(OnlyToday)
-local time=UTILS.SecondsOfToday()
-local sunrise=nil
-local delta=nil
-if OnlyToday then
-sunrise=self:GetSunrise(true)
-delta=sunrise-time
-else
-local DayOfYear=UTILS.GetMissionDayOfYear()+1
-local Latitude,Longitude=self:GetLLDDM()
-local Tdiff=UTILS.GMTToLocalTimeDifference()
-sunrise=UTILS.GetSunRiseAndSet(DayOfYear,Latitude,Longitude,true,Tdiff)
-delta=sunrise+UTILS.SecondsToMidnight()
-end
-return delta/60
-end
-function COORDINATE:IsDay(Clock)
-if Clock then
-local Time=UTILS.ClockToSeconds(Clock)
-local clock=UTILS.Split(Clock,"+")[1]
-local DayOfYear=UTILS.GetMissionDayOfYear(Time)
-local Latitude,Longitude=self:GetLLDDM()
-local Tdiff=UTILS.GMTToLocalTimeDifference()
-local sunrise=UTILS.GetSunRiseAndSet(DayOfYear,Latitude,Longitude,true,Tdiff)
-local sunset=UTILS.GetSunRiseAndSet(DayOfYear,Latitude,Longitude,false,Tdiff)
-local time=UTILS.ClockToSeconds(clock)
-if time>sunrise and time<=sunset then
-return true
-else
-return false
-end
-else
-local sunrise=self:GetSunrise(true)
-local sunset=self:GetSunset(true)
-local time=UTILS.SecondsOfToday()
-if time>sunrise and time<=sunset then
-return true
-else
-return false
-end
-end
-end
-function COORDINATE:IsNight(Clock)
-return not self:IsDay(Clock)
-end
-function COORDINATE:GetSunsetAtDate(Day,Month,Year,InSeconds)
-local DayOfYear=UTILS.GetDayOfYear(Year,Month,Day)
-local Latitude,Longitude=self:GetLLDDM()
-local Tdiff=UTILS.GMTToLocalTimeDifference()
-local sunset=UTILS.GetSunRiseAndSet(DayOfYear,Latitude,Longitude,false,Tdiff)
-if InSeconds then
-return sunset
-else
-return UTILS.SecondsToClock(sunset,true)
-end
-end
-function COORDINATE:GetSunset(InSeconds)
-local DayOfYear=UTILS.GetMissionDayOfYear()
-local Latitude,Longitude=self:GetLLDDM()
-local Tdiff=UTILS.GMTToLocalTimeDifference()
-local sunrise=UTILS.GetSunRiseAndSet(DayOfYear,Latitude,Longitude,false,Tdiff)
-local date=UTILS.GetDCSMissionDate()
-if InSeconds then
-return sunrise
-else
-return UTILS.SecondsToClock(sunrise,true)
-end
-end
-function COORDINATE:GetMinutesToSunset(OnlyToday)
-local time=UTILS.SecondsOfToday()
-local sunset=nil
-local delta=nil
-if OnlyToday then
-sunset=self:GetSunset(true)
-delta=sunset-time
-else
-local DayOfYear=UTILS.GetMissionDayOfYear()+1
-local Latitude,Longitude=self:GetLLDDM()
-local Tdiff=UTILS.GMTToLocalTimeDifference()
-sunset=UTILS.GetSunRiseAndSet(DayOfYear,Latitude,Longitude,false,Tdiff)
-delta=sunset+UTILS.SecondsToMidnight()
-end
-return delta/60
-end
-function COORDINATE:ToStringBR(FromCoordinate,Settings,MagVar)
-local DirectionVec3=FromCoordinate:GetDirectionVec3(self)
-local AngleRadians=self:GetAngleRadians(DirectionVec3)
-local Distance=self:Get2DDistance(FromCoordinate)
-return"BR, "..self:GetBRText(AngleRadians,Distance,Settings,nil,MagVar)
-end
-function COORDINATE:ToStringBRA(FromCoordinate,Settings,MagVar)
-local DirectionVec3=FromCoordinate:GetDirectionVec3(self)
-local AngleRadians=self:GetAngleRadians(DirectionVec3)
-local Distance=FromCoordinate:Get2DDistance(self)
-local Altitude=self:GetAltitudeText()
-return"BRA, "..self:GetBRAText(AngleRadians,Distance,Settings,nil,MagVar)
-end
-function COORDINATE:ToStringBRAANATO(FromCoordinate,Bogey,Spades,SSML,Angels,Zeros)
-local BRAANATO="Merged."
-local currentCoord=FromCoordinate
-local DirectionVec3=FromCoordinate:GetDirectionVec3(self)
-local AngleRadians=self:GetAngleRadians(DirectionVec3)
-local bearing=UTILS.Round(UTILS.ToDegree(AngleRadians),0)
-local rangeMetres=self:Get2DDistance(currentCoord)
-local rangeNM=UTILS.Round(UTILS.MetersToNM(rangeMetres),0)
-local aspect=self:ToStringAspect(currentCoord)
-local alt=UTILS.Round(UTILS.MetersToFeet(self.y)/1000,0)
-local alttext=string.format("%d thousand",alt)
-if Angels then
-alttext=string.format("Angels %d",alt)
-end
-if alt<1 then
-alttext="very low"
-end
-local track="Maneuver"
-if self.Heading then
-track=UTILS.BearingToCardinal(self.Heading)or"North"
-end
-if rangeNM>3 then
-if SSML then
-if Zeros then
-bearing=string.format("%03d",bearing)
-local AngleDegText=string.gsub(bearing,"%d","%1 ")
-AngleDegText=string.gsub(AngleDegText," $","")
-AngleDegText=string.gsub(AngleDegText,"0","zero")
-if aspect==""then
-BRAANATO=string.format("brah %s, %d miles, %s, Track %s",AngleDegText,rangeNM,alttext,track)
-else
-BRAANATO=string.format("brah %s, %d miles, %s, %s, Track %s",AngleDegText,rangeNM,alttext,aspect,track)
-end
-else
-if aspect==""then
-BRAANATO=string.format("brah %03d, %d miles, %s, Track %s",bearing,rangeNM,alttext,track)
-else
-BRAANATO=string.format("brah %03d, %d miles, %s, %s, Track %s",bearing,rangeNM,alttext,aspect,track)
-end
-end
-if Bogey and Spades then
-BRAANATO=BRAANATO..", Bogey, Spades."
-elseif Bogey then
-BRAANATO=BRAANATO..", Bogey."
-elseif Spades then
-BRAANATO=BRAANATO..", Spades."
-else
-BRAANATO=BRAANATO.."."
-end
-else
-if aspect==""then
-BRAANATO=string.format("BRA %03d, %d miles, %s, Track %s",bearing,rangeNM,alttext,track)
-else
-BRAANATO=string.format("BRAA %03d, %d miles, %s, %s, Track %s",bearing,rangeNM,alttext,aspect,track)
-end
-if Bogey and Spades then
-BRAANATO=BRAANATO..", Bogey, Spades."
-elseif Bogey then
-BRAANATO=BRAANATO..", Bogey."
-elseif Spades then
-BRAANATO=BRAANATO..", Spades."
-else
-BRAANATO=BRAANATO.."."
-end
-end
-end
-return BRAANATO
-end
-function COORDINATE.GetBullseyeCoordinate(Coalition)
-return COORDINATE:NewFromVec3(coalition.getMainRefPoint(Coalition))
-end
-function COORDINATE:ToStringBULLS(Coalition,Settings,MagVar)
-local BullsCoordinate=COORDINATE:NewFromVec3(coalition.getMainRefPoint(Coalition))
-local DirectionVec3=BullsCoordinate:GetDirectionVec3(self)
-local AngleRadians=self:GetAngleRadians(DirectionVec3)
-local Distance=self:Get2DDistance(BullsCoordinate)
-local Altitude=self:GetAltitudeText()
-return"BULLS, "..self:GetBRText(AngleRadians,Distance,Settings,nil,MagVar)
-end
-function COORDINATE:ToStringAspect(TargetCoordinate)
-local Heading=self.Heading
-local DirectionVec3=self:GetDirectionVec3(TargetCoordinate)
-local Angle=self:GetAngleDegrees(DirectionVec3)
-if Heading then
-local Aspect=Angle-Heading
-if Aspect>-135 and Aspect<=-45 then
-return"Flanking"
-end
-if Aspect>-45 and Aspect<=45 then
-return"Hot"
-end
-if Aspect>45 and Aspect<=135 then
-return"Flanking"
-end
-if Aspect>135 or Aspect<=-135 then
-return"Cold"
-end
-end
-return""
-end
-function COORDINATE:GetLLDDM()
-return coord.LOtoLL(self:GetVec3())
-end
-function COORDINATE:ToStringLLDMS(Settings)
-local LL_Accuracy=Settings and Settings.LL_Accuracy or _SETTINGS.LL_Accuracy
-local lat,lon=coord.LOtoLL(self:GetVec3())
-return"LL DMS "..UTILS.tostringLL(lat,lon,LL_Accuracy,true)
-end
-function COORDINATE:ToStringLLDDM(Settings)
-local LL_Accuracy=Settings and Settings.LL_Accuracy or _SETTINGS.LL_Accuracy
-local lat,lon=coord.LOtoLL(self:GetVec3())
-return"LL DDM "..UTILS.tostringLL(lat,lon,LL_Accuracy,false)
-end
-function COORDINATE:ToStringMGRS(Settings)
-local MGRS_Accuracy=Settings and Settings.MGRS_Accuracy or _SETTINGS.MGRS_Accuracy
-local lat,lon=coord.LOtoLL(self:GetVec3())
-local MGRS=coord.LLtoMGRS(lat,lon)
-return"MGRS "..UTILS.tostringMGRS(MGRS,MGRS_Accuracy)
-end
-function COORDINATE:NewFromMGRSString(MGRSString)
-local myparts=UTILS.Split(MGRSString," ")
-local northing=tostring(myparts[5])or""
-local easting=tostring(myparts[4])or""
-if string.len(easting)<5 then easting=easting..string.rep("0",5-string.len(easting))end
-if string.len(northing)<5 then northing=northing..string.rep("0",5-string.len(northing))end
-local MGRS={
-UTMZone=myparts[2],
-MGRSDigraph=myparts[3],
-Easting=easting,
-Northing=northing,
-}
-local lat,lon=coord.MGRStoLL(MGRS)
-local point=coord.LLtoLO(lat,lon,0)
-local coord=COORDINATE:NewFromVec2({x=point.x,y=point.z})
-return coord
-end
-function COORDINATE:NewFromMGRS(UTMZone,MGRSDigraph,Easting,Northing)
-if string.len(Easting)<5 then Easting=Easting..string.rep("0",5-string.len(Easting))end
-if string.len(Northing)<5 then Northing=Northing..string.rep("0",5-string.len(Northing))end
-local MGRS={
-UTMZone=UTMZone,
-MGRSDigraph=MGRSDigraph,
-Easting=Easting,
-Northing=Northing,
-}
-local lat,lon=coord.MGRStoLL(MGRS)
-local point=coord.LLtoLO(lat,lon,0)
-local coord=COORDINATE:NewFromVec2({x=point.x,y=point.z})
-end
-function COORDINATE:ToStringFromRP(ReferenceCoord,ReferenceName,Controllable,Settings,MagVar)
-self:F2({ReferenceCoord=ReferenceCoord,ReferenceName=ReferenceName})
-local Settings=Settings or(Controllable and _DATABASE:GetPlayerSettings(Controllable:GetPlayerName()))or _SETTINGS
-local IsAir=Controllable and Controllable:IsAirPlane()or false
-if IsAir then
-local DirectionVec3=ReferenceCoord:GetDirectionVec3(self)
-local AngleRadians=self:GetAngleRadians(DirectionVec3)
-local Distance=self:Get2DDistance(ReferenceCoord)
-return"Targets are the last seen "..self:GetBRText(AngleRadians,Distance,Settings,nil,MagVar).." from "..ReferenceName
-else
-local DirectionVec3=ReferenceCoord:GetDirectionVec3(self)
-local AngleRadians=self:GetAngleRadians(DirectionVec3)
-local Distance=self:Get2DDistance(ReferenceCoord)
-return"Target are located "..self:GetBRText(AngleRadians,Distance,Settings,nil,MagVar).." from "..ReferenceName
-end
-return nil
-end
-function COORDINATE:ToStringFromRPShort(ReferenceCoord,ReferenceName,Controllable,Settings,MagVar)
-self:F2({ReferenceCoord=ReferenceCoord,ReferenceName=ReferenceName})
-local Settings=Settings or(Controllable and _DATABASE:GetPlayerSettings(Controllable:GetPlayerName()))or _SETTINGS
-local IsAir=Controllable and Controllable:IsAirPlane()or false
-if IsAir then
-local DirectionVec3=ReferenceCoord:GetDirectionVec3(self)
-local AngleRadians=self:GetAngleRadians(DirectionVec3)
-local Distance=self:Get2DDistance(ReferenceCoord)
-return self:GetBRText(AngleRadians,Distance,Settings,nil,MagVar).." from "..ReferenceName
-else
-local DirectionVec3=ReferenceCoord:GetDirectionVec3(self)
-local AngleRadians=self:GetAngleRadians(DirectionVec3)
-local Distance=self:Get2DDistance(ReferenceCoord)
-return self:GetBRText(AngleRadians,Distance,Settings,nil,MagVar).." from "..ReferenceName
-end
-return nil
-end
-function COORDINATE:ToStringA2G(Controllable,Settings,MagVar)
-self:F2({Controllable=Controllable and Controllable:GetName()})
-local Settings=Settings or(Controllable and _DATABASE:GetPlayerSettings(Controllable:GetPlayerName()))or _SETTINGS
-if Settings:IsA2G_BR()then
-if Controllable then
-local Coordinate=Controllable:GetCoordinate()
-return Controllable and self:ToStringBR(Coordinate,Settings,MagVar)or self:ToStringMGRS(Settings)
-else
-return self:ToStringMGRS(Settings)
-end
-end
-if Settings:IsA2G_LL_DMS()then
-return self:ToStringLLDMS(Settings)
-end
-if Settings:IsA2G_LL_DDM()then
-return self:ToStringLLDDM(Settings)
-end
-if Settings:IsA2G_MGRS()then
-return self:ToStringMGRS(Settings)
-end
-return nil
-end
-function COORDINATE:ToStringA2A(Controllable,Settings,MagVar)
-self:F2({Controllable=Controllable and Controllable:GetName()})
-local Settings=Settings or(Controllable and _DATABASE:GetPlayerSettings(Controllable:GetPlayerName()))or _SETTINGS
-if Settings:IsA2A_BRAA()then
-if Controllable then
-local Coordinate=Controllable:GetCoordinate()
-return self:ToStringBRA(Coordinate,Settings,MagVar)
-else
-return self:ToStringMGRS(Settings)
-end
-end
-if Settings:IsA2A_BULLS()then
-local Coalition=Controllable:GetCoalition()
-return self:ToStringBULLS(Coalition,Settings,MagVar)
-end
-if Settings:IsA2A_LL_DMS()then
-return self:ToStringLLDMS(Settings)
-end
-if Settings:IsA2A_LL_DDM()then
-return self:ToStringLLDDM(Settings)
-end
-if Settings:IsA2A_MGRS()then
-return self:ToStringMGRS(Settings)
-end
-return nil
-end
-function COORDINATE:ToString(Controllable,Settings,Task)
-local Settings=Settings or(Controllable and _DATABASE:GetPlayerSettings(Controllable:GetPlayerName()))or _SETTINGS
-local ModeA2A=nil
-if Task then
-if Task:IsInstanceOf(TASK_A2A)then
-ModeA2A=true
-else
-if Task:IsInstanceOf(TASK_A2G)then
-ModeA2A=false
-else
-if Task:IsInstanceOf(TASK_CARGO)then
-ModeA2A=false
-end
-if Task:IsInstanceOf(TASK_CAPTURE_ZONE)then
-ModeA2A=false
-end
-end
-end
-end
-if ModeA2A==nil then
-local IsAir=Controllable and(Controllable:IsAirPlane()or Controllable:IsHelicopter())or false
-if IsAir then
-ModeA2A=true
-else
-ModeA2A=false
-end
-end
-if ModeA2A==true then
-return self:ToStringA2A(Controllable,Settings)
-else
-return self:ToStringA2G(Controllable,Settings)
-end
-return nil
-end
-function COORDINATE:ToStringPressure(Controllable,Settings)
-self:F2({Controllable=Controllable and Controllable:GetName()})
-local Settings=Settings or(Controllable and _DATABASE:GetPlayerSettings(Controllable:GetPlayerName()))or _SETTINGS
-return self:GetPressureText(nil,Settings)
-end
-function COORDINATE:ToStringWind(Controllable,Settings)
-self:F2({Controllable=Controllable and Controllable:GetName()})
-local Settings=Settings or(Controllable and _DATABASE:GetPlayerSettings(Controllable:GetPlayerName()))or _SETTINGS
-return self:GetWindText(nil,Settings)
-end
-function COORDINATE:ToStringTemperature(Controllable,Settings)
-self:F2({Controllable=Controllable and Controllable:GetName()})
-local Settings=Settings or(Controllable and _DATABASE:GetPlayerSettings(Controllable:GetPlayerName()))or _SETTINGS
-return self:GetTemperatureText(nil,Settings)
-end
-function COORDINATE:IsInSteepArea(Radius,Minelevation)
-local steep=false
-local elev=Minelevation or 8
-local bdelta=0
-local h0=self:GetLandHeight()
-local radius=Radius or 50
-local diam=radius*2
-for i=0,150,30 do
-local polar=math.fmod(i+180,360)
-local c1=self:Translate(radius,i,false,false)
-local c2=self:Translate(radius,polar,false,false)
-local h1=c1:GetLandHeight()
-local h2=c2:GetLandHeight()
-local d1=math.abs(h1-h2)
-local d2=math.abs(h0-h1)
-local d3=math.abs(h0-h2)
-local dm=d1>d2 and d1 or d2
-local dm1=dm>d3 and dm or d3
-bdelta=dm1>bdelta and dm1 or bdelta
-self:T(string.format("d1=%d, d2=%d, d3=%d, max delta=%d",d1,d2,d3,bdelta))
-end
-local steepness=bdelta/(radius/100)
-if steepness>=elev then steep=true end
-return steep,math.floor(steepness)
-end
-function COORDINATE:IsInFlatArea(Radius,Minelevation)
-local steep,elev=self:IsInSteepArea(Radius,Minelevation)
-local flat=not steep
-return flat,elev
-end
-end
-do
-POINT_VEC3={
-ClassName="POINT_VEC3",
-Metric=true,
-RoutePointAltType={
-BARO="BARO",
-},
-RoutePointType={
-TakeOffParking="TakeOffParking",
-TurningPoint="Turning Point",
-},
-RoutePointAction={
-FromParkingArea="From Parking Area",
-TurningPoint="Turning Point",
-},
-}
-function POINT_VEC3:New(x,y,z)
-local self=BASE:Inherit(self,COORDINATE:New(x,y,z))
-self:F2(self)
-return self
-end
-function POINT_VEC3:NewFromVec2(Vec2,LandHeightAdd)
-local self=BASE:Inherit(self,COORDINATE:NewFromVec2(Vec2,LandHeightAdd))
-self:F2(self)
-return self
-end
-function POINT_VEC3:NewFromVec3(Vec3)
-local self=BASE:Inherit(self,COORDINATE:NewFromVec3(Vec3))
-self:F2(self)
-return self
-end
-function POINT_VEC3:GetX()
-return self.x
-end
-function POINT_VEC3:GetY()
-return self.y
-end
-function POINT_VEC3:GetZ()
-return self.z
-end
-function POINT_VEC3:SetX(x)
-self.x=x
-return self
-end
-function POINT_VEC3:SetY(y)
-self.y=y
-return self
-end
-function POINT_VEC3:SetZ(z)
-self.z=z
-return self
-end
-function POINT_VEC3:AddX(x)
-self.x=self.x+x
-return self
-end
-function POINT_VEC3:AddY(y)
-self.y=self.y+y
-return self
-end
-function POINT_VEC3:AddZ(z)
-self.z=self.z+z
-return self
-end
-function POINT_VEC3:GetRandomPointVec3InRadius(OuterRadius,InnerRadius)
-return POINT_VEC3:NewFromVec3(self:GetRandomVec3InRadius(OuterRadius,InnerRadius))
-end
-end
-do
-POINT_VEC2={
-ClassName="POINT_VEC2",
-}
-function POINT_VEC2:New(x,y,LandHeightAdd)
-local LandHeight=land.getHeight({["x"]=x,["y"]=y})
-LandHeightAdd=LandHeightAdd or 0
-LandHeight=LandHeight+LandHeightAdd
-local self=BASE:Inherit(self,COORDINATE:New(x,LandHeight,y))
-self:F2(self)
-return self
-end
-function POINT_VEC2:NewFromVec2(Vec2,LandHeightAdd)
-local LandHeight=land.getHeight(Vec2)
-LandHeightAdd=LandHeightAdd or 0
-LandHeight=LandHeight+LandHeightAdd
-local self=BASE:Inherit(self,COORDINATE:NewFromVec2(Vec2,LandHeightAdd))
-self:F2(self)
-return self
-end
-function POINT_VEC2:NewFromVec3(Vec3)
-local self=BASE:Inherit(self,COORDINATE:NewFromVec3(Vec3))
-self:F2(self)
-return self
-end
-function POINT_VEC2:GetX()
-return self.x
-end
-function POINT_VEC2:GetY()
-return self.z
-end
-function POINT_VEC2:SetX(x)
-self.x=x
-return self
-end
-function POINT_VEC2:SetY(y)
-self.z=y
-return self
-end
-function POINT_VEC2:GetLat()
-return self.x
-end
-function POINT_VEC2:SetLat(x)
-self.x=x
-return self
-end
-function POINT_VEC2:GetLon()
-return self.z
-end
-function POINT_VEC2:SetLon(z)
-self.z=z
-return self
-end
-function POINT_VEC2:GetAlt()
-return self.y~=0 or land.getHeight({x=self.x,y=self.z})
-end
-function POINT_VEC2:SetAlt(Altitude)
-self.y=Altitude or land.getHeight({x=self.x,y=self.z})
-return self
-end
-function POINT_VEC2:AddX(x)
-self.x=self.x+x
-return self
-end
-function POINT_VEC2:AddY(y)
-self.z=self.z+y
-return self
-end
-function POINT_VEC2:AddAlt(Altitude)
-self.y=land.getHeight({x=self.x,y=self.z})+Altitude or 0
-return self
-end
-function POINT_VEC2:GetRandomPointVec2InRadius(OuterRadius,InnerRadius)
-self:F2({OuterRadius,InnerRadius})
-return POINT_VEC2:NewFromVec2(self:GetRandomVec2InRadius(OuterRadius,InnerRadius))
-end
-function POINT_VEC2:DistanceFromPointVec2(PointVec2Reference)
-self:F2(PointVec2Reference)
-local Distance=((PointVec2Reference.x-self.x)^2+(PointVec2Reference.z-self.z)^2)^0.5
-self:T2(Distance)
-return Distance
-end
-end
-REPORT={
-ClassName="REPORT",
-Title="",
-}
-function REPORT:New(Title)
-local self=BASE:Inherit(self,BASE:New())
-self.Report={}
-self:SetTitle(Title or"")
-self:SetIndent(3)
-return self
-end
-function REPORT:HasText()
-return#self.Report>0
-end
-function REPORT:SetIndent(Indent)
-self.Indent=Indent
-return self
-end
-function REPORT:Add(Text)
-self.Report[#self.Report+1]=Text
-return self
-end
-function REPORT:AddIndent(Text,Separator)
-self.Report[#self.Report+1]=((Separator and Separator..string.rep(" ",self.Indent-1))or string.rep(" ",self.Indent))..Text:gsub("\n","\n"..string.rep(" ",self.Indent))
-return self
-end
-function REPORT:Text(Delimiter)
-Delimiter=Delimiter or"\n"
-local ReportText=(self.Title~=""and self.Title..Delimiter or self.Title)..table.concat(self.Report,Delimiter)or""
-return ReportText
-end
-function REPORT:SetTitle(Title)
-self.Title=Title
-return self
-end
-function REPORT:GetCount()
-return#self.Report
-end
-SCHEDULEDISPATCHER={
-ClassName="SCHEDULEDISPATCHER",
-CallID=0,
-PersistentSchedulers={},
-ObjectSchedulers={},
-Schedule=nil,
-}
-function SCHEDULEDISPATCHER:New()
-local self=BASE:Inherit(self,BASE:New())
-self:F3()
-return self
-end
-function SCHEDULEDISPATCHER:AddSchedule(Scheduler,ScheduleFunction,ScheduleArguments,Start,Repeat,Randomize,Stop,TraceLevel,Fsm)
-self:F2({Scheduler,ScheduleFunction,ScheduleArguments,Start,Repeat,Randomize,Stop,TraceLevel,Fsm})
-self.CallID=self.CallID+1
-local CallID=self.CallID.."#"..(Scheduler.MasterObject and Scheduler.MasterObject.GetClassNameAndID and Scheduler.MasterObject:GetClassNameAndID()or"")or""
-self:T2(string.format("Adding schedule #%d CallID=%s",self.CallID,CallID))
-self.PersistentSchedulers=self.PersistentSchedulers or{}
-self.ObjectSchedulers=self.ObjectSchedulers or setmetatable({},{__mode="v"})
-if Scheduler.MasterObject then
-self.ObjectSchedulers[CallID]=Scheduler
-self:F3({CallID=CallID,ObjectScheduler=tostring(self.ObjectSchedulers[CallID]),MasterObject=tostring(Scheduler.MasterObject)})
-else
-self.PersistentSchedulers[CallID]=Scheduler
-self:F3({CallID=CallID,PersistentScheduler=self.PersistentSchedulers[CallID]})
-end
-self.Schedule=self.Schedule or setmetatable({},{__mode="k"})
-self.Schedule[Scheduler]=self.Schedule[Scheduler]or{}
-self.Schedule[Scheduler][CallID]={}
-self.Schedule[Scheduler][CallID].Function=ScheduleFunction
-self.Schedule[Scheduler][CallID].Arguments=ScheduleArguments
-self.Schedule[Scheduler][CallID].StartTime=timer.getTime()+(Start or 0)
-self.Schedule[Scheduler][CallID].Start=Start+0.001
-self.Schedule[Scheduler][CallID].Repeat=Repeat or 0
-self.Schedule[Scheduler][CallID].Randomize=Randomize or 0
-self.Schedule[Scheduler][CallID].Stop=Stop
-local Info={}
-if debug then
-TraceLevel=TraceLevel or 2
-Info=debug.getinfo(TraceLevel,"nlS")
-local name_fsm=debug.getinfo(TraceLevel-1,"n").name
-if name_fsm then
-Info.name=name_fsm
-end
-end
-self:T3(self.Schedule[Scheduler][CallID])
-self.Schedule[Scheduler][CallID].CallHandler=function(Params)
-local CallID=Params.CallID
-local Info=Params.Info or{}
-local Source=Info.source or"?"
-local Line=Info.currentline or"?"
-local Name=Info.name or"?"
-local ErrorHandler=function(errmsg)
-env.info("Error in timer function: "..errmsg)
-if BASE.Debug~=nil then
-env.info(BASE.Debug.traceback())
-end
-return errmsg
-end
-local Scheduler=self.ObjectSchedulers[CallID]
-if not Scheduler then
-Scheduler=self.PersistentSchedulers[CallID]
-end
-if Scheduler then
-local MasterObject=tostring(Scheduler.MasterObject)
-local Schedule=self.Schedule[Scheduler][CallID]
-local SchedulerObject=Scheduler.MasterObject
-local ShowTrace=Scheduler.ShowTrace
-local ScheduleFunction=Schedule.Function
-local ScheduleArguments=Schedule.Arguments or{}
-local Start=Schedule.Start
-local Repeat=Schedule.Repeat or 0
-local Randomize=Schedule.Randomize or 0
-local Stop=Schedule.Stop or 0
-local ScheduleID=Schedule.ScheduleID
-local Prefix=(Repeat==0)and"--->"or"+++>"
-local Status,Result
-if SchedulerObject then
-local function Timer()
-if ShowTrace then
-SchedulerObject:T(Prefix..Name..":"..Line.." ("..Source..")")
-end
-return ScheduleFunction(SchedulerObject,unpack(ScheduleArguments))
-end
-Status,Result=xpcall(Timer,ErrorHandler)
-else
-local function Timer()
-if ShowTrace then
-self:T(Prefix..Name..":"..Line.." ("..Source..")")
-end
-return ScheduleFunction(unpack(ScheduleArguments))
-end
-Status,Result=xpcall(Timer,ErrorHandler)
-end
-local CurrentTime=timer.getTime()
-local StartTime=Schedule.StartTime
-self:F3({CallID=CallID,ScheduleID=ScheduleID,Master=MasterObject,CurrentTime=CurrentTime,StartTime=StartTime,Start=Start,Repeat=Repeat,Randomize=Randomize,Stop=Stop})
-if Status and((Result==nil)or(Result and Result~=false))then
-if Repeat~=0 and((Stop==0)or(Stop~=0 and CurrentTime<=StartTime+Stop))then
-local ScheduleTime=CurrentTime+Repeat+math.random(-(Randomize*Repeat/2),(Randomize*Repeat/2))+0.0001
-return ScheduleTime
-else
-self:Stop(Scheduler,CallID)
-end
-else
-self:Stop(Scheduler,CallID)
-end
-else
-self:I("<<<>"..Name..":"..Line.." ("..Source..")")
-end
-return nil
-end
-self:Start(Scheduler,CallID,Info)
-return CallID
-end
-function SCHEDULEDISPATCHER:RemoveSchedule(Scheduler,CallID)
-self:F2({Remove=CallID,Scheduler=Scheduler})
-if CallID then
-self:Stop(Scheduler,CallID)
-self.Schedule[Scheduler][CallID]=nil
-end
-end
-function SCHEDULEDISPATCHER:Start(Scheduler,CallID,Info)
-self:F2({Start=CallID,Scheduler=Scheduler})
-if CallID then
-local Schedule=self.Schedule[Scheduler][CallID]
-if not Schedule.ScheduleID then
-local Tnow=timer.getTime()
-Schedule.StartTime=Tnow
-Schedule.ScheduleID=timer.scheduleFunction(Schedule.CallHandler,{CallID=CallID,Info=Info},Tnow+Schedule.Start)
-self:T(string.format("Starting SCHEDULEDISPATCHER Call ID=%s ==> Schedule ID=%s",tostring(CallID),tostring(Schedule.ScheduleID)))
-end
-else
-for CallID,Schedule in pairs(self.Schedule[Scheduler]or{})do
-self:Start(Scheduler,CallID,Info)
-end
-end
-end
-function SCHEDULEDISPATCHER:Stop(Scheduler,CallID)
-self:F2({Stop=CallID,Scheduler=Scheduler})
-if CallID then
-local Schedule=self.Schedule[Scheduler][CallID]
-if Schedule.ScheduleID then
-self:T(string.format("SCHEDULEDISPATCHER stopping scheduler CallID=%s, ScheduleID=%s",tostring(CallID),tostring(Schedule.ScheduleID)))
-timer.removeFunction(Schedule.ScheduleID)
-Schedule.ScheduleID=nil
-else
-self:T(string.format("Error no ScheduleID for CallID=%s",tostring(CallID)))
-end
-else
-for CallID,Schedule in pairs(self.Schedule[Scheduler]or{})do
-self:Stop(Scheduler,CallID)
-end
-end
-end
-function SCHEDULEDISPATCHER:Clear(Scheduler)
-self:F2({Scheduler=Scheduler})
-for CallID,Schedule in pairs(self.Schedule[Scheduler]or{})do
-self:Stop(Scheduler,CallID)
-end
-end
-function SCHEDULEDISPATCHER:ShowTrace(Scheduler)
-self:F2({Scheduler=Scheduler})
-Scheduler.ShowTrace=true
-end
-function SCHEDULEDISPATCHER:NoTrace(Scheduler)
-self:F2({Scheduler=Scheduler})
-Scheduler.ShowTrace=false
-end
-SCHEDULER={
-ClassName="SCHEDULER",
-Schedules={},
-MasterObject=nil,
-ShowTrace=nil,
-}
-function SCHEDULER:New(MasterObject,SchedulerFunction,SchedulerArguments,Start,Repeat,RandomizeFactor,Stop)
-local self=BASE:Inherit(self,BASE:New())
-self:F2({Start,Repeat,RandomizeFactor,Stop})
-local ScheduleID=nil
-self.MasterObject=MasterObject
-self.ShowTrace=false
-if SchedulerFunction then
-ScheduleID=self:Schedule(MasterObject,SchedulerFunction,SchedulerArguments,Start,Repeat,RandomizeFactor,Stop,3)
-end
-return self,ScheduleID
-end
-function SCHEDULER:Schedule(MasterObject,SchedulerFunction,SchedulerArguments,Start,Repeat,RandomizeFactor,Stop,TraceLevel,Fsm)
-self:F2({Start,Repeat,RandomizeFactor,Stop})
-self:T3({SchedulerArguments})
-local ObjectName="-"
-if MasterObject and MasterObject.ClassName and MasterObject.ClassID then
-ObjectName=MasterObject.ClassName..MasterObject.ClassID
-end
-self:F3({"Schedule :",ObjectName,tostring(MasterObject),Start,Repeat,RandomizeFactor,Stop})
-self.MasterObject=MasterObject
-local ScheduleID=_SCHEDULEDISPATCHER:AddSchedule(self,
-SchedulerFunction,
-SchedulerArguments,
-Start,
-Repeat,
-RandomizeFactor,
-Stop,
-TraceLevel or 3,
-Fsm
-)
-self.Schedules[#self.Schedules+1]=ScheduleID
-return ScheduleID
-end
-function SCHEDULER:Start(ScheduleID)
-self:F3({ScheduleID})
-self:T(string.format("Starting scheduler ID=%s",tostring(ScheduleID)))
-_SCHEDULEDISPATCHER:Start(self,ScheduleID)
-end
-function SCHEDULER:Stop(ScheduleID)
-self:F3({ScheduleID})
-self:T(string.format("Stopping scheduler ID=%s",tostring(ScheduleID)))
-_SCHEDULEDISPATCHER:Stop(self,ScheduleID)
-end
-function SCHEDULER:Remove(ScheduleID)
-self:F3({ScheduleID})
-self:T(string.format("Removing scheduler ID=%s",tostring(ScheduleID)))
-_SCHEDULEDISPATCHER:RemoveSchedule(self,ScheduleID)
-end
-function SCHEDULER:Clear()
-self:F3()
-self:T(string.format("Clearing scheduler"))
-_SCHEDULEDISPATCHER:Clear(self)
-end
-function SCHEDULER:ShowTrace()
-_SCHEDULEDISPATCHER:ShowTrace(self)
-end
-function SCHEDULER:NoTrace()
-_SCHEDULEDISPATCHER:NoTrace(self)
-end
-do
-SET_BASE={
-ClassName="SET_BASE",
-Filter={},
-Set={},
-List={},
-Index={},
-Database=nil,
-CallScheduler=nil,
-TimeInterval=nil,
-YieldInterval=nil,
-}
-function SET_BASE:New(Database)
-local self=BASE:Inherit(self,FSM:New())
-self.Database=Database
-self:SetStartState("Started")
-self:AddTransition("*","Added","*")
-self:AddTransition("*","Removed","*")
-self.YieldInterval=10
-self.TimeInterval=0.001
-self.Set={}
-self.Index={}
-self.CallScheduler=SCHEDULER:New(self)
-self:SetEventPriority(2)
-return self
-end
-function SET_BASE:Clear(TriggerEvent)
-for Name,Object in pairs(self.Set)do
-self:Remove(Name,not TriggerEvent)
-end
-return self
-end
-function SET_BASE:_Find(ObjectName)
-local ObjectFound=self.Set[ObjectName]
-return ObjectFound
-end
-function SET_BASE:GetSet()
-self:F2()
-return self.Set or{}
-end
-function SET_BASE:GetSetNames()
-self:F2()
-local Names={}
-for Name,Object in pairs(self.Set)do
-table.insert(Names,Name)
-end
-return Names
-end
-function SET_BASE:GetSetObjects()
-self:F2()
-local Objects={}
-for Name,Object in pairs(self.Set)do
-table.insert(Objects,Object)
-end
-return Objects
-end
-function SET_BASE:Remove(ObjectName,NoTriggerEvent)
-self:F2({ObjectName=ObjectName})
-local TriggerEvent=true
-if NoTriggerEvent then
-TriggerEvent=false
-else
-TriggerEvent=true
-end
-local Object=self.Set[ObjectName]
-if Object then
-for Index,Key in ipairs(self.Index)do
-if Key==ObjectName then
-table.remove(self.Index,Index)
-self.Set[ObjectName]=nil
-break
-end
-end
-if TriggerEvent then
-self:Removed(ObjectName,Object)
-end
-end
-end
-function SET_BASE:Add(ObjectName,Object)
-self:T2({ObjectName=ObjectName,Object=Object})
-if self.Set[ObjectName]then
-self:Remove(ObjectName,true)
-end
-self.Set[ObjectName]=Object
-table.insert(self.Index,ObjectName)
-self:Added(ObjectName,Object)
-return self
-end
-function SET_BASE:AddObject(Object)
-self:F2(Object.ObjectName)
-self:T(Object.UnitName)
-self:T(Object.ObjectName)
-self:Add(Object.ObjectName,Object)
-end
-function SET_BASE:SortByName()
-local function sort(a,b)
-return a=Limit then
-break
-end
-end
-return true
-end
-local co=CoRoutine
-local function Schedule()
-local status,res=co()
-self:T3({status,res})
-if status==false then
-error(res)
-end
-if res==false then
-return true
-end
-return false
-end
-Schedule()
-return self
-end
-function SET_BASE:IsIncludeObject(Object)
-self:F3(Object)
-return true
-end
-function SET_BASE:IsInSet(Object)
-self:F3(Object)
-local outcome=false
-local name=Object:GetName()
-self:ForEach(
-function(object)
-if object:GetName()==name then
-outcome=true
-end
-end
-)
-return outcome
-end
-function SET_BASE:IsNotInSet(Object)
-self:F3(Object)
-return not self:IsInSet(Object)
-end
-function SET_BASE:GetObjectNames()
-self:F3()
-local ObjectNames=""
-for ObjectName,Object in pairs(self.Set)do
-ObjectNames=ObjectNames..ObjectName..", "
-end
-return ObjectNames
-end
-function SET_BASE:Flush(MasterObject)
-self:F3()
-local ObjectNames=""
-for ObjectName,Object in pairs(self.Set)do
-ObjectNames=ObjectNames..ObjectName..", "
-end
-self:F({MasterObject=MasterObject and MasterObject:GetClassNameAndID(),"Objects in Set:",ObjectNames})
-return ObjectNames
-end
-end
-do
-SET_GROUP={
-ClassName="SET_GROUP",
-Filter={
-Coalitions=nil,
-Categories=nil,
-Countries=nil,
-GroupPrefixes=nil,
-Zones=nil,
-},
-FilterMeta={
-Coalitions={
-red=coalition.side.RED,
-blue=coalition.side.BLUE,
-neutral=coalition.side.NEUTRAL,
-},
-Categories={
-plane=Group.Category.AIRPLANE,
-helicopter=Group.Category.HELICOPTER,
-ground=Group.Category.GROUND,
-ship=Group.Category.SHIP,
-structure=Group.Category.STRUCTURE,
-},
-},
-}
-function SET_GROUP:New()
-local self=BASE:Inherit(self,SET_BASE:New(_DATABASE.GROUPS))
-self:FilterActive(false)
-return self
-end
-function SET_GROUP:GetAliveSet()
-self:F2()
-local AliveSet=SET_GROUP:New()
-for GroupName,GroupObject in pairs(self.Set)do
-local GroupObject=GroupObject
-if GroupObject then
-if GroupObject:IsAlive()then
-AliveSet:Add(GroupName,GroupObject)
-end
-end
-end
-return AliveSet.Set or{}
-end
-function SET_GROUP:GetUnitTypeNames()
-self:F2()
-local MT={}
-local UnitTypes={}
-local ReportUnitTypes=REPORT:New()
-for GroupID,GroupData in pairs(self:GetSet())do
-local Units=GroupData:GetUnits()
-for UnitID,UnitData in pairs(Units)do
-if UnitData:IsAlive()then
-local UnitType=UnitData:GetTypeName()
-if not UnitTypes[UnitType]then
-UnitTypes[UnitType]=1
-else
-UnitTypes[UnitType]=UnitTypes[UnitType]+1
-end
-end
-end
-end
-for UnitTypeID,UnitType in pairs(UnitTypes)do
-ReportUnitTypes:Add(UnitType.." of "..UnitTypeID)
-end
-return ReportUnitTypes
-end
-function SET_GROUP:AddGroup(group,DontSetCargoBayLimit)
-self:Add(group:GetName(),group)
-if not DontSetCargoBayLimit then
-for UnitID,UnitData in pairs(group:GetUnits())do
-if UnitData and UnitData:IsAlive()then
-UnitData:SetCargoBayWeightLimit()
-end
-end
-end
-return self
-end
-function SET_GROUP:AddGroupsByName(AddGroupNames)
-local AddGroupNamesArray=(type(AddGroupNames)=="table")and AddGroupNames or{AddGroupNames}
-for AddGroupID,AddGroupName in pairs(AddGroupNamesArray)do
-self:Add(AddGroupName,GROUP:FindByName(AddGroupName))
-end
-return self
-end
-function SET_GROUP:RemoveGroupsByName(RemoveGroupNames)
-local RemoveGroupNamesArray=(type(RemoveGroupNames)=="table")and RemoveGroupNames or{RemoveGroupNames}
-for RemoveGroupID,RemoveGroupName in pairs(RemoveGroupNamesArray)do
-self:Remove(RemoveGroupName)
-end
-return self
-end
-function SET_GROUP:FindGroup(GroupName)
-local GroupFound=self.Set[GroupName]
-return GroupFound
-end
-function SET_GROUP:FindNearestGroupFromPointVec2(PointVec2)
-self:F2(PointVec2)
-local NearestGroup=nil
-local ClosestDistance=nil
-local Set=self:GetAliveSet()
-for ObjectID,ObjectData in pairs(Set)do
-if NearestGroup==nil then
-NearestGroup=ObjectData
-ClosestDistance=PointVec2:DistanceFromPointVec2(ObjectData:GetCoordinate())
-else
-local Distance=PointVec2:DistanceFromPointVec2(ObjectData:GetCoordinate())
-if DistanceMaxThreatLevelA2G then
-MaxThreatLevelA2G=ThreatLevelA2G
-MaxThreatText=ThreatText
-end
-end
-self:F({MaxThreatLevelA2G=MaxThreatLevelA2G,MaxThreatText=MaxThreatText})
-return MaxThreatLevelA2G,MaxThreatText
-end
-function SET_UNIT:GetCoordinate()
-local function GetSetVec3(units)
-local x=0
-local y=0
-local z=0
-local n=0
-for _,unit in pairs(units)do
-local vec3=nil
-if unit and unit:IsAlive()then
-vec3=unit:GetVec3()
-end
-if vec3 then
-x=x+vec3.x
-y=y+vec3.y
-z=z+vec3.z
-n=n+1
-end
-end
-if n>0 then
-local Vec3={x=x/n,y=y/n,z=z/n}
-return Vec3
-end
-return nil
-end
-local Coordinate=nil
-local Vec3=GetSetVec3(self.Set)
-if Vec3 then
-Coordinate=COORDINATE:NewFromVec3(Vec3)
-end
-if Coordinate then
-local heading=self:GetHeading()or 0
-local velocity=self:GetVelocity()or 0
-Coordinate:SetHeading(heading)
-Coordinate:SetVelocity(velocity)
-self:I(UTILS.PrintTableToLog(Coordinate))
-end
-return Coordinate
-end
-function SET_UNIT:GetVelocity()
-local Coordinate=self:GetFirst():GetCoordinate()
-local MaxVelocity=0
-for UnitName,UnitData in pairs(self:GetSet())do
-local Unit=UnitData
-local Coordinate=Unit:GetCoordinate()
-local Velocity=Coordinate:GetVelocity()
-if Velocity~=0 then
-MaxVelocity=(MaxVelocity5 then
-HeadingSet=nil
-break
-end
-end
-end
-end
-return HeadingSet
-end
-function SET_UNIT:HasRadar(RadarType)
-self:F2(RadarType)
-local RadarCount=0
-for UnitID,UnitData in pairs(self:GetSet())do
-local UnitSensorTest=UnitData
-local HasSensors
-if RadarType then
-HasSensors=UnitSensorTest:HasSensors(Unit.SensorType.RADAR,RadarType)
-else
-HasSensors=UnitSensorTest:HasSensors(Unit.SensorType.RADAR)
-end
-self:T3(HasSensors)
-if HasSensors then
-RadarCount=RadarCount+1
-end
-end
-return RadarCount
-end
-function SET_UNIT:HasSEAD()
-self:F2()
-local SEADCount=0
-for UnitID,UnitData in pairs(self:GetSet())do
-local UnitSEAD=UnitData
-if UnitSEAD:IsAlive()then
-local UnitSEADAttributes=UnitSEAD:GetDesc().attributes
-local HasSEAD=UnitSEAD:HasSEAD()
-self:T3(HasSEAD)
-if HasSEAD then
-SEADCount=SEADCount+1
-end
-end
-end
-return SEADCount
-end
-function SET_UNIT:HasGroundUnits()
-self:F2()
-local GroundUnitCount=0
-for UnitID,UnitData in pairs(self:GetSet())do
-local UnitTest=UnitData
-if UnitTest:IsGround()then
-GroundUnitCount=GroundUnitCount+1
-end
-end
-return GroundUnitCount
-end
-function SET_UNIT:HasAirUnits()
-self:F2()
-local AirUnitCount=0
-for UnitID,UnitData in pairs(self:GetSet())do
-local UnitTest=UnitData
-if UnitTest:IsAir()then
-AirUnitCount=AirUnitCount+1
-end
-end
-return AirUnitCount
-end
-function SET_UNIT:HasFriendlyUnits(FriendlyCoalition)
-self:F2()
-local FriendlyUnitCount=0
-for UnitID,UnitData in pairs(self:GetSet())do
-local UnitTest=UnitData
-if UnitTest:IsFriendly(FriendlyCoalition)then
-FriendlyUnitCount=FriendlyUnitCount+1
-end
-end
-return FriendlyUnitCount
-end
-function SET_UNIT:IsIncludeObject(MUnit)
-self:F2({MUnit})
-local MUnitInclude=false
-if MUnit:IsAlive()~=nil then
-MUnitInclude=true
-if self.Filter.Active~=nil then
-local MUnitActive=false
-if self.Filter.Active==false or(self.Filter.Active==true and MUnit:IsActive()==true)then
-MUnitActive=true
-end
-MUnitInclude=MUnitInclude and MUnitActive
-end
-if self.Filter.Coalitions then
-local MUnitCoalition=false
-for CoalitionID,CoalitionName in pairs(self.Filter.Coalitions)do
-self:F({"Coalition:",MUnit:GetCoalition(),self.FilterMeta.Coalitions[CoalitionName],CoalitionName})
-if self.FilterMeta.Coalitions[CoalitionName]and self.FilterMeta.Coalitions[CoalitionName]==MUnit:GetCoalition()then
-MUnitCoalition=true
-end
-end
-MUnitInclude=MUnitInclude and MUnitCoalition
-end
-if self.Filter.Categories then
-local MUnitCategory=false
-for CategoryID,CategoryName in pairs(self.Filter.Categories)do
-self:T3({"Category:",MUnit:GetDesc().category,self.FilterMeta.Categories[CategoryName],CategoryName})
-if self.FilterMeta.Categories[CategoryName]and self.FilterMeta.Categories[CategoryName]==MUnit:GetDesc().category then
-MUnitCategory=true
-end
-end
-MUnitInclude=MUnitInclude and MUnitCategory
-end
-if self.Filter.Types then
-local MUnitType=false
-for TypeID,TypeName in pairs(self.Filter.Types)do
-self:T3({"Type:",MUnit:GetTypeName(),TypeName})
-if TypeName==MUnit:GetTypeName()then
-MUnitType=true
-end
-end
-MUnitInclude=MUnitInclude and MUnitType
-end
-if self.Filter.Countries then
-local MUnitCountry=false
-for CountryID,CountryName in pairs(self.Filter.Countries)do
-self:T3({"Country:",MUnit:GetCountry(),CountryName})
-if country.id[CountryName]==MUnit:GetCountry()then
-MUnitCountry=true
-end
-end
-MUnitInclude=MUnitInclude and MUnitCountry
-end
-if self.Filter.UnitPrefixes then
-local MUnitPrefix=false
-for UnitPrefixId,UnitPrefix in pairs(self.Filter.UnitPrefixes)do
-self:T3({"Prefix:",string.find(MUnit:GetName(),UnitPrefix,1),UnitPrefix})
-if string.find(MUnit:GetName(),UnitPrefix,1)then
-MUnitPrefix=true
-end
-end
-MUnitInclude=MUnitInclude and MUnitPrefix
-end
-if self.Filter.RadarTypes then
-local MUnitRadar=false
-for RadarTypeID,RadarType in pairs(self.Filter.RadarTypes)do
-self:T3({"Radar:",RadarType})
-if MUnit:HasSensors(Unit.SensorType.RADAR,RadarType)==true then
-if MUnit:GetRadar()==true then
-self:T3("RADAR Found")
-end
-MUnitRadar=true
-end
-end
-MUnitInclude=MUnitInclude and MUnitRadar
-end
-if self.Filter.SEAD then
-local MUnitSEAD=false
-if MUnit:HasSEAD()==true then
-self:T3("SEAD Found")
-MUnitSEAD=true
-end
-MUnitInclude=MUnitInclude and MUnitSEAD
-end
-end
-if self.Filter.Zones then
-local MGroupZone=false
-for ZoneName,Zone in pairs(self.Filter.Zones)do
-self:T3("Zone:",ZoneName)
-if MUnit:IsInZone(Zone)then
-MGroupZone=true
-end
-end
-MUnitInclude=MUnitInclude and MGroupZone
-end
-self:T2(MUnitInclude)
-return MUnitInclude
-end
-function SET_UNIT:GetTypeNames(Delimiter)
-Delimiter=Delimiter or", "
-local TypeReport=REPORT:New()
-local Types={}
-for UnitName,UnitData in pairs(self:GetSet())do
-local Unit=UnitData
-local UnitTypeName=Unit:GetTypeName()
-if not Types[UnitTypeName]then
-Types[UnitTypeName]=UnitTypeName
-TypeReport:Add(UnitTypeName)
-end
-end
-return TypeReport:Text(Delimiter)
-end
-function SET_UNIT:SetCargoBayWeightLimit()
-local Set=self:GetSet()
-for UnitID,UnitData in pairs(Set)do
-UnitData:SetCargoBayWeightLimit()
-end
-end
-end
-do
-SET_STATIC={
-ClassName="SET_STATIC",
-Statics={},
-Filter={
-Coalitions=nil,
-Categories=nil,
-Types=nil,
-Countries=nil,
-StaticPrefixes=nil,
-Zones=nil,
-},
-FilterMeta={
-Coalitions={
-red=coalition.side.RED,
-blue=coalition.side.BLUE,
-neutral=coalition.side.NEUTRAL,
-},
-Categories={
-plane=Unit.Category.AIRPLANE,
-helicopter=Unit.Category.HELICOPTER,
-ground=Unit.Category.GROUND_STATIC,
-ship=Unit.Category.SHIP,
-structure=Unit.Category.STRUCTURE,
-},
-},
-}
-function SET_STATIC:New()
-local self=BASE:Inherit(self,SET_BASE:New(_DATABASE.STATICS))
-return self
-end
-function SET_STATIC:AddStatic(AddStatic)
-self:F2(AddStatic:GetName())
-self:Add(AddStatic:GetName(),AddStatic)
-return self
-end
-function SET_STATIC:AddStaticsByName(AddStaticNames)
-local AddStaticNamesArray=(type(AddStaticNames)=="table")and AddStaticNames or{AddStaticNames}
-self:T(AddStaticNamesArray)
-for AddStaticID,AddStaticName in pairs(AddStaticNamesArray)do
-self:Add(AddStaticName,STATIC:FindByName(AddStaticName))
-end
-return self
-end
-function SET_STATIC:RemoveStaticsByName(RemoveStaticNames)
-local RemoveStaticNamesArray=(type(RemoveStaticNames)=="table")and RemoveStaticNames or{RemoveStaticNames}
-for RemoveStaticID,RemoveStaticName in pairs(RemoveStaticNamesArray)do
-self:Remove(RemoveStaticName)
-end
-return self
-end
-function SET_STATIC:FindStatic(StaticName)
-local StaticFound=self.Set[StaticName]
-return StaticFound
-end
-function SET_STATIC:FilterCoalitions(Coalitions)
-if not self.Filter.Coalitions then
-self.Filter.Coalitions={}
-end
-if type(Coalitions)~="table"then
-Coalitions={Coalitions}
-end
-for CoalitionID,Coalition in pairs(Coalitions)do
-self.Filter.Coalitions[Coalition]=Coalition
-end
-return self
-end
-function SET_STATIC:FilterZones(Zones)
-if not self.Filter.Zones then
-self.Filter.Zones={}
-end
-local zones={}
-if Zones.ClassName and Zones.ClassName=="SET_ZONE"then
-zones=Zones.Set
-elseif type(Zones)~="table"or(type(Zones)=="table"and Zones.ClassName)then
-self:E("***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!")
-return self
-else
-zones=Zones
-end
-for _,Zone in pairs(zones)do
-local zonename=Zone:GetName()
-self.Filter.Zones[zonename]=Zone
-end
-return self
-end
-function SET_STATIC:FilterCategories(Categories)
-if not self.Filter.Categories then
-self.Filter.Categories={}
-end
-if type(Categories)~="table"then
-Categories={Categories}
-end
-for CategoryID,Category in pairs(Categories)do
-self.Filter.Categories[Category]=Category
-end
-return self
-end
-function SET_STATIC:FilterTypes(Types)
-if not self.Filter.Types then
-self.Filter.Types={}
-end
-if type(Types)~="table"then
-Types={Types}
-end
-for TypeID,Type in pairs(Types)do
-self.Filter.Types[Type]=Type
-end
-return self
-end
-function SET_STATIC:FilterCountries(Countries)
-if not self.Filter.Countries then
-self.Filter.Countries={}
-end
-if type(Countries)~="table"then
-Countries={Countries}
-end
-for CountryID,Country in pairs(Countries)do
-self.Filter.Countries[Country]=Country
-end
-return self
-end
-function SET_STATIC:FilterPrefixes(Prefixes)
-if not self.Filter.StaticPrefixes then
-self.Filter.StaticPrefixes={}
-end
-if type(Prefixes)~="table"then
-Prefixes={Prefixes}
-end
-for PrefixID,Prefix in pairs(Prefixes)do
-self.Filter.StaticPrefixes[Prefix]=Prefix
-end
-return self
-end
-function SET_STATIC:FilterStart()
-if _DATABASE then
-self:_FilterStart()
-self:HandleEvent(EVENTS.Birth,self._EventOnBirth)
-self:HandleEvent(EVENTS.Dead,self._EventOnDeadOrCrash)
-self:HandleEvent(EVENTS.Crash,self._EventOnDeadOrCrash)
-end
-return self
-end
-function SET_STATIC:CountAlive()
-local Set=self:GetSet()
-local CountU=0
-for UnitID,UnitData in pairs(Set)do
-if UnitData and UnitData:IsAlive()then
-CountU=CountU+1
-end
-end
-return CountU
-end
-function SET_STATIC:AddInDatabase(Event)
-self:F3({Event})
-if Event.IniObjectCategory==Object.Category.STATIC then
-if not self.Database[Event.IniDCSUnitName]then
-self.Database[Event.IniDCSUnitName]=STATIC:Register(Event.IniDCSUnitName)
-self:T3(self.Database[Event.IniDCSUnitName])
-end
-end
-return Event.IniDCSUnitName,self.Database[Event.IniDCSUnitName]
-end
-function SET_STATIC:FindInDatabase(Event)
-self:F2({Event.IniDCSUnitName,self.Set[Event.IniDCSUnitName],Event})
-return Event.IniDCSUnitName,self.Set[Event.IniDCSUnitName]
-end
-do
-function SET_STATIC:IsPartiallyInZone(Zone)
-local IsPartiallyInZone=false
-local function EvaluateZone(ZoneStatic)
-local ZoneStaticName=ZoneStatic:GetName()
-if self:FindStatic(ZoneStaticName)then
-IsPartiallyInZone=true
-return false
-end
-return true
-end
-return IsPartiallyInZone
-end
-function SET_STATIC:IsNotInZone(Zone)
-local IsNotInZone=true
-local function EvaluateZone(ZoneStatic)
-local ZoneStaticName=ZoneStatic:GetName()
-if self:FindStatic(ZoneStaticName)then
-IsNotInZone=false
-return false
-end
-return true
-end
-Zone:Search(EvaluateZone)
-return IsNotInZone
-end
-function SET_STATIC:ForEachStaticInZone(IteratorFunction,...)
-self:F2(arg)
-self:ForEach(IteratorFunction,arg,self:GetSet())
-return self
-end
-end
-function SET_STATIC:ForEachStatic(IteratorFunction,...)
-self:F2(arg)
-self:ForEach(IteratorFunction,arg,self:GetSet())
-return self
-end
-function SET_STATIC:ForEachStaticCompletelyInZone(ZoneObject,IteratorFunction,...)
-self:F2(arg)
-self:ForEach(IteratorFunction,arg,self:GetSet(),
-function(ZoneObject,StaticObject)
-if StaticObject:IsInZone(ZoneObject)then
-return true
-else
-return false
-end
-end,{ZoneObject})
-return self
-end
-function SET_STATIC:ForEachStaticNotInZone(ZoneObject,IteratorFunction,...)
-self:F2(arg)
-self:ForEach(IteratorFunction,arg,self:GetSet(),
-function(ZoneObject,StaticObject)
-if StaticObject:IsNotInZone(ZoneObject)then
-return true
-else
-return false
-end
-end,{ZoneObject})
-return self
-end
-function SET_STATIC:GetStaticTypes()
-self:F2()
-local MT={}
-local StaticTypes={}
-for StaticID,StaticData in pairs(self:GetSet())do
-local TextStatic=StaticData
-if TextStatic:IsAlive()then
-local StaticType=TextStatic:GetTypeName()
-if not StaticTypes[StaticType]then
-StaticTypes[StaticType]=1
-else
-StaticTypes[StaticType]=StaticTypes[StaticType]+1
-end
-end
-end
-for StaticTypeID,StaticType in pairs(StaticTypes)do
-MT[#MT+1]=StaticType.." of "..StaticTypeID
-end
-return StaticTypes
-end
-function SET_STATIC:GetStaticTypesText()
-self:F2()
-local MT={}
-local StaticTypes=self:GetStaticTypes()
-for StaticTypeID,StaticType in pairs(StaticTypes)do
-MT[#MT+1]=StaticType.." of "..StaticTypeID
-end
-return table.concat(MT,", ")
-end
-function SET_STATIC:GetCoordinate()
-local Coordinate=self:GetFirst():GetCoordinate()
-local x1=Coordinate.x
-local x2=Coordinate.x
-local y1=Coordinate.y
-local y2=Coordinate.y
-local z1=Coordinate.z
-local z2=Coordinate.z
-local MaxVelocity=0
-local AvgHeading=nil
-local MovingCount=0
-for StaticName,StaticData in pairs(self:GetSet())do
-local Static=StaticData
-local Coordinate=Static:GetCoordinate()
-x1=(Coordinate.xx2)and Coordinate.x or x2
-y1=(Coordinate.yy2)and Coordinate.y or y2
-z1=(Coordinate.yz2)and Coordinate.z or z2
-local Velocity=Coordinate:GetVelocity()
-if Velocity~=0 then
-MaxVelocity=(MaxVelocity5 then
-HeadingSet=nil
-break
-end
-end
-end
-end
-return HeadingSet
-end
-function SET_STATIC:CalculateThreatLevelA2G()
-local MaxThreatLevelA2G=0
-local MaxThreatText=""
-for StaticName,StaticData in pairs(self:GetSet())do
-local ThreatStatic=StaticData
-local ThreatLevelA2G,ThreatText=ThreatStatic:GetThreatLevel()
-if ThreatLevelA2G>MaxThreatLevelA2G then
-MaxThreatLevelA2G=ThreatLevelA2G
-MaxThreatText=ThreatText
-end
-end
-self:F({MaxThreatLevelA2G=MaxThreatLevelA2G,MaxThreatText=MaxThreatText})
-return MaxThreatLevelA2G,MaxThreatText
-end
-function SET_STATIC:IsIncludeObject(MStatic)
-self:F2(MStatic)
-local MStaticInclude=true
-if self.Filter.Coalitions then
-local MStaticCoalition=false
-for CoalitionID,CoalitionName in pairs(self.Filter.Coalitions)do
-self:T3({"Coalition:",MStatic:GetCoalition(),self.FilterMeta.Coalitions[CoalitionName],CoalitionName})
-if self.FilterMeta.Coalitions[CoalitionName]and self.FilterMeta.Coalitions[CoalitionName]==MStatic:GetCoalition()then
-MStaticCoalition=true
-end
-end
-MStaticInclude=MStaticInclude and MStaticCoalition
-end
-if self.Filter.Categories then
-local MStaticCategory=false
-for CategoryID,CategoryName in pairs(self.Filter.Categories)do
-self:T3({"Category:",MStatic:GetDesc().category,self.FilterMeta.Categories[CategoryName],CategoryName})
-if self.FilterMeta.Categories[CategoryName]and self.FilterMeta.Categories[CategoryName]==MStatic:GetDesc().category then
-MStaticCategory=true
-end
-end
-MStaticInclude=MStaticInclude and MStaticCategory
-end
-if self.Filter.Types then
-local MStaticType=false
-for TypeID,TypeName in pairs(self.Filter.Types)do
-self:T3({"Type:",MStatic:GetTypeName(),TypeName})
-if TypeName==MStatic:GetTypeName()then
-MStaticType=true
-end
-end
-MStaticInclude=MStaticInclude and MStaticType
-end
-if self.Filter.Countries then
-local MStaticCountry=false
-for CountryID,CountryName in pairs(self.Filter.Countries)do
-self:T3({"Country:",MStatic:GetCountry(),CountryName})
-if country.id[CountryName]==MStatic:GetCountry()then
-MStaticCountry=true
-end
-end
-MStaticInclude=MStaticInclude and MStaticCountry
-end
-if self.Filter.StaticPrefixes then
-local MStaticPrefix=false
-for StaticPrefixId,StaticPrefix in pairs(self.Filter.StaticPrefixes)do
-self:T3({"Prefix:",string.find(MStatic:GetName(),StaticPrefix,1),StaticPrefix})
-if string.find(MStatic:GetName(),StaticPrefix,1)then
-MStaticPrefix=true
-end
-end
-MStaticInclude=MStaticInclude and MStaticPrefix
-end
-if self.Filter.Zones then
-local MStaticZone=false
-for ZoneName,Zone in pairs(self.Filter.Zones)do
-self:T3("Zone:",ZoneName)
-if MStatic and MStatic:IsInZone(Zone)then
-MStaticZone=true
-end
-end
-MStaticInclude=MStaticInclude and MStaticZone
-end
-self:T2(MStaticInclude)
-return MStaticInclude
-end
-function SET_STATIC:GetTypeNames(Delimiter)
-Delimiter=Delimiter or", "
-local TypeReport=REPORT:New()
-local Types={}
-for StaticName,StaticData in pairs(self:GetSet())do
-local Static=StaticData
-local StaticTypeName=Static:GetTypeName()
-if not Types[StaticTypeName]then
-Types[StaticTypeName]=StaticTypeName
-TypeReport:Add(StaticTypeName)
-end
-end
-return TypeReport:Text(Delimiter)
-end
-function SET_STATIC:GetClosestStatic(Coordinate,Coalitions)
-local Set=self:GetSet()
-local dmin=math.huge
-local gmin=nil
-for GroupID,GroupData in pairs(Set)do
-local group=GroupData
-if group and group:IsAlive()and(Coalitions==nil or UTILS.IsAnyInTable(Coalitions,group:GetCoalition()))then
-local coord=group:GetCoord()
-local d=UTILS.VecDist3D(Coordinate,coord)
-if d1 then
-x=x/count
-y=y/count
-z=z/count
-end
-local coord=COORDINATE:New(x,y,z)
-return coord
-end
-function SET_ZONE:IsIncludeObject(MZone)
-self:F2(MZone)
-local MZoneInclude=true
-if MZone then
-local MZoneName=MZone:GetName()
-if self.Filter.Prefixes then
-local MZonePrefix=false
-for ZonePrefixId,ZonePrefix in pairs(self.Filter.Prefixes)do
-self:T2({"Prefix:",string.find(MZoneName,ZonePrefix,1),ZonePrefix})
-if string.find(MZoneName,ZonePrefix,1)then
-MZonePrefix=true
-end
-end
-self:T({"Evaluated Prefix",MZonePrefix})
-MZoneInclude=MZoneInclude and MZonePrefix
-end
-end
-self:T2(MZoneInclude)
-return MZoneInclude
-end
-function SET_ZONE:OnEventNewZone(EventData)
-self:F({"New Zone",EventData})
-if EventData.Zone then
-if EventData.Zone and self:IsIncludeObject(EventData.Zone)then
-self:Add(EventData.Zone.ZoneName,EventData.Zone)
-end
-end
-end
-function SET_ZONE:OnEventDeleteZone(EventData)
-self:F3({EventData})
-if EventData.Zone then
-local Zone=_DATABASE:FindZone(EventData.Zone.ZoneName)
-if Zone and Zone.ZoneName then
-self:F({ZoneNoDestroy=Zone.NoDestroy})
-if Zone.NoDestroy then
-else
-self:Remove(Zone.ZoneName)
-end
-end
-end
-end
-function SET_ZONE:IsCoordinateInZone(Coordinate)
-for _,Zone in pairs(self:GetSet())do
-local Zone=Zone
-if Zone:IsCoordinateInZone(Coordinate)then
-return Zone
-end
-end
-return nil
-end
-function SET_ZONE:GetClosestZone(Coordinate)
-local dmin=math.huge
-local zmin=nil
-for _,Zone in pairs(self:GetSet())do
-local Zone=Zone
-local d=Zone:Get2DDistance(Coordinate)
-if dx2)and Coordinate.x or x2
-y1=(Coordinate.yy2)and Coordinate.y or y2
-z1=(Coordinate.yz2)and Coordinate.z or z2
-end
-Coordinate.x=(x2-x1)/2+x1
-Coordinate.y=(y2-y1)/2+y1
-Coordinate.z=(z2-z1)/2+z1
-self:F({Coordinate=Coordinate})
-return Coordinate
-end
-function SET_SCENERY:IsIncludeObject(MScenery)
-self:T(MScenery.SceneryName)
-local MSceneryInclude=true
-if MScenery then
-local MSceneryName=MScenery:GetName()
-if self.Filter.Prefixes then
-local MSceneryPrefix=false
-for ZonePrefixId,ZonePrefix in pairs(self.Filter.Prefixes)do
-self:T({"Prefix:",string.find(MSceneryName,ZonePrefix,1),ZonePrefix})
-if string.find(MSceneryName,ZonePrefix,1)then
-MSceneryPrefix=true
-end
-end
-self:T({"Evaluated Prefix",MSceneryPrefix})
-MSceneryInclude=MSceneryInclude and MSceneryPrefix
-end
-if self.Filter.Zones then
-local MSceneryZone=false
-for ZoneName,Zone in pairs(self.Filter.Zones)do
-local coord=MScenery:GetCoordinate()
-if coord and Zone:IsCoordinateInZone(coord)then
-MSceneryZone=true
-end
-self:T({"Evaluated Zone",MSceneryZone})
-end
-MSceneryInclude=MSceneryInclude and MSceneryZone
-end
-if self.Filter.SceneryRoles then
-local MSceneryRole=false
-local Role=MScenery:GetProperty("ROLE")or"none"
-for ZoneRoleId,ZoneRole in pairs(self.Filter.SceneryRoles)do
-self:T({"Role:",ZoneRole,Role})
-if ZoneRole==Role then
-MSceneryRole=true
-end
-end
-self:T({"Evaluated Role ",MSceneryRole})
-MSceneryInclude=MSceneryInclude and MSceneryRole
-end
-end
-self:T2(MSceneryInclude)
-return MSceneryInclude
-end
-function SET_SCENERY:FilterOnce()
-for ObjectName,Object in pairs(self:GetSet())do
-self:T(ObjectName)
-if self:IsIncludeObject(Object)then
-self:Add(ObjectName,Object)
-else
-self:Remove(ObjectName,true)
-end
-end
-return self
-end
-function SET_SCENERY:GetLife0()
-local life0=0
-self:ForEachScenery(
-function(obj)
-local Obj=obj
-life0=life0+Obj:GetLife0()
-end
-)
-return life0
-end
-function SET_SCENERY:GetLife()
-local life=0
-self:ForEachScenery(
-function(obj)
-local Obj=obj
-life=life+Obj:GetLife()
-end
-)
-return life
-end
-function SET_SCENERY:GetRelativeLife()
-local life=self:GetLife()
-local life0=self:GetLife0()
-self:T2(string.format("Set Lifepoints: %d life0 | %d life",life0,life))
-local rlife=math.floor((life/life0)*100)
-return rlife
-end
-end
-SETTINGS={
-ClassName="SETTINGS",
-ShowPlayerMenu=true,
-MenuShort=false,
-MenuStatic=false,
-}
-SETTINGS.__Enum={}
-SETTINGS.__Enum.Era={
-WWII=1,
-Korea=2,
-Cold=3,
-Modern=4,
-}
-do
-function SETTINGS:Set(PlayerName)
-if PlayerName==nil then
-local self=BASE:Inherit(self,BASE:New())
-self:SetMetric()
-self:SetA2G_BR()
-self:SetA2A_BRAA()
-self:SetLL_Accuracy(3)
-self:SetMGRS_Accuracy(5)
-self:SetMessageTime(MESSAGE.Type.Briefing,180)
-self:SetMessageTime(MESSAGE.Type.Detailed,60)
-self:SetMessageTime(MESSAGE.Type.Information,30)
-self:SetMessageTime(MESSAGE.Type.Overview,60)
-self:SetMessageTime(MESSAGE.Type.Update,15)
-self:SetEraModern()
-self:SetLocale("en")
-return self
-else
-local Settings=_DATABASE:GetPlayerSettings(PlayerName)
-if not Settings then
-Settings=BASE:Inherit(self,BASE:New())
-_DATABASE:SetPlayerSettings(PlayerName,Settings)
-end
-return Settings
-end
-end
-function SETTINGS:SetMenutextShort(onoff)
-_SETTINGS.MenuShort=onoff
-end
-function SETTINGS:SetMenuStatic(onoff)
-_SETTINGS.MenuStatic=onoff
-end
-function SETTINGS:SetMetric()
-self.Metric=true
-end
-function SETTINGS:SetLocale(Locale)
-self.Locale=Locale or"en"
-end
-function SETTINGS:GetLocale()
-return self.Locale or _SETTINGS:GetLocale()
-end
-function SETTINGS:IsMetric()
-return(self.Metric~=nil and self.Metric==true)or(self.Metric==nil and _SETTINGS:IsMetric())
-end
-function SETTINGS:SetImperial()
-self.Metric=false
-end
-function SETTINGS:IsImperial()
-return(self.Metric~=nil and self.Metric==false)or(self.Metric==nil and _SETTINGS:IsImperial())
-end
-function SETTINGS:SetLL_Accuracy(LL_Accuracy)
-self.LL_Accuracy=LL_Accuracy
-end
-function SETTINGS:GetLL_DDM_Accuracy()
-return self.LL_DDM_Accuracy or _SETTINGS:GetLL_DDM_Accuracy()
-end
-function SETTINGS:SetMGRS_Accuracy(MGRS_Accuracy)
-self.MGRS_Accuracy=MGRS_Accuracy
-end
-function SETTINGS:GetMGRS_Accuracy()
-return self.MGRS_Accuracy or _SETTINGS:GetMGRS_Accuracy()
-end
-function SETTINGS:SetMessageTime(MessageType,MessageTime)
-self.MessageTypeTimings=self.MessageTypeTimings or{}
-self.MessageTypeTimings[MessageType]=MessageTime
-end
-function SETTINGS:GetMessageTime(MessageType)
-return(self.MessageTypeTimings and self.MessageTypeTimings[MessageType])or _SETTINGS:GetMessageTime(MessageType)
-end
-function SETTINGS:SetA2G_LL_DMS()
-self.A2GSystem="LL DMS"
-end
-function SETTINGS:SetA2G_LL_DDM()
-self.A2GSystem="LL DDM"
-end
-function SETTINGS:IsA2G_LL_DMS()
-return(self.A2GSystem and self.A2GSystem=="LL DMS")or(not self.A2GSystem and _SETTINGS:IsA2G_LL_DMS())
-end
-function SETTINGS:IsA2G_LL_DDM()
-return(self.A2GSystem and self.A2GSystem=="LL DDM")or(not self.A2GSystem and _SETTINGS:IsA2G_LL_DDM())
-end
-function SETTINGS:SetA2G_MGRS()
-self.A2GSystem="MGRS"
-end
-function SETTINGS:IsA2G_MGRS()
-return(self.A2GSystem and self.A2GSystem=="MGRS")or(not self.A2GSystem and _SETTINGS:IsA2G_MGRS())
-end
-function SETTINGS:SetA2G_BR()
-self.A2GSystem="BR"
-end
-function SETTINGS:IsA2G_BR()
-return(self.A2GSystem and self.A2GSystem=="BR")or(not self.A2GSystem and _SETTINGS:IsA2G_BR())
-end
-function SETTINGS:SetA2A_BRAA()
-self.A2ASystem="BRAA"
-end
-function SETTINGS:IsA2A_BRAA()
-return(self.A2ASystem and self.A2ASystem=="BRAA")or(not self.A2ASystem and _SETTINGS:IsA2A_BRAA())
-end
-function SETTINGS:SetA2A_BULLS()
-self.A2ASystem="BULLS"
-end
-function SETTINGS:IsA2A_BULLS()
-return(self.A2ASystem and self.A2ASystem=="BULLS")or(not self.A2ASystem and _SETTINGS:IsA2A_BULLS())
-end
-function SETTINGS:SetA2A_LL_DMS()
-self.A2ASystem="LL DMS"
-end
-function SETTINGS:SetA2A_LL_DDM()
-self.A2ASystem="LL DDM"
-end
-function SETTINGS:IsA2A_LL_DMS()
-return(self.A2ASystem and self.A2ASystem=="LL DMS")or(not self.A2ASystem and _SETTINGS:IsA2A_LL_DMS())
-end
-function SETTINGS:IsA2A_LL_DDM()
-return(self.A2ASystem and self.A2ASystem=="LL DDM")or(not self.A2ASystem and _SETTINGS:IsA2A_LL_DDM())
-end
-function SETTINGS:SetA2A_MGRS()
-self.A2ASystem="MGRS"
-end
-function SETTINGS:IsA2A_MGRS()
-return(self.A2ASystem and self.A2ASystem=="MGRS")or(not self.A2ASystem and _SETTINGS:IsA2A_MGRS())
-end
-function SETTINGS:SetSystemMenu(MenuGroup,RootMenu)
-local MenuText="System Settings"
-local MenuTime=timer.getTime()
-local SettingsMenu=MENU_GROUP:New(MenuGroup,MenuText,RootMenu):SetTime(MenuTime)
-local text="A2G Coordinate System"
-if _SETTINGS.MenuShort then
-text="A2G Coordinates"
-end
-local A2GCoordinateMenu=MENU_GROUP:New(MenuGroup,text,SettingsMenu):SetTime(MenuTime)
-if not self:IsA2G_LL_DMS()then
-local text="Lat/Lon Degree Min Sec (LL DMS)"
-if _SETTINGS.MenuShort then
-text="LL DMS"
-end
-MENU_GROUP_COMMAND:New(MenuGroup,text,A2GCoordinateMenu,self.A2GMenuSystem,self,MenuGroup,RootMenu,"LL DMS"):SetTime(MenuTime)
-end
-if not self:IsA2G_LL_DDM()then
-local text="Lat/Lon Degree Dec Min (LL DDM)"
-if _SETTINGS.MenuShort then
-text="LL DDM"
-end
-MENU_GROUP_COMMAND:New(MenuGroup,"Lat/Lon Degree Dec Min (LL DDM)",A2GCoordinateMenu,self.A2GMenuSystem,self,MenuGroup,RootMenu,"LL DDM"):SetTime(MenuTime)
-end
-if self:IsA2G_LL_DDM()then
-local text1="LL DDM Accuracy 1"
-local text2="LL DDM Accuracy 2"
-local text3="LL DDM Accuracy 3"
-if _SETTINGS.MenuShort then
-text1="LL DDM"
-end
-MENU_GROUP_COMMAND:New(MenuGroup,"LL DDM Accuracy 1",A2GCoordinateMenu,self.MenuLL_DDM_Accuracy,self,MenuGroup,RootMenu,1):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"LL DDM Accuracy 2",A2GCoordinateMenu,self.MenuLL_DDM_Accuracy,self,MenuGroup,RootMenu,2):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"LL DDM Accuracy 3",A2GCoordinateMenu,self.MenuLL_DDM_Accuracy,self,MenuGroup,RootMenu,3):SetTime(MenuTime)
-end
-if not self:IsA2G_BR()then
-local text="Bearing, Range (BR)"
-if _SETTINGS.MenuShort then
-text="BR"
-end
-MENU_GROUP_COMMAND:New(MenuGroup,text,A2GCoordinateMenu,self.A2GMenuSystem,self,MenuGroup,RootMenu,"BR"):SetTime(MenuTime)
-end
-if not self:IsA2G_MGRS()then
-local text="Military Grid (MGRS)"
-if _SETTINGS.MenuShort then
-text="MGRS"
-end
-MENU_GROUP_COMMAND:New(MenuGroup,text,A2GCoordinateMenu,self.A2GMenuSystem,self,MenuGroup,RootMenu,"MGRS"):SetTime(MenuTime)
-end
-if self:IsA2G_MGRS()then
-MENU_GROUP_COMMAND:New(MenuGroup,"MGRS Accuracy 1",A2GCoordinateMenu,self.MenuMGRS_Accuracy,self,MenuGroup,RootMenu,1):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"MGRS Accuracy 2",A2GCoordinateMenu,self.MenuMGRS_Accuracy,self,MenuGroup,RootMenu,2):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"MGRS Accuracy 3",A2GCoordinateMenu,self.MenuMGRS_Accuracy,self,MenuGroup,RootMenu,3):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"MGRS Accuracy 4",A2GCoordinateMenu,self.MenuMGRS_Accuracy,self,MenuGroup,RootMenu,4):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"MGRS Accuracy 5",A2GCoordinateMenu,self.MenuMGRS_Accuracy,self,MenuGroup,RootMenu,5):SetTime(MenuTime)
-end
-local text="A2A Coordinate System"
-if _SETTINGS.MenuShort then
-text="A2A Coordinates"
-end
-local A2ACoordinateMenu=MENU_GROUP:New(MenuGroup,text,SettingsMenu):SetTime(MenuTime)
-if not self:IsA2A_LL_DMS()then
-local text="Lat/Lon Degree Min Sec (LL DMS)"
-if _SETTINGS.MenuShort then
-text="LL DMS"
-end
-MENU_GROUP_COMMAND:New(MenuGroup,text,A2ACoordinateMenu,self.A2AMenuSystem,self,MenuGroup,RootMenu,"LL DMS"):SetTime(MenuTime)
-end
-if not self:IsA2A_LL_DDM()then
-local text="Lat/Lon Degree Dec Min (LL DDM)"
-if _SETTINGS.MenuShort then
-text="LL DDM"
-end
-MENU_GROUP_COMMAND:New(MenuGroup,text,A2ACoordinateMenu,self.A2AMenuSystem,self,MenuGroup,RootMenu,"LL DDM"):SetTime(MenuTime)
-end
-if self:IsA2A_LL_DDM()or self:IsA2A_LL_DMS()then
-MENU_GROUP_COMMAND:New(MenuGroup,"LL Accuracy 0",A2ACoordinateMenu,self.MenuLL_DDM_Accuracy,self,MenuGroup,RootMenu,0):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"LL Accuracy 1",A2ACoordinateMenu,self.MenuLL_DDM_Accuracy,self,MenuGroup,RootMenu,1):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"LL Accuracy 2",A2ACoordinateMenu,self.MenuLL_DDM_Accuracy,self,MenuGroup,RootMenu,2):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"LL Accuracy 3",A2ACoordinateMenu,self.MenuLL_DDM_Accuracy,self,MenuGroup,RootMenu,3):SetTime(MenuTime)
-end
-if not self:IsA2A_BULLS()then
-local text="Bullseye (BULLS)"
-if _SETTINGS.MenuShort then
-text="Bulls"
-end
-MENU_GROUP_COMMAND:New(MenuGroup,text,A2ACoordinateMenu,self.A2AMenuSystem,self,MenuGroup,RootMenu,"BULLS"):SetTime(MenuTime)
-end
-if not self:IsA2A_BRAA()then
-local text="Bearing Range Altitude Aspect (BRAA)"
-if _SETTINGS.MenuShort then
-text="BRAA"
-end
-MENU_GROUP_COMMAND:New(MenuGroup,text,A2ACoordinateMenu,self.A2AMenuSystem,self,MenuGroup,RootMenu,"BRAA"):SetTime(MenuTime)
-end
-if not self:IsA2A_MGRS()then
-local text="Military Grid (MGRS)"
-if _SETTINGS.MenuShort then
-text="MGRS"
-end
-MENU_GROUP_COMMAND:New(MenuGroup,text,A2ACoordinateMenu,self.A2AMenuSystem,self,MenuGroup,RootMenu,"MGRS"):SetTime(MenuTime)
-end
-if self:IsA2A_MGRS()then
-MENU_GROUP_COMMAND:New(MenuGroup,"MGRS Accuracy 1",A2ACoordinateMenu,self.MenuMGRS_Accuracy,self,MenuGroup,RootMenu,1):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"MGRS Accuracy 2",A2ACoordinateMenu,self.MenuMGRS_Accuracy,self,MenuGroup,RootMenu,2):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"MGRS Accuracy 3",A2ACoordinateMenu,self.MenuMGRS_Accuracy,self,MenuGroup,RootMenu,3):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"MGRS Accuracy 4",A2ACoordinateMenu,self.MenuMGRS_Accuracy,self,MenuGroup,RootMenu,4):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"MGRS Accuracy 5",A2ACoordinateMenu,self.MenuMGRS_Accuracy,self,MenuGroup,RootMenu,5):SetTime(MenuTime)
-end
-local text="Measures and Weights System"
-if _SETTINGS.MenuShort then
-text="Unit System"
-end
-local MetricsMenu=MENU_GROUP:New(MenuGroup,text,SettingsMenu):SetTime(MenuTime)
-if self:IsMetric()then
-local text="Imperial (Miles,Feet)"
-if _SETTINGS.MenuShort then
-text="Imperial"
-end
-MENU_GROUP_COMMAND:New(MenuGroup,text,MetricsMenu,self.MenuMWSystem,self,MenuGroup,RootMenu,false):SetTime(MenuTime)
-end
-if self:IsImperial()then
-local text="Metric (Kilometers,Meters)"
-if _SETTINGS.MenuShort then
-text="Metric"
-end
-MENU_GROUP_COMMAND:New(MenuGroup,text,MetricsMenu,self.MenuMWSystem,self,MenuGroup,RootMenu,true):SetTime(MenuTime)
-end
-local text="Messages and Reports"
-if _SETTINGS.MenuShort then
-text="Messages & Reports"
-end
-local MessagesMenu=MENU_GROUP:New(MenuGroup,text,SettingsMenu):SetTime(MenuTime)
-local UpdateMessagesMenu=MENU_GROUP:New(MenuGroup,"Update Messages",MessagesMenu):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"Off",UpdateMessagesMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Update,0):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"5 seconds",UpdateMessagesMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Update,5):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"10 seconds",UpdateMessagesMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Update,10):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"15 seconds",UpdateMessagesMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Update,15):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"30 seconds",UpdateMessagesMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Update,30):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"1 minute",UpdateMessagesMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Update,60):SetTime(MenuTime)
-local InformationMessagesMenu=MENU_GROUP:New(MenuGroup,"Information Messages",MessagesMenu):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"5 seconds",InformationMessagesMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Information,5):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"10 seconds",InformationMessagesMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Information,10):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"15 seconds",InformationMessagesMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Information,15):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"30 seconds",InformationMessagesMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Information,30):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"1 minute",InformationMessagesMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Information,60):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"2 minutes",InformationMessagesMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Information,120):SetTime(MenuTime)
-local BriefingReportsMenu=MENU_GROUP:New(MenuGroup,"Briefing Reports",MessagesMenu):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"15 seconds",BriefingReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Briefing,15):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"30 seconds",BriefingReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Briefing,30):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"1 minute",BriefingReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Briefing,60):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"2 minutes",BriefingReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Briefing,120):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"3 minutes",BriefingReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Briefing,180):SetTime(MenuTime)
-local OverviewReportsMenu=MENU_GROUP:New(MenuGroup,"Overview Reports",MessagesMenu):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"15 seconds",OverviewReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Overview,15):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"30 seconds",OverviewReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Overview,30):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"1 minute",OverviewReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Overview,60):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"2 minutes",OverviewReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Overview,120):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"3 minutes",OverviewReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.Overview,180):SetTime(MenuTime)
-local DetailedReportsMenu=MENU_GROUP:New(MenuGroup,"Detailed Reports",MessagesMenu):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"15 seconds",DetailedReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.DetailedReportsMenu,15):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"30 seconds",DetailedReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.DetailedReportsMenu,30):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"1 minute",DetailedReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.DetailedReportsMenu,60):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"2 minutes",DetailedReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.DetailedReportsMenu,120):SetTime(MenuTime)
-MENU_GROUP_COMMAND:New(MenuGroup,"3 minutes",DetailedReportsMenu,self.MenuMessageTimingsSystem,self,MenuGroup,RootMenu,MESSAGE.Type.DetailedReportsMenu,180):SetTime(MenuTime)
-SettingsMenu:Remove(MenuTime)
-return self
-end
-function SETTINGS:SetPlayerMenuOn()
-self.ShowPlayerMenu=true
-end
-function SETTINGS:SetPlayerMenuOff()
-self.ShowPlayerMenu=false
-end
-function SETTINGS:SetPlayerMenu(PlayerUnit)
-if _SETTINGS.ShowPlayerMenu==true then
-local PlayerGroup=PlayerUnit:GetGroup()
-local PlayerName=PlayerUnit:GetPlayerName()
-local PlayerNames=PlayerGroup:GetPlayerNames()
-local PlayerMenu=MENU_GROUP:New(PlayerGroup,'Settings "'..PlayerName..'"')
-self.PlayerMenu=PlayerMenu
-self:T(string.format("Setting menu for player %s",tostring(PlayerName)))
-local submenu=MENU_GROUP:New(PlayerGroup,"LL Accuracy",PlayerMenu)
-MENU_GROUP_COMMAND:New(PlayerGroup,"LL 0 Decimals",submenu,self.MenuGroupLL_DDM_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,0)
-MENU_GROUP_COMMAND:New(PlayerGroup,"LL 1 Decimal",submenu,self.MenuGroupLL_DDM_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,1)
-MENU_GROUP_COMMAND:New(PlayerGroup,"LL 2 Decimals",submenu,self.MenuGroupLL_DDM_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,2)
-MENU_GROUP_COMMAND:New(PlayerGroup,"LL 3 Decimals",submenu,self.MenuGroupLL_DDM_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,3)
-MENU_GROUP_COMMAND:New(PlayerGroup,"LL 4 Decimals",submenu,self.MenuGroupLL_DDM_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,4)
-local submenu=MENU_GROUP:New(PlayerGroup,"MGRS Accuracy",PlayerMenu)
-MENU_GROUP_COMMAND:New(PlayerGroup,"MRGS Accuracy 0",submenu,self.MenuGroupMGRS_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,0)
-MENU_GROUP_COMMAND:New(PlayerGroup,"MRGS Accuracy 1",submenu,self.MenuGroupMGRS_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,1)
-MENU_GROUP_COMMAND:New(PlayerGroup,"MRGS Accuracy 2",submenu,self.MenuGroupMGRS_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,2)
-MENU_GROUP_COMMAND:New(PlayerGroup,"MRGS Accuracy 3",submenu,self.MenuGroupMGRS_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,3)
-MENU_GROUP_COMMAND:New(PlayerGroup,"MRGS Accuracy 4",submenu,self.MenuGroupMGRS_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,4)
-MENU_GROUP_COMMAND:New(PlayerGroup,"MRGS Accuracy 5",submenu,self.MenuGroupMGRS_AccuracySystem,self,PlayerUnit,PlayerGroup,PlayerName,5)
-local text="A2G Coordinate System"
-if _SETTINGS.MenuShort then
-text="A2G Coordinates"
-end
-local A2GCoordinateMenu=MENU_GROUP:New(PlayerGroup,text,PlayerMenu)
-if not self:IsA2G_LL_DMS()or _SETTINGS.MenuStatic then
-local text="Lat/Lon Degree Min Sec (LL DMS)"
-if _SETTINGS.MenuShort then
-text="A2G LL DMS"
-end
-MENU_GROUP_COMMAND:New(PlayerGroup,text,A2GCoordinateMenu,self.MenuGroupA2GSystem,self,PlayerUnit,PlayerGroup,PlayerName,"LL DMS")
-end
-if not self:IsA2G_LL_DDM()or _SETTINGS.MenuStatic then
-local text="Lat/Lon Degree Dec Min (LL DDM)"
-if _SETTINGS.MenuShort then
-text="A2G LL DDM"
-end
-MENU_GROUP_COMMAND:New(PlayerGroup,text,A2GCoordinateMenu,self.MenuGroupA2GSystem,self,PlayerUnit,PlayerGroup,PlayerName,"LL DDM")
-end
-if not self:IsA2G_BR()or _SETTINGS.MenuStatic then
-local text="Bearing, Range (BR)"
-if _SETTINGS.MenuShort then
-text="A2G BR"
-end
-MENU_GROUP_COMMAND:New(PlayerGroup,text,A2GCoordinateMenu,self.MenuGroupA2GSystem,self,PlayerUnit,PlayerGroup,PlayerName,"BR")
-end
-if not self:IsA2G_MGRS()or _SETTINGS.MenuStatic then
-local text="Military Grid (MGRS)"
-if _SETTINGS.MenuShort then
-text="A2G MGRS"
-end
-MENU_GROUP_COMMAND:New(PlayerGroup,text,A2GCoordinateMenu,self.MenuGroupA2GSystem,self,PlayerUnit,PlayerGroup,PlayerName,"MGRS")
-end
-local text="A2A Coordinate System"
-if _SETTINGS.MenuShort then
-text="A2A Coordinates"
-end
-local A2ACoordinateMenu=MENU_GROUP:New(PlayerGroup,text,PlayerMenu)
-if not self:IsA2A_LL_DMS()or _SETTINGS.MenuStatic then
-local text="Lat/Lon Degree Min Sec (LL DMS)"
-if _SETTINGS.MenuShort then
-text="A2A LL DMS"
-end
-MENU_GROUP_COMMAND:New(PlayerGroup,text,A2ACoordinateMenu,self.MenuGroupA2GSystem,self,PlayerUnit,PlayerGroup,PlayerName,"LL DMS")
-end
-if not self:IsA2A_LL_DDM()or _SETTINGS.MenuStatic then
-local text="Lat/Lon Degree Dec Min (LL DDM)"
-if _SETTINGS.MenuShort then
-text="A2A LL DDM"
-end
-MENU_GROUP_COMMAND:New(PlayerGroup,text,A2ACoordinateMenu,self.MenuGroupA2ASystem,self,PlayerUnit,PlayerGroup,PlayerName,"LL DDM")
-end
-if not self:IsA2A_BULLS()or _SETTINGS.MenuStatic then
-local text="Bullseye (BULLS)"
-if _SETTINGS.MenuShort then
-text="A2A BULLS"
-end
-MENU_GROUP_COMMAND:New(PlayerGroup,text,A2ACoordinateMenu,self.MenuGroupA2ASystem,self,PlayerUnit,PlayerGroup,PlayerName,"BULLS")
-end
-if not self:IsA2A_BRAA()or _SETTINGS.MenuStatic then
-local text="Bearing Range Altitude Aspect (BRAA)"
-if _SETTINGS.MenuShort then
-text="A2A BRAA"
-end
-MENU_GROUP_COMMAND:New(PlayerGroup,text,A2ACoordinateMenu,self.MenuGroupA2ASystem,self,PlayerUnit,PlayerGroup,PlayerName,"BRAA")
-end
-if not self:IsA2A_MGRS()or _SETTINGS.MenuStatic then
-local text="Military Grid (MGRS)"
-if _SETTINGS.MenuShort then
-text="A2A MGRS"
-end
-MENU_GROUP_COMMAND:New(PlayerGroup,text,A2ACoordinateMenu,self.MenuGroupA2ASystem,self,PlayerUnit,PlayerGroup,PlayerName,"MGRS")
-end
-local text="Measures and Weights System"
-if _SETTINGS.MenuShort then
-text="Unit System"
-end
-local MetricsMenu=MENU_GROUP:New(PlayerGroup,text,PlayerMenu)
-if self:IsMetric()or _SETTINGS.MenuStatic then
-local text="Imperial (Miles,Feet)"
-if _SETTINGS.MenuShort then
-text="Imperial"
-end
-MENU_GROUP_COMMAND:New(PlayerGroup,text,MetricsMenu,self.MenuGroupMWSystem,self,PlayerUnit,PlayerGroup,PlayerName,false)
-end
-if self:IsImperial()or _SETTINGS.MenuStatic then
-local text="Metric (Kilometers,Meters)"
-if _SETTINGS.MenuShort then
-text="Metric"
-end
-MENU_GROUP_COMMAND:New(PlayerGroup,text,MetricsMenu,self.MenuGroupMWSystem,self,PlayerUnit,PlayerGroup,PlayerName,true)
-end
-local text="Messages and Reports"
-if _SETTINGS.MenuShort then
-text="Messages & Reports"
-end
-local MessagesMenu=MENU_GROUP:New(PlayerGroup,text,PlayerMenu)
-local UpdateMessagesMenu=MENU_GROUP:New(PlayerGroup,"Update Messages",MessagesMenu)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Updates Off",UpdateMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Update,0)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Updates 5 sec",UpdateMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Update,5)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Updates 10 sec",UpdateMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Update,10)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Updates 15 sec",UpdateMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Update,15)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Updates 30 sec",UpdateMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Update,30)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Updates 1 min",UpdateMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Update,60)
-local InformationMessagesMenu=MENU_GROUP:New(PlayerGroup,"Info Messages",MessagesMenu)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Info 5 sec",InformationMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Information,5)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Info 10 sec",InformationMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Information,10)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Info 15 sec",InformationMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Information,15)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Info 30 sec",InformationMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Information,30)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Info 1 min",InformationMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Information,60)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Info 2 min",InformationMessagesMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Information,120)
-local BriefingReportsMenu=MENU_GROUP:New(PlayerGroup,"Briefing Reports",MessagesMenu)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Brief 15 sec",BriefingReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Briefing,15)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Brief 30 sec",BriefingReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Briefing,30)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Brief 1 min",BriefingReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Briefing,60)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Brief 2 min",BriefingReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Briefing,120)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Brief 3 min",BriefingReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Briefing,180)
-local OverviewReportsMenu=MENU_GROUP:New(PlayerGroup,"Overview Reports",MessagesMenu)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Overview 15 sec",OverviewReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Overview,15)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Overview 30 sec",OverviewReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Overview,30)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Overview 1 min",OverviewReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Overview,60)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Overview 2 min",OverviewReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Overview,120)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Overview 3 min",OverviewReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.Overview,180)
-local DetailedReportsMenu=MENU_GROUP:New(PlayerGroup,"Detailed Reports",MessagesMenu)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Detailed 15 sec",DetailedReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.DetailedReportsMenu,15)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Detailed 30 sec",DetailedReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.DetailedReportsMenu,30)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Detailed 1 min",DetailedReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.DetailedReportsMenu,60)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Detailed 2 min",DetailedReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.DetailedReportsMenu,120)
-MENU_GROUP_COMMAND:New(PlayerGroup,"Detailed 3 min",DetailedReportsMenu,self.MenuGroupMessageTimingsSystem,self,PlayerUnit,PlayerGroup,PlayerName,MESSAGE.Type.DetailedReportsMenu,180)
-end
-return self
-end
-function SETTINGS:RemovePlayerMenu(PlayerUnit)
-if self.PlayerMenu then
-self.PlayerMenu:Remove()
-self.PlayerMenu=nil
-end
-return self
-end
-function SETTINGS:A2GMenuSystem(MenuGroup,RootMenu,A2GSystem)
-self.A2GSystem=A2GSystem
-MESSAGE:New(string.format("Settings: Default A2G coordinate system set to %s for all players!",A2GSystem),5):ToAll()
-self:SetSystemMenu(MenuGroup,RootMenu)
-end
-function SETTINGS:A2AMenuSystem(MenuGroup,RootMenu,A2ASystem)
-self.A2ASystem=A2ASystem
-MESSAGE:New(string.format("Settings: Default A2A coordinate system set to %s for all players!",A2ASystem),5):ToAll()
-self:SetSystemMenu(MenuGroup,RootMenu)
-end
-function SETTINGS:MenuLL_DDM_Accuracy(MenuGroup,RootMenu,LL_Accuracy)
-self.LL_Accuracy=LL_Accuracy
-MESSAGE:New(string.format("Settings: Default LL accuracy set to %s for all players!",LL_Accuracy),5):ToAll()
-self:SetSystemMenu(MenuGroup,RootMenu)
-end
-function SETTINGS:MenuMGRS_Accuracy(MenuGroup,RootMenu,MGRS_Accuracy)
-self.MGRS_Accuracy=MGRS_Accuracy
-MESSAGE:New(string.format("Settings: Default MGRS accuracy set to %s for all players!",MGRS_Accuracy),5):ToAll()
-self:SetSystemMenu(MenuGroup,RootMenu)
-end
-function SETTINGS:MenuMWSystem(MenuGroup,RootMenu,MW)
-self.Metric=MW
-MESSAGE:New(string.format("Settings: Default measurement format set to %s for all players!",MW and"Metric"or"Imperial"),5):ToAll()
-self:SetSystemMenu(MenuGroup,RootMenu)
-end
-function SETTINGS:MenuMessageTimingsSystem(MenuGroup,RootMenu,MessageType,MessageTime)
-self:SetMessageTime(MessageType,MessageTime)
-MESSAGE:New(string.format("Settings: Default message time set for %s to %d.",MessageType,MessageTime),5):ToAll()
-end
-do
-function SETTINGS:MenuGroupA2GSystem(PlayerUnit,PlayerGroup,PlayerName,A2GSystem)
-self.A2GSystem=A2GSystem
-MESSAGE:New(string.format("Settings: A2G format set to %s for player %s.",A2GSystem,PlayerName),5):ToGroup(PlayerGroup)
-if _SETTINGS.MenuStatic==false then
-self:RemovePlayerMenu(PlayerUnit)
-self:SetPlayerMenu(PlayerUnit)
-end
-end
-function SETTINGS:MenuGroupA2ASystem(PlayerUnit,PlayerGroup,PlayerName,A2ASystem)
-self.A2ASystem=A2ASystem
-MESSAGE:New(string.format("Settings: A2A format set to %s for player %s.",A2ASystem,PlayerName),5):ToGroup(PlayerGroup)
-if _SETTINGS.MenuStatic==false then
-self:RemovePlayerMenu(PlayerUnit)
-self:SetPlayerMenu(PlayerUnit)
-end
-end
-function SETTINGS:MenuGroupLL_DDM_AccuracySystem(PlayerUnit,PlayerGroup,PlayerName,LL_Accuracy)
-self.LL_Accuracy=LL_Accuracy
-MESSAGE:New(string.format("Settings: LL format accuracy set to %d decimal places for player %s.",LL_Accuracy,PlayerName),5):ToGroup(PlayerGroup)
-if _SETTINGS.MenuStatic==false then
-self:RemovePlayerMenu(PlayerUnit)
-self:SetPlayerMenu(PlayerUnit)
-end
-end
-function SETTINGS:MenuGroupMGRS_AccuracySystem(PlayerUnit,PlayerGroup,PlayerName,MGRS_Accuracy)
-self.MGRS_Accuracy=MGRS_Accuracy
-MESSAGE:New(string.format("Settings: MGRS format accuracy set to %d for player %s.",MGRS_Accuracy,PlayerName),5):ToGroup(PlayerGroup)
-if _SETTINGS.MenuStatic==false then
-self:RemovePlayerMenu(PlayerUnit)
-self:SetPlayerMenu(PlayerUnit)
-end
-end
-function SETTINGS:MenuGroupMWSystem(PlayerUnit,PlayerGroup,PlayerName,MW)
-self.Metric=MW
-MESSAGE:New(string.format("Settings: Measurement format set to %s for player %s.",MW and"Metric"or"Imperial",PlayerName),5):ToGroup(PlayerGroup)
-if _SETTINGS.MenuStatic==false then
-self:RemovePlayerMenu(PlayerUnit)
-self:SetPlayerMenu(PlayerUnit)
-end
-end
-function SETTINGS:MenuGroupMessageTimingsSystem(PlayerUnit,PlayerGroup,PlayerName,MessageType,MessageTime)
-self:SetMessageTime(MessageType,MessageTime)
-MESSAGE:New(string.format("Settings: Default message time set for %s to %d.",MessageType,MessageTime),5):ToGroup(PlayerGroup)
-end
-end
-function SETTINGS:SetEraWWII()
-self.Era=SETTINGS.__Enum.Era.WWII
-end
-function SETTINGS:SetEraKorea()
-self.Era=SETTINGS.__Enum.Era.Korea
-end
-function SETTINGS:SetEraCold()
-self.Era=SETTINGS.__Enum.Era.Cold
-end
-function SETTINGS:SetEraModern()
-self.Era=SETTINGS.__Enum.Era.Modern
-end
-end
-SPAWN={
-ClassName="SPAWN",
-SpawnTemplatePrefix=nil,
-SpawnAliasPrefix=nil,
-}
-SPAWN.Takeoff={
-Air=1,
-Runway=2,
-Hot=3,
-Cold=4,
-}
-function SPAWN:New(SpawnTemplatePrefix)
-local self=BASE:Inherit(self,BASE:New())
-self:F({SpawnTemplatePrefix})
-local TemplateGroup=GROUP:FindByName(SpawnTemplatePrefix)
-if TemplateGroup then
-self.SpawnTemplatePrefix=SpawnTemplatePrefix
-self.SpawnIndex=0
-self.SpawnCount=0
-self.AliveUnits=0
-self.SpawnIsScheduled=false
-self.SpawnTemplate=self._GetTemplate(self,SpawnTemplatePrefix)
-self.Repeat=false
-self.UnControlled=false
-self.SpawnInitLimit=false
-self.SpawnMaxUnitsAlive=0
-self.SpawnMaxGroups=0
-self.SpawnRandomize=false
-self.SpawnVisible=false
-self.AIOnOff=true
-self.SpawnUnControlled=false
-self.SpawnInitKeepUnitNames=false
-self.DelayOnOff=false
-self.SpawnGrouping=nil
-self.SpawnInitLivery=nil
-self.SpawnInitSkill=nil
-self.SpawnInitFreq=nil
-self.SpawnInitModu=nil
-self.SpawnInitRadio=nil
-self.SpawnInitModex=nil
-self.SpawnInitModexPrefix=nil
-self.SpawnInitModexPostfix=nil
-self.SpawnInitAirbase=nil
-self.TweakedTemplate=false
-self.SpawnRandomCallsign=false
-self.SpawnGroups={}
-else
-error("SPAWN:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '"..SpawnTemplatePrefix.."'")
-end
-self:SetEventPriority(5)
-self.SpawnHookScheduler=SCHEDULER:New(nil)
-return self
-end
-function SPAWN:NewWithAlias(SpawnTemplatePrefix,SpawnAliasPrefix)
-local self=BASE:Inherit(self,BASE:New())
-self:F({SpawnTemplatePrefix,SpawnAliasPrefix})
-local TemplateGroup=GROUP:FindByName(SpawnTemplatePrefix)
-if TemplateGroup then
-self.SpawnTemplatePrefix=SpawnTemplatePrefix
-self.SpawnAliasPrefix=SpawnAliasPrefix
-self.SpawnIndex=0
-self.SpawnCount=0
-self.AliveUnits=0
-self.SpawnIsScheduled=false
-self.SpawnTemplate=self._GetTemplate(self,SpawnTemplatePrefix)
-self.Repeat=false
-self.UnControlled=false
-self.SpawnInitLimit=false
-self.SpawnMaxUnitsAlive=0
-self.SpawnMaxGroups=0
-self.SpawnRandomize=false
-self.SpawnVisible=false
-self.AIOnOff=true
-self.SpawnUnControlled=false
-self.SpawnInitKeepUnitNames=false
-self.DelayOnOff=false
-self.SpawnGrouping=nil
-self.SpawnInitLivery=nil
-self.SpawnInitSkill=nil
-self.SpawnInitFreq=nil
-self.SpawnInitModu=nil
-self.SpawnInitRadio=nil
-self.SpawnInitModex=nil
-self.SpawnInitModexPrefix=nil
-self.SpawnInitModexPostfix=nil
-self.SpawnInitAirbase=nil
-self.TweakedTemplate=false
-self.SpawnGroups={}
-else
-error("SPAWN:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '"..SpawnTemplatePrefix.."'")
-end
-self:SetEventPriority(5)
-self.SpawnHookScheduler=SCHEDULER:New(nil)
-return self
-end
-function SPAWN:NewFromTemplate(SpawnTemplate,SpawnTemplatePrefix,SpawnAliasPrefix,NoMooseNamingPostfix)
-local self=BASE:Inherit(self,BASE:New())
-self:F({SpawnTemplate,SpawnTemplatePrefix,SpawnAliasPrefix})
-if SpawnTemplatePrefix==nil or SpawnTemplatePrefix==""then
-BASE:I("ERROR: in function NewFromTemplate, required parameter SpawnTemplatePrefix is not set")
-return nil
-end
-if SpawnTemplate then
-self.SpawnTemplate=SpawnTemplate
-self.SpawnTemplatePrefix=SpawnTemplatePrefix
-self.SpawnAliasPrefix=SpawnAliasPrefix or SpawnTemplatePrefix
-self.SpawnTemplate.name=SpawnTemplatePrefix
-self.SpawnIndex=0
-self.SpawnCount=0
-self.AliveUnits=0
-self.SpawnIsScheduled=false
-self.Repeat=false
-self.UnControlled=false
-self.SpawnInitLimit=false
-self.SpawnMaxUnitsAlive=0
-self.SpawnMaxGroups=0
-self.SpawnRandomize=false
-self.SpawnVisible=false
-self.AIOnOff=true
-self.SpawnUnControlled=false
-self.SpawnInitKeepUnitNames=false
-self.DelayOnOff=false
-self.Grouping=nil
-self.SpawnInitLivery=nil
-self.SpawnInitSkill=nil
-self.SpawnInitFreq=nil
-self.SpawnInitModu=nil
-self.SpawnInitRadio=nil
-self.SpawnInitModex=nil
-self.SpawnInitModexPrefix=nil
-self.SpawnInitModexPostfix=nil
-self.SpawnInitAirbase=nil
-self.TweakedTemplate=true
-self.MooseNameing=true
-if NoMooseNamingPostfix==true then
-self.MooseNameing=false
-end
-self.SpawnGroups={}
-else
-error("There is no template provided for SpawnTemplatePrefix = '"..SpawnTemplatePrefix.."'")
-end
-self:SetEventPriority(5)
-self.SpawnHookScheduler=SCHEDULER:New(nil)
-return self
-end
-function SPAWN:InitLimit(SpawnMaxUnitsAlive,SpawnMaxGroups)
-self:F({self.SpawnTemplatePrefix,SpawnMaxUnitsAlive,SpawnMaxGroups})
-self.SpawnInitLimit=true
-self.SpawnMaxUnitsAlive=SpawnMaxUnitsAlive
-self.SpawnMaxGroups=SpawnMaxGroups
-for SpawnGroupID=1,self.SpawnMaxGroups do
-self:_InitializeSpawnGroups(SpawnGroupID)
-end
-return self
-end
-function SPAWN:InitKeepUnitNames(KeepUnitNames)
-self:F()
-self.SpawnInitKeepUnitNames=KeepUnitNames or true
-return self
-end
-function SPAWN:InitLateActivated(LateActivated)
-self:F()
-self.LateActivated=LateActivated or true
-return self
-end
-function SPAWN:InitAirbase(AirbaseName,Takeoff,TerminalType)
-self:F()
-self.SpawnInitAirbase=AIRBASE:FindByName(AirbaseName)
-self.SpawnInitTakeoff=Takeoff or SPAWN.Takeoff.Hot
-self.SpawnInitTerminalType=TerminalType
-return self
-end
-function SPAWN:InitHeading(HeadingMin,HeadingMax)
-self:F()
-self.SpawnInitHeadingMin=HeadingMin
-self.SpawnInitHeadingMax=HeadingMax
-return self
-end
-function SPAWN:InitGroupHeading(HeadingMin,HeadingMax,unitVar)
-self:F({HeadingMin=HeadingMin,HeadingMax=HeadingMax,unitVar=unitVar})
-self.SpawnInitGroupHeadingMin=HeadingMin
-self.SpawnInitGroupHeadingMax=HeadingMax
-self.SpawnInitGroupUnitVar=unitVar
-return self
-end
-function SPAWN:InitCoalition(Coalition)
-self:F({coalition=Coalition})
-self.SpawnInitCoalition=Coalition
-return self
-end
-function SPAWN:InitCountry(Country)
-self:F()
-self.SpawnInitCountry=Country
-return self
-end
-function SPAWN:InitCategory(Category)
-self:F()
-self.SpawnInitCategory=Category
-return self
-end
-function SPAWN:InitLivery(Livery)
-self:F({livery=Livery})
-self.SpawnInitLivery=Livery
-return self
-end
-function SPAWN:InitSkill(Skill)
-self:F({skill=Skill})
-if Skill:lower()=="average"then
-self.SpawnInitSkill="Average"
-elseif Skill:lower()=="good"then
-self.SpawnInitSkill="Good"
-elseif Skill:lower()=="excellent"then
-self.SpawnInitSkill="Excellent"
-elseif Skill:lower()=="random"then
-self.SpawnInitSkill="Random"
-else
-self.SpawnInitSkill="High"
-end
-return self
-end
-function SPAWN:InitRadioCommsOnOff(switch)
-self:F({switch=switch})
-self.SpawnInitRadio=switch or true
-return self
-end
-function SPAWN:InitRadioFrequency(frequency)
-self:F({frequency=frequency})
-self.SpawnInitFreq=frequency
-return self
-end
-function SPAWN:InitRadioModulation(modulation)
-self:F({modulation=modulation})
-if modulation and modulation:lower()=="fm"then
-self.SpawnInitModu=radio.modulation.FM
-else
-self.SpawnInitModu=radio.modulation.AM
-end
-return self
-end
-function SPAWN:InitModex(modex,prefix,postfix)
-if modex then
-self.SpawnInitModex=tonumber(modex)
-end
-self.SpawnInitModexPrefix=prefix
-self.SpawnInitModexPostfix=postfix
-return self
-end
-function SPAWN:InitRandomizeRoute(SpawnStartPoint,SpawnEndPoint,SpawnRadius,SpawnHeight)
-self:F({self.SpawnTemplatePrefix,SpawnStartPoint,SpawnEndPoint,SpawnRadius,SpawnHeight})
-self.SpawnRandomizeRoute=true
-self.SpawnRandomizeRouteStartPoint=SpawnStartPoint
-self.SpawnRandomizeRouteEndPoint=SpawnEndPoint
-self.SpawnRandomizeRouteRadius=SpawnRadius
-self.SpawnRandomizeRouteHeight=SpawnHeight
-for GroupID=1,self.SpawnMaxGroups do
-self:_RandomizeRoute(GroupID)
-end
-return self
-end
-function SPAWN:InitRandomizePosition(RandomizePosition,OuterRadius,InnerRadius)
-self:F({self.SpawnTemplatePrefix,RandomizePosition,OuterRadius,InnerRadius})
-self.SpawnRandomizePosition=RandomizePosition or false
-self.SpawnRandomizePositionOuterRadius=OuterRadius or 0
-self.SpawnRandomizePositionInnerRadius=InnerRadius or 0
-for GroupID=1,self.SpawnMaxGroups do
-self:_RandomizeRoute(GroupID)
-end
-return self
-end
-function SPAWN:InitRandomizeUnits(RandomizeUnits,OuterRadius,InnerRadius)
-self:F({self.SpawnTemplatePrefix,RandomizeUnits,OuterRadius,InnerRadius})
-self.SpawnRandomizeUnits=RandomizeUnits or false
-self.SpawnOuterRadius=OuterRadius or 0
-self.SpawnInnerRadius=InnerRadius or 0
-for GroupID=1,self.SpawnMaxGroups do
-self:_RandomizeRoute(GroupID)
-end
-return self
-end
-function SPAWN:InitSetUnitRelativePositions(Positions)
-self:F({self.SpawnTemplatePrefix,Positions})
-self.SpawnUnitsWithRelativePositions=true
-self.UnitsRelativePositions=Positions
-return self
-end
-function SPAWN:InitSetUnitAbsolutePositions(Positions)
-self:F({self.SpawnTemplatePrefix,Positions})
-self.SpawnUnitsWithAbsolutePositions=true
-self.UnitsAbsolutePositions=Positions
-return self
-end
-function SPAWN:InitRandomizeTemplate(SpawnTemplatePrefixTable)
-self:F({self.SpawnTemplatePrefix,SpawnTemplatePrefixTable})
-local temptable={}
-for _,_temp in pairs(SpawnTemplatePrefixTable)do
-temptable[#temptable+1]=_temp
-end
-self.SpawnTemplatePrefixTable=UTILS.ShuffleTable(temptable)
-self.SpawnRandomizeTemplate=true
-for SpawnGroupID=1,self.SpawnMaxGroups do
-self:_RandomizeTemplate(SpawnGroupID)
-end
-return self
-end
-function SPAWN:InitRandomizeTemplateSet(SpawnTemplateSet)
-self:F({self.SpawnTemplatePrefix})
-local setnames=SpawnTemplateSet:GetSetNames()
-self:InitRandomizeTemplate(setnames)
-return self
-end
-function SPAWN:InitRandomizeTemplatePrefixes(SpawnTemplatePrefixes)
-self:F({self.SpawnTemplatePrefix})
-local SpawnTemplateSet=SET_GROUP:New():FilterPrefixes(SpawnTemplatePrefixes):FilterOnce()
-self:InitRandomizeTemplateSet(SpawnTemplateSet)
-return self
-end
-function SPAWN:InitGrouping(Grouping)
-self:F({self.SpawnTemplatePrefix,Grouping})
-self.SpawnGrouping=Grouping
-return self
-end
-function SPAWN:InitRandomizeZones(SpawnZoneTable)
-self:F({self.SpawnTemplatePrefix,SpawnZoneTable})
-local temptable={}
-for _,_temp in pairs(SpawnZoneTable)do
-temptable[#temptable+1]=_temp
-end
-self.SpawnZoneTable=UTILS.ShuffleTable(temptable)
-self.SpawnRandomizeZones=true
-for SpawnGroupID=1,self.SpawnMaxGroups do
-self:_RandomizeZones(SpawnGroupID)
-end
-return self
-end
-function SPAWN:InitRandomizeCallsign()
-self.SpawnRandomCallsign=true
-return self
-end
-function SPAWN:InitCallSign(ID,Name,Minor,Major)
-self.SpawnInitCallSign=true
-self.SpawnInitCallSignID=ID or 1
-self.SpawnInitCallSignMinor=Minor or 1
-self.SpawnInitCallSignMajor=Major or 1
-self.SpawnInitCallSignName=string.lower(Name)or"enfield"
-return self
-end
-function SPAWN:InitPositionCoordinate(Coordinate)
-self:T({self.SpawnTemplatePrefix,Coordinate:GetVec2()})
-self:InitPositionVec2(Coordinate:GetVec2())
-return self
-end
-function SPAWN:InitPositionVec2(Vec2)
-self:T({self.SpawnTemplatePrefix,Vec2})
-self.SpawnInitPosition=Vec2
-self.SpawnFromNewPosition=true
-self:I("MaxGroups:"..self.SpawnMaxGroups)
-for SpawnGroupID=1,self.SpawnMaxGroups do
-self:_SetInitialPosition(SpawnGroupID)
-end
-return self
-end
-function SPAWN:InitRepeat()
-self:F({self.SpawnTemplatePrefix,self.SpawnIndex})
-self.Repeat=true
-self.RepeatOnEngineShutDown=false
-self.RepeatOnLanding=true
-return self
-end
-function SPAWN:InitRepeatOnLanding()
-self:F({self.SpawnTemplatePrefix})
-self:InitRepeat()
-self.RepeatOnEngineShutDown=false
-self.RepeatOnLanding=true
-return self
-end
-function SPAWN:InitRepeatOnEngineShutDown()
-self:F({self.SpawnTemplatePrefix})
-self:InitRepeat()
-self.RepeatOnEngineShutDown=true
-self.RepeatOnLanding=false
-return self
-end
-function SPAWN:InitCleanUp(SpawnCleanUpInterval)
-self:F({self.SpawnTemplatePrefix,SpawnCleanUpInterval})
-self.SpawnCleanUpInterval=SpawnCleanUpInterval
-self.SpawnCleanUpTimeStamps={}
-local SpawnGroup,SpawnCursor=self:GetFirstAliveGroup()
-self:T({"CleanUp Scheduler:",SpawnGroup})
-self.CleanUpScheduler=SCHEDULER:New(self,self._SpawnCleanUpScheduler,{},1,SpawnCleanUpInterval,0.2)
-return self
-end
-function SPAWN:InitArray(SpawnAngle,SpawnWidth,SpawnDeltaX,SpawnDeltaY)
-self:F({self.SpawnTemplatePrefix,SpawnAngle,SpawnWidth,SpawnDeltaX,SpawnDeltaY})
-self.SpawnVisible=true
-local SpawnX=0
-local SpawnY=0
-local SpawnXIndex=0
-local SpawnYIndex=0
-for SpawnGroupID=1,self.SpawnMaxGroups do
-self:T({SpawnX,SpawnY,SpawnXIndex,SpawnYIndex})
-self.SpawnGroups[SpawnGroupID].Visible=true
-self.SpawnGroups[SpawnGroupID].Spawned=false
-SpawnXIndex=SpawnXIndex+1
-if SpawnWidth and SpawnWidth~=0 then
-if SpawnXIndex>=SpawnWidth then
-SpawnXIndex=0
-SpawnYIndex=SpawnYIndex+1
-end
-end
-local SpawnRootX=self.SpawnGroups[SpawnGroupID].SpawnTemplate.x
-local SpawnRootY=self.SpawnGroups[SpawnGroupID].SpawnTemplate.y
-self:_TranslateRotate(SpawnGroupID,SpawnRootX,SpawnRootY,SpawnX,SpawnY,SpawnAngle)
-self.SpawnGroups[SpawnGroupID].SpawnTemplate.lateActivation=true
-self.SpawnGroups[SpawnGroupID].SpawnTemplate.visible=true
-self.SpawnGroups[SpawnGroupID].Visible=true
-self:HandleEvent(EVENTS.Birth,self._OnBirth)
-self:HandleEvent(EVENTS.Dead,self._OnDeadOrCrash)
-self:HandleEvent(EVENTS.Crash,self._OnDeadOrCrash)
-self:HandleEvent(EVENTS.RemoveUnit,self._OnDeadOrCrash)
-if self.Repeat then
-self:HandleEvent(EVENTS.Takeoff,self._OnTakeOff)
-self:HandleEvent(EVENTS.Land,self._OnLand)
-end
-if self.RepeatOnEngineShutDown then
-self:HandleEvent(EVENTS.EngineShutdown,self._OnEngineShutDown)
-end
-self.SpawnGroups[SpawnGroupID].Group=_DATABASE:Spawn(self.SpawnGroups[SpawnGroupID].SpawnTemplate)
-SpawnX=SpawnXIndex*SpawnDeltaX
-SpawnY=SpawnYIndex*SpawnDeltaY
-end
-return self
-end
-do
-function SPAWN:InitAIOnOff(AIOnOff)
-self.AIOnOff=AIOnOff
-return self
-end
-function SPAWN:InitAIOn()
-return self:InitAIOnOff(true)
-end
-function SPAWN:InitAIOff()
-return self:InitAIOnOff(false)
-end
-end
-do
-function SPAWN:InitDelayOnOff(DelayOnOff)
-self.DelayOnOff=DelayOnOff
-return self
-end
-function SPAWN:InitDelayOn()
-return self:InitDelayOnOff(true)
-end
-function SPAWN:InitDelayOff()
-return self:InitDelayOnOff(false)
-end
-end
-function SPAWN:Spawn()
-self:F({self.SpawnTemplatePrefix,self.SpawnIndex,self.AliveUnits})
-if self.SpawnInitAirbase then
-return self:SpawnAtAirbase(self.SpawnInitAirbase,self.SpawnInitTakeoff,nil,self.SpawnInitTerminalType)
-else
-return self:SpawnWithIndex(self.SpawnIndex+1)
-end
-end
-function SPAWN:ReSpawn(SpawnIndex)
-self:F({self.SpawnTemplatePrefix,SpawnIndex})
-if not SpawnIndex then
-SpawnIndex=1
-end
-local SpawnGroup=self:GetGroupFromIndex(SpawnIndex)
-local WayPoints=SpawnGroup and SpawnGroup.WayPoints or nil
-if SpawnGroup then
-local SpawnDCSGroup=SpawnGroup:GetDCSObject()
-if SpawnDCSGroup then
-SpawnGroup:Destroy()
-end
-end
-local SpawnGroup=self:SpawnWithIndex(SpawnIndex)
-if SpawnGroup and WayPoints then
-SpawnGroup:WayPointInitialize(WayPoints)
-SpawnGroup:WayPointExecute(1,5)
-end
-if SpawnGroup.ReSpawnFunction then
-SpawnGroup:ReSpawnFunction()
-end
-SpawnGroup:ResetEvents()
-return SpawnGroup
-end
-function SPAWN:SetSpawnIndex(SpawnIndex)
-self.SpawnIndex=SpawnIndex or 0
-end
-function SPAWN:SpawnWithIndex(SpawnIndex,NoBirth)
-self:F2({SpawnTemplatePrefix=self.SpawnTemplatePrefix,SpawnIndex=SpawnIndex,AliveUnits=self.AliveUnits,SpawnMaxGroups=self.SpawnMaxGroups})
-if self:_GetSpawnIndex(SpawnIndex)then
-if self.SpawnFromNewPosition then
-self:_SetInitialPosition(SpawnIndex)
-end
-if self.SpawnGroups[self.SpawnIndex].Visible then
-self.SpawnGroups[self.SpawnIndex].Group:Activate()
-else
-local SpawnTemplate=self.SpawnGroups[self.SpawnIndex].SpawnTemplate
-self:T(SpawnTemplate.name)
-if SpawnTemplate then
-local PointVec3=POINT_VEC3:New(SpawnTemplate.route.points[1].x,SpawnTemplate.route.points[1].alt,SpawnTemplate.route.points[1].y)
-self:T({"Current point of ",self.SpawnTemplatePrefix,PointVec3})
-if self.SpawnRandomizePosition then
-local RandomVec2=PointVec3:GetRandomVec2InRadius(self.SpawnRandomizePositionOuterRadius,self.SpawnRandomizePositionInnerRadius)
-local CurrentX=SpawnTemplate.units[1].x
-local CurrentY=SpawnTemplate.units[1].y
-SpawnTemplate.x=RandomVec2.x
-SpawnTemplate.y=RandomVec2.y
-for UnitID=1,#SpawnTemplate.units do
-SpawnTemplate.units[UnitID].x=SpawnTemplate.units[UnitID].x+(RandomVec2.x-CurrentX)
-SpawnTemplate.units[UnitID].y=SpawnTemplate.units[UnitID].y+(RandomVec2.y-CurrentY)
-self:T('SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y)
-end
-end
-if self.SpawnRandomizeUnits then
-for UnitID=1,#SpawnTemplate.units do
-local RandomVec2=PointVec3:GetRandomVec2InRadius(self.SpawnOuterRadius,self.SpawnInnerRadius)
-SpawnTemplate.units[UnitID].x=RandomVec2.x
-SpawnTemplate.units[UnitID].y=RandomVec2.y
-self:T('SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y)
-end
-end
-local function _Heading(courseDeg)
-local h
-if courseDeg<=180 then
-h=math.rad(courseDeg)
-else
-h=-math.rad(360-courseDeg)
-end
-return h
-end
-local Rad180=math.rad(180)
-local function _HeadingRad(courseRad)
-if courseRad<=Rad180 then
-return courseRad
-else
-return-((2*Rad180)-courseRad)
-end
-end
-local function _RandomInRange(min,max)
-if min and max then
-return min+(math.random()*(max-min))
-else
-return min
-end
-end
-if self.SpawnInitGroupHeadingMin and#SpawnTemplate.units>0 then
-local pivotX=SpawnTemplate.units[1].x
-local pivotY=SpawnTemplate.units[1].y
-local headingRad=math.rad(_RandomInRange(self.SpawnInitGroupHeadingMin or 0,self.SpawnInitGroupHeadingMax))
-local cosHeading=math.cos(headingRad)
-local sinHeading=math.sin(headingRad)
-local unitVarRad=math.rad(self.SpawnInitGroupUnitVar or 0)
-for UnitID=1,#SpawnTemplate.units do
-if UnitID>1 then
-local unitXOff=SpawnTemplate.units[UnitID].x-pivotX
-local unitYOff=SpawnTemplate.units[UnitID].y-pivotY
-SpawnTemplate.units[UnitID].x=pivotX+(unitXOff*cosHeading)-(unitYOff*sinHeading)
-SpawnTemplate.units[UnitID].y=pivotY+(unitYOff*cosHeading)+(unitXOff*sinHeading)
-end
-local unitHeading=SpawnTemplate.units[UnitID].heading+headingRad
-SpawnTemplate.units[UnitID].heading=_HeadingRad(_RandomInRange(unitHeading-unitVarRad,unitHeading+unitVarRad))
-SpawnTemplate.units[UnitID].psi=-SpawnTemplate.units[UnitID].heading
-end
-end
-if self.SpawnInitHeadingMin then
-for UnitID=1,#SpawnTemplate.units do
-SpawnTemplate.units[UnitID].heading=_Heading(_RandomInRange(self.SpawnInitHeadingMin,self.SpawnInitHeadingMax))
-SpawnTemplate.units[UnitID].psi=-SpawnTemplate.units[UnitID].heading
-end
-end
-if self.SpawnUnitsWithRelativePositions and self.UnitsRelativePositions then
-local BaseX=SpawnTemplate.units[1].x or 0
-local BaseY=SpawnTemplate.units[1].y or 0
-local BaseZ=SpawnTemplate.units[1].z or 0
-for UnitID=1,#SpawnTemplate.units do
-if self.UnitsRelativePositions[UnitID].heading then
-SpawnTemplate.units[UnitID].heading=math.rad(self.UnitsRelativePositions[UnitID].heading or 0)
-end
-SpawnTemplate.units[UnitID].x=BaseX+(self.UnitsRelativePositions[UnitID].x or 0)
-SpawnTemplate.units[UnitID].y=BaseY+(self.UnitsRelativePositions[UnitID].y or 0)
-if self.UnitsRelativePositions[UnitID].z then
-SpawnTemplate.units[UnitID].z=BaseZ+(self.UnitsRelativePositions[UnitID].z or 0)
-end
-end
-end
-if self.SpawnUnitsWithAbsolutePositions and self.UnitsAbsolutePositions then
-for UnitID=1,#SpawnTemplate.units do
-if self.UnitsAbsolutePositions[UnitID].heading then
-SpawnTemplate.units[UnitID].heading=math.rad(self.UnitsAbsolutePositions[UnitID].heading or 0)
-end
-SpawnTemplate.units[UnitID].x=self.UnitsAbsolutePositions[UnitID].x or 0
-SpawnTemplate.units[UnitID].y=self.UnitsAbsolutePositions[UnitID].y or 0
-if self.UnitsAbsolutePositions[UnitID].z then
-SpawnTemplate.units[UnitID].z=self.UnitsAbsolutePositions[UnitID].z or 0
-end
-end
-end
-if self.SpawnInitLivery then
-for UnitID=1,#SpawnTemplate.units do
-SpawnTemplate.units[UnitID].livery_id=self.SpawnInitLivery
-end
-end
-if self.SpawnInitSkill then
-for UnitID=1,#SpawnTemplate.units do
-SpawnTemplate.units[UnitID].skill=self.SpawnInitSkill
-end
-end
-if self.SpawnInitModex then
-for UnitID=1,#SpawnTemplate.units do
-local modexnumber=string.format("%03d",self.SpawnInitModex+(UnitID-1))
-if self.SpawnInitModexPrefix then modexnumber=self.SpawnInitModexPrefix..modexnumber end
-if self.SpawnInitModexPostfix then modexnumber=modexnumber..self.SpawnInitModexPostfix end
-SpawnTemplate.units[UnitID].onboard_num=modexnumber
-end
-end
-if self.SpawnInitRadio then
-SpawnTemplate.communication=self.SpawnInitRadio
-end
-if self.SpawnInitFreq then
-SpawnTemplate.frequency=self.SpawnInitFreq
-end
-if self.SpawnInitModu then
-SpawnTemplate.modulation=self.SpawnInitModu
-end
-SpawnTemplate.CategoryID=self.SpawnInitCategory or SpawnTemplate.CategoryID
-SpawnTemplate.CountryID=self.SpawnInitCountry or SpawnTemplate.CountryID
-SpawnTemplate.CoalitionID=self.SpawnInitCoalition or SpawnTemplate.CoalitionID
-end
-if not NoBirth then
-self:HandleEvent(EVENTS.Birth,self._OnBirth)
-end
-self:HandleEvent(EVENTS.Dead,self._OnDeadOrCrash)
-self:HandleEvent(EVENTS.Crash,self._OnDeadOrCrash)
-self:HandleEvent(EVENTS.RemoveUnit,self._OnDeadOrCrash)
-if self.Repeat then
-self:HandleEvent(EVENTS.Takeoff,self._OnTakeOff)
-self:HandleEvent(EVENTS.Land,self._OnLand)
-end
-if self.RepeatOnEngineShutDown then
-self:HandleEvent(EVENTS.EngineShutdown,self._OnEngineShutDown)
-end
-self.SpawnGroups[self.SpawnIndex].Group=_DATABASE:Spawn(SpawnTemplate)
-local SpawnGroup=self.SpawnGroups[self.SpawnIndex].Group
-if SpawnGroup then
-SpawnGroup:SetAIOnOff(self.AIOnOff)
-end
-self:T3(SpawnTemplate.name)
-if self.SpawnFunctionHook then
-self.SpawnHookScheduler:Schedule(nil,self.SpawnFunctionHook,{self.SpawnGroups[self.SpawnIndex].Group,unpack(self.SpawnFunctionArguments)},0.1)
-end
-end
-self.SpawnGroups[self.SpawnIndex].Spawned=true
-return self.SpawnGroups[self.SpawnIndex].Group
-else
-end
-return nil
-end
-function SPAWN:SpawnScheduled(SpawnTime,SpawnTimeVariation,WithDelay)
-self:F({SpawnTime,SpawnTimeVariation})
-local SpawnTime=SpawnTime or 60
-local SpawnTimeVariation=SpawnTimeVariation or 0.5
-if SpawnTime~=nil and SpawnTimeVariation~=nil then
-local InitialDelay=0
-if WithDelay or self.DelayOnOff==true then
-InitialDelay=math.random(SpawnTime-SpawnTime*SpawnTimeVariation,SpawnTime+SpawnTime*SpawnTimeVariation)
-end
-self.SpawnScheduler=SCHEDULER:New(self,self._Scheduler,{},InitialDelay,SpawnTime,SpawnTimeVariation)
-end
-return self
-end
-function SPAWN:SpawnScheduleStart()
-self:F({self.SpawnTemplatePrefix})
-self.SpawnScheduler:Start()
-return self
-end
-function SPAWN:SpawnScheduleStop()
-self:F({self.SpawnTemplatePrefix})
-self.SpawnScheduler:Stop()
-return self
-end
-function SPAWN:OnSpawnGroup(SpawnCallBackFunction,...)
-self:F("OnSpawnGroup")
-self.SpawnFunctionHook=SpawnCallBackFunction
-self.SpawnFunctionArguments={}
-if arg then
-self.SpawnFunctionArguments=arg
-end
-return self
-end
-function SPAWN:SpawnAtAirbase(SpawnAirbase,Takeoff,TakeoffAltitude,TerminalType,EmergencyAirSpawn,Parkingdata)
-self:F({self.SpawnTemplatePrefix,SpawnAirbase,Takeoff,TakeoffAltitude,TerminalType})
-local PointVec3=SpawnAirbase:GetCoordinate()
-self:T2(PointVec3)
-Takeoff=Takeoff or SPAWN.Takeoff.Hot
-if EmergencyAirSpawn==nil then
-EmergencyAirSpawn=true
-end
-self:F({SpawnIndex=self.SpawnIndex})
-if self:_GetSpawnIndex(self.SpawnIndex+1)then
-local SpawnTemplate=self.SpawnGroups[self.SpawnIndex].SpawnTemplate
-self:F({SpawnTemplate=SpawnTemplate})
-if SpawnTemplate then
-local GroupAlive=self:GetGroupFromIndex(self.SpawnIndex)
-self:F({GroupAlive=GroupAlive})
-self:T({"Current point of ",self.SpawnTemplatePrefix,SpawnAirbase})
-local TemplateGroup=GROUP:FindByName(self.SpawnTemplatePrefix)
-local TemplateUnit=TemplateGroup:GetUnit(1)
-local group=TemplateGroup
-local istransport=group:HasAttribute("Transports")and group:HasAttribute("Planes")
-local isawacs=group:HasAttribute("AWACS")
-local isfighter=group:HasAttribute("Fighters")or group:HasAttribute("Interceptors")or group:HasAttribute("Multirole fighters")or(group:HasAttribute("Bombers")and not group:HasAttribute("Strategic bombers"))
-local isbomber=group:HasAttribute("Strategic bombers")
-local istanker=group:HasAttribute("Tankers")
-local ishelo=TemplateUnit:HasAttribute("Helicopters")
-local nunits=#SpawnTemplate.units
-local SpawnPoint=SpawnTemplate.route.points[1]
-SpawnPoint.linkUnit=nil
-SpawnPoint.helipadId=nil
-SpawnPoint.airdromeId=nil
-local AirbaseID=SpawnAirbase:GetID()
-local AirbaseCategory=SpawnAirbase:GetAirbaseCategory()
-self:F({AirbaseCategory=AirbaseCategory})
-if AirbaseCategory==Airbase.Category.SHIP then
-SpawnPoint.linkUnit=AirbaseID
-SpawnPoint.helipadId=AirbaseID
-elseif AirbaseCategory==Airbase.Category.HELIPAD then
-SpawnPoint.linkUnit=AirbaseID
-SpawnPoint.helipadId=AirbaseID
-elseif AirbaseCategory==Airbase.Category.AIRDROME then
-SpawnPoint.airdromeId=AirbaseID
-end
-SpawnPoint.alt=0
-SpawnPoint.type=GROUPTEMPLATE.Takeoff[Takeoff][1]
-SpawnPoint.action=GROUPTEMPLATE.Takeoff[Takeoff][2]
-local spawnonground=not(Takeoff==SPAWN.Takeoff.Air)
-self:T({spawnonground=spawnonground,TOtype=Takeoff,TOair=Takeoff==SPAWN.Takeoff.Air})
-local spawnonship=false
-local spawnonfarp=false
-local spawnonrunway=false
-local spawnonairport=false
-if spawnonground then
-if AirbaseCategory==Airbase.Category.SHIP then
-spawnonship=true
-elseif AirbaseCategory==Airbase.Category.HELIPAD then
-spawnonfarp=true
-elseif AirbaseCategory==Airbase.Category.AIRDROME then
-spawnonairport=true
-end
-spawnonrunway=Takeoff==SPAWN.Takeoff.Runway
-end
-local parkingspots={}
-local parkingindex={}
-local spots
-if spawnonground and not SpawnTemplate.parked then
-local nfree=0
-local termtype=TerminalType
-if spawnonrunway then
-if spawnonship then
-if ishelo then
-termtype=AIRBASE.TerminalType.HelicopterUsable
-else
-termtype=AIRBASE.TerminalType.OpenMedOrBig
-end
-else
-termtype=AIRBASE.TerminalType.Runway
-end
-end
-local scanradius=50
-local scanunits=true
-local scanstatics=true
-local scanscenery=false
-local verysafe=false
-if spawnonship or spawnonfarp or spawnonrunway then
-self:T(string.format("Group %s is spawned on farp/ship/runway %s.",self.SpawnTemplatePrefix,SpawnAirbase:GetName()))
-nfree=SpawnAirbase:GetFreeParkingSpotsNumber(termtype,true)
-spots=SpawnAirbase:GetFreeParkingSpotsTable(termtype,true)
-else
-if ishelo then
-if termtype==nil then
-self:T(string.format("Helo group %s is at %s using terminal type %d.",self.SpawnTemplatePrefix,SpawnAirbase:GetName(),AIRBASE.TerminalType.HelicopterOnly))
-spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup,AIRBASE.TerminalType.HelicopterOnly,scanradius,scanunits,scanstatics,scanscenery,verysafe,nunits,Parkingdata)
-nfree=#spots
-if nfree=1 then
-for i=1,nunits do
-table.insert(parkingspots,spots[1].Coordinate)
-table.insert(parkingindex,spots[1].TerminalID)
-end
-PointVec3=spots[1].Coordinate
-else
-_notenough=true
-end
-elseif spawnonairport then
-if nfree>=nunits then
-for i=1,nunits do
-table.insert(parkingspots,spots[i].Coordinate)
-table.insert(parkingindex,spots[i].TerminalID)
-end
-else
-_notenough=true
-end
-end
-if _notenough then
-if EmergencyAirSpawn and not self.SpawnUnControlled then
-self:E(string.format("WARNING: Group %s has no parking spots at %s ==> air start!",self.SpawnTemplatePrefix,SpawnAirbase:GetName()))
-spawnonground=false
-spawnonship=false
-spawnonfarp=false
-spawnonrunway=false
-SpawnPoint.type=GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][1]
-SpawnPoint.action=GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][2]
-PointVec3.x=PointVec3.x+math.random(-500,500)
-PointVec3.z=PointVec3.z+math.random(-500,500)
-if ishelo then
-PointVec3.y=PointVec3:GetLandHeight()+math.random(100,1000)
-else
-PointVec3.y=PointVec3:GetLandHeight()+math.random(500,2500)
-end
-Takeoff=GROUP.Takeoff.Air
-else
-self:E(string.format("WARNING: Group %s has no parking spots at %s ==> No emergency air start or uncontrolled spawning ==> No spawn!",self.SpawnTemplatePrefix,SpawnAirbase:GetName()))
-return nil
-end
-end
-else
-if TakeoffAltitude then
-PointVec3.y=TakeoffAltitude
-else
-if ishelo then
-PointVec3.y=PointVec3:GetLandHeight()+math.random(100,1000)
-else
-PointVec3.y=PointVec3:GetLandHeight()+math.random(500,2500)
-end
-end
-end
-if not SpawnTemplate.parked then
-SpawnTemplate.parked=true
-for UnitID=1,nunits do
-self:T2('Before Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y)
-local UnitTemplate=SpawnTemplate.units[UnitID]
-local SX=UnitTemplate.x
-local SY=UnitTemplate.y
-local BX=SpawnTemplate.route.points[1].x
-local BY=SpawnTemplate.route.points[1].y
-local TX=PointVec3.x+(SX-BX)
-local TY=PointVec3.z+(SY-BY)
-if spawnonground then
-if spawnonship or spawnonfarp or spawnonrunway then
-self:T(string.format("Group %s spawning at farp, ship or runway %s.",self.SpawnTemplatePrefix,SpawnAirbase:GetName()))
-SpawnTemplate.units[UnitID].x=PointVec3.x
-SpawnTemplate.units[UnitID].y=PointVec3.z
-SpawnTemplate.units[UnitID].alt=PointVec3.y
-else
-self:T(string.format("Group %s spawning at airbase %s on parking spot id %d",self.SpawnTemplatePrefix,SpawnAirbase:GetName(),parkingindex[UnitID]))
-SpawnTemplate.units[UnitID].x=parkingspots[UnitID].x
-SpawnTemplate.units[UnitID].y=parkingspots[UnitID].z
-SpawnTemplate.units[UnitID].alt=parkingspots[UnitID].y
-end
-else
-self:T(string.format("Group %s spawning in air at %s.",self.SpawnTemplatePrefix,SpawnAirbase:GetName()))
-SpawnTemplate.units[UnitID].x=TX
-SpawnTemplate.units[UnitID].y=TY
-SpawnTemplate.units[UnitID].alt=PointVec3.y
-end
-UnitTemplate.parking=nil
-UnitTemplate.parking_id=nil
-if parkingindex[UnitID]then
-UnitTemplate.parking=parkingindex[UnitID]
-end
-self:T(string.format("Group %s unit number %d: Parking = %s",self.SpawnTemplatePrefix,UnitID,tostring(UnitTemplate.parking)))
-self:T(string.format("Group %s unit number %d: Parking ID = %s",self.SpawnTemplatePrefix,UnitID,tostring(UnitTemplate.parking_id)))
-self:T2('After Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y)
-end
-end
-SpawnPoint.x=PointVec3.x
-SpawnPoint.y=PointVec3.z
-SpawnPoint.alt=PointVec3.y
-SpawnTemplate.x=PointVec3.x
-SpawnTemplate.y=PointVec3.z
-SpawnTemplate.uncontrolled=self.SpawnUnControlled
-local GroupSpawned=self:SpawnWithIndex(self.SpawnIndex)
-if Takeoff==GROUP.Takeoff.Air then
-for UnitID,UnitSpawned in pairs(GroupSpawned:GetUnits())do
-SCHEDULER:New(nil,BASE.CreateEventTakeoff,{GroupSpawned,timer.getTime(),UnitSpawned:GetDCSObject()},5)
-end
-end
-if Takeoff~=SPAWN.Takeoff.Runway and Takeoff~=SPAWN.Takeoff.Air and spawnonairport then
-SCHEDULER:New(nil,AIRBASE.CheckOnRunWay,{SpawnAirbase,GroupSpawned,75,true},1.0)
-end
-return GroupSpawned
-end
-end
-return nil
-end
-function SPAWN:SpawnAtParkingSpot(Airbase,Spots,Takeoff)
-self:F({Airbase=Airbase,Spots=Spots,Takeoff=Takeoff})
-if type(Spots)~="table"then
-Spots={Spots}
-end
-if type(Airbase)=="string"then
-Airbase=AIRBASE:FindByName(Airbase)
-end
-local group=GROUP:FindByName(self.SpawnTemplatePrefix)
-local nunits=self.SpawnGrouping or#group:GetUnits()
-if nunits then
-if#Spots=nunits then
-return self:SpawnAtAirbase(Airbase,Takeoff,nil,nil,nil,Parkingdata)
-else
-self:E("ERROR: Could not find enough free parking spots!")
-end
-else
-self:E("ERROR: Could not get number of units in group!")
-end
-return nil
-end
-function SPAWN:ParkAircraft(SpawnAirbase,TerminalType,Parkingdata,SpawnIndex)
-self:F({SpawnIndex=SpawnIndex,SpawnMaxGroups=self.SpawnMaxGroups})
-local PointVec3=SpawnAirbase:GetCoordinate()
-self:T2(PointVec3)
-local Takeoff=SPAWN.Takeoff.Cold
-local SpawnTemplate=self.SpawnGroups[SpawnIndex].SpawnTemplate
-if SpawnTemplate then
-local GroupAlive=self:GetGroupFromIndex(SpawnIndex)
-self:T({"Current point of ",self.SpawnTemplatePrefix,SpawnAirbase})
-local TemplateGroup=GROUP:FindByName(self.SpawnTemplatePrefix)
-local TemplateUnit=TemplateGroup:GetUnit(1)
-local ishelo=TemplateUnit:HasAttribute("Helicopters")
-local isbomber=TemplateUnit:HasAttribute("Bombers")
-local istransport=TemplateUnit:HasAttribute("Transports")
-local isfighter=TemplateUnit:HasAttribute("Battleplanes")
-local nunits=#SpawnTemplate.units
-local SpawnPoint=SpawnTemplate.route.points[1]
-SpawnPoint.linkUnit=nil
-SpawnPoint.helipadId=nil
-SpawnPoint.airdromeId=nil
-local AirbaseID=SpawnAirbase:GetID()
-local AirbaseCategory=SpawnAirbase:GetAirbaseCategory()
-self:F({AirbaseCategory=AirbaseCategory})
-if AirbaseCategory==Airbase.Category.SHIP then
-SpawnPoint.linkUnit=AirbaseID
-SpawnPoint.helipadId=AirbaseID
-elseif AirbaseCategory==Airbase.Category.HELIPAD then
-SpawnPoint.linkUnit=AirbaseID
-SpawnPoint.helipadId=AirbaseID
-elseif AirbaseCategory==Airbase.Category.AIRDROME then
-SpawnPoint.airdromeId=AirbaseID
-end
-SpawnPoint.alt=0
-SpawnPoint.type=GROUPTEMPLATE.Takeoff[Takeoff][1]
-SpawnPoint.action=GROUPTEMPLATE.Takeoff[Takeoff][2]
-local spawnonground=not(Takeoff==SPAWN.Takeoff.Air)
-self:T({spawnonground=spawnonground,TOtype=Takeoff,TOair=Takeoff==SPAWN.Takeoff.Air})
-local spawnonship=false
-local spawnonfarp=false
-local spawnonrunway=false
-local spawnonairport=false
-if spawnonground then
-if AirbaseCategory==Airbase.Category.SHIP then
-spawnonship=true
-elseif AirbaseCategory==Airbase.Category.HELIPAD then
-spawnonfarp=true
-elseif AirbaseCategory==Airbase.Category.AIRDROME then
-spawnonairport=true
-end
-spawnonrunway=Takeoff==SPAWN.Takeoff.Runway
-end
-local parkingspots={}
-local parkingindex={}
-local spots
-if spawnonground and not SpawnTemplate.parked then
-local nfree=0
-local termtype=TerminalType
-local scanradius=50
-local scanunits=true
-local scanstatics=true
-local scanscenery=false
-local verysafe=false
-if spawnonship or spawnonfarp or spawnonrunway then
-self:T(string.format("Group %s is spawned on farp/ship/runway %s.",self.SpawnTemplatePrefix,SpawnAirbase:GetName()))
-nfree=SpawnAirbase:GetFreeParkingSpotsNumber(termtype,true)
-spots=SpawnAirbase:GetFreeParkingSpotsTable(termtype,true)
-else
-if ishelo then
-if termtype==nil then
-self:T(string.format("Helo group %s is at %s using terminal type %d.",self.SpawnTemplatePrefix,SpawnAirbase:GetName(),AIRBASE.TerminalType.HelicopterOnly))
-spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup,AIRBASE.TerminalType.HelicopterOnly,scanradius,scanunits,scanstatics,scanscenery,verysafe,nunits,Parkingdata)
-nfree=#spots
-if nfree=1 then
-for i=1,nunits do
-table.insert(parkingspots,spots[1].Coordinate)
-table.insert(parkingindex,spots[1].TerminalID)
-end
-PointVec3=spots[1].Coordinate
-else
-_notenough=true
-end
-elseif spawnonairport then
-if nfree>=nunits then
-for i=1,nunits do
-table.insert(parkingspots,spots[i].Coordinate)
-table.insert(parkingindex,spots[i].TerminalID)
-end
-else
-_notenough=true
-end
-end
-if _notenough then
-if not self.SpawnUnControlled then
-else
-self:E(string.format("WARNING: Group %s has no parking spots at %s ==> No emergency air start or uncontrolled spawning ==> No spawn!",self.SpawnTemplatePrefix,SpawnAirbase:GetName()))
-return nil
-end
-end
-else
-end
-if not SpawnTemplate.parked then
-SpawnTemplate.parked=true
-for UnitID=1,nunits do
-self:F('Before Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y)
-local UnitTemplate=SpawnTemplate.units[UnitID]
-local SX=UnitTemplate.x
-local SY=UnitTemplate.y
-local BX=SpawnTemplate.route.points[1].x
-local BY=SpawnTemplate.route.points[1].y
-local TX=PointVec3.x+(SX-BX)
-local TY=PointVec3.z+(SY-BY)
-if spawnonground then
-if spawnonship or spawnonfarp or spawnonrunway then
-self:T(string.format("Group %s spawning at farp, ship or runway %s.",self.SpawnTemplatePrefix,SpawnAirbase:GetName()))
-SpawnTemplate.units[UnitID].x=PointVec3.x
-SpawnTemplate.units[UnitID].y=PointVec3.z
-SpawnTemplate.units[UnitID].alt=PointVec3.y
-else
-self:T(string.format("Group %s spawning at airbase %s on parking spot id %d",self.SpawnTemplatePrefix,SpawnAirbase:GetName(),parkingindex[UnitID]))
-SpawnTemplate.units[UnitID].x=parkingspots[UnitID].x
-SpawnTemplate.units[UnitID].y=parkingspots[UnitID].z
-SpawnTemplate.units[UnitID].alt=parkingspots[UnitID].y
-end
-else
-self:T(string.format("Group %s spawning in air at %s.",self.SpawnTemplatePrefix,SpawnAirbase:GetName()))
-SpawnTemplate.units[UnitID].x=TX
-SpawnTemplate.units[UnitID].y=TY
-SpawnTemplate.units[UnitID].alt=PointVec3.y
-end
-UnitTemplate.parking=nil
-UnitTemplate.parking_id=nil
-if parkingindex[UnitID]then
-UnitTemplate.parking=parkingindex[UnitID]
-end
-self:T2(string.format("Group %s unit number %d: Parking = %s",self.SpawnTemplatePrefix,UnitID,tostring(UnitTemplate.parking)))
-self:T2(string.format("Group %s unit number %d: Parking ID = %s",self.SpawnTemplatePrefix,UnitID,tostring(UnitTemplate.parking_id)))
-self:T2('After Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y)
-end
-end
-SpawnPoint.x=PointVec3.x
-SpawnPoint.y=PointVec3.z
-SpawnPoint.alt=PointVec3.y
-SpawnTemplate.x=PointVec3.x
-SpawnTemplate.y=PointVec3.z
-SpawnTemplate.uncontrolled=true
-local GroupSpawned=self:SpawnWithIndex(SpawnIndex,true)
-if Takeoff==GROUP.Takeoff.Air then
-for UnitID,UnitSpawned in pairs(GroupSpawned:GetUnits())do
-SCHEDULER:New(nil,BASE.CreateEventTakeoff,{GroupSpawned,timer.getTime(),UnitSpawned:GetDCSObject()},5)
-end
-end
-if Takeoff~=SPAWN.Takeoff.Runway and Takeoff~=SPAWN.Takeoff.Air and spawnonairport then
-SCHEDULER:New(nil,AIRBASE.CheckOnRunWay,{SpawnAirbase,GroupSpawned,75,true},1.0)
-end
-end
-end
-function SPAWN:ParkAtAirbase(SpawnAirbase,TerminalType,Parkingdata)
-self:F({self.SpawnTemplatePrefix,SpawnAirbase,TerminalType})
-self:ParkAircraft(SpawnAirbase,TerminalType,Parkingdata,1)
-for SpawnIndex=2,self.SpawnMaxGroups do
-self:ParkAircraft(SpawnAirbase,TerminalType,Parkingdata,SpawnIndex)
-end
-self:SetSpawnIndex(0)
-return nil
-end
-function SPAWN:SpawnFromVec3(Vec3,SpawnIndex)
-self:F({self.SpawnTemplatePrefix,Vec3,SpawnIndex})
-local PointVec3=POINT_VEC3:NewFromVec3(Vec3)
-self:T2(PointVec3)
-if SpawnIndex then
-else
-SpawnIndex=self.SpawnIndex+1
-end
-if self:_GetSpawnIndex(SpawnIndex)then
-local SpawnTemplate=self.SpawnGroups[self.SpawnIndex].SpawnTemplate
-if SpawnTemplate then
-self:T({"Current point of ",self.SpawnTemplatePrefix,Vec3})
-local TemplateHeight=SpawnTemplate.route and SpawnTemplate.route.points[1].alt or nil
-SpawnTemplate.route=SpawnTemplate.route or{}
-SpawnTemplate.route.points=SpawnTemplate.route.points or{}
-SpawnTemplate.route.points[1]=SpawnTemplate.route.points[1]or{}
-SpawnTemplate.route.points[1].x=SpawnTemplate.route.points[1].x or 0
-SpawnTemplate.route.points[1].y=SpawnTemplate.route.points[1].y or 0
-for UnitID=1,#SpawnTemplate.units do
-local UnitTemplate=SpawnTemplate.units[UnitID]
-local SX=UnitTemplate.x or 0
-local SY=UnitTemplate.y or 0
-local BX=SpawnTemplate.route.points[1].x
-local BY=SpawnTemplate.route.points[1].y
-local TX=Vec3.x+(SX-BX)
-local TY=Vec3.z+(SY-BY)
-SpawnTemplate.units[UnitID].x=TX
-SpawnTemplate.units[UnitID].y=TY
-if SpawnTemplate.CategoryID~=Group.Category.SHIP then
-SpawnTemplate.units[UnitID].alt=Vec3.y or TemplateHeight
-end
-self:T('After Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y)
-end
-SpawnTemplate.route.points[1].x=Vec3.x
-SpawnTemplate.route.points[1].y=Vec3.z
-if SpawnTemplate.CategoryID~=Group.Category.SHIP then
-SpawnTemplate.route.points[1].alt=Vec3.y or TemplateHeight
-end
-SpawnTemplate.x=Vec3.x
-SpawnTemplate.y=Vec3.z
-SpawnTemplate.alt=Vec3.y or TemplateHeight
-return self:SpawnWithIndex(self.SpawnIndex)
-end
-end
-return nil
-end
-function SPAWN:SpawnFromCoordinate(Coordinate,SpawnIndex)
-self:F({self.SpawnTemplatePrefix,SpawnIndex})
-return self:SpawnFromVec3(Coordinate:GetVec3(),SpawnIndex)
-end
-function SPAWN:SpawnFromPointVec3(PointVec3,SpawnIndex)
-self:F({self.SpawnTemplatePrefix,SpawnIndex})
-return self:SpawnFromVec3(PointVec3:GetVec3(),SpawnIndex)
-end
-function SPAWN:SpawnFromVec2(Vec2,MinHeight,MaxHeight,SpawnIndex)
-self:F({self.SpawnTemplatePrefix,self.SpawnIndex,Vec2,MinHeight,MaxHeight,SpawnIndex})
-local Height=nil
-if MinHeight and MaxHeight then
-Height=math.random(MinHeight,MaxHeight)
-end
-return self:SpawnFromVec3({x=Vec2.x,y=Height,z=Vec2.y},SpawnIndex)
-end
-function SPAWN:SpawnFromPointVec2(PointVec2,MinHeight,MaxHeight,SpawnIndex)
-self:F({self.SpawnTemplatePrefix,self.SpawnIndex})
-return self:SpawnFromVec2(PointVec2:GetVec2(),MinHeight,MaxHeight,SpawnIndex)
-end
-function SPAWN:SpawnFromUnit(HostUnit,MinHeight,MaxHeight,SpawnIndex)
-self:F({self.SpawnTemplatePrefix,HostUnit,MinHeight,MaxHeight,SpawnIndex})
-if HostUnit and HostUnit:IsAlive()~=nil then
-return self:SpawnFromVec2(HostUnit:GetVec2(),MinHeight,MaxHeight,SpawnIndex)
-end
-return nil
-end
-function SPAWN:SpawnFromStatic(HostStatic,MinHeight,MaxHeight,SpawnIndex)
-self:F({self.SpawnTemplatePrefix,HostStatic,MinHeight,MaxHeight,SpawnIndex})
-if HostStatic and HostStatic:IsAlive()then
-return self:SpawnFromVec2(HostStatic:GetVec2(),MinHeight,MaxHeight,SpawnIndex)
-end
-return nil
-end
-function SPAWN:SpawnInZone(Zone,RandomizeGroup,MinHeight,MaxHeight,SpawnIndex)
-self:F({self.SpawnTemplatePrefix,Zone,RandomizeGroup,MinHeight,MaxHeight,SpawnIndex})
-if Zone then
-if RandomizeGroup then
-return self:SpawnFromVec2(Zone:GetRandomVec2(),MinHeight,MaxHeight,SpawnIndex)
-else
-return self:SpawnFromVec2(Zone:GetVec2(),MinHeight,MaxHeight,SpawnIndex)
-end
-end
-return nil
-end
-function SPAWN:InitUnControlled(UnControlled)
-self:F2({self.SpawnTemplatePrefix,UnControlled})
-self.SpawnUnControlled=(UnControlled==true)and true or nil
-for SpawnGroupID=1,self.SpawnMaxGroups do
-self.SpawnGroups[SpawnGroupID].UnControlled=self.SpawnUnControlled
-end
-return self
-end
-function SPAWN:GetCoordinate()
-local LateGroup=GROUP:FindByName(self.SpawnTemplatePrefix)
-if LateGroup then
-return LateGroup:GetCoordinate()
-end
-return nil
-end
-function SPAWN:SpawnGroupName(SpawnIndex)
-self:F({self.SpawnTemplatePrefix,SpawnIndex})
-local SpawnPrefix=self.SpawnTemplatePrefix
-if self.SpawnAliasPrefix then
-SpawnPrefix=self.SpawnAliasPrefix
-end
-if SpawnIndex then
-local SpawnName=string.format('%s#%03d',SpawnPrefix,SpawnIndex)
-self:T(SpawnName)
-return SpawnName
-else
-self:T(SpawnPrefix)
-return SpawnPrefix
-end
-end
-function SPAWN:GetFirstAliveGroup()
-self:F({self.SpawnTemplatePrefix,self.SpawnAliasPrefix})
-for SpawnIndex=1,self.SpawnCount do
-local SpawnGroup=self:GetGroupFromIndex(SpawnIndex)
-if SpawnGroup and SpawnGroup:IsAlive()then
-return SpawnGroup,SpawnIndex
-end
-end
-return nil,nil
-end
-function SPAWN:GetNextAliveGroup(SpawnIndexStart)
-self:F({self.SpawnTemplatePrefix,self.SpawnAliasPrefix,SpawnIndexStart})
-SpawnIndexStart=SpawnIndexStart+1
-for SpawnIndex=SpawnIndexStart,self.SpawnCount do
-local SpawnGroup=self:GetGroupFromIndex(SpawnIndex)
-if SpawnGroup and SpawnGroup:IsAlive()then
-return SpawnGroup,SpawnIndex
-end
-end
-return nil,nil
-end
-function SPAWN:GetLastAliveGroup()
-self:F({self.SpawnTemplatePrefix,self.SpawnAliasPrefix})
-for SpawnIndex=self.SpawnCount,1,-1 do
-local SpawnGroup=self:GetGroupFromIndex(SpawnIndex)
-if SpawnGroup and SpawnGroup:IsAlive()then
-self.SpawnIndex=SpawnIndex
-return SpawnGroup
-end
-end
-self.SpawnIndex=nil
-return nil
-end
-function SPAWN:GetGroupFromIndex(SpawnIndex)
-self:F({self.SpawnTemplatePrefix,self.SpawnAliasPrefix,SpawnIndex})
-if not SpawnIndex then
-SpawnIndex=1
-end
-if self.SpawnGroups and self.SpawnGroups[SpawnIndex]then
-local SpawnGroup=self.SpawnGroups[SpawnIndex].Group
-return SpawnGroup
-else
-return nil
-end
-end
-function SPAWN:_GetPrefixFromGroup(SpawnGroup)
-local GroupName=SpawnGroup:GetName()
-if GroupName then
-local SpawnPrefix=self:_GetPrefixFromGroupName(GroupName)
-return SpawnPrefix
-end
-return nil
-end
-function SPAWN:_GetPrefixFromGroupName(SpawnGroupName)
-if SpawnGroupName then
-local SpawnPrefix=string.match(SpawnGroupName,".*#")
-if SpawnPrefix then
-SpawnPrefix=SpawnPrefix:sub(1,-2)
-end
-return SpawnPrefix
-end
-return nil
-end
-function SPAWN:GetSpawnIndexFromGroup(SpawnGroup)
-self:F2({self.SpawnTemplatePrefix,self.SpawnAliasPrefix,SpawnGroup})
-local IndexString=string.match(SpawnGroup:GetName(),"#(%d*)$"):sub(2)
-local Index=tonumber(IndexString)
-self:T3(IndexString,Index)
-return Index
-end
-function SPAWN:_GetLastIndex()
-self:F({self.SpawnTemplatePrefix,self.SpawnAliasPrefix})
-return self.SpawnMaxGroups
-end
-function SPAWN:_InitializeSpawnGroups(SpawnIndex)
-self:F3({self.SpawnTemplatePrefix,self.SpawnAliasPrefix,SpawnIndex})
-if not self.SpawnGroups[SpawnIndex]then
-self.SpawnGroups[SpawnIndex]={}
-self.SpawnGroups[SpawnIndex].Visible=false
-self.SpawnGroups[SpawnIndex].Spawned=false
-self.SpawnGroups[SpawnIndex].UnControlled=false
-self.SpawnGroups[SpawnIndex].SpawnTime=0
-self.SpawnGroups[SpawnIndex].SpawnTemplatePrefix=self.SpawnTemplatePrefix
-self.SpawnGroups[SpawnIndex].SpawnTemplate=self:_Prepare(self.SpawnGroups[SpawnIndex].SpawnTemplatePrefix,SpawnIndex)
-end
-self:_RandomizeTemplate(SpawnIndex)
-self:_RandomizeRoute(SpawnIndex)
-return self.SpawnGroups[SpawnIndex]
-end
-function SPAWN:_GetGroupCategoryID(SpawnPrefix)
-local TemplateGroup=Group.getByName(SpawnPrefix)
-if TemplateGroup then
-return TemplateGroup:getCategory()
-else
-return nil
-end
-end
-function SPAWN:_GetGroupCoalitionID(SpawnPrefix)
-local TemplateGroup=Group.getByName(SpawnPrefix)
-if TemplateGroup then
-return TemplateGroup:getCoalition()
-else
-return nil
-end
-end
-function SPAWN:_GetGroupCountryID(SpawnPrefix)
-self:F({self.SpawnTemplatePrefix,self.SpawnAliasPrefix,SpawnPrefix})
-local TemplateGroup=Group.getByName(SpawnPrefix)
-if TemplateGroup then
-local TemplateUnits=TemplateGroup:getUnits()
-return TemplateUnits[1]:getCountry()
-else
-return nil
-end
-end
-function SPAWN:_GetTemplate(SpawnTemplatePrefix)
-self:F({self.SpawnTemplatePrefix,self.SpawnAliasPrefix,SpawnTemplatePrefix})
-local SpawnTemplate=nil
-if _DATABASE.Templates.Groups[SpawnTemplatePrefix]==nil then
-error('No Template exists for SpawnTemplatePrefix = '..SpawnTemplatePrefix)
-end
-local Template=_DATABASE.Templates.Groups[SpawnTemplatePrefix].Template
-self:F({Template=Template})
-SpawnTemplate=UTILS.DeepCopy(_DATABASE.Templates.Groups[SpawnTemplatePrefix].Template)
-if SpawnTemplate==nil then
-error('No Template returned for SpawnTemplatePrefix = '..SpawnTemplatePrefix)
-end
-self:T3({SpawnTemplate})
-return SpawnTemplate
-end
-function SPAWN:_Prepare(SpawnTemplatePrefix,SpawnIndex)
-self:F({self.SpawnTemplatePrefix,self.SpawnAliasPrefix})
-local SpawnTemplate
-if self.TweakedTemplate~=nil and self.TweakedTemplate==true then
-BASE:I("WARNING: You are using a tweaked template.")
-SpawnTemplate=self.SpawnTemplate
-if self.MooseNameing==true then
-SpawnTemplate.name=self:SpawnGroupName(SpawnIndex)
-else
-SpawnTemplate.name=self:SpawnGroupName()
-end
-else
-SpawnTemplate=self:_GetTemplate(SpawnTemplatePrefix)
-SpawnTemplate.name=self:SpawnGroupName(SpawnIndex)
-end
-SpawnTemplate.groupId=nil
-SpawnTemplate.lateActivation=self.LateActivated or false
-if SpawnTemplate.CategoryID==Group.Category.GROUND then
-self:T3("For ground units, visible needs to be false...")
-SpawnTemplate.visible=false
-end
-if self.SpawnGrouping then
-local UnitAmount=#SpawnTemplate.units
-self:F({UnitAmount=UnitAmount,SpawnGrouping=self.SpawnGrouping})
-if UnitAmount>self.SpawnGrouping then
-for UnitID=self.SpawnGrouping+1,UnitAmount do
-SpawnTemplate.units[UnitID]=nil
-end
-else
-if UnitAmount=self.SpawnCount+1 then
-self.SpawnCount=self.SpawnCount+1
-SpawnIndex=self.SpawnCount
-end
-self.SpawnIndex=SpawnIndex
-if not self.SpawnGroups[self.SpawnIndex]then
-self:_InitializeSpawnGroups(self.SpawnIndex)
-end
-else
-return nil
-end
-else
-return nil
-end
-return self.SpawnIndex
-end
-function SPAWN:_OnBirth(EventData)
-self:F(self.SpawnTemplatePrefix)
-local SpawnGroup=EventData.IniGroup
-if SpawnGroup then
-local EventPrefix=self:_GetPrefixFromGroup(SpawnGroup)
-if EventPrefix then
-self:T({"Birth Event:",EventPrefix,self.SpawnTemplatePrefix})
-if EventPrefix==self.SpawnTemplatePrefix or(self.SpawnAliasPrefix and EventPrefix==self.SpawnAliasPrefix)then
-self.AliveUnits=self.AliveUnits+1
-self:T("Alive Units: "..self.AliveUnits)
-end
-end
-end
-end
-function SPAWN:_OnDeadOrCrash(EventData)
-self:F(self.SpawnTemplatePrefix)
-local unit=UNIT:FindByName(EventData.IniUnitName)
-if unit then
-local EventPrefix=self:_GetPrefixFromGroupName(unit.GroupName)
-if EventPrefix then
-self:T({"Dead event: "..EventPrefix})
-if EventPrefix==self.SpawnTemplatePrefix or(self.SpawnAliasPrefix and EventPrefix==self.SpawnAliasPrefix)then
-self.AliveUnits=self.AliveUnits-1
-self:T("Alive Units: "..self.AliveUnits)
-end
-end
-end
-end
-function SPAWN:_OnTakeOff(EventData)
-self:F(self.SpawnTemplatePrefix)
-local SpawnGroup=EventData.IniGroup
-if SpawnGroup then
-local EventPrefix=self:_GetPrefixFromGroup(SpawnGroup)
-if EventPrefix then
-self:T({"TakeOff event: "..EventPrefix})
-if EventPrefix==self.SpawnTemplatePrefix or(self.SpawnAliasPrefix and EventPrefix==self.SpawnAliasPrefix)then
-self:T("self.Landed = false")
-SpawnGroup:SetState(SpawnGroup,"Spawn_Landed",false)
-end
-end
-end
-end
-function SPAWN:_OnLand(EventData)
-self:F(self.SpawnTemplatePrefix)
-local SpawnGroup=EventData.IniGroup
-if SpawnGroup then
-local EventPrefix=self:_GetPrefixFromGroup(SpawnGroup)
-if EventPrefix then
-self:T({"Land event: "..EventPrefix})
-if EventPrefix==self.SpawnTemplatePrefix or(self.SpawnAliasPrefix and EventPrefix==self.SpawnAliasPrefix)then
-SpawnGroup:SetState(SpawnGroup,"Spawn_Landed",true)
-if self.RepeatOnLanding then
-local SpawnGroupIndex=self:GetSpawnIndexFromGroup(SpawnGroup)
-self:T({"Landed:","ReSpawn:",SpawnGroup:GetName(),SpawnGroupIndex})
-SCHEDULER:New(nil,self.ReSpawn,{self,SpawnGroupIndex},3)
-end
-end
-end
-end
-end
-function SPAWN:_OnEngineShutDown(EventData)
-self:F(self.SpawnTemplatePrefix)
-local SpawnGroup=EventData.IniGroup
-if SpawnGroup then
-local EventPrefix=self:_GetPrefixFromGroup(SpawnGroup)
-if EventPrefix then
-self:T({"EngineShutdown event: "..EventPrefix})
-if EventPrefix==self.SpawnTemplatePrefix or(self.SpawnAliasPrefix and EventPrefix==self.SpawnAliasPrefix)then
-local Landed=SpawnGroup:GetState(SpawnGroup,"Spawn_Landed")
-if Landed and self.RepeatOnEngineShutDown then
-local SpawnGroupIndex=self:GetSpawnIndexFromGroup(SpawnGroup)
-self:T({"EngineShutDown: ","ReSpawn:",SpawnGroup:GetName(),SpawnGroupIndex})
-SCHEDULER:New(nil,self.ReSpawn,{self,SpawnGroupIndex},3)
-end
-end
-end
-end
-end
-function SPAWN:_Scheduler()
-self:F2({"_Scheduler",self.SpawnTemplatePrefix,self.SpawnAliasPrefix,self.SpawnIndex,self.SpawnMaxGroups,self.SpawnMaxUnitsAlive})
-self:Spawn()
-return true
-end
-function SPAWN:_SpawnCleanUpScheduler()
-self:F({"CleanUp Scheduler:",self.SpawnTemplatePrefix})
-local SpawnGroup,SpawnCursor=self:GetFirstAliveGroup()
-self:T({"CleanUp Scheduler:",SpawnGroup,SpawnCursor})
-local IsHelo=false
-while SpawnGroup do
-IsHelo=SpawnGroup:IsHelicopter()
-local SpawnUnits=SpawnGroup:GetUnits()
-for UnitID,UnitData in pairs(SpawnUnits)do
-local SpawnUnit=UnitData
-local SpawnUnitName=SpawnUnit:GetName()
-self.SpawnCleanUpTimeStamps[SpawnUnitName]=self.SpawnCleanUpTimeStamps[SpawnUnitName]or{}
-local Stamp=self.SpawnCleanUpTimeStamps[SpawnUnitName]
-self:T({SpawnUnitName,Stamp})
-if Stamp.Vec2 then
-if(SpawnUnit:InAir()==false and SpawnUnit:GetVelocityKMH()<1)or IsHelo then
-local NewVec2=SpawnUnit:GetVec2()or{x=0,y=0}
-if(Stamp.Vec2.x==NewVec2.x and Stamp.Vec2.y==NewVec2.y)or(SpawnUnit:GetLife()<=1)then
-if Stamp.Time+self.SpawnCleanUpInterval0 then
-self.Tstop=timer.getTime()+Delay
-else
-if self.tid then
-self:T(self.lid..string.format("Stopping timer by removing timer function after %d calls!",self.ncalls))
-local status=pcall(
-function()
-timer.removeFunction(self.tid)
-end
-)
-if status then
-self:T2(self.lid..string.format("Stopped timer!"))
-else
-self:E(self.lid..string.format("WARNING: Could not remove timer function! isrunning=%s",tostring(self.isrunning)))
-end
-self.isrunning=false
-end
-end
-return self
-end
-function TIMER:SetMaxFunctionCalls(Nmax)
-self.ncallsMax=Nmax
-return self
-end
-function TIMER:SetTimeInterval(dT)
-self.dT=dT
-return self
-end
-function TIMER:IsRunning()
-return self.isrunning
-end
-function TIMER:_Function(time)
-self.func(unpack(self.para))
-self.ncalls=self.ncalls+1
-local Tnext=self.dT and time+self.dT or nil
-local stopme=false
-if Tnext==nil then
-self:T(self.lid..string.format("No next time as dT=nil ==> Stopping timer after %d function calls",self.ncalls))
-stopme=true
-elseif self.Tstop and Tnext>self.Tstop then
-self:T(self.lid..string.format("Stop time passed %.3f > %.3f ==> Stopping timer after %d function calls",Tnext,self.Tstop,self.ncalls))
-stopme=true
-elseif self.ncallsMax and self.ncalls>=self.ncallsMax then
-self:T(self.lid..string.format("Max function calls Nmax=%d reached ==> Stopping timer after %d function calls",self.ncallsMax,self.ncalls))
-stopme=true
-end
-if stopme then
-self:Stop()
-return nil
-else
-return Tnext
-end
-end
-do
-USERFLAG={
-ClassName="USERFLAG",
-UserFlagName=nil,
-}
-function USERFLAG:New(UserFlagName)
-local self=BASE:Inherit(self,BASE:New())
-self.UserFlagName=UserFlagName
-return self
-end
-function USERFLAG:GetName()
-return self.UserFlagName
-end
-function USERFLAG:Set(Number,Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,USERFLAG.Set,self,Number)
-else
-trigger.action.setUserFlag(self.UserFlagName,Number)
-end
-return self
-end
-function USERFLAG:Get()
-return trigger.misc.getUserFlag(self.UserFlagName)
-end
-function USERFLAG:Is(Number)
-return trigger.misc.getUserFlag(self.UserFlagName)==Number
-end
-end
-do
-VELOCITY={
-ClassName="VELOCITY",
-}
-function VELOCITY:New(VelocityMps)
-local self=BASE:Inherit(self,BASE:New())
-self:F({})
-self.Velocity=VelocityMps
-return self
-end
-function VELOCITY:Set(VelocityMps)
-self.Velocity=VelocityMps
-return self
-end
-function VELOCITY:Get()
-return self.Velocity
-end
-function VELOCITY:SetKmph(VelocityKmph)
-self.Velocity=UTILS.KmphToMps(VelocityKmph)
-return self
-end
-function VELOCITY:GetKmph()
-return UTILS.MpsToKmph(self.Velocity)
-end
-function VELOCITY:SetMiph(VelocityMiph)
-self.Velocity=UTILS.MiphToMps(VelocityMiph)
-return self
-end
-function VELOCITY:GetMiph()
-return UTILS.MpsToMiph(self.Velocity)
-end
-function VELOCITY:GetText(Settings)
-local Settings=Settings or _SETTINGS
-if self.Velocity~=0 then
-if Settings:IsMetric()then
-return string.format("%d km/h",UTILS.MpsToKmph(self.Velocity))
-else
-return string.format("%d mi/h",UTILS.MpsToMiph(self.Velocity))
-end
-else
-return"stationary"
-end
-end
-function VELOCITY:ToString(VelocityGroup,Settings)
-self:F({Group=VelocityGroup and VelocityGroup:GetName()})
-local Settings=Settings or(VelocityGroup and _DATABASE:GetPlayerSettings(VelocityGroup:GetPlayerName()))or _SETTINGS
-return self:GetText(Settings)
-end
-end
-do
-VELOCITY_POSITIONABLE={
-ClassName="VELOCITY_POSITIONABLE",
-}
-function VELOCITY_POSITIONABLE:New(Positionable)
-local self=BASE:Inherit(self,VELOCITY:New())
-self:F({})
-self.Positionable=Positionable
-return self
-end
-function VELOCITY_POSITIONABLE:Get()
-return self.Positionable:GetVelocityMPS()or 0
-end
-function VELOCITY_POSITIONABLE:GetKmph()
-return UTILS.MpsToKmph(self.Positionable:GetVelocityMPS()or 0)
-end
-function VELOCITY_POSITIONABLE:GetMiph()
-return UTILS.MpsToMiph(self.Positionable:GetVelocityMPS()or 0)
-end
-function VELOCITY_POSITIONABLE:ToString()
-self:F({Group=self.Positionable and self.Positionable:GetName()})
-local Settings=Settings or(self.Positionable and _DATABASE:GetPlayerSettings(self.Positionable:GetPlayerName()))or _SETTINGS
-self.Velocity=self.Positionable:GetVelocityMPS()
-return self:GetText(Settings)
-end
-end
-ZONE_DETECTION={
-ClassName="ZONE_DETECTION",
-}
-function ZONE_DETECTION:New(ZoneName,Detection,Radius)
-local self=BASE:Inherit(self,ZONE_BASE:New(ZoneName))
-self:F({ZoneName,Detection,Radius})
-self.Detection=Detection
-self.Radius=Radius
-return self
-end
-function ZONE_DETECTION:BoundZone(Points,CountryID,UnBound)
-local Point={}
-local Vec2=self:GetVec2()
-Points=Points and Points or 360
-local Angle
-local RadialBase=math.pi*2
-for Angle=0,360,(360/Points)do
-local Radial=Angle*RadialBase/360
-Point.x=Vec2.x+math.cos(Radial)*self:GetRadius()
-Point.y=Vec2.y+math.sin(Radial)*self:GetRadius()
-local CountryName=_DATABASE.COUNTRY_NAME[CountryID]
-local Tire={
-["country"]=CountryName,
-["category"]="Fortifications",
-["canCargo"]=false,
-["shape_name"]="H-tyre_B_WF",
-["type"]="Black_Tyre_WF",
-["y"]=Point.y,
-["x"]=Point.x,
-["name"]=string.format("%s-Tire #%0d",self:GetName(),Angle),
-["heading"]=0,
-}
-local Group=coalition.addStaticObject(CountryID,Tire)
-if UnBound and UnBound==true then
-Group:destroy()
-end
-end
-return self
-end
-function ZONE_DETECTION:SmokeZone(SmokeColor,Points,AddHeight,AngleOffset)
-self:F2(SmokeColor)
-local Point={}
-local Vec2=self:GetVec2()
-AddHeight=AddHeight or 0
-AngleOffset=AngleOffset or 0
-Points=Points and Points or 360
-local Angle
-local RadialBase=math.pi*2
-for Angle=0,360,360/Points do
-local Radial=(Angle+AngleOffset)*RadialBase/360
-Point.x=Vec2.x+math.cos(Radial)*self:GetRadius()
-Point.y=Vec2.y+math.sin(Radial)*self:GetRadius()
-POINT_VEC2:New(Point.x,Point.y,AddHeight):Smoke(SmokeColor)
-end
-return self
-end
-function ZONE_DETECTION:FlareZone(FlareColor,Points,Azimuth,AddHeight)
-self:F2({FlareColor,Azimuth})
-local Point={}
-local Vec2=self:GetVec2()
-AddHeight=AddHeight or 0
-Points=Points and Points or 360
-local Angle
-local RadialBase=math.pi*2
-for Angle=0,360,360/Points do
-local Radial=Angle*RadialBase/360
-Point.x=Vec2.x+math.cos(Radial)*self:GetRadius()
-Point.y=Vec2.y+math.sin(Radial)*self:GetRadius()
-POINT_VEC2:New(Point.x,Point.y,AddHeight):Flare(FlareColor,Azimuth)
-end
-return self
-end
-function ZONE_DETECTION:GetRadius()
-self:F2(self.ZoneName)
-self:T2({self.Radius})
-return self.Radius
-end
-function ZONE_DETECTION:SetRadius(Radius)
-self:F2(self.ZoneName)
-self.Radius=Radius
-self:T2({self.Radius})
-return self.Radius
-end
-function ZONE_DETECTION:IsVec2InZone(Vec2)
-self:F2(Vec2)
-local Coordinates=self.Detection:GetDetectedItemCoordinates()
-for CoordinateID,Coordinate in pairs(Coordinates)do
-local ZoneVec2=Coordinate:GetVec2()
-if ZoneVec2 then
-if((Vec2.x-ZoneVec2.x)^2+(Vec2.y-ZoneVec2.y)^2)^0.5<=self:GetRadius()then
-return true
-end
-end
-end
-return false
-end
-function ZONE_DETECTION:IsVec3InZone(Vec3)
-self:F2(Vec3)
-local InZone=self:IsVec2InZone({x=Vec3.x,y=Vec3.z})
-return InZone
-end
-ZONE_BASE={
-ClassName="ZONE_BASE",
-ZoneName="",
-ZoneProbability=1,
-DrawID=nil,
-Color={},
-ZoneID=nil,
-Properties={},
-Surface=nil,
-Checktime=5,
-}
-function ZONE_BASE:New(ZoneName)
-local self=BASE:Inherit(self,FSM:New())
-self:F(ZoneName)
-self.ZoneName=ZoneName
-return self
-end
-function ZONE_BASE:GetName()
-self:F2()
-return self.ZoneName
-end
-function ZONE_BASE:SetName(ZoneName)
-self:F2()
-self.ZoneName=ZoneName
-end
-function ZONE_BASE:IsVec2InZone(Vec2)
-self:F2(Vec2)
-return false
-end
-function ZONE_BASE:IsVec3InZone(Vec3)
-if not Vec3 then return false end
-local InZone=self:IsVec2InZone({x=Vec3.x,y=Vec3.z})
-return InZone
-end
-function ZONE_BASE:IsCoordinateInZone(Coordinate)
-if not Coordinate then return false end
-local InZone=self:IsVec2InZone(Coordinate:GetVec2())
-return InZone
-end
-function ZONE_BASE:IsPointVec2InZone(Coordinate)
-local InZone=self:IsVec2InZone(Coordinate:GetVec2())
-return InZone
-end
-function ZONE_BASE:IsPointVec3InZone(PointVec3)
-local InZone=self:IsPointVec2InZone(PointVec3)
-return InZone
-end
-function ZONE_BASE:GetVec2()
-return nil
-end
-function ZONE_BASE:GetPointVec2()
-self:F2(self.ZoneName)
-local Vec2=self:GetVec2()
-local PointVec2=POINT_VEC2:NewFromVec2(Vec2)
-self:T2({PointVec2})
-return PointVec2
-end
-function ZONE_BASE:GetVec3(Height)
-self:F2(self.ZoneName)
-Height=Height or 0
-local Vec2=self:GetVec2()
-local Vec3={x=Vec2.x,y=Height and Height or land.getHeight(self:GetVec2()),z=Vec2.y}
-self:T2({Vec3})
-return Vec3
-end
-function ZONE_BASE:GetPointVec3(Height)
-self:F2(self.ZoneName)
-local Vec3=self:GetVec3(Height)
-local PointVec3=POINT_VEC3:NewFromVec3(Vec3)
-self:T2({PointVec3})
-return PointVec3
-end
-function ZONE_BASE:GetCoordinate(Height)
-self:F2(self.ZoneName)
-local Vec3=self:GetVec3(Height)
-if self.Coordinate then
-self.Coordinate.x=Vec3.x
-self.Coordinate.y=Vec3.y
-self.Coordinate.z=Vec3.z
-else
-self.Coordinate=COORDINATE:NewFromVec3(Vec3)
-end
-return self.Coordinate
-end
-function ZONE_BASE:Get2DDistance(Coordinate)
-local a=self:GetVec2()
-local b={}
-if Coordinate.z then
-b.x=Coordinate.x
-b.y=Coordinate.z
-else
-b.x=Coordinate.x
-b.y=Coordinate.y
-end
-local dist=UTILS.VecDist2D(a,b)
-return dist
-end
-function ZONE_BASE:GetRandomVec2()
-return nil
-end
-function ZONE_BASE:GetRandomPointVec2()
-return nil
-end
-function ZONE_BASE:GetRandomPointVec3()
-return nil
-end
-function ZONE_BASE:GetBoundingSquare()
-return nil
-end
-function ZONE_BASE:GetSurfaceType()
-local coord=self:GetCoordinate()
-local surface=coord:GetSurfaceType()
-return surface
-end
-function ZONE_BASE:BoundZone()
-self:F2()
-end
-function ZONE_BASE:SetDrawCoalition(Coalition)
-self.drawCoalition=Coalition or-1
-return self
-end
-function ZONE_BASE:GetDrawCoalition()
-return self.drawCoalition or-1
-end
-function ZONE_BASE:SetColor(RGBcolor,Alpha)
-RGBcolor=RGBcolor or{1,0,0}
-Alpha=Alpha or 0.15
-self.Color={}
-self.Color[1]=RGBcolor[1]
-self.Color[2]=RGBcolor[2]
-self.Color[3]=RGBcolor[3]
-self.Color[4]=Alpha
-return self
-end
-function ZONE_BASE:GetColor()
-return self.Color or{1,0,0,0.15}
-end
-function ZONE_BASE:GetColorRGB()
-local rgb={}
-local Color=self:GetColor()
-rgb[1]=Color[1]
-rgb[2]=Color[2]
-rgb[3]=Color[3]
-return rgb
-end
-function ZONE_BASE:GetColorAlpha()
-local Color=self:GetColor()
-local alpha=Color[4]
-return alpha
-end
-function ZONE_BASE:SetFillColor(RGBcolor,Alpha)
-RGBcolor=RGBcolor or{1,0,0}
-Alpha=Alpha or 0.15
-self.FillColor={}
-self.FillColor[1]=RGBcolor[1]
-self.FillColor[2]=RGBcolor[2]
-self.FillColor[3]=RGBcolor[3]
-self.FillColor[4]=Alpha
-return self
-end
-function ZONE_BASE:GetFillColor()
-return self.FillColor or{1,0,0,0.15}
-end
-function ZONE_BASE:GetFillColorRGB()
-local rgb={}
-local FillColor=self:GetFillColor()
-rgb[1]=FillColor[1]
-rgb[2]=FillColor[2]
-rgb[3]=FillColor[3]
-return rgb
-end
-function ZONE_BASE:GetFillColorAlpha()
-local FillColor=self:GetFillColor()
-local alpha=FillColor[4]
-return alpha
-end
-function ZONE_BASE:UndrawZone(Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,ZONE_BASE.UndrawZone,self)
-else
-if self.DrawID then
-if type(self.DrawID)~="table"then
-UTILS.RemoveMark(self.DrawID)
-else
-for _,mark_id in pairs(self.DrawID)do
-UTILS.RemoveMark(mark_id)
-end
-end
-end
-end
-return self
-end
-function ZONE_BASE:GetDrawID()
-return self.DrawID
-end
-function ZONE_BASE:SmokeZone(SmokeColor)
-self:F2(SmokeColor)
-end
-function ZONE_BASE:SetZoneProbability(ZoneProbability)
-self:F({self:GetName(),ZoneProbability=ZoneProbability})
-self.ZoneProbability=ZoneProbability or 1
-return self
-end
-function ZONE_BASE:GetZoneProbability()
-self:F2()
-return self.ZoneProbability
-end
-function ZONE_BASE:GetZoneMaybe()
-self:F2()
-local Randomization=math.random()
-if Randomization<=self.ZoneProbability then
-return self
-else
-return nil
-end
-end
-function ZONE_BASE:SetCheckTime(seconds)
-self.Checktime=seconds or 5
-return self
-end
-function ZONE_BASE:Trigger(Objects)
-self:SetStartState("TriggerStopped")
-self:AddTransition("TriggerStopped","TriggerStart","TriggerRunning")
-self:AddTransition("*","EnteredZone","*")
-self:AddTransition("*","LeftZone","*")
-self:AddTransition("*","TriggerRunCheck","*")
-self:AddTransition("*","TriggerStop","TriggerStopped")
-self:TriggerStart()
-self.checkobjects=Objects
-if UTILS.IsInstanceOf(Objects,"SET_BASE")then
-self.objectset=Objects.Set
-else
-self.objectset={Objects}
-end
-self:_TriggerCheck(true)
-self:__TriggerRunCheck(self.Checktime)
-return self
-end
-function ZONE_BASE:_TriggerCheck(fromstart)
-local objectset=self.objectset or{}
-if fromstart then
-for _,_object in pairs(objectset)do
-local obj=_object
-if not obj.TriggerInZone then obj.TriggerInZone={}end
-if obj and obj:IsAlive()and self:IsCoordinateInZone(obj:GetCoordinate())then
-obj.TriggerInZone[self.ZoneName]=true
-else
-obj.TriggerInZone[self.ZoneName]=false
-end
-end
-else
-for _,_object in pairs(objectset)do
-local obj=_object
-if obj and obj:IsAlive()then
-if not obj.TriggerInZone then
-obj.TriggerInZone={}
-end
-if not obj.TriggerInZone[self.ZoneName]then
-obj.TriggerInZone[self.ZoneName]=false
-end
-local inzone=self:IsCoordinateInZone(obj:GetCoordinate())
-if inzone and not obj.TriggerInZone[self.ZoneName]then
-self:__EnteredZone(0.5,obj)
-obj.TriggerInZone[self.ZoneName]=true
-elseif(not inzone)and obj.TriggerInZone[self.ZoneName]then
-self:__LeftZone(0.5,obj)
-obj.TriggerInZone[self.ZoneName]=false
-else
-end
-end
-end
-end
-return self
-end
-function ZONE_BASE:onafterTriggerRunCheck(From,Event,To)
-if self:GetState()~="TriggerStopped"then
-self:_TriggerCheck()
-self:__TriggerRunCheck(self.Checktime)
-end
-return self
-end
-function ZONE_BASE:GetProperty(PropertyName)
-return self.Properties[PropertyName]
-end
-function ZONE_BASE:GetAllProperties()
-return self.Properties
-end
-ZONE_RADIUS={
-ClassName="ZONE_RADIUS",
-}
-function ZONE_RADIUS:New(ZoneName,Vec2,Radius,DoNotRegisterZone)
-local self=BASE:Inherit(self,ZONE_BASE:New(ZoneName))
-self:F({ZoneName,Vec2,Radius})
-self.Radius=Radius
-self.Vec2=Vec2
-if not DoNotRegisterZone then
-_EVENTDISPATCHER:CreateEventNewZone(self)
-end
-return self
-end
-function ZONE_RADIUS:UpdateFromVec2(Vec2,Radius)
-self.Vec2=Vec2
-if Radius then
-self.Radius=Radius
-end
-return self
-end
-function ZONE_RADIUS:UpdateFromVec3(Vec3,Radius)
-self.Vec2.x=Vec3.x
-self.Vec2.y=Vec3.z
-if Radius then
-self.Radius=Radius
-end
-return self
-end
-function ZONE_RADIUS:MarkZone(Points)
-local Point={}
-local Vec2=self:GetVec2()
-Points=Points and Points or 360
-local Angle
-local RadialBase=math.pi*2
-for Angle=0,360,(360/Points)do
-local Radial=Angle*RadialBase/360
-Point.x=Vec2.x+math.cos(Radial)*self:GetRadius()
-Point.y=Vec2.y+math.sin(Radial)*self:GetRadius()
-COORDINATE:NewFromVec2(Point):MarkToAll(self:GetName())
-end
-end
-function ZONE_RADIUS:DrawZone(Coalition,Color,Alpha,FillColor,FillAlpha,LineType,ReadOnly)
-local coordinate=self:GetCoordinate()
-local Radius=self:GetRadius()
-Color=Color or self:GetColorRGB()
-Alpha=Alpha or 1
-FillColor=FillColor or UTILS.DeepCopy(Color)
-FillAlpha=FillAlpha or self:GetColorAlpha()
-self.DrawID=coordinate:CircleToAll(Radius,Coalition,Color,Alpha,FillColor,FillAlpha,LineType,ReadOnly)
-return self
-end
-function ZONE_RADIUS:BoundZone(Points,CountryID,UnBound)
-local Point={}
-local Vec2=self:GetVec2()
-Points=Points and Points or 360
-local Angle
-local RadialBase=math.pi*2
-for Angle=0,360,(360/Points)do
-local Radial=Angle*RadialBase/360
-Point.x=Vec2.x+math.cos(Radial)*self:GetRadius()
-Point.y=Vec2.y+math.sin(Radial)*self:GetRadius()
-local CountryName=_DATABASE.COUNTRY_NAME[CountryID]
-local Tire={
-["country"]=CountryName,
-["category"]="Fortifications",
-["canCargo"]=false,
-["shape_name"]="H-tyre_B_WF",
-["type"]="Black_Tyre_WF",
-["y"]=Point.y,
-["x"]=Point.x,
-["name"]=string.format("%s-Tire #%0d",self:GetName(),Angle),
-["heading"]=0,
-}
-local Group=coalition.addStaticObject(CountryID,Tire)
-if UnBound and UnBound==true then
-Group:destroy()
-end
-end
-return self
-end
-function ZONE_RADIUS:SmokeZone(SmokeColor,Points,AddHeight,AngleOffset)
-self:F2(SmokeColor)
-local Point={}
-local Vec2=self:GetVec2()
-AddHeight=AddHeight or 0
-AngleOffset=AngleOffset or 0
-Points=Points and Points or 360
-local Angle
-local RadialBase=math.pi*2
-for Angle=0,360,360/Points do
-local Radial=(Angle+AngleOffset)*RadialBase/360
-Point.x=Vec2.x+math.cos(Radial)*self:GetRadius()
-Point.y=Vec2.y+math.sin(Radial)*self:GetRadius()
-POINT_VEC2:New(Point.x,Point.y,AddHeight):Smoke(SmokeColor)
-end
-return self
-end
-function ZONE_RADIUS:FlareZone(FlareColor,Points,Azimuth,AddHeight)
-self:F2({FlareColor,Azimuth})
-local Point={}
-local Vec2=self:GetVec2()
-AddHeight=AddHeight or 0
-Points=Points and Points or 360
-local Angle
-local RadialBase=math.pi*2
-for Angle=0,360,360/Points do
-local Radial=Angle*RadialBase/360
-Point.x=Vec2.x+math.cos(Radial)*self:GetRadius()
-Point.y=Vec2.y+math.sin(Radial)*self:GetRadius()
-POINT_VEC2:New(Point.x,Point.y,AddHeight):Flare(FlareColor,Azimuth)
-end
-return self
-end
-function ZONE_RADIUS:GetRadius()
-self:F2(self.ZoneName)
-self:T2({self.Radius})
-return self.Radius
-end
-function ZONE_RADIUS:SetRadius(Radius)
-self:F2(self.ZoneName)
-self.Radius=Radius
-self:T2({self.Radius})
-return self.Radius
-end
-function ZONE_RADIUS:GetVec2()
-self:F2(self.ZoneName)
-self:T2({self.Vec2})
-return self.Vec2
-end
-function ZONE_RADIUS:SetVec2(Vec2)
-self:F2(self.ZoneName)
-self.Vec2=Vec2
-self:T2({self.Vec2})
-return self.Vec2
-end
-function ZONE_RADIUS:GetVec3(Height)
-self:F2({self.ZoneName,Height})
-Height=Height or 0
-local Vec2=self:GetVec2()
-local Vec3={x=Vec2.x,y=land.getHeight(self:GetVec2())+Height,z=Vec2.y}
-self:T2({Vec3})
-return Vec3
-end
-function ZONE_RADIUS:Scan(ObjectCategories,UnitCategories)
-self.ScanData={}
-self.ScanData.Coalitions={}
-self.ScanData.Scenery={}
-self.ScanData.SceneryTable={}
-self.ScanData.Units={}
-local ZoneCoord=self:GetCoordinate()
-local ZoneRadius=self:GetRadius()
-local SphereSearch={
-id=world.VolumeType.SPHERE,
-params={
-point=ZoneCoord:GetVec3(),
-radius=ZoneRadius,
-}
-}
-local function EvaluateZone(ZoneObject)
-if ZoneObject then
-local ObjectCategory=Object.getCategory(ZoneObject)
-if(ObjectCategory==Object.Category.UNIT and ZoneObject:isExist()and ZoneObject:isActive())or(ObjectCategory==Object.Category.STATIC and ZoneObject:isExist())then
-local CoalitionDCSUnit=ZoneObject:getCoalition()
-local Include=false
-if not UnitCategories then
-Include=true
-else
-local CategoryDCSUnit=ZoneObject:getDesc().category
-for UnitCategoryID,UnitCategory in pairs(UnitCategories)do
-if UnitCategory==CategoryDCSUnit then
-Include=true
-break
-end
-end
-end
-if Include then
-local CoalitionDCSUnit=ZoneObject:getCoalition()
-self.ScanData.Coalitions[CoalitionDCSUnit]=true
-self.ScanData.Units[ZoneObject]=ZoneObject
-self:F2({Name=ZoneObject:getName(),Coalition=CoalitionDCSUnit})
-end
-end
-if ObjectCategory==Object.Category.SCENERY then
-local SceneryType=ZoneObject:getTypeName()
-local SceneryName=ZoneObject:getName()
-self.ScanData.Scenery[SceneryType]=self.ScanData.Scenery[SceneryType]or{}
-self.ScanData.Scenery[SceneryType][SceneryName]=SCENERY:Register(tostring(SceneryName),ZoneObject)
-table.insert(self.ScanData.SceneryTable,self.ScanData.Scenery[SceneryType][SceneryName])
-self:T({SCENERY=self.ScanData.Scenery[SceneryType][SceneryName]})
-end
-end
-return true
-end
-world.searchObjects(ObjectCategories,SphereSearch,EvaluateZone)
-end
-function ZONE_RADIUS:RemoveJunk()
-local radius=self.Radius
-local vec3=self:GetVec3()
-local volS={
-id=world.VolumeType.SPHERE,
-params={point=vec3,radius=radius}
-}
-local n=world.removeJunk(volS)
-return n
-end
-function ZONE_RADIUS:GetScannedUnits()
-return self.ScanData.Units
-end
-function ZONE_RADIUS:GetScannedSetUnit()
-local SetUnit=SET_UNIT:New()
-if self.ScanData then
-for ObjectID,UnitObject in pairs(self.ScanData.Units)do
-local UnitObject=UnitObject
-if UnitObject:isExist()then
-local FoundUnit=UNIT:FindByName(UnitObject:getName())
-if FoundUnit then
-SetUnit:AddUnit(FoundUnit)
-else
-local FoundStatic=STATIC:FindByName(UnitObject:getName())
-if FoundStatic then
-SetUnit:AddUnit(FoundStatic)
-end
-end
-end
-end
-end
-return SetUnit
-end
-function ZONE_RADIUS:GetScannedSetGroup()
-self.ScanSetGroup=self.ScanSetGroup or SET_GROUP:New()
-self.ScanSetGroup.Set={}
-if self.ScanData then
-for ObjectID,UnitObject in pairs(self.ScanData.Units)do
-local UnitObject=UnitObject
-if UnitObject:isExist()then
-local FoundUnit=UNIT:FindByName(UnitObject:getName())
-if FoundUnit then
-local group=FoundUnit:GetGroup()
-self.ScanSetGroup:AddGroup(group)
-end
-end
-end
-end
-return self.ScanSetGroup
-end
-function ZONE_RADIUS:CountScannedCoalitions()
-local Count=0
-for CoalitionID,Coalition in pairs(self.ScanData.Coalitions)do
-Count=Count+1
-end
-return Count
-end
-function ZONE_RADIUS:CheckScannedCoalition(Coalition)
-if Coalition then
-return self.ScanData.Coalitions[Coalition]
-end
-return nil
-end
-function ZONE_RADIUS:GetScannedCoalition(Coalition)
-if Coalition then
-return self.ScanData.Coalitions[Coalition]
-else
-local Count=0
-local ReturnCoalition=nil
-for CoalitionID,Coalition in pairs(self.ScanData.Coalitions)do
-Count=Count+1
-ReturnCoalition=CoalitionID
-end
-if Count~=1 then
-ReturnCoalition=nil
-end
-return ReturnCoalition
-end
-end
-function ZONE_RADIUS:GetScannedSceneryType(SceneryType)
-return self.ScanData.Scenery[SceneryType]
-end
-function ZONE_RADIUS:GetScannedScenery()
-return self.ScanData.Scenery
-end
-function ZONE_RADIUS:GetScannedSceneryObjects()
-return self.ScanData.SceneryTable
-end
-function ZONE_RADIUS:GetScannedSetScenery()
-local scenery=SET_SCENERY:New()
-local objects=self:GetScannedSceneryObjects()
-for _,_obj in pairs(objects)do
-scenery:AddScenery(_obj)
-end
-return scenery
-end
-function ZONE_RADIUS:IsAllInZoneOfCoalition(Coalition)
-return self:CountScannedCoalitions()==1 and self:GetScannedCoalition(Coalition)==true
-end
-function ZONE_RADIUS:IsAllInZoneOfOtherCoalition(Coalition)
-return self:CountScannedCoalitions()==1 and self:GetScannedCoalition(Coalition)==nil
-end
-function ZONE_RADIUS:IsSomeInZoneOfCoalition(Coalition)
-return self:CountScannedCoalitions()>1 and self:GetScannedCoalition(Coalition)==true
-end
-function ZONE_RADIUS:IsNoneInZoneOfCoalition(Coalition)
-return self:GetScannedCoalition(Coalition)==nil
-end
-function ZONE_RADIUS:IsNoneInZone()
-return self:CountScannedCoalitions()==0
-end
-function ZONE_RADIUS:SearchZone(EvaluateFunction,ObjectCategories)
-local SearchZoneResult=true
-local ZoneCoord=self:GetCoordinate()
-local ZoneRadius=self:GetRadius()
-self:F({ZoneCoord=ZoneCoord,ZoneRadius=ZoneRadius,ZoneCoordLL=ZoneCoord:ToStringLLDMS()})
-local SphereSearch={
-id=world.VolumeType.SPHERE,
-params={
-point=ZoneCoord:GetVec3(),
-radius=ZoneRadius/2,
-}
-}
-local function EvaluateZone(ZoneDCSUnit)
-local ZoneUnit=UNIT:Find(ZoneDCSUnit)
-return EvaluateFunction(ZoneUnit)
-end
-world.searchObjects(Object.Category.UNIT,SphereSearch,EvaluateZone)
-end
-function ZONE_RADIUS:IsVec2InZone(Vec2)
-self:F2(Vec2)
-if not Vec2 then return false end
-local ZoneVec2=self:GetVec2()
-if ZoneVec2 then
-if((Vec2.x-ZoneVec2.x)^2+(Vec2.y-ZoneVec2.y)^2)^0.5<=self:GetRadius()then
-return true
-end
-end
-return false
-end
-function ZONE_RADIUS:IsVec3InZone(Vec3)
-self:F2(Vec3)
-if not Vec3 then return false end
-local InZone=self:IsVec2InZone({x=Vec3.x,y=Vec3.z})
-return InZone
-end
-function ZONE_RADIUS:GetRandomVec2(inner,outer,surfacetypes)
-local Vec2=self:GetVec2()
-local _inner=inner or 0
-local _outer=outer or self:GetRadius()
-if surfacetypes and type(surfacetypes)~="table"then
-surfacetypes={surfacetypes}
-end
-local function _getpoint()
-local point={}
-local angle=math.random()*math.pi*2
-point.x=Vec2.x+math.cos(angle)*math.random(_inner,_outer)
-point.y=Vec2.y+math.sin(angle)*math.random(_inner,_outer)
-return point
-end
-local function _checkSurface(point)
-local stype=land.getSurfaceType(point)
-for _,sf in pairs(surfacetypes)do
-if sf==stype then
-return true
-end
-end
-return false
-end
-local point=_getpoint()
-if surfacetypes then
-local N=1;local Nmax=100;local gotit=false
-while gotit==false and N<=Nmax do
-gotit=_checkSurface(point)
-if gotit then
-else
-point=_getpoint()
-N=N+1
-end
-end
-end
-return point
-end
-function ZONE_RADIUS:GetRandomPointVec2(inner,outer)
-self:F(self.ZoneName,inner,outer)
-local PointVec2=POINT_VEC2:NewFromVec2(self:GetRandomVec2(inner,outer))
-self:T3({PointVec2})
-return PointVec2
-end
-function ZONE_RADIUS:GetRandomVec3(inner,outer)
-self:F(self.ZoneName,inner,outer)
-local Vec2=self:GetRandomVec2(inner,outer)
-self:T3({x=Vec2.x,y=self.y,z=Vec2.y})
-return{x=Vec2.x,y=self.y,z=Vec2.y}
-end
-function ZONE_RADIUS:GetRandomPointVec3(inner,outer)
-self:F(self.ZoneName,inner,outer)
-local PointVec3=POINT_VEC3:NewFromVec2(self:GetRandomVec2(inner,outer))
-self:T3({PointVec3})
-return PointVec3
-end
-function ZONE_RADIUS:GetRandomCoordinate(inner,outer,surfacetypes)
-local vec2=self:GetRandomVec2(inner,outer,surfacetypes)
-local Coordinate=COORDINATE:NewFromVec2(vec2)
-return Coordinate
-end
-function ZONE_RADIUS:GetRandomCoordinateWithoutBuildings(inner,outer,distance,markbuildings,markfinal)
-local dist=distance or 100
-local objects={}
-if self.ScanData and self.ScanData.Scenery then
-objects=self:GetScannedScenery()
-else
-self:Scan({Object.Category.SCENERY})
-objects=self:GetScannedScenery()
-end
-local T0=timer.getTime()
-local T1=timer.getTime()
-local buildings={}
-local buildingzones={}
-if self.ScanData and self.ScanData.BuildingCoordinates then
-buildings=self.ScanData.BuildingCoordinates
-buildingzones=self.ScanData.BuildingZones
-else
-for _,_object in pairs(objects)do
-for _,_scen in pairs(_object)do
-local scenery=_scen
-local description=scenery:GetDesc()
-if description and description.attributes and description.attributes.Buildings then
-if markbuildings then
-MARKER:New(scenery:GetCoordinate(),"Building"):ToAll()
-end
-buildings[#buildings+1]=scenery:GetCoordinate()
-local bradius=scenery:GetBoundingRadius()or dist
-local bzone=ZONE_RADIUS:New("Building-"..math.random(1,100000),scenery:GetVec2(),bradius,false)
-buildingzones[#buildingzones+1]=bzone
-end
-end
-end
-self.ScanData.BuildingCoordinates=buildings
-self.ScanData.BuildingZones=buildingzones
-end
-local rcoord=nil
-local found=true
-local iterations=0
-for i=1,1000 do
-iterations=iterations+1
-rcoord=self:GetRandomCoordinate(inner,outer)
-found=true
-for _,_coord in pairs(buildingzones)do
-local zone=_coord
-if zone:IsPointVec2InZone(rcoord)then
-found=false
-break
-end
-end
-if found then
-if markfinal then
-MARKER:New(rcoord,"FREE"):ToAll()
-end
-break
-end
-end
-if not found then
-local rcoord=nil
-local found=true
-local iterations=0
-for i=1,1000 do
-iterations=iterations+1
-rcoord=self:GetRandomCoordinate(inner,outer)
-found=true
-for _,_coord in pairs(buildings)do
-local coord=_coord
-if coord:Get3DDistance(rcoord)0)or(d2>0)or(d3>0)
-return not(has_neg and has_pos)
-end
-function _ZONE_TRIANGLE:GetRandomVec2(points)
-points=points or self.Points
-local pt={math.random(),math.random()}
-table.sort(pt)
-local s=pt[1]
-local t=pt[2]-pt[1]
-local u=1-pt[2]
-return{x=s*points[1].x+t*points[2].x+u*points[3].x,
-y=s*points[1].y+t*points[2].y+u*points[3].y}
-end
-function _ZONE_TRIANGLE:Draw(Coalition,Color,Alpha,FillColor,FillAlpha,LineType,ReadOnly)
-Coalition=Coalition or-1
-Color=Color or{1,0,0}
-Alpha=Alpha or 1
-FillColor=FillColor or Color
-if not FillColor then UTILS.DeepCopy(Color)end
-FillAlpha=FillAlpha or Alpha
-if not FillAlpha then FillAlpha=1 end
-for i=1,#self.Coords do
-local c1=self.Coords[i]
-local c2=self.Coords[i%#self.Coords+1]
-local id=c1:LineToAll(c2,Coalition,Color,Alpha,LineType,ReadOnly)
-self.DrawID[#self.DrawID+1]=id
-end
-local newID=self.Coords[1]:MarkupToAllFreeForm({self.Coords[2],self.Coords[3]},Coalition,Color,Alpha,FillColor,FillAlpha,LineType,ReadOnly)
-self.DrawID[#self.DrawID+1]=newID
-return self.DrawID
-end
-function _ZONE_TRIANGLE:Fill(Coalition,FillColor,FillAlpha,ReadOnly)
-Coalition=Coalition or-1
-FillColor=FillColor
-FillAlpha=FillAlpha
-local newID=self.Coords[1]:MarkupToAllFreeForm({self.Coords[2],self.Coords[3]},Coalition,nil,nil,FillColor,FillAlpha,0,nil)
-self.DrawID[#self.DrawID+1]=newID
-return self.DrawID
-end
-ZONE_POLYGON_BASE={
-ClassName="ZONE_POLYGON_BASE",
-_Triangles={},
-SurfaceArea=0,
-DrawID={},
-FillTriangles={},
-Borderlines={},
-}
-function ZONE_POLYGON_BASE:New(ZoneName,PointsArray)
-local self=BASE:Inherit(self,ZONE_BASE:New(ZoneName))
-self:F({ZoneName,PointsArray})
-if PointsArray then
-self._.Polygon={}
-for i=1,#PointsArray do
-self._.Polygon[i]={}
-self._.Polygon[i].x=PointsArray[i].x
-self._.Polygon[i].y=PointsArray[i].y
-end
-self._Triangles=self:_Triangulate()
-self.SurfaceArea=self:_CalculateSurfaceArea()
-end
-return self
-end
-function ZONE_POLYGON_BASE:_Triangulate()
-local points=self._.Polygon
-local triangles={}
-local function get_orientation(shape_points)
-local sum=0
-for i=1,#shape_points do
-local j=i%#shape_points+1
-sum=sum+(shape_points[j].x-shape_points[i].x)*(shape_points[j].y+shape_points[i].y)
-end
-return sum>=0 and"clockwise"or"counter-clockwise"
-end
-local function ensure_clockwise(shape_points)
-local orientation=get_orientation(shape_points)
-if orientation=="counter-clockwise"then
-local reversed={}
-for i=#shape_points,1,-1 do
-table.insert(reversed,shape_points[i])
-end
-return reversed
-end
-return shape_points
-end
-local function is_clockwise(p1,p2,p3)
-local cross_product=(p2.x-p1.x)*(p3.y-p1.y)-(p2.y-p1.y)*(p3.x-p1.x)
-return cross_product<0
-end
-local function divide_recursively(shape_points)
-if#shape_points==3 then
-table.insert(triangles,_ZONE_TRIANGLE:New(shape_points[1],shape_points[2],shape_points[3]))
-elseif#shape_points>3 then
-for i,p1 in ipairs(shape_points)do
-local p2=shape_points[(i%#shape_points)+1]
-local p3=shape_points[(i+1)%#shape_points+1]
-local triangle=_ZONE_TRIANGLE:New(p1,p2,p3)
-local is_ear=true
-if not is_clockwise(p1,p2,p3)then
-is_ear=false
-else
-for _,point in ipairs(shape_points)do
-if point~=p1 and point~=p2 and point~=p3 and triangle:ContainsPoint(point)then
-is_ear=false
-break
-end
-end
-end
-if is_ear then
-local is_valid_triangle=true
-for _,point in ipairs(points)do
-if point~=p1 and point~=p2 and point~=p3 and triangle:ContainsPoint(point)then
-is_valid_triangle=false
-break
-end
-end
-if is_valid_triangle then
-table.insert(triangles,triangle)
-local remaining_points={}
-for j,point in ipairs(shape_points)do
-if point~=p2 then
-table.insert(remaining_points,point)
-end
-end
-divide_recursively(remaining_points)
-break
-end
-else
-end
-end
-end
-end
-points=ensure_clockwise(points)
-divide_recursively(points)
-return triangles
-end
-function ZONE_POLYGON_BASE:UpdateFromVec2(Vec2Array)
-self._.Polygon={}
-for i=1,#Vec2Array do
-self._.Polygon[i]={}
-self._.Polygon[i].x=Vec2Array[i].x
-self._.Polygon[i].y=Vec2Array[i].y
-end
-self._Triangles=self:_Triangulate()
-self.SurfaceArea=self:_CalculateSurfaceArea()
-return self
-end
-function ZONE_POLYGON_BASE:UpdateFromVec3(Vec3Array)
-self._.Polygon={}
-for i=1,#Vec3Array do
-self._.Polygon[i]={}
-self._.Polygon[i].x=Vec3Array[i].x
-self._.Polygon[i].y=Vec3Array[i].z
-end
-self._Triangles=self:_Triangulate()
-self.SurfaceArea=self:_CalculateSurfaceArea()
-return self
-end
-function ZONE_POLYGON_BASE:_CalculateSurfaceArea()
-local area=0
-for _,triangle in pairs(self._Triangles)do
-area=area+triangle.SurfaceArea
-end
-return area
-end
-function ZONE_POLYGON_BASE:GetVec2()
-self:F(self.ZoneName)
-local Bounds=self:GetBoundingSquare()
-return{x=(Bounds.x2+Bounds.x1)/2,y=(Bounds.y2+Bounds.y1)/2}
-end
-function ZONE_POLYGON_BASE:GetVertexVec2(Index)
-return self._.Polygon[Index or 1]
-end
-function ZONE_POLYGON_BASE:GetVertexVec3(Index)
-local vec2=self:GetVertexVec2(Index)
-if vec2 then
-local vec3={x=vec2.x,y=land.getHeight(vec2),z=vec2.y}
-return vec3
-end
-return nil
-end
-function ZONE_POLYGON_BASE:GetVertexCoordinate(Index)
-local vec2=self:GetVertexVec2(Index)
-if vec2 then
-local coord=COORDINATE:NewFromVec2(vec2)
-return coord
-end
-return nil
-end
-function ZONE_POLYGON_BASE:GetVerticiesVec2()
-return self._.Polygon
-end
-function ZONE_POLYGON_BASE:GetVerticiesVec3()
-local coords={}
-for i,vec2 in ipairs(self._.Polygon)do
-local vec3={x=vec2.x,y=land.getHeight(vec2),z=vec2.y}
-table.insert(coords,vec3)
-end
-return coords
-end
-function ZONE_POLYGON_BASE:GetVerticiesCoordinates()
-local coords={}
-for i,vec2 in ipairs(self._.Polygon)do
-local coord=COORDINATE:NewFromVec2(vec2)
-table.insert(coords,coord)
-end
-return coords
-end
-function ZONE_POLYGON_BASE:Flush()
-self:F2()
-self:F({Polygon=self.ZoneName,Coordinates=self._.Polygon})
-return self
-end
-function ZONE_POLYGON_BASE:BoundZone(UnBound)
-local i
-local j
-local Segments=10
-i=1
-j=#self._.Polygon
-while i<=#self._.Polygon do
-self:T({i,j,self._.Polygon[i],self._.Polygon[j]})
-local DeltaX=self._.Polygon[j].x-self._.Polygon[i].x
-local DeltaY=self._.Polygon[j].y-self._.Polygon[i].y
-for Segment=0,Segments do
-local PointX=self._.Polygon[i].x+(Segment*DeltaX/Segments)
-local PointY=self._.Polygon[i].y+(Segment*DeltaY/Segments)
-local Tire={
-["country"]="USA",
-["category"]="Fortifications",
-["canCargo"]=false,
-["shape_name"]="H-tyre_B_WF",
-["type"]="Black_Tyre_WF",
-["y"]=PointY,
-["x"]=PointX,
-["name"]=string.format("%s-Tire #%0d",self:GetName(),((i-1)*Segments)+Segment),
-["heading"]=0,
-}
-local Group=coalition.addStaticObject(country.id.USA,Tire)
-if UnBound and UnBound==true then
-Group:destroy()
-end
-end
-j=i
-i=i+1
-end
-return self
-end
-function ZONE_POLYGON_BASE:DrawZone(Coalition,Color,Alpha,FillColor,FillAlpha,LineType,ReadOnly,IncludeTriangles)
-if self._.Polygon and#self._.Polygon>=3 then
-Coalition=Coalition or self:GetDrawCoalition()
-self:SetDrawCoalition(Coalition)
-Color=Color or self:GetColorRGB()
-Alpha=Alpha or self:GetColorAlpha()
-FillColor=FillColor or self:GetFillColorRGB()
-FillAlpha=FillAlpha or self:GetFillColorAlpha()
-if FillColor then
-self:ReFill(FillColor,FillAlpha)
-end
-if Color then
-self:ReDrawBorderline(Color,Alpha,LineType)
-end
-end
-return self
-end
-function ZONE_POLYGON_BASE:ReFill(Color,Alpha)
-local color=Color or self:GetFillColorRGB()or{1,0,0}
-local alpha=Alpha or self:GetFillColorAlpha()or 1
-local coalition=self:GetDrawCoalition()or-1
-if#self.FillTriangles>0 then
-for _,triangle in pairs(self._Triangles)do
-triangle:UndrawZone()
-end
-for _,_value in pairs(self.FillTriangles)do
-table.remove_by_value(self.DrawID,_value)
-end
-self.FillTriangles=nil
-self.FillTriangles={}
-end
-for _,triangle in pairs(self._Triangles)do
-local draw_ids=triangle:Fill(coalition,color,alpha,nil)
-self.FillTriangles=draw_ids
-table.combine(self.DrawID,draw_ids)
-end
-return self
-end
-function ZONE_POLYGON_BASE:ReDrawBorderline(Color,Alpha,LineType)
-local color=Color or self:GetFillColorRGB()or{1,0,0}
-local alpha=Alpha or self:GetFillColorAlpha()or 1
-local coalition=self:GetDrawCoalition()or-1
-local linetype=LineType or 1
-if#self.Borderlines>0 then
-for _,MarkID in pairs(self.Borderlines)do
-trigger.action.removeMark(MarkID)
-end
-for _,_value in pairs(self.Borderlines)do
-table.remove_by_value(self.DrawID,_value)
-end
-self.Borderlines=nil
-self.Borderlines={}
-end
-local coords=self:GetVerticiesCoordinates()
-for i=1,#coords do
-local c1=coords[i]
-local c2=coords[i%#coords+1]
-local newID=c1:LineToAll(c2,coalition,color,alpha,linetype,nil)
-self.DrawID[#self.DrawID+1]=newID
-self.Borderlines[#self.Borderlines+1]=newID
-end
-return self
-end
-function ZONE_POLYGON_BASE:GetSurfaceArea()
-return self.SurfaceArea
-end
-function ZONE_POLYGON_BASE:GetRadius()
-local center=self:GetVec2()
-local radius=0
-for _,_vec2 in pairs(self._.Polygon)do
-local vec2=_vec2
-local r=UTILS.VecDist2D(center,vec2)
-if r>radius then
-radius=r
-end
-end
-return radius
-end
-function ZONE_POLYGON_BASE:GetZoneRadius(ZoneName,DoNotRegisterZone)
-local center=self:GetVec2()
-local radius=self:GetRadius()
-local zone=ZONE_RADIUS:New(ZoneName or self.ZoneName,center,radius,DoNotRegisterZone)
-return zone
-end
-function ZONE_POLYGON_BASE:GetZoneQuad(ZoneName,DoNotRegisterZone)
-local vec1,vec3=self:GetBoundingVec2()
-local vec2={x=vec1.x,y=vec3.y}
-local vec4={x=vec3.x,y=vec1.y}
-local zone=ZONE_POLYGON_BASE:New(ZoneName or self.ZoneName,{vec1,vec2,vec3,vec4})
-return zone
-end
-function ZONE_POLYGON_BASE:RemoveJunk(Height)
-Height=Height or 1000
-local vec2SW,vec2NE=self:GetBoundingVec2()
-local vec3SW={x=vec2SW.x,y=-Height,z=vec2SW.y}
-local vec3NE={x=vec2NE.x,y=Height,z=vec2NE.y}
-local volume={
-id=world.VolumeType.BOX,
-params={
-min=vec3SW,
-max=vec3SW
-}
-}
-local n=world.removeJunk(volume)
-return n
-end
-function ZONE_POLYGON_BASE:SmokeZone(SmokeColor,Segments)
-self:F2(SmokeColor)
-Segments=Segments or 10
-local i=1
-local j=#self._.Polygon
-while i<=#self._.Polygon do
-self:T({i,j,self._.Polygon[i],self._.Polygon[j]})
-local DeltaX=self._.Polygon[j].x-self._.Polygon[i].x
-local DeltaY=self._.Polygon[j].y-self._.Polygon[i].y
-for Segment=0,Segments do
-local PointX=self._.Polygon[i].x+(Segment*DeltaX/Segments)
-local PointY=self._.Polygon[i].y+(Segment*DeltaY/Segments)
-POINT_VEC2:New(PointX,PointY):Smoke(SmokeColor)
-end
-j=i
-i=i+1
-end
-return self
-end
-function ZONE_POLYGON_BASE:FlareZone(FlareColor,Segments,Azimuth,AddHeight)
-self:F2(FlareColor)
-Segments=Segments or 10
-AddHeight=AddHeight or 0
-local i=1
-local j=#self._.Polygon
-while i<=#self._.Polygon do
-self:T({i,j,self._.Polygon[i],self._.Polygon[j]})
-local DeltaX=self._.Polygon[j].x-self._.Polygon[i].x
-local DeltaY=self._.Polygon[j].y-self._.Polygon[i].y
-for Segment=0,Segments do
-local PointX=self._.Polygon[i].x+(Segment*DeltaX/Segments)
-local PointY=self._.Polygon[i].y+(Segment*DeltaY/Segments)
-POINT_VEC2:New(PointX,PointY,AddHeight):Flare(FlareColor,Azimuth)
-end
-j=i
-i=i+1
-end
-return self
-end
-function ZONE_POLYGON_BASE:IsVec2InZone(Vec2)
-self:F2(Vec2)
-if not Vec2 then return false end
-local Next
-local Prev
-local InPolygon=false
-Next=1
-Prev=#self._.Polygon
-while Next<=#self._.Polygon do
-self:T({Next,Prev,self._.Polygon[Next],self._.Polygon[Prev]})
-if(((self._.Polygon[Next].y>Vec2.y)~=(self._.Polygon[Prev].y>Vec2.y))and
-(Vec2.x<(self._.Polygon[Prev].x-self._.Polygon[Next].x)*(Vec2.y-self._.Polygon[Next].y)/(self._.Polygon[Prev].y-self._.Polygon[Next].y)+self._.Polygon[Next].x)
-)then
-InPolygon=not InPolygon
-end
-self:T2({InPolygon=InPolygon})
-Prev=Next
-Next=Next+1
-end
-self:T({InPolygon=InPolygon})
-return InPolygon
-end
-function ZONE_POLYGON_BASE:IsVec3InZone(Vec3)
-self:F2(Vec3)
-if not Vec3 then return false end
-local InZone=self:IsVec2InZone({x=Vec3.x,y=Vec3.z})
-return InZone
-end
-function ZONE_POLYGON_BASE:GetRandomVec2()
-local weights={}
-for _,triangle in pairs(self._Triangles)do
-weights[triangle]=triangle.SurfaceArea/self.SurfaceArea
-end
-local random_weight=math.random()
-local accumulated_weight=0
-for triangle,weight in pairs(weights)do
-accumulated_weight=accumulated_weight+weight
-if accumulated_weight>=random_weight then
-return triangle:GetRandomVec2()
-end
-end
-end
-function ZONE_POLYGON_BASE:GetRandomPointVec2()
-self:F2()
-local PointVec2=POINT_VEC2:NewFromVec2(self:GetRandomVec2())
-self:T2(PointVec2)
-return PointVec2
-end
-function ZONE_POLYGON_BASE:GetRandomPointVec3()
-self:F2()
-local PointVec3=POINT_VEC3:NewFromVec2(self:GetRandomVec2())
-self:T2(PointVec3)
-return PointVec3
-end
-function ZONE_POLYGON_BASE:GetRandomCoordinate()
-self:F2()
-local Coordinate=COORDINATE:NewFromVec2(self:GetRandomVec2())
-self:T2(Coordinate)
-return Coordinate
-end
-function ZONE_POLYGON_BASE:GetBoundingSquare()
-local x1=self._.Polygon[1].x
-local y1=self._.Polygon[1].y
-local x2=self._.Polygon[1].x
-local y2=self._.Polygon[1].y
-for i=2,#self._.Polygon do
-self:T2({self._.Polygon[i],x1,y1,x2,y2})
-x1=(x1>self._.Polygon[i].x)and self._.Polygon[i].x or x1
-x2=(x2self._.Polygon[i].y)and self._.Polygon[i].y or y1
-y2=(y2self._.Polygon[i].x)and self._.Polygon[i].x or x1
-x2=(x2self._.Polygon[i].y)and self._.Polygon[i].y or y1
-y2=(y21 and self:GetScannedCoalition(Coalition)==true
-end
-function ZONE_POLYGON:IsNoneInZoneOfCoalition(Coalition)
-return self:GetScannedCoalition(Coalition)==nil
-end
-function ZONE_POLYGON:IsNoneInZone()
-return self:CountScannedCoalitions()==0
-end
-end
-do
-ZONE_ELASTIC={
-ClassName="ZONE_ELASTIC",
-points={},
-setGroups={}
-}
-function ZONE_ELASTIC:New(ZoneName,Points)
-local self=BASE:Inherit(self,ZONE_POLYGON_BASE:New(ZoneName,Points))
-_EVENTDISPATCHER:CreateEventNewZone(self)
-if Points then
-self.points=Points
-end
-return self
-end
-function ZONE_ELASTIC:AddVertex2D(Vec2)
-table.insert(self.points,Vec2)
-return self
-end
-function ZONE_ELASTIC:AddVertex3D(Vec3)
-table.insert(self.points,{x=Vec3.x,y=Vec3.z})
-return self
-end
-function ZONE_ELASTIC:AddSetGroup(GroupSet)
-table.insert(self.setGroups,GroupSet)
-return self
-end
-function ZONE_ELASTIC:Update(Delay,Draw)
-self:T(string.format("Updating ZONE_ELASTIC %s",tostring(self.ZoneName)))
-local points=UTILS.DeepCopy(self.points or{})
-if self.setGroups then
-for _,_setGroup in pairs(self.setGroups)do
-local setGroup=_setGroup
-for _,_group in pairs(setGroup.Set)do
-local group=_group
-if group and group:IsAlive()then
-table.insert(points,group:GetVec2())
-end
-end
-end
-end
-self._.Polygon=self:_ConvexHull(points)
-if Draw~=false then
-if self.DrawID or Draw==true then
-self:UndrawZone()
-self:DrawZone()
-end
-end
-return self
-end
-function ZONE_ELASTIC:StartUpdate(Tstart,dT,Tstop,Draw)
-self.updateID=self:ScheduleRepeat(Tstart,dT,0,Tstop,ZONE_ELASTIC.Update,self,0,Draw)
-return self
-end
-function ZONE_ELASTIC:StopUpdate(Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,ZONE_ELASTIC.StopUpdate,self)
-else
-if self.updateID then
-self:ScheduleStop(self.updateID)
-self.updateID=nil
-end
-end
-return self
-end
-function ZONE_ELASTIC:_ConvexHull(pl)
-if#pl==0 then
-return{}
-end
-table.sort(pl,function(left,right)
-return left.x(b.y-a.y)*(c.x-a.x)
-end
-for i,pt in pairs(pl)do
-while#h>=2 and not ccw(h[#h-1],h[#h],pt)do
-table.remove(h,#h)
-end
-table.insert(h,pt)
-end
-local t=#h+1
-for i=#pl,1,-1 do
-local pt=pl[i]
-while#h>=t and not ccw(h[#h-1],h[#h],pt)do
-table.remove(h,#h)
-end
-table.insert(h,pt)
-end
-table.remove(h,#h)
-return h
-end
-end
-ZONE_OVAL={
-ClassName="OVAL",
-ZoneName="",
-MajorAxis=nil,
-MinorAxis=nil,
-Angle=0,
-DrawPoly=nil
-}
-function ZONE_OVAL:New(name,vec2,major_axis,minor_axis,angle)
-self=BASE:Inherit(self,ZONE_BASE:New())
-self.ZoneName=name
-self.CenterVec2=vec2
-self.MajorAxis=major_axis
-self.MinorAxis=minor_axis
-self.Angle=angle or 0
-_DATABASE:AddZone(name,self)
-return self
-end
-function ZONE_OVAL:NewFromDrawing(DrawingName)
-self=BASE:Inherit(self,ZONE_BASE:New(DrawingName))
-for _,layer in pairs(env.mission.drawings.layers)do
-for _,object in pairs(layer["objects"])do
-if string.find(object["name"],DrawingName,1,true)then
-if object["polygonMode"]=="oval"then
-self.CenterVec2={x=object["mapX"],y=object["mapY"]}
-self.MajorAxis=object["r1"]
-self.MinorAxis=object["r2"]
-self.Angle=object["angle"]
-end
-end
-end
-end
-_DATABASE:AddZone(DrawingName,self)
-return self
-end
-function ZONE_OVAL:GetMajorAxis()
-return self.MajorAxis
-end
-function ZONE_OVAL:GetMinorAxis()
-return self.MinorAxis
-end
-function ZONE_OVAL:GetAngle()
-return self.Angle
-end
-function ZONE_OVAL:GetVec2()
-return self.CenterVec2
-end
-function ZONE_OVAL:IsVec2InZone(vec2)
-local cos,sin=math.cos,math.sin
-local dx=vec2.x-self.CenterVec2.x
-local dy=vec2.y-self.CenterVec2.y
-local rx=dx*cos(self.Angle)+dy*sin(self.Angle)
-local ry=-dx*sin(self.Angle)+dy*cos(self.Angle)
-return rx*rx/(self.MajorAxis*self.MajorAxis)+ry*ry/(self.MinorAxis*self.MinorAxis)<=1
-end
-function ZONE_OVAL:GetBoundingSquare()
-local min_x=self.CenterVec2.x-self.MajorAxis
-local min_y=self.CenterVec2.y-self.MinorAxis
-local max_x=self.CenterVec2.x+self.MajorAxis
-local max_y=self.CenterVec2.y+self.MinorAxis
-return{
-{x=min_x,y=min_x},{x=max_x,y=min_y},{x=max_x,y=max_y},{x=min_x,y=max_y}
-}
-end
-function ZONE_OVAL:PointsOnEdge(num_points)
-num_points=num_points or 40
-local points={}
-local dtheta=2*math.pi/num_points
-for i=0,num_points-1 do
-local theta=i*dtheta
-local x=self.CenterVec2.x+self.MajorAxis*math.cos(theta)*math.cos(self.Angle)-self.MinorAxis*math.sin(theta)*math.sin(self.Angle)
-local y=self.CenterVec2.y+self.MajorAxis*math.cos(theta)*math.sin(self.Angle)+self.MinorAxis*math.sin(theta)*math.cos(self.Angle)
-table.insert(points,{x=x,y=y})
-end
-return points
-end
-function ZONE_OVAL:GetRandomVec2()
-local theta=math.rad(self.Angle)
-local random_point=math.sqrt(math.random())
-local phi=math.random()*2*math.pi
-local x_c=random_point*math.cos(phi)
-local y_c=random_point*math.sin(phi)
-local x_e=x_c*self.MajorAxis
-local y_e=y_c*self.MinorAxis
-local rx=(x_e*math.cos(theta)-y_e*math.sin(theta))+self.CenterVec2.x
-local ry=(x_e*math.sin(theta)+y_e*math.cos(theta))+self.CenterVec2.y
-return{x=rx,y=ry}
-end
-function ZONE_OVAL:GetRandomPointVec2()
-return POINT_VEC2:NewFromVec2(self:GetRandomVec2())
-end
-function ZONE_OVAL:GetRandomPointVec3()
-return POINT_VEC2:NewFromVec3(self:GetRandomVec2())
-end
-function ZONE_OVAL:DrawZone(Coalition,Color,Alpha,FillColor,FillAlpha,LineType)
-Coalition=Coalition or self:GetDrawCoalition()
-self:SetDrawCoalition(Coalition)
-Color=Color or self:GetColorRGB()
-Alpha=Alpha or 1
-self:SetColor(Color,Alpha)
-FillColor=FillColor or self:GetFillColorRGB()
-if not FillColor then
-UTILS.DeepCopy(Color)
-end
-FillAlpha=FillAlpha or self:GetFillColorAlpha()
-if not FillAlpha then
-FillAlpha=0.15
-end
-LineType=LineType or 1
-self:SetFillColor(FillColor,FillAlpha)
-self.DrawPoly=ZONE_POLYGON:NewFromPointsArray(self.ZoneName,self:PointsOnEdge(80))
-self.DrawPoly:DrawZone(Coalition,Color,Alpha,FillColor,FillAlpha,LineType)
-end
-function ZONE_OVAL:UndrawZone()
-if self.DrawPoly then
-self.DrawPoly:UndrawZone()
-end
-end
-do
-ZONE_AIRBASE={
-ClassName="ZONE_AIRBASE",
-}
-function ZONE_AIRBASE:New(AirbaseName,Radius)
-Radius=Radius or 4000
-local Airbase=AIRBASE:FindByName(AirbaseName)
-local self=BASE:Inherit(self,ZONE_RADIUS:New(AirbaseName,Airbase:GetVec2(),Radius,true))
-self._.ZoneAirbase=Airbase
-self._.ZoneVec2Cache=self._.ZoneAirbase:GetVec2()
-if Airbase:IsShip()then
-self.isShip=true
-self.isHelipad=false
-self.isAirdrome=false
-elseif Airbase:IsHelipad()then
-self.isShip=false
-self.isHelipad=true
-self.isAirdrome=false
-elseif Airbase:IsAirdrome()then
-self.isShip=false
-self.isHelipad=false
-self.isAirdrome=true
-end
-_EVENTDISPATCHER:CreateEventNewZone(self)
-return self
-end
-function ZONE_AIRBASE:GetAirbase()
-return self._.ZoneAirbase
-end
-function ZONE_AIRBASE:GetVec2()
-self:F(self.ZoneName)
-local ZoneVec2=nil
-if self._.ZoneAirbase:IsAlive()then
-ZoneVec2=self._.ZoneAirbase:GetVec2()
-self._.ZoneVec2Cache=ZoneVec2
-else
-ZoneVec2=self._.ZoneVec2Cache
-end
-self:T({ZoneVec2})
-return ZoneVec2
-end
-function ZONE_AIRBASE:GetRandomPointVec2(inner,outer)
-self:F(self.ZoneName,inner,outer)
-local PointVec2=POINT_VEC2:NewFromVec2(self:GetRandomVec2())
-self:T3({PointVec2})
-return PointVec2
-end
-end
-PATHLINE={
-ClassName="PATHLINE",
-lid=nil,
-points={},
-}
-PATHLINE.version="0.1.0"
-function PATHLINE:New(Name)
-local self=BASE:Inherit(self,BASE:New())
-self.name=Name or"Unknown Path"
-self.lid=string.format("PATHLINE %s | ",Name)
-return self
-end
-function PATHLINE:NewFromVec2Array(Name,Vec2Array)
-local self=PATHLINE:New(Name)
-for i=1,#Vec2Array do
-self:AddPointFromVec2(Vec2Array[i])
-end
-return self
-end
-function PATHLINE:NewFromVec3Array(Name,Vec3Array)
-local self=PATHLINE:New(Name)
-for i=1,#Vec3Array do
-self:AddPointFromVec3(Vec3Array[i])
-end
-return self
-end
-function PATHLINE:FindByName(Name)
-local pathline=_DATABASE:FindPathline(Name)
-return pathline
-end
-function PATHLINE:AddPointFromVec2(Vec2)
-if Vec2 then
-local point=self:_CreatePoint(Vec2)
-table.insert(self.points,point)
-end
-return self
-end
-function PATHLINE:AddPointFromVec3(Vec3)
-if Vec3 then
-local point=self:_CreatePoint(Vec3)
-table.insert(self.points,point)
-end
-return self
-end
-function PATHLINE:GetName()
-return self.name
-end
-function PATHLINE:GetNumberOfPoints()
-local N=#self.points
-return N
-end
-function PATHLINE:GetPoints()
-return self.points
-end
-function PATHLINE:GetPoints3D()
-local vecs={}
-for _,_point in pairs(self.points)do
-local point=_point
-table.insert(vecs,point.vec3)
-end
-return vecs
-end
-function PATHLINE:GetPoints2D()
-local vecs={}
-for _,_point in pairs(self.points)do
-local point=_point
-table.insert(vecs,point.vec2)
-end
-return vecs
-end
-function PATHLINE:GetCoordinats()
-local vecs={}
-for _,_point in pairs(self.points)do
-local point=_point
-local coord=COORDINATE:NewFromVec3(point.vec3)
-end
-return vecs
-end
-function PATHLINE:GetPointFromIndex(n)
-local N=self:GetNumberOfPoints()
-n=n or 1
-local point=nil
-if n>=1 and n<=N then
-point=self.point[n]
-else
-self:E(self.lid..string.format("ERROR: No point in pathline for N=%s",tostring(n)))
-end
-return point
-end
-function PATHLINE:GetPoint3DFromIndex(n)
-local point=self:GetPointFromIndex(n)
-if point then
-return point.vec3
-end
-return nil
-end
-function PATHLINE:GetPoint2DFromIndex(n)
-local point=self:GetPointFromIndex(n)
-if point then
-return point.vec2
-end
-return nil
-end
-function PATHLINE:MarkPoints(Switch)
-for i,_point in pairs(self.points)do
-local point=_point
-if Switch==false then
-if point.markerID then
-UTILS.RemoveMark(point.markerID,Delay)
-end
-else
-if point.markerID then
-UTILS.RemoveMark(point.markerID)
-end
-point.markerID=UTILS.GetMarkID()
-local text=string.format("Pathline %s: Point #%d\nSurface Type=%d\nHeight=%.1f m\nDepth=%.1f m",self.name,i,point.surfaceType,point.landHeight,point.depth)
-trigger.action.markToAll(point.markerID,text,point.vec3,"")
-end
-end
-end
-function PATHLINE:_CreatePoint(Vec)
-local point={}
-if Vec.z then
-point.vec3=UTILS.DeepCopy(Vec)
-point.vec2={x=Vec.x,y=Vec.z}
-else
-point.vec2=UTILS.DeepCopy(Vec)
-point.vec3={x=Vec.x,y=land.getHeight(Vec),z=Vec.y}
-end
-point.surfaceType=land.getSurfaceType(point.vec2)
-point.landHeight,point.depth=land.getSurfaceHeightWithSeabed(point.vec2)
-point.markerID=nil
-return point
-end
-AIRBASE={
-ClassName="AIRBASE",
-CategoryName={
-[Airbase.Category.AIRDROME]="Airdrome",
-[Airbase.Category.HELIPAD]="Helipad",
-[Airbase.Category.SHIP]="Ship",
-},
-activerwyno=nil,
-}
-AIRBASE.Caucasus={
-["Gelendzhik"]="Gelendzhik",
-["Krasnodar_Pashkovsky"]="Krasnodar-Pashkovsky",
-["Sukhumi_Babushara"]="Sukhumi-Babushara",
-["Gudauta"]="Gudauta",
-["Batumi"]="Batumi",
-["Senaki_Kolkhi"]="Senaki-Kolkhi",
-["Kobuleti"]="Kobuleti",
-["Kutaisi"]="Kutaisi",
-["Tbilisi_Lochini"]="Tbilisi-Lochini",
-["Soganlug"]="Soganlug",
-["Vaziani"]="Vaziani",
-["Anapa_Vityazevo"]="Anapa-Vityazevo",
-["Krasnodar_Center"]="Krasnodar-Center",
-["Novorossiysk"]="Novorossiysk",
-["Krymsk"]="Krymsk",
-["Maykop_Khanskaya"]="Maykop-Khanskaya",
-["Sochi_Adler"]="Sochi-Adler",
-["Mineralnye_Vody"]="Mineralnye Vody",
-["Nalchik"]="Nalchik",
-["Mozdok"]="Mozdok",
-["Beslan"]="Beslan",
-}
-AIRBASE.Nevada={
-["Creech_AFB"]="Creech",
-["Groom_Lake_AFB"]="Groom Lake",
-["McCarran_International_Airport"]="McCarran International",
-["Nellis_AFB"]="Nellis",
-["Beatty_Airport"]="Beatty",
-["Boulder_City_Airport"]="Boulder City",
-["Echo_Bay"]="Echo Bay",
-["Henderson_Executive_Airport"]="Henderson Executive",
-["Jean_Airport"]="Jean",
-["Laughlin_Airport"]="Laughlin",
-["Lincoln_County"]="Lincoln County",
-["Mesquite"]="Mesquite",
-["Mina_Airport"]="Mina",
-["North_Las_Vegas"]="North Las Vegas",
-["Pahute_Mesa_Airstrip"]="Pahute Mesa",
-["Tonopah_Airport"]="Tonopah",
-["Tonopah_Test_Range_Airfield"]="Tonopah Test Range",
-}
-AIRBASE.Normandy={
-["Saint_Pierre_du_Mont"]="Saint Pierre du Mont",
-["Lignerolles"]="Lignerolles",
-["Cretteville"]="Cretteville",
-["Maupertus"]="Maupertus",
-["Brucheville"]="Brucheville",
-["Meautis"]="Meautis",
-["Cricqueville_en_Bessin"]="Cricqueville-en-Bessin",
-["Lessay"]="Lessay",
-["Sainte_Laurent_sur_Mer"]="Sainte-Laurent-sur-Mer",
-["Biniville"]="Biniville",
-["Cardonville"]="Cardonville",
-["Deux_Jumeaux"]="Deux Jumeaux",
-["Chippelle"]="Chippelle",
-["Beuzeville"]="Beuzeville",
-["Azeville"]="Azeville",
-["Picauville"]="Picauville",
-["Le_Molay"]="Le Molay",
-["Longues_sur_Mer"]="Longues-sur-Mer",
-["Carpiquet"]="Carpiquet",
-["Bazenville"]="Bazenville",
-["Sainte_Croix_sur_Mer"]="Sainte-Croix-sur-Mer",
-["Beny_sur_Mer"]="Beny-sur-Mer",
-["Rucqueville"]="Rucqueville",
-["Sommervieu"]="Sommervieu",
-["Lantheuil"]="Lantheuil",
-["Evreux"]="Evreux",
-["Chailey"]="Chailey",
-["Needs_Oar_Point"]="Needs Oar Point",
-["Funtington"]="Funtington",
-["Tangmere"]="Tangmere",
-["Ford"]="Ford",
-["Argentan"]="Argentan",
-["Goulet"]="Goulet",
-["Barville"]="Barville",
-["Essay"]="Essay",
-["Hauterive"]="Hauterive",
-["Lymington"]="Lymington",
-["Vrigny"]="Vrigny",
-["Odiham"]="Odiham",
-["Conches"]="Conches",
-["West_Malling"]="West Malling",
-["Villacoublay"]="Villacoublay",
-["Kenley"]="Kenley",
-["Beauvais_Tille"]="Beauvais-Tille",
-["Cormeilles_en_Vexin"]="Cormeilles-en-Vexin",
-["Creil"]="Creil",
-["Guyancourt"]="Guyancourt",
-["Lonrai"]="Lonrai",
-["Dinan_Trelivan"]="Dinan-Trelivan",
-["Heathrow"]="Heathrow",
-["Fecamp_Benouville"]="Fecamp-Benouville",
-["Farnborough"]="Farnborough",
-["Friston"]="Friston",
-["Deanland "]="Deanland ",
-["Triqueville"]="Triqueville",
-["Poix"]="Poix",
-["Orly"]="Orly",
-["Stoney_Cross"]="Stoney Cross",
-["Amiens_Glisy"]="Amiens-Glisy",
-["Ronai"]="Ronai",
-["Rouen_Boos"]="Rouen-Boos",
-["Deauville"]="Deauville",
-["Saint_Aubin"]="Saint-Aubin",
-["Flers"]="Flers",
-["Avranches_Le_Val_Saint_Pere"]="Avranches Le Val-Saint-Pere",
-["Gravesend"]="Gravesend",
-["Beaumont_le_Roger"]="Beaumont-le-Roger",
-["Broglie"]="Broglie",
-["Bernay_Saint_Martin"]="Bernay Saint Martin",
-["Saint_Andre_de_lEure"]="Saint-Andre-de-lEure",
-["Biggin_Hill"]="Biggin Hill",
-["Manston"]="Manston",
-["Detling"]="Detling",
-["Lympne"]="Lympne",
-["Abbeville_Drucat"]="Abbeville Drucat",
-["Merville_Calonne"]="Merville Calonne",
-["Saint_Omer_Wizernes"]="Saint-Omer Wizernes",
-}
-AIRBASE.PersianGulf={
-["Abu_Dhabi_International_Airport"]="Abu Dhabi Intl",
-["Abu_Musa_Island_Airport"]="Abu Musa Island",
-["Al_Ain_International_Airport"]="Al Ain Intl",
-["Al_Bateen_Airport"]="Al-Bateen",
-["Al_Dhafra_AB"]="Al Dhafra AFB",
-["Al_Maktoum_Intl"]="Al Maktoum Intl",
-["Al_Minhad_AB"]="Al Minhad AFB",
-["Bandar_Abbas_Intl"]="Bandar Abbas Intl",
-["Bandar_Lengeh"]="Bandar Lengeh",
-["Bandar_e_Jask_airfield"]="Bandar-e-Jask",
-["Dubai_Intl"]="Dubai Intl",
-["Fujairah_Intl"]="Fujairah Intl",
-["Havadarya"]="Havadarya",
-["Jiroft_Airport"]="Jiroft",
-["Kerman_Airport"]="Kerman",
-["Khasab"]="Khasab",
-["Kish_International_Airport"]="Kish Intl",
-["Lar_Airbase"]="Lar",
-["Lavan_Island_Airport"]="Lavan Island",
-["Liwa_Airbase"]="Liwa AFB",
-["Qeshm_Island"]="Qeshm Island",
-["Ras_Al_Khaimah"]="Ras Al Khaimah Intl",
-["Sas_Al_Nakheel_Airport"]="Sas Al Nakheel",
-["Sharjah_Intl"]="Sharjah Intl",
-["Shiraz_International_Airport"]="Shiraz Intl",
-["Sir_Abu_Nuayr"]="Sir Abu Nuayr",
-["Sirri_Island"]="Sirri Island",
-["Tunb_Island_AFB"]="Tunb Island AFB",
-["Tunb_Kochak"]="Tunb Kochak",
-}
-AIRBASE.TheChannel={
-["Abbeville_Drucat"]="Abbeville Drucat",
-["Merville_Calonne"]="Merville Calonne",
-["Saint_Omer_Longuenesse"]="Saint Omer Longuenesse",
-["Dunkirk_Mardyck"]="Dunkirk Mardyck",
-["Manston"]="Manston",
-["Hawkinge"]="Hawkinge",
-["Lympne"]="Lympne",
-["Detling"]="Detling",
-["High_Halden"]="High Halden",
-["Biggin_Hill"]="Biggin Hill",
-["Eastchurch"]="Eastchurch",
-["Headcorn"]="Headcorn",
-}
-AIRBASE.Syria={
-["Kuweires"]="Kuweires",
-["Marj_Ruhayyil"]="Marj Ruhayyil",
-["Kiryat_Shmona"]="Kiryat Shmona",
-["Marj_as_Sultan_North"]="Marj as Sultan North",
-["Eyn_Shemer"]="Eyn Shemer",
-["Incirlik"]="Incirlik",
-["Damascus"]="Damascus",
-["Bassel_Al_Assad"]="Bassel Al-Assad",
-["Rosh_Pina"]="Rosh Pina",
-["Aleppo"]="Aleppo",
-["Al_Qusayr"]="Al Qusayr",
-["Wujah_Al_Hajar"]="Wujah Al Hajar",
-["Al_Dumayr"]="Al-Dumayr",
-["Gazipasa"]="Gazipasa",
-["Hatay"]="Hatay",
-["Pinarbashi"]="Pinarbashi",
-["Paphos"]="Paphos",
-["Kingsfield"]="Kingsfield",
-["Thalah"]="Tha'lah",
-["Haifa"]="Haifa",
-["Khalkhalah"]="Khalkhalah",
-["Megiddo"]="Megiddo",
-["Lakatamia"]="Lakatamia",
-["Rayak"]="Rayak",
-["Larnaca"]="Larnaca",
-["Mezzeh"]="Mezzeh",
-["Gecitkale"]="Gecitkale",
-["Akrotiri"]="Akrotiri",
-["Naqoura"]="Naqoura",
-["Gaziantep"]="Gaziantep",
-["Sayqal"]="Sayqal",
-["Tiyas"]="Tiyas",
-["Shayrat"]="Shayrat",
-["Taftanaz"]="Taftanaz",
-["H4"]="H4",
-["King_Hussein_Air_College"]="King Hussein Air College",
-["Rene_Mouawad"]="Rene Mouawad",
-["Jirah"]="Jirah",
-["Ramat_David"]="Ramat David",
-["Qabr_as_Sitt"]="Qabr as Sitt",
-["Minakh"]="Minakh",
-["Adana_Sakirpasa"]="Adana Sakirpasa",
-["Palmyra"]="Palmyra",
-["Hama"]="Hama",
-["Ercan"]="Ercan",
-["Marj_as_Sultan_South"]="Marj as Sultan South",
-["Tabqa"]="Tabqa",
-["Beirut_Rafic_Hariri"]="Beirut-Rafic Hariri",
-["An_Nasiriyah"]="An Nasiriyah",
-["Abu_al_Duhur"]="Abu al-Duhur",
-["At_Tanf"]="At Tanf",
-["H3"]="H3",
-["H3_Northwest"]="H3 Northwest",
-["H3_Southwest"]="H3 Southwest",
-["Kharab_Ishk"]="Kharab Ishk",
-["Ruwayshid"]="Ruwayshid",
-["Sanliurfa"]="Sanliurfa",
-["Tal_Siman"]="Tal Siman",
-["Deir_ez_Zor"]="Deir ez-Zor",
-}
-AIRBASE.MarianaIslands={
-["Rota_Intl"]="Rota Intl",
-["Andersen_AFB"]="Andersen AFB",
-["Antonio_B_Won_Pat_Intl"]="Antonio B. Won Pat Intl",
-["Saipan_Intl"]="Saipan Intl",
-["Tinian_Intl"]="Tinian Intl",
-["Olf_Orote"]="Olf Orote",
-["Pagan_Airstrip"]="Pagan Airstrip",
-["North_West_Field"]="North West Field",
-}
-AIRBASE.SouthAtlantic={
-["Port_Stanley"]="Port Stanley",
-["Mount_Pleasant"]="Mount Pleasant",
-["San_Carlos_FOB"]="San Carlos FOB",
-["Rio_Grande"]="Rio Grande",
-["Rio_Gallegos"]="Rio Gallegos",
-["Ushuaia"]="Ushuaia",
-["Ushuaia_Helo_Port"]="Ushuaia Helo Port",
-["Punta_Arenas"]="Punta Arenas",
-["Pampa_Guanaco"]="Pampa Guanaco",
-["San_Julian"]="San Julian",
-["Puerto_Williams"]="Puerto Williams",
-["Puerto_Natales"]="Puerto Natales",
-["El_Calafate"]="El Calafate",
-["Puerto_Santa_Cruz"]="Puerto Santa Cruz",
-["Comandante_Luis_Piedrabuena"]="Comandante Luis Piedrabuena",
-["Aerodromo_De_Tolhuin"]="Aerodromo De Tolhuin",
-["Porvenir_Airfield"]="Porvenir Airfield",
-["Almirante_Schroeders"]="Almirante Schroeders",
-["Rio_Turbio"]="Rio Turbio",
-["Rio_Chico"]="Rio Chico",
-["Franco_Bianco"]="Franco Bianco",
-["Goose_Green"]="Goose Green",
-["Hipico_Flying_Club"]="Hipico Flying Club",
-["CaletaTortel"]="CaletaTortel",
-["Aeropuerto_de_Gobernador_Gregores"]="Aeropuerto de Gobernador Gregores",
-["Aerodromo_O_Higgins"]="Aerodromo O'Higgins",
-["Cullen_Airport"]="Cullen Airport",
-["Gull_Point"]="Gull Point",
-}
-AIRBASE.Sinai={
-["Hatzerim"]="Hatzerim",
-["Abu_Suwayr"]="Abu Suwayr",
-["Sde_Dov"]="Sde Dov",
-["AzZaqaziq"]="AzZaqaziq",
-["Hatzor"]="Hatzor",
-["Kedem"]="Kedem",
-["Nevatim"]="Nevatim",
-["Cairo_International_Airport"]="Cairo International Airport",
-["Al_Ismailiyah"]="Al Ismailiyah",
-["As_Salihiyah"]="As Salihiyah",
-["Fayed"]="Fayed",
-["Bilbeis_Air_Base"]="Bilbeis Air Base",
-["Ramon_Airbase"]="Ramon Airbase",
-["Kibrit_Air_Base"]="Kibrit Air Base",
-["El_Arish"]="El Arish",
-["Ovda"]="Ovda",
-["Melez"]="Melez",
-["Al_Mansurah"]="Al Mansurah",
-["Palmahim"]="Palmahim",
-["Baluza"]="Baluza",
-["El_Gora"]="El Gora",
-["Difarsuwar_Airfield"]="Difarsuwar Airfield",
-["Wadi_al_Jandali"]="Wadi al Jandali",
-["St_Catherine"]="St Catherine",
-["Tel_Nof"]="Tel Nof",
-["Abu_Rudeis"]="Abu Rudeis",
-["Inshas_Airbase"]="Inshas Airbase",
-["Ben_Gurion"]="Ben-Gurion",
-["Bir_Hasanah"]="Bir Hasanah",
-["Cairo_West"]="Cairo West",
-}
-AIRBASE.TerminalType={
-Runway=16,
-HelicopterOnly=40,
-Shelter=68,
-OpenMed=72,
-OpenBig=104,
-OpenMedOrBig=176,
-HelicopterUsable=216,
-FighterAircraft=244,
-}
-AIRBASE.SpotStatus={
-FREE="Free",
-OCCUPIED="Occupied",
-RESERVED="Reserved",
-}
-function AIRBASE:Register(AirbaseName)
-local self=BASE:Inherit(self,POSITIONABLE:New(AirbaseName))
-self.AirbaseName=AirbaseName
-self.AirbaseID=self:GetID(true)
-self.descriptors=self:GetDesc()
-self.category=self.descriptors and self.descriptors.category or Airbase.Category.AIRDROME
-if self.category==Airbase.Category.AIRDROME then
-self.isAirdrome=true
-elseif self.category==Airbase.Category.HELIPAD then
-self.isHelipad=true
-elseif self.category==Airbase.Category.SHIP then
-self.isShip=true
-if self.descriptors.typeName=="Oil rig"or self.descriptors.typeName=="Ga"then
-self.isHelipad=true
-self.isShip=false
-self.category=Airbase.Category.HELIPAD
-_DATABASE:AddStatic(AirbaseName)
-end
-else
-self:E("ERROR: Unknown airbase category!")
-end
-self:_InitRunways()
-if self.isAirdrome then
-self:SetActiveRunway()
-end
-self:_InitParkingSpots()
-local vec2=self:GetVec2()
-self:GetCoordinate()
-self.storage=_DATABASE:AddStorage(AirbaseName)
-if vec2 then
-if self.isShip then
-local unit=UNIT:FindByName(AirbaseName)
-if unit then
-self.AirbaseZone=ZONE_UNIT:New(AirbaseName,unit,2500)
-end
-else
-self.AirbaseZone=ZONE_RADIUS:New(AirbaseName,vec2,2500)
-end
-else
-self:E(string.format("ERROR: Cound not get position Vec2 of airbase %s",AirbaseName))
-end
-self:T2(string.format("Registered airbase %s",tostring(self.AirbaseName)))
-return self
-end
-function AIRBASE:Find(DCSAirbase)
-local AirbaseName=DCSAirbase:getName()
-local AirbaseFound=_DATABASE:FindAirbase(AirbaseName)
-return AirbaseFound
-end
-function AIRBASE:FindByName(AirbaseName)
-local AirbaseFound=_DATABASE:FindAirbase(AirbaseName)
-return AirbaseFound
-end
-function AIRBASE:FindByID(id)
-for name,_airbase in pairs(_DATABASE.AIRBASES)do
-local airbase=_airbase
-local aid=tonumber(airbase:GetID(true))
-if aid==id then
-return airbase
-end
-end
-return nil
-end
-function AIRBASE:GetDCSObject()
-local DCSAirbase=Airbase.getByName(self.AirbaseName)
-if DCSAirbase then
-return DCSAirbase
-end
-return nil
-end
-function AIRBASE:GetZone()
-return self.AirbaseZone
-end
-function AIRBASE:GetWarehouse()
-local warehouse=nil
-local airbase=self:GetDCSObject()
-if airbase and Airbase.getWarehouse then
-warehouse=airbase:getWarehouse()
-end
-return warehouse
-end
-function AIRBASE:GetStorage()
-return self.storage
-end
-function AIRBASE:SetAutoCapture(Switch)
-local airbase=self:GetDCSObject()
-if airbase then
-airbase:autoCapture(Switch)
-end
-return self
-end
-function AIRBASE:SetAutoCaptureON()
-self:SetAutoCapture(true)
-return self
-end
-function AIRBASE:SetAutoCaptureOFF()
-self:SetAutoCapture(false)
-return self
-end
-function AIRBASE:IsAutoCapture()
-local airbase=self:GetDCSObject()
-local auto=nil
-if airbase then
-auto=airbase:autoCaptureIsOn()
-end
-return auto
-end
-function AIRBASE:SetCoalition(Coal)
-local airbase=self:GetDCSObject()
-if airbase then
-airbase:setCoalition(Coal)
-end
-return self
-end
-function AIRBASE.GetAllAirbases(coalition,category)
-local airbases={}
-for _,_airbase in pairs(_DATABASE.AIRBASES)do
-local airbase=_airbase
-if coalition==nil or airbase:GetCoalition()==coalition then
-if category==nil or category==airbase:GetAirbaseCategory()then
-table.insert(airbases,airbase)
-end
-end
-end
-return airbases
-end
-function AIRBASE.GetAllAirbaseNames(coalition,category)
-local airbases={}
-for airbasename,_airbase in pairs(_DATABASE.AIRBASES)do
-local airbase=_airbase
-if coalition==nil or airbase:GetCoalition()==coalition then
-if category==nil or category==airbase:GetAirbaseCategory()then
-table.insert(airbases,airbasename)
-end
-end
-end
-return airbases
-end
-function AIRBASE:GetID(unique)
-if self.AirbaseID then
-return unique and self.AirbaseID or math.abs(self.AirbaseID)
-else
-for DCSAirbaseId,DCSAirbase in ipairs(world.getAirbases())do
-local AirbaseName=DCSAirbase:getName()
-local airbaseID=tonumber(DCSAirbase:getID())
-local airbaseCategory=self:GetAirbaseCategory()
-if AirbaseName==self.AirbaseName then
-if airbaseCategory==Airbase.Category.SHIP or airbaseCategory==Airbase.Category.HELIPAD then
-return unique and-airbaseID or airbaseID
-else
-return airbaseID
-end
-end
-end
-end
-return nil
-end
-function AIRBASE:SetParkingSpotWhitelist(TerminalIdWhitelist)
-if TerminalIdWhitelist==nil then
-self.parkingWhitelist={}
-return self
-end
-if type(TerminalIdWhitelist)~="table"then
-TerminalIdWhitelist={TerminalIdWhitelist}
-end
-self.parkingWhitelist=TerminalIdWhitelist
-return self
-end
-function AIRBASE:SetParkingSpotBlacklist(TerminalIdBlacklist)
-if TerminalIdBlacklist==nil then
-self.parkingBlacklist={}
-return self
-end
-if type(TerminalIdBlacklist)~="table"then
-TerminalIdBlacklist={TerminalIdBlacklist}
-end
-self.parkingBlacklist=TerminalIdBlacklist
-return self
-end
-function AIRBASE:SetRadioSilentMode(Silent)
-local airbase=self:GetDCSObject()
-if airbase then
-airbase:setRadioSilentMode(Silent)
-end
-return self
-end
-function AIRBASE:GetRadioSilentMode()
-local silent=nil
-local airbase=self:GetDCSObject()
-if airbase then
-silent=airbase:getRadioSilentMode()
-end
-return silent
-end
-function AIRBASE:GetAirbaseCategory()
-return self.category
-end
-function AIRBASE:IsAirdrome()
-return self.isAirdrome
-end
-function AIRBASE:IsHelipad()
-return self.isHelipad
-end
-function AIRBASE:IsShip()
-return self.isShip
-end
-function AIRBASE:GetParkingData(available)
-self:F2(available)
-local DCSAirbase=self:GetDCSObject()
-local parkingdata=nil
-if DCSAirbase then
-parkingdata=DCSAirbase:getParking(available)
-end
-self:T2({parkingdata=parkingdata})
-return parkingdata
-end
-function AIRBASE:GetParkingSpotsNumber(termtype)
-local parkingdata=self:GetParkingData(false)
-local nspots=0
-for _,parkingspot in pairs(parkingdata)do
-if AIRBASE._CheckTerminalType(parkingspot.Term_Type,termtype)then
-nspots=nspots+1
-end
-end
-return nspots
-end
-function AIRBASE:GetFreeParkingSpotsNumber(termtype,allowTOAC)
-local parkingdata=self:GetParkingData(true)
-local nfree=0
-for _,parkingspot in pairs(parkingdata)do
-if AIRBASE._CheckTerminalType(parkingspot.Term_Type,termtype)then
-if(allowTOAC and allowTOAC==true)or parkingspot.TO_AC==false then
-nfree=nfree+1
-end
-end
-end
-return nfree
-end
-function AIRBASE:GetFreeParkingSpotsCoordinates(termtype,allowTOAC)
-local parkingdata=self:GetParkingData(true)
-local spots={}
-for _,parkingspot in pairs(parkingdata)do
-if AIRBASE._CheckTerminalType(parkingspot.Term_Type,termtype)then
-if(allowTOAC and allowTOAC==true)or parkingspot.TO_AC==false then
-table.insert(spots,COORDINATE:NewFromVec3(parkingspot.vTerminalPos))
-end
-end
-end
-return spots
-end
-function AIRBASE:GetParkingSpotsCoordinates(termtype)
-local parkingdata=self:GetParkingData(false)
-local spots={}
-for _,parkingspot in ipairs(parkingdata)do
-if AIRBASE._CheckTerminalType(parkingspot.Term_Type,termtype)then
-local _coord=COORDINATE:NewFromVec3(parkingspot.vTerminalPos)
-table.insert(spots,_coord)
-end
-end
-return spots
-end
-function AIRBASE:_InitParkingSpots()
-local parkingdata=self:GetParkingData(false)
-self.parking={}
-self.parkingByID={}
-self.NparkingTotal=0
-self.NparkingTerminal={}
-for _,terminalType in pairs(AIRBASE.TerminalType)do
-self.NparkingTerminal[terminalType]=0
-end
-local function isClient(coord)
-local clients=_DATABASE.CLIENTS
-for clientname,_client in pairs(clients)do
-local client=_client
-if client and client.SpawnCoord then
-local dist=client.SpawnCoord:Get2DDistance(coord)
-if dist<2 then
-return true,clientname
-end
-end
-end
-return false,nil
-end
-for _,spot in pairs(parkingdata)do
-local park={}
-park.Vec3=spot.vTerminalPos
-park.Coordinate=COORDINATE:NewFromVec3(spot.vTerminalPos)
-park.DistToRwy=spot.fDistToRW
-park.Free=nil
-park.TerminalID=spot.Term_Index
-park.TerminalID0=spot.Term_Index_0
-park.TerminalType=spot.Term_Type
-park.TOAC=spot.TO_AC
-park.ClientSpot,park.ClientName=isClient(park.Coordinate)
-park.AirbaseName=self.AirbaseName
-self.NparkingTotal=self.NparkingTotal+1
-for _,terminalType in pairs(AIRBASE.TerminalType)do
-if self._CheckTerminalType(terminalType,park.TerminalType)then
-self.NparkingTerminal[terminalType]=self.NparkingTerminal[terminalType]+1
-end
-end
-self.parkingByID[park.TerminalID]=park
-table.insert(self.parking,park)
-end
-return self
-end
-function AIRBASE:_GetParkingSpotByID(TerminalID)
-return self.parkingByID[TerminalID]
-end
-function AIRBASE:GetParkingSpotsTable(termtype)
-local parkingdata=self:GetParkingData(false)
-local parkingfree=self:GetParkingData(true)
-local function _isfree(_tocheck)
-for _,_spot in pairs(parkingfree)do
-if _spot.Term_Index==_tocheck.Term_Index then
-return true
-end
-end
-return false
-end
-local spots={}
-for _,_spot in pairs(parkingdata)do
-if AIRBASE._CheckTerminalType(_spot.Term_Type,termtype)then
-local spot=self:_GetParkingSpotByID(_spot.Term_Index)
-if spot then
-spot.Free=_isfree(_spot)
-spot.TOAC=_spot.TO_AC
-spot.AirbaseName=self.AirbaseName
-table.insert(spots,spot)
-else
-self:E(string.format("ERROR: Parking spot %s is nil!",tostring(_spot.Term_Index)))
-end
-end
-end
-return spots
-end
-function AIRBASE:GetFreeParkingSpotsTable(termtype,allowTOAC)
-local parkingfree=self:GetParkingData(true)
-local freespots={}
-for _,_spot in pairs(parkingfree)do
-if AIRBASE._CheckTerminalType(_spot.Term_Type,termtype)and _spot.Term_Index>0 then
-if(allowTOAC and allowTOAC==true)or _spot.TO_AC==false then
-local spot=self:_GetParkingSpotByID(_spot.Term_Index)
-spot.Free=true
-spot.TOAC=_spot.TO_AC
-spot.AirbaseName=self.AirbaseName
-table.insert(freespots,spot)
-end
-end
-end
-return freespots
-end
-function AIRBASE:GetParkingSpotData(TerminalID)
-local parkingdata=self:GetParkingSpotsTable()
-for _,_spot in pairs(parkingdata)do
-local spot=_spot
-self:T({TerminalID=spot.TerminalID,TerminalType=spot.TerminalType})
-if TerminalID==spot.TerminalID then
-return spot
-end
-end
-self:E("ERROR: Could not find spot with Terminal ID="..tostring(TerminalID))
-return nil
-end
-function AIRBASE:MarkParkingSpots(termtype,mark)
-if mark==nil then
-mark=true
-end
-local parkingdata=self:GetParkingSpotsTable(termtype)
-local airbasename=self:GetName()
-self:E(string.format("Parking spots at %s for terminal type %s:",airbasename,tostring(termtype)))
-for _,_spot in pairs(parkingdata)do
-local _text=string.format("Term Index=%d, Term Type=%d, Free=%s, TOAC=%s, Term ID0=%d, Dist2Rwy=%.1f m",
-_spot.TerminalID,_spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy)
-if mark then
-_spot.Coordinate:MarkToAll(_text)
-end
-local _text=string.format("%s, Term Index=%3d, Term Type=%03d, Free=%5s, TOAC=%5s, Term ID0=%3d, Dist2Rwy=%.1f m",
-airbasename,_spot.TerminalID,_spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy)
-self:E(_text)
-end
-end
-function AIRBASE:FindFreeParkingSpotForAircraft(group,terminaltype,scanradius,scanunits,scanstatics,scanscenery,verysafe,nspots,parkingdata)
-scanradius=scanradius or 50
-if scanunits==nil then
-scanunits=true
-end
-if scanstatics==nil then
-scanstatics=true
-end
-if scanscenery==nil then
-scanscenery=false
-end
-if verysafe==nil then
-verysafe=false
-end
-local function _overlap(object1,object2,dist)
-local pos1=object1
-local pos2=object2
-local r1=pos1:GetBoundingRadius()
-local r2=pos2:GetBoundingRadius()
-if r1 and r2 then
-local safedist=(r1+r2)*1.1
-local safe=(dist>safedist)
-self:T2(string.format("r1=%.1f r2=%.1f s=%.1f d=%.1f ==> safe=%s",r1,r2,safedist,dist,tostring(safe)))
-return safe
-else
-return true
-end
-end
-local airport=self:GetName()
-parkingdata=parkingdata or self:GetParkingSpotsTable(terminaltype)
-local aircraft=nil
-local _aircraftsize=23
-local ax=23
-local ay=7
-local az=17
-if group and group.ClassName=="GROUP"then
-aircraft=group:GetUnit(1)
-if aircraft then
-_aircraftsize,ax,ay,az=aircraft:GetObjectSize()
-end
-end
-local _nspots=nspots or group:GetSize()
-self:T(string.format("%s: Looking for %d parking spot(s) for aircraft of size %.1f m (x=%.1f,y=%.1f,z=%.1f) at terminal type %s.",airport,_nspots,_aircraftsize,ax,ay,az,tostring(terminaltype)))
-local validspots={}
-local nvalid=0
-local _test=false
-if _test then
-return validspots
-end
-local markobstacles=false
-for _,parkingspot in pairs(parkingdata)do
-local _spot=parkingspot.Coordinate
-local _termid=parkingspot.TerminalID
-if AIRBASE._CheckTerminalType(parkingspot.TerminalType,terminaltype)and self:_CheckParkingLists(_termid)then
-if verysafe and(parkingspot.Free==false or parkingspot.TOAC==true)then
-self:T(string.format("%s: Parking spot id %d NOT free (or aircraft has not taken off yet). Free=%s, TOAC=%s.",airport,parkingspot.TerminalID,tostring(parkingspot.Free),tostring(parkingspot.TOAC)))
-else
-local _,_,_,_units,_statics,_sceneries=_spot:ScanObjects(scanradius,scanunits,scanstatics,scanscenery)
-local occupied=false
-for _,unit in pairs(_units)do
-local _coord=unit:GetCoordinate()
-local _dist=_coord:Get2DDistance(_spot)
-local _safe=_overlap(aircraft,unit,_dist)
-if markobstacles then
-local l,x,y,z=unit:GetObjectSize()
-_coord:MarkToAll(string.format("Unit %s\nx=%.1f y=%.1f z=%.1f\nl=%.1f d=%.1f\nspot %d safe=%s",unit:GetName(),x,y,z,l,_dist,_termid,tostring(_safe)))
-end
-if scanunits and not _safe then
-occupied=true
-end
-end
-for _,static in pairs(_statics)do
-local _static=STATIC:Find(static)
-local _vec3=static:getPoint()
-local _coord=COORDINATE:NewFromVec3(_vec3)
-local _dist=_coord:Get2DDistance(_spot)
-local _safe=_overlap(aircraft,_static,_dist)
-if markobstacles then
-local l,x,y,z=_static:GetObjectSize()
-_coord:MarkToAll(string.format("Static %s\nx=%.1f y=%.1f z=%.1f\nl=%.1f d=%.1f\nspot %d safe=%s",static:getName(),x,y,z,l,_dist,_termid,tostring(_safe)))
-end
-if scanstatics and not _safe then
-occupied=true
-end
-end
-for _,scenery in pairs(_sceneries)do
-local _scenery=SCENERY:Register(scenery:getTypeName(),scenery)
-local _vec3=scenery:getPoint()
-local _coord=COORDINATE:NewFromVec3(_vec3)
-local _dist=_coord:Get2DDistance(_spot)
-local _safe=_overlap(aircraft,_scenery,_dist)
-if markobstacles then
-local l,x,y,z=scenery:GetObjectSize(scenery)
-_coord:MarkToAll(string.format("Scenery %s\nx=%.1f y=%.1f z=%.1f\nl=%.1f d=%.1f\nspot %d safe=%s",scenery:getTypeName(),x,y,z,l,_dist,_termid,tostring(_safe)))
-end
-if scanscenery and not _safe then
-occupied=true
-end
-end
-for _,_takenspot in pairs(validspots)do
-local _dist=_takenspot.Coordinate:Get2DDistance(_spot)
-local _safe=_overlap(aircraft,aircraft,_dist)
-if not _safe then
-occupied=true
-end
-end
-if occupied then
-self:T(string.format("%s: Parking spot id %d occupied.",airport,_termid))
-else
-self:T(string.format("%s: Parking spot id %d free.",airport,_termid))
-if nvalid<_nspots then
-table.insert(validspots,{Coordinate=_spot,TerminalID=_termid})
-end
-nvalid=nvalid+1
-self:T(string.format("%s: Parking spot id %d free. Nfree=%d/%d.",airport,_termid,nvalid,_nspots))
-end
-end
-if nvalid>=_nspots then
-return validspots
-end
-end
-end
-return validspots
-end
-function AIRBASE:_CheckParkingLists(TerminalID)
-if self.parkingBlacklist and#self.parkingBlacklist>0 then
-for _,terminalID in pairs(self.parkingBlacklist or{})do
-if terminalID==TerminalID then
-return false
-end
-end
-end
-if self.parkingWhitelist and#self.parkingWhitelist>0 then
-for _,terminalID in pairs(self.parkingWhitelist or{})do
-if terminalID==TerminalID then
-return true
-end
-end
-return false
-end
-return true
-end
-function AIRBASE._CheckTerminalType(Term_Type,termtype)
-if Term_Type==nil then
-return false
-end
-if termtype==nil then
-if Term_Type==AIRBASE.TerminalType.Runway then
-return false
-else
-return true
-end
-end
-local match=false
-if Term_Type==termtype then
-match=true
-end
-if termtype==AIRBASE.TerminalType.OpenMedOrBig then
-if Term_Type==AIRBASE.TerminalType.OpenMed or Term_Type==AIRBASE.TerminalType.OpenBig then
-match=true
-end
-elseif termtype==AIRBASE.TerminalType.HelicopterUsable then
-if Term_Type==AIRBASE.TerminalType.OpenMed or Term_Type==AIRBASE.TerminalType.OpenBig or Term_Type==AIRBASE.TerminalType.HelicopterOnly then
-match=true
-end
-elseif termtype==AIRBASE.TerminalType.FighterAircraft then
-if Term_Type==AIRBASE.TerminalType.OpenMed or Term_Type==AIRBASE.TerminalType.OpenBig or Term_Type==AIRBASE.TerminalType.Shelter then
-match=true
-end
-end
-return match
-end
-function AIRBASE:GetRunways()
-return self.runways or{}
-end
-function AIRBASE:GetRunwayByName(Name)
-if Name==nil then
-return
-end
-if Name then
-for _,_runway in pairs(self.runways)do
-local runway=_runway
-local name=self:GetRunwayName(runway)
-if name==Name:upper()then
-return runway
-end
-end
-end
-self:E("ERROR: Could not find runway with name "..tostring(Name))
-return nil
-end
-function AIRBASE:_InitRunways(IncludeInverse)
-if IncludeInverse==nil then
-IncludeInverse=true
-end
-local Runways={}
-if self:GetAirbaseCategory()~=Airbase.Category.AIRDROME then
-self.runways={}
-return{}
-end
-local function _createRunway(name,course,width,length,center)
-local bearing=-1*course
-local heading=math.deg(bearing)
-local runway={}
-runway.name=string.format("%02d",tonumber(name))
-runway.magheading=tonumber(runway.name)*10
-runway.heading=heading
-runway.width=width or 0
-runway.length=length or 0
-runway.center=COORDINATE:NewFromVec3(center)
-if runway.heading>360 then
-runway.heading=runway.heading-360
-elseif runway.heading<0 then
-runway.heading=runway.heading+360
-end
-if math.abs(runway.heading-runway.magheading)>60 then
-self:T(string.format("WARNING: Runway %s: heading=%.1f magheading=%.1f",runway.name,runway.heading,runway.magheading))
-runway.heading=runway.heading-180
-end
-if runway.heading>360 then
-runway.heading=runway.heading-360
-elseif runway.heading<0 then
-runway.heading=runway.heading+360
-end
-runway.position=runway.center:Translate(-runway.length/2,runway.heading)
-runway.endpoint=runway.center:Translate(runway.length/2,runway.heading)
-local init=runway.center:GetVec3()
-local width=runway.width/2
-local L2=runway.length/2
-local offset1={x=init.x+(math.cos(bearing+math.pi)*L2),y=init.z+(math.sin(bearing+math.pi)*L2)}
-local offset2={x=init.x-(math.cos(bearing+math.pi)*L2),y=init.z-(math.sin(bearing+math.pi)*L2)}
-local points={}
-points[1]={x=offset1.x+(math.cos(bearing+(math.pi/2))*width),y=offset1.y+(math.sin(bearing+(math.pi/2))*width)}
-points[2]={x=offset1.x+(math.cos(bearing-(math.pi/2))*width),y=offset1.y+(math.sin(bearing-(math.pi/2))*width)}
-points[3]={x=offset2.x+(math.cos(bearing-(math.pi/2))*width),y=offset2.y+(math.sin(bearing-(math.pi/2))*width)}
-points[4]={x=offset2.x+(math.cos(bearing+(math.pi/2))*width),y=offset2.y+(math.sin(bearing+(math.pi/2))*width)}
-runway.zone=ZONE_POLYGON_BASE:New(string.format("%s Runway %s",self.AirbaseName,runway.name),points)
-return runway
-end
-local airbase=self:GetDCSObject()
-if airbase then
-local runways=airbase:getRunways()
-self:T2(runways)
-if runways then
-for _,rwy in pairs(runways)do
-self:T(rwy)
-local runway=_createRunway(rwy.Name,rwy.course,rwy.width,rwy.length,rwy.position)
-table.insert(Runways,runway)
-if IncludeInverse then
-local idx=tonumber(runway.name)
-local name2=tostring(idx-18)
-if idx<18 then
-name2=tostring(idx+18)
-end
-local runway=_createRunway(name2,rwy.course-math.pi,rwy.width,rwy.length,rwy.position)
-table.insert(Runways,runway)
-end
-end
-end
-end
-local rpairs={}
-for i,_ri in pairs(Runways)do
-local ri=_ri
-for j,_rj in pairs(Runways)do
-local rj=_rj
-if i0
-end
-for i,j in pairs(rpairs)do
-local ri=Runways[i]
-local rj=Runways[j]
-local c0=ri.center
-local a=UTILS.VecTranslate(c0,1000,ri.heading)
-local b=UTILS.VecSubstract(rj.center,ri.center)
-b=UTILS.VecAdd(ri.center,b)
-local left=isLeft(c0,a,b)
-if left then
-ri.isLeft=false
-rj.isLeft=true
-else
-ri.isLeft=true
-rj.isLeft=false
-end
-end
-self.runways=Runways
-return Runways
-end
-function AIRBASE:GetRunwayData(magvar,mark)
-local runways={}
-if self:GetAirbaseCategory()~=Airbase.Category.AIRDROME then
-return{}
-end
-local runwaycoords=self:GetParkingSpotsCoordinates(AIRBASE.TerminalType.Runway)
-if false then
-for i,_coord in pairs(runwaycoords)do
-local coord=_coord
-coord:Translate(100,0):MarkToAll("Runway i="..i)
-end
-end
-magvar=magvar or UTILS.GetMagneticDeclination()
-local N=#runwaycoords
-local N2=N/2
-local exception=false
-local name=self:GetName()
-if name==AIRBASE.Nevada.Jean_Airport or
-name==AIRBASE.Nevada.Creech_AFB or
-name==AIRBASE.PersianGulf.Abu_Dhabi_International_Airport or
-name==AIRBASE.PersianGulf.Dubai_Intl or
-name==AIRBASE.PersianGulf.Shiraz_International_Airport or
-name==AIRBASE.PersianGulf.Kish_International_Airport or
-name==AIRBASE.MarianaIslands.Andersen_AFB then
-exception=1
-elseif UTILS.GetDCSMap()==DCSMAP.Syria and N>=2 and
-name~=AIRBASE.Syria.Minakh and
-name~=AIRBASE.Syria.Damascus and
-name~=AIRBASE.Syria.Khalkhalah and
-name~=AIRBASE.Syria.Marj_Ruhayyil and
-name~=AIRBASE.Syria.Beirut_Rafic_Hariri then
-exception=2
-end
-local function f(i)
-local j
-if exception==1 then
-j=N-(i-1)
-elseif exception==2 then
-if i<=N2 then
-j=i+N2
-else
-j=i-N2
-end
-else
-if i%2==0 then
-j=i-1
-else
-j=i+1
-end
-end
-if name==AIRBASE.Syria.Beirut_Rafic_Hariri then
-if i==1 then
-j=3
-elseif i==2 then
-j=6
-elseif i==3 then
-j=1
-elseif i==4 then
-j=5
-elseif i==5 then
-j=4
-elseif i==6 then
-j=2
-end
-end
-if name==AIRBASE.Syria.Ramat_David then
-if i==1 then
-j=4
-elseif i==2 then
-j=6
-elseif i==3 then
-j=5
-elseif i==4 then
-j=1
-elseif i==5 then
-j=3
-elseif i==6 then
-j=2
-end
-end
-return j
-end
-for i=1,N do
-local j=f(i)
-local c1=runwaycoords[i]
-local c2=runwaycoords[j]
-local hdg=c1:HeadingTo(c2)
-local idx=string.format("%02d",UTILS.Round((hdg-magvar)/10,0))
-local runway={}
-runway.heading=hdg
-runway.idx=idx
-runway.length=c1:Get2DDistance(c2)
-runway.position=c1
-runway.endpoint=c2
-if mark then
-runway.position:MarkToAll(string.format("Runway %s: true heading=%03d (magvar=%d), length=%d m, i=%d, j=%d",runway.idx,runway.heading,magvar,runway.length,i,j))
-end
-table.insert(runways,runway)
-end
-return runways
-end
-function AIRBASE:SetActiveRunway(Name,PreferLeft)
-self:SetActiveRunwayTakeoff(Name,PreferLeft)
-self:SetActiveRunwayLanding(Name,PreferLeft)
-end
-function AIRBASE:SetActiveRunwayLanding(Name,PreferLeft)
-local runway=self:GetRunwayByName(Name)
-if not runway then
-runway=self:GetRunwayIntoWind(PreferLeft)
-end
-if runway then
-self:T(string.format("%s: Setting active runway for landing as %s",self.AirbaseName,self:GetRunwayName(runway)))
-else
-self:E("ERROR: Could not set the runway for landing!")
-end
-self.runwayLanding=runway
-return runway
-end
-function AIRBASE:GetActiveRunway()
-return self.runwayLanding,self.runwayTakeoff
-end
-function AIRBASE:GetActiveRunwayLanding()
-return self.runwayLanding
-end
-function AIRBASE:GetActiveRunwayTakeoff()
-return self.runwayTakeoff
-end
-function AIRBASE:SetActiveRunwayTakeoff(Name,PreferLeft)
-local runway=self:GetRunwayByName(Name)
-if not runway then
-runway=self:GetRunwayIntoWind(PreferLeft)
-end
-if runway then
-self:T(string.format("%s: Setting active runway for takeoff as %s",self.AirbaseName,self:GetRunwayName(runway)))
-else
-self:E("ERROR: Could not set the runway for takeoff!")
-end
-self.runwayTakeoff=runway
-return runway
-end
-function AIRBASE:GetRunwayIntoWind(PreferLeft)
-local runways=self:GetRunways()
-local Vwind=self:GetCoordinate():GetWindWithTurbulenceVec3()
-local norm=UTILS.VecNorm(Vwind)
-local iact=1
-if norm>0 then
-Vwind.x=Vwind.x/norm
-Vwind.y=0
-Vwind.z=Vwind.z/norm
-local dotmin=nil
-for i,_runway in pairs(runways)do
-local runway=_runway
-if PreferLeft==nil or PreferLeft==runway.isLeft then
-local alpha=math.rad(runway.heading)
-local Vrunway={x=math.cos(alpha),y=0,z=math.sin(alpha)}
-local dot=UTILS.VecDot(Vwind,Vrunway)
-if dotmin==nil or dot radius %.1f m. Despawn = %s.",self:GetName(),unit:GetName(),group:GetName(),_i,dist,radius,tostring(despawn)))
-end
-end
-else
-self:T(string.format("%s, checking if unit %s of group %s is on runway. Unit is NOT alive.",self:GetName(),unit:GetName(),group:GetName()))
-end
-end
-else
-self:T(string.format("%s, checking if group %s is on runway. Group is NOT alive.",self:GetName(),group:GetName()))
-end
-return false
-end
-function AIRBASE:GetCategory()
-return self.category
-end
-function AIRBASE:GetCategoryName()
-return AIRBASE.CategoryName[self.category]
-end
-CLIENT={
-ClassName="CLIENT",
-ClientName=nil,
-ClientAlive=false,
-ClientTransport=false,
-ClientBriefingShown=false,
-_Menus={},
-_Tasks={},
-Messages={},
-Players={},
-}
-function CLIENT:Find(DCSUnit,Error)
-local ClientName=DCSUnit:getName()
-local ClientFound=_DATABASE:FindClient(ClientName)
-if ClientFound then
-ClientFound:F(ClientName)
-return ClientFound
-end
-if not Error then
-error("CLIENT not found for: "..ClientName)
-end
-end
-function CLIENT:FindByPlayerName(Name)
-local foundclient=nil
-_DATABASE:ForEachClient(
-function(client)
-if client:GetPlayerName()==Name then
-foundclient=client
-end
-end
-)
-return foundclient
-end
-function CLIENT:FindByName(ClientName,ClientBriefing,Error)
-local ClientFound=_DATABASE:FindClient(ClientName)
-if ClientFound then
-ClientFound:F({ClientName,ClientBriefing})
-ClientFound:AddBriefing(ClientBriefing)
-ClientFound.MessageSwitch=true
-return ClientFound
-end
-if not Error then
-error("CLIENT not found for: "..ClientName)
-end
-end
-function CLIENT:Register(ClientName)
-local self=BASE:Inherit(self,UNIT:Register(ClientName))
-self.ClientName=ClientName
-self.MessageSwitch=true
-self.ClientAlive2=false
-return self
-end
-function CLIENT:Transport()
-self.ClientTransport=true
-return self
-end
-function CLIENT:AddBriefing(ClientBriefing)
-self.ClientBriefing=ClientBriefing
-self.ClientBriefingShown=false
-return self
-end
-function CLIENT:AddPlayer(PlayerName)
-table.insert(self.Players,PlayerName)
-return self
-end
-function CLIENT:GetPlayers()
-return self.Players
-end
-function CLIENT:GetPlayer()
-if#self.Players>0 then
-return self.Players[1]
-end
-return nil
-end
-function CLIENT:RemovePlayer(PlayerName)
-for i,playername in pairs(self.Players)do
-if PlayerName==playername then
-table.remove(self.Players,i)
-break
-end
-end
-return self
-end
-function CLIENT:RemovePlayers()
-self.Players={}
-return self
-end
-function CLIENT:ShowBriefing()
-if not self.ClientBriefingShown then
-self.ClientBriefingShown=true
-local Briefing=""
-if self.ClientBriefing and self.ClientBriefing~=""then
-Briefing=Briefing..self.ClientBriefing
-self:Message(Briefing,60,"Briefing")
-end
-end
-return self
-end
-function CLIENT:ShowMissionBriefing(MissionBriefing)
-self:F({self.ClientName})
-if MissionBriefing then
-self:Message(MissionBriefing,60,"Mission Briefing")
-end
-return self
-end
-function CLIENT:Reset(ClientName)
-self:F()
-self._Menus={}
-end
-function CLIENT:IsMultiSeated()
-self:F(self.ClientName)
-local ClientMultiSeatedTypes={
-["Mi-8MT"]="Mi-8MT",
-["UH-1H"]="UH-1H",
-["P-51B"]="P-51B"
-}
-if self:IsAlive()then
-local ClientTypeName=self:GetClientGroupUnit():GetTypeName()
-if ClientMultiSeatedTypes[ClientTypeName]then
-return true
-end
-end
-return false
-end
-function CLIENT:Alive(CallBackFunction,...)
-self:F()
-self.ClientCallBack=CallBackFunction
-self.ClientParameters=arg
-self.AliveCheckScheduler=SCHEDULER:New(self,self._AliveCheckScheduler,{"Client Alive "..self.ClientName},0.1,5,0.5)
-self.AliveCheckScheduler:NoTrace()
-return self
-end
-function CLIENT:_AliveCheckScheduler(SchedulerName)
-self:F3({SchedulerName,self.ClientName,self.ClientAlive2,self.ClientBriefingShown,self.ClientCallBack})
-if self:IsAlive()then
-if self.ClientAlive2==false then
-self:ShowBriefing()
-if self.ClientCallBack then
-self:T("Calling Callback function")
-self.ClientCallBack(self,unpack(self.ClientParameters))
-end
-self.ClientAlive2=true
-end
-else
-if self.ClientAlive2==true then
-self.ClientAlive2=false
-end
-end
-return true
-end
-function CLIENT:GetDCSGroup()
-self:F3()
-local ClientUnit=Unit.getByName(self.ClientName)
-local CoalitionsData={AlivePlayersRed=coalition.getPlayers(coalition.side.RED),AlivePlayersBlue=coalition.getPlayers(coalition.side.BLUE)}
-for CoalitionId,CoalitionData in pairs(CoalitionsData)do
-self:T3({"CoalitionData:",CoalitionData})
-for UnitId,UnitData in pairs(CoalitionData)do
-self:T3({"UnitData:",UnitData})
-if UnitData and UnitData:isExist()then
-if ClientUnit then
-local ClientGroup=ClientUnit:getGroup()
-if ClientGroup then
-self:T3("ClientGroup = "..self.ClientName)
-if ClientGroup:isExist()and UnitData:getGroup():isExist()then
-if ClientGroup:getID()==UnitData:getGroup():getID()then
-self:T3("Normal logic")
-self:T3(self.ClientName.." : group found!")
-self.ClientGroupID=ClientGroup:getID()
-self.ClientGroupName=ClientGroup:getName()
-return ClientGroup
-end
-else
-self:T3("Bug 1.5 logic")
-local ClientGroupTemplate=_DATABASE.Templates.Units[self.ClientName].GroupTemplate
-self.ClientGroupID=ClientGroupTemplate.groupId
-self.ClientGroupName=_DATABASE.Templates.Units[self.ClientName].GroupName
-self:T3(self.ClientName.." : group found in bug 1.5 resolvement logic!")
-return ClientGroup
-end
-end
-else
-end
-end
-end
-end
-if ClientUnit then
-local ClientGroup=ClientUnit:getGroup()
-if ClientGroup then
-self:T3("ClientGroup = "..self.ClientName)
-if ClientGroup:isExist()then
-self:T3("Normal logic")
-self:T3(self.ClientName.." : group found!")
-return ClientGroup
-end
-end
-end
-self.ClientGroupID=nil
-self.ClientGroupName=nil
-return nil
-end
-function CLIENT:GetClientGroupID()
-self:GetDCSGroup()
-return self.ClientGroupID
-end
-function CLIENT:GetClientGroupName()
-self:GetDCSGroup()
-return self.ClientGroupName
-end
-function CLIENT:GetClientGroupUnit()
-self:F2()
-local ClientDCSUnit=Unit.getByName(self.ClientName)
-self:T(self.ClientDCSUnit)
-if ClientDCSUnit and ClientDCSUnit:isExist()then
-local ClientUnit=_DATABASE:FindUnit(self.ClientName)
-return ClientUnit
-end
-return nil
-end
-function CLIENT:GetClientGroupDCSUnit()
-self:F2()
-local ClientDCSUnit=Unit.getByName(self.ClientName)
-if ClientDCSUnit and ClientDCSUnit:isExist()then
-self:T2(ClientDCSUnit)
-return ClientDCSUnit
-end
-end
-function CLIENT:IsTransport()
-self:F()
-return self.ClientTransport
-end
-function CLIENT:ShowCargo()
-self:F()
-local CargoMsg=""
-for CargoName,Cargo in pairs(CARGOS)do
-if self==Cargo:IsLoadedInClient()then
-CargoMsg=CargoMsg..Cargo.CargoName.." Type:"..Cargo.CargoType.." Weight: "..Cargo.CargoWeight.."\n"
-end
-end
-if CargoMsg==""then
-CargoMsg="empty"
-end
-self:Message(CargoMsg,15,"Co-Pilot: Cargo Status",30)
-end
-function CLIENT:Message(Message,MessageDuration,MessageCategory,MessageInterval,MessageID)
-self:F({Message,MessageDuration,MessageCategory,MessageInterval})
-if self.MessageSwitch==true then
-if MessageCategory==nil then
-MessageCategory="Messages"
-end
-if MessageID~=nil then
-if self.Messages[MessageID]==nil then
-self.Messages[MessageID]={}
-self.Messages[MessageID].MessageId=MessageID
-self.Messages[MessageID].MessageTime=timer.getTime()
-self.Messages[MessageID].MessageDuration=MessageDuration
-if MessageInterval==nil then
-self.Messages[MessageID].MessageInterval=600
-else
-self.Messages[MessageID].MessageInterval=MessageInterval
-end
-MESSAGE:New(Message,MessageDuration,MessageCategory):ToClient(self)
-else
-if self:GetClientGroupDCSUnit()and not self:GetClientGroupDCSUnit():inAir()then
-if timer.getTime()-self.Messages[MessageID].MessageTime>=self.Messages[MessageID].MessageDuration+10 then
-MESSAGE:New(Message,MessageDuration,MessageCategory):ToClient(self)
-self.Messages[MessageID].MessageTime=timer.getTime()
-end
-else
-if timer.getTime()-self.Messages[MessageID].MessageTime>=self.Messages[MessageID].MessageDuration+self.Messages[MessageID].MessageInterval then
-MESSAGE:New(Message,MessageDuration,MessageCategory):ToClient(self)
-self.Messages[MessageID].MessageTime=timer.getTime()
-end
-end
-end
-else
-MESSAGE:New(Message,MessageDuration,MessageCategory):ToClient(self)
-end
-end
-end
-function CLIENT:GetUCID()
-local PID=NET.GetPlayerIDByName(nil,self:GetPlayerName())
-return net.get_player_info(tonumber(PID),'ucid')
-end
-function CLIENT:GetPlayerInfo(Attribute)
-local PID=NET.GetPlayerIDByName(nil,self:GetPlayerName())
-if PID then
-return net.get_player_info(tonumber(PID),Attribute)
-else
-return nil
-end
-end
-CONTROLLABLE={
-ClassName="CONTROLLABLE",
-ControllableName="",
-WayPointFunctions={},
-}
-function CONTROLLABLE:New(ControllableName)
-local self=BASE:Inherit(self,POSITIONABLE:New(ControllableName))
-self.ControllableName=ControllableName
-self.TaskScheduler=SCHEDULER:New(self)
-return self
-end
-function CONTROLLABLE:_GetController()
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local ControllableController=DCSControllable:getController()
-return ControllableController
-end
-return nil
-end
-function CONTROLLABLE:GetLife()
-self:F2(self.ControllableName)
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local UnitLife=0
-local Units=self:GetUnits()
-if#Units==1 then
-local Unit=Units[1]
-UnitLife=Unit:GetLife()
-else
-local UnitLifeTotal=0
-for UnitID,Unit in pairs(Units)do
-local Unit=Unit
-UnitLifeTotal=UnitLifeTotal+Unit:GetLife()
-end
-UnitLife=UnitLifeTotal/#Units
-end
-return UnitLife
-end
-return nil
-end
-function CONTROLLABLE:GetLife0()
-self:F2(self.ControllableName)
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local UnitLife=0
-local Units=self:GetUnits()
-if#Units==1 then
-local Unit=Units[1]
-UnitLife=Unit:GetLife0()
-else
-local UnitLifeTotal=0
-for UnitID,Unit in pairs(Units)do
-local Unit=Unit
-UnitLifeTotal=UnitLifeTotal+Unit:GetLife0()
-end
-UnitLife=UnitLifeTotal/#Units
-end
-return UnitLife
-end
-return nil
-end
-function CONTROLLABLE:GetFuelMin()
-self:F(self.ControllableName)
-return nil
-end
-function CONTROLLABLE:GetFuelAve()
-self:F(self.ControllableName)
-return nil
-end
-function CONTROLLABLE:GetFuel()
-self:F(self.ControllableName)
-return nil
-end
-function CONTROLLABLE:ClearTasks()
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-Controller:resetTask()
-return self
-end
-return nil
-end
-function CONTROLLABLE:PopCurrentTask()
-self:F2()
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-Controller:popTask()
-return self
-end
-return nil
-end
-function CONTROLLABLE:PushTask(DCSTask,WaitTime)
-self:F2()
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local DCSControllableName=self:GetName()
-local function PushTask(Controller,DCSTask)
-if self and self:IsAlive()then
-local Controller=self:_GetController()
-Controller:pushTask(DCSTask)
-else
-BASE:E({DCSControllableName.." is not alive anymore.",DCSTask=DCSTask})
-end
-end
-if not WaitTime or WaitTime==0 then
-PushTask(self,DCSTask)
-else
-self.TaskScheduler:Schedule(self,PushTask,{DCSTask},WaitTime)
-end
-return self
-end
-return nil
-end
-function CONTROLLABLE:SetTask(DCSTask,WaitTime)
-self:F({"SetTask",WaitTime,DCSTask=DCSTask})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local DCSControllableName=self:GetName()
-self:T2("Controllable Name = "..DCSControllableName)
-local function SetTask(Controller,DCSTask)
-if self and self:IsAlive()then
-local Controller=self:_GetController()
-Controller:setTask(DCSTask)
-self:T({ControllableName=self:GetName(),DCSTask=DCSTask})
-else
-BASE:E({DCSControllableName.." is not alive anymore.",DCSTask=DCSTask})
-end
-end
-if not WaitTime or WaitTime==0 then
-SetTask(self,DCSTask)
-self:T({ControllableName=self:GetName(),DCSTask=DCSTask})
-else
-self.TaskScheduler:Schedule(self,SetTask,{DCSTask},WaitTime)
-end
-return self
-end
-return nil
-end
-function CONTROLLABLE:HasTask()
-local HasTaskResult=false
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-HasTaskResult=Controller:hasTask()
-end
-return HasTaskResult
-end
-function CONTROLLABLE:TaskCondition(time,userFlag,userFlagValue,condition,duration,lastWayPoint)
-local DCSStopCondition={}
-DCSStopCondition.time=time
-DCSStopCondition.userFlag=userFlag
-DCSStopCondition.userFlagValue=userFlagValue
-DCSStopCondition.condition=condition
-DCSStopCondition.duration=duration
-DCSStopCondition.lastWayPoint=lastWayPoint
-return DCSStopCondition
-end
-function CONTROLLABLE:TaskControlled(DCSTask,DCSStopCondition)
-local DCSTaskControlled={
-id='ControlledTask',
-params={
-task=DCSTask,
-stopCondition=DCSStopCondition,
-},
-}
-return DCSTaskControlled
-end
-function CONTROLLABLE:TaskCombo(DCSTasks)
-local DCSTaskCombo={
-id='ComboTask',
-params={
-tasks=DCSTasks,
-},
-}
-return DCSTaskCombo
-end
-function CONTROLLABLE:TaskWrappedAction(DCSCommand,Index)
-local DCSTaskWrappedAction={
-id="WrappedAction",
-enabled=true,
-number=Index or 1,
-auto=false,
-params={
-action=DCSCommand,
-},
-}
-return DCSTaskWrappedAction
-end
-function CONTROLLABLE:TaskEmptyTask()
-local DCSTaskWrappedAction={
-["id"]="WrappedAction",
-["params"]={
-["action"]={
-["id"]="Script",
-["params"]={
-["command"]="",
-},
-},
-},
-}
-return DCSTaskWrappedAction
-end
-function CONTROLLABLE:SetTaskWaypoint(Waypoint,Task)
-Waypoint.task=self:TaskCombo({Task})
-self:F({Waypoint.task})
-return Waypoint.task
-end
-function CONTROLLABLE:SetCommand(DCSCommand)
-self:F2(DCSCommand)
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-Controller:setCommand(DCSCommand)
-return self
-end
-return nil
-end
-function CONTROLLABLE:CommandSwitchWayPoint(FromWayPoint,ToWayPoint)
-self:F2({FromWayPoint,ToWayPoint})
-local CommandSwitchWayPoint={
-id='SwitchWaypoint',
-params={
-fromWaypointIndex=FromWayPoint,
-goToWaypointIndex=ToWayPoint,
-},
-}
-self:T3({CommandSwitchWayPoint})
-return CommandSwitchWayPoint
-end
-function CONTROLLABLE:CommandStopRoute(StopRoute)
-self:F2({StopRoute})
-local CommandStopRoute={
-id='StopRoute',
-params={
-value=StopRoute,
-},
-}
-self:T3({CommandStopRoute})
-return CommandStopRoute
-end
-function CONTROLLABLE:StartUncontrolled(delay)
-if delay and delay>0 then
-SCHEDULER:New(nil,CONTROLLABLE.StartUncontrolled,{self},delay)
-else
-self:SetCommand({id='Start',params={}})
-end
-return self
-end
-function CONTROLLABLE:CommandActivateBeacon(Type,System,Frequency,UnitID,Channel,ModeChannel,AA,Callsign,Bearing,Delay)
-AA=AA or self:IsAir()
-UnitID=UnitID or self:GetID()
-local CommandActivateBeacon={
-id="ActivateBeacon",
-params={
-["type"]=Type,
-["system"]=System,
-["frequency"]=Frequency,
-["unitId"]=UnitID,
-["channel"]=Channel,
-["modeChannel"]=ModeChannel,
-["AA"]=AA,
-["callsign"]=Callsign,
-["bearing"]=Bearing,
-},
-}
-if Delay and Delay>0 then
-SCHEDULER:New(nil,self.CommandActivateBeacon,{self,Type,System,Frequency,UnitID,Channel,ModeChannel,AA,Callsign,Bearing},Delay)
-else
-self:SetCommand(CommandActivateBeacon)
-end
-return self
-end
-function CONTROLLABLE:CommandActivateACLS(UnitID,Name,Delay)
-local CommandActivateACLS={
-id='ActivateACLS',
-params={
-unitId=UnitID or self:GetID(),
-name=Name or"ACL",
-}
-}
-self:T({CommandActivateACLS})
-if Delay and Delay>0 then
-SCHEDULER:New(nil,self.CommandActivateACLS,{self,UnitID,Name},Delay)
-else
-local controller=self:_GetController()
-controller:setCommand(CommandActivateACLS)
-end
-return self
-end
-function CONTROLLABLE:CommandDeactivateACLS(Delay)
-local CommandDeactivateACLS={
-id='DeactivateACLS',
-params={}
-}
-if Delay and Delay>0 then
-SCHEDULER:New(nil,self.CommandDeactivateACLS,{self},Delay)
-else
-local controller=self:_GetController()
-controller:setCommand(CommandDeactivateACLS)
-end
-return self
-end
-function CONTROLLABLE:CommandActivateICLS(Channel,UnitID,Callsign,Delay)
-local CommandActivateICLS={
-id="ActivateICLS",
-params={
-["type"]=BEACON.Type.ICLS,
-["channel"]=Channel,
-["unitId"]=UnitID or self:GetID(),
-["callsign"]=Callsign,
-},
-}
-if Delay and Delay>0 then
-SCHEDULER:New(nil,self.CommandActivateICLS,{self,Channel,UnitID,Callsign},Delay)
-else
-self:SetCommand(CommandActivateICLS)
-end
-return self
-end
-function CONTROLLABLE:CommandActivateLink4(Frequency,UnitID,Callsign,Delay)
-local freq=Frequency or 336
-local CommandActivateLink4={
-id="ActivateLink4",
-params={
-["frequency"]=freq*1000000,
-["unitId"]=UnitID or self:GetID(),
-["name"]=Callsign or"LNK",
-}
-}
-self:T({CommandActivateLink4})
-if Delay and Delay>0 then
-SCHEDULER:New(nil,self.CommandActivateLink4,{self,Frequency,UnitID,Callsign},Delay)
-else
-local controller=self:_GetController()
-controller:setCommand(CommandActivateLink4)
-end
-return self
-end
-function CONTROLLABLE:CommandDeactivateBeacon(Delay)
-local CommandDeactivateBeacon={id='DeactivateBeacon',params={}}
-local CommandDeactivateBeacon={id='DeactivateBeacon',params={}}
-if Delay and Delay>0 then
-SCHEDULER:New(nil,self.CommandDeactivateBeacon,{self},Delay)
-else
-self:SetCommand(CommandDeactivateBeacon)
-end
-return self
-end
-function CONTROLLABLE:CommandDeactivateLink4(Delay)
-local CommandDeactivateLink4={id='DeactivateLink4',params={}}
-if Delay and Delay>0 then
-SCHEDULER:New(nil,self.CommandDeactivateLink4,{self},Delay)
-else
-local controller=self:_GetController()
-controller:setCommand(CommandDeactivateLink4)
-end
-return self
-end
-function CONTROLLABLE:CommandDeactivateICLS(Delay)
-local CommandDeactivateICLS={id='DeactivateICLS',params={}}
-if Delay and Delay>0 then
-SCHEDULER:New(nil,self.CommandDeactivateICLS,{self},Delay)
-else
-self:SetCommand(CommandDeactivateICLS)
-end
-return self
-end
-function CONTROLLABLE:CommandSetCallsign(CallName,CallNumber,Delay)
-local CommandSetCallsign={id='SetCallsign',params={callname=CallName,number=CallNumber or 1}}
-if Delay and Delay>0 then
-SCHEDULER:New(nil,self.CommandSetCallsign,{self,CallName,CallNumber},Delay)
-else
-self:SetCommand(CommandSetCallsign)
-end
-return self
-end
-function CONTROLLABLE:CommandEPLRS(SwitchOnOff,Delay)
-if SwitchOnOff==nil then
-SwitchOnOff=true
-end
-local CommandEPLRS={
-id='EPLRS',
-params={
-value=SwitchOnOff,
-groupId=self:GetID(),
-},
-}
-if Delay and Delay>0 then
-SCHEDULER:New(nil,self.CommandEPLRS,{self,SwitchOnOff},Delay)
-else
-self:T(string.format("EPLRS=%s for controllable %s (id=%s)",tostring(SwitchOnOff),tostring(self:GetName()),tostring(self:GetID())))
-self:SetCommand(CommandEPLRS)
-end
-return self
-end
-function CONTROLLABLE:CommandSetUnlimitedFuel(OnOff,Delay)
-local CommandSetFuel={
-id='SetUnlimitedFuel',
-params={
-value=OnOff
-}
-}
-if Delay and Delay>0 then
-SCHEDULER:New(nil,self.CommandSetUnlimitedFuel,{self,OnOff},Delay)
-else
-self:SetCommand(CommandSetFuel)
-end
-return self
-end
-function CONTROLLABLE:CommandSetFrequency(Frequency,Modulation,Delay)
-local CommandSetFrequency={
-id='SetFrequency',
-params={
-frequency=Frequency*1000000,
-modulation=Modulation or radio.modulation.AM,
-},
-}
-if Delay and Delay>0 then
-SCHEDULER:New(nil,self.CommandSetFrequency,{self,Frequency,Modulation},Delay)
-else
-self:SetCommand(CommandSetFrequency)
-end
-return self
-end
-function CONTROLLABLE:TaskEPLRS(SwitchOnOff,idx)
-if SwitchOnOff==nil then
-SwitchOnOff=true
-end
-local CommandEPLRS={
-id='EPLRS',
-params={
-value=SwitchOnOff,
-groupId=self:GetID(),
-},
-}
-return self:TaskWrappedAction(CommandEPLRS,idx or 1)
-end
-function CONTROLLABLE:TaskAttackGroup(AttackGroup,WeaponType,WeaponExpend,AttackQty,Direction,Altitude,AttackQtyLimit,GroupAttack)
-local DCSTask={id='AttackGroup',
-params={
-groupId=AttackGroup:GetID(),
-weaponType=WeaponType or 1073741822,
-expend=WeaponExpend or"Auto",
-attackQtyLimit=AttackQty and true or false,
-attackQty=AttackQty or 1,
-directionEnabled=Direction and true or false,
-direction=Direction and math.rad(Direction)or 0,
-altitudeEnabled=Altitude and true or false,
-altitude=Altitude,
-groupAttack=GroupAttack and true or false,
-},
-}
-return DCSTask
-end
-function CONTROLLABLE:TaskAttackUnit(AttackUnit,GroupAttack,WeaponExpend,AttackQty,Direction,Altitude,WeaponType)
-local DCSTask={
-id='AttackUnit',
-params={
-unitId=AttackUnit:GetID(),
-groupAttack=GroupAttack and GroupAttack or false,
-expend=WeaponExpend or"Auto",
-directionEnabled=Direction and true or false,
-direction=Direction and math.rad(Direction)or 0,
-altitudeEnabled=Altitude and true or false,
-altitude=Altitude,
-attackQtyLimit=AttackQty and true or false,
-attackQty=AttackQty,
-weaponType=WeaponType or 1073741822,
-},
-}
-return DCSTask
-end
-function CONTROLLABLE:TaskBombing(Vec2,GroupAttack,WeaponExpend,AttackQty,Direction,Altitude,WeaponType,Divebomb)
-local DCSTask={
-id='Bombing',
-params={
-point=Vec2,
-x=Vec2.x,
-y=Vec2.y,
-groupAttack=GroupAttack and GroupAttack or false,
-expend=WeaponExpend or"Auto",
-attackQtyLimit=AttackQty and true or false,
-attackQty=AttackQty or 1,
-directionEnabled=Direction and true or false,
-direction=Direction and math.rad(Direction)or 0,
-altitudeEnabled=Altitude and true or false,
-altitude=Altitude or 2000,
-weaponType=WeaponType or 1073741822,
-attackType=Divebomb and"Dive"or nil,
-},
-}
-return DCSTask
-end
-function CONTROLLABLE:TaskStrafing(Vec2,AttackQty,Length,WeaponType,WeaponExpend,Direction,GroupAttack)
-local DCSTask={
-id='Strafing',
-params={
-point=Vec2,
-weaponType=WeaponType or 1073741822,
-expend=WeaponExpend or"Auto",
-attackQty=AttackQty or 1,
-attackQtyLimit=AttackQty>1 and true or false,
-direction=Direction and math.rad(Direction)or 0,
-directionEnabled=Direction and true or false,
-groupAttack=GroupAttack or false,
-length=Length,
-}
-}
-return DCSTask
-end
-function CONTROLLABLE:TaskAttackMapObject(Vec2,GroupAttack,WeaponExpend,AttackQty,Direction,Altitude,WeaponType)
-local DCSTask={
-id='AttackMapObject',
-params={
-point=Vec2,
-x=Vec2.x,
-y=Vec2.y,
-groupAttack=GroupAttack or false,
-expend=WeaponExpend or"Auto",
-attackQtyLimit=AttackQty and true or false,
-directionEnabled=Direction and true or false,
-direction=Direction and math.rad(Direction)or 0,
-altitudeEnabled=Altitude and true or false,
-altitude=Altitude,
-weaponType=WeaponType or 1073741822,
-},
-}
-return DCSTask
-end
-function CONTROLLABLE:TaskCarpetBombing(Vec2,GroupAttack,WeaponExpend,AttackQty,Direction,Altitude,WeaponType,CarpetLength)
-local DCSTask={
-id='CarpetBombing',
-params={
-attackType="Carpet",
-x=Vec2.x,
-y=Vec2.y,
-groupAttack=GroupAttack and GroupAttack or false,
-carpetLength=CarpetLength or 500,
-weaponType=WeaponType or ENUMS.WeaponFlag.AnyBomb,
-expend=WeaponExpend or"All",
-attackQtyLimit=AttackQty and true or false,
-attackQty=AttackQty or 1,
-directionEnabled=Direction and true or false,
-direction=Direction and math.rad(Direction)or 0,
-altitudeEnabled=Altitude and true or false,
-altitude=Altitude,
-},
-}
-return DCSTask
-end
-function CONTROLLABLE:TaskFollowBigFormation(FollowControllable,Vec3,LastWaypointIndex)
-local DCSTask={
-id='FollowBigFormation',
-params={
-groupId=FollowControllable:GetID(),
-pos=Vec3,
-lastWptIndexFlag=LastWaypointIndex and true or false,
-lastWptIndex=LastWaypointIndex,
-},
-}
-return DCSTask
-end
-function CONTROLLABLE:TaskEmbarking(Coordinate,GroupSetForEmbarking,Duration,Distribution)
-local g4e={}
-if GroupSetForEmbarking then
-for _,_group in pairs(GroupSetForEmbarking:GetSet())do
-local group=_group
-table.insert(g4e,group:GetID())
-end
-else
-self:E("ERROR: No groups for embarking specified!")
-return nil
-end
-local groupID=self and self:GetID()
-local DCSTask={
-id='Embarking',
-params={
-selectedTransport=groupID,
-x=Coordinate.x,
-y=Coordinate.z,
-groupsForEmbarking=g4e,
-durationFlag=Duration and true or false,
-duration=Duration,
-distributionFlag=Distribution and true or false,
-distribution=Distribution,
-},
-}
-return DCSTask
-end
-function CONTROLLABLE:TaskEmbarkToTransport(Coordinate,Radius,UnitType)
-local EmbarkToTransport={
-id="EmbarkToTransport",
-params={
-x=Coordinate.x,
-y=Coordinate.z,
-zoneRadius=Radius or 200,
-selectedType=UnitType,
-},
-}
-return EmbarkToTransport
-end
-function CONTROLLABLE:TaskDisembarking(Coordinate,GroupSetToDisembark)
-local g4e={}
-if GroupSetToDisembark then
-for _,_group in pairs(GroupSetToDisembark:GetSet())do
-local group=_group
-table.insert(g4e,group:GetID())
-end
-else
-self:E("ERROR: No groups for disembarking specified!")
-return nil
-end
-local Disembarking={
-id="Disembarking",
-params={
-x=Coordinate.x,
-y=Coordinate.z,
-groupsForEmbarking=g4e,
-},
-}
-return Disembarking
-end
-function CONTROLLABLE:TaskOrbitCircleAtVec2(Point,Altitude,Speed)
-self:F2({self.ControllableName,Point,Altitude,Speed})
-local DCSTask={
-id='Orbit',
-params={
-pattern=AI.Task.OrbitPattern.CIRCLE,
-point=Point,
-speed=Speed,
-altitude=Altitude+land.getHeight(Point),
-},
-}
-return DCSTask
-end
-function CONTROLLABLE:TaskOrbit(Coord,Altitude,Speed,CoordRaceTrack)
-local Pattern=AI.Task.OrbitPattern.CIRCLE
-local P1={x=Coord.x,y=Coord.z or Coord.y}
-local P2=nil
-if CoordRaceTrack then
-Pattern=AI.Task.OrbitPattern.RACE_TRACK
-P2={x=CoordRaceTrack.x,y=CoordRaceTrack.z or CoordRaceTrack.y}
-end
-local Task={
-id='Orbit',
-params={
-pattern=Pattern,
-point=P1,
-point2=P2,
-speed=Speed or UTILS.KnotsToMps(250),
-altitude=Altitude or Coord.y,
-},
-}
-return Task
-end
-function CONTROLLABLE:TaskOrbitCircle(Altitude,Speed,Coordinate)
-self:F2({self.ControllableName,Altitude,Speed})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local OrbitVec2=Coordinate and Coordinate:GetVec2()or self:GetVec2()
-return self:TaskOrbitCircleAtVec2(OrbitVec2,Altitude,Speed)
-end
-return nil
-end
-function CONTROLLABLE:TaskHoldPosition()
-self:F2({self.ControllableName})
-return self:TaskOrbitCircle(30,10)
-end
-function CONTROLLABLE:TaskBombingRunway(Airbase,WeaponType,WeaponExpend,AttackQty,Direction,GroupAttack)
-local DCSTask={
-id='BombingRunway',
-params={
-runwayId=Airbase:GetID(),
-weaponType=WeaponType or ENUMS.WeaponFlag.AnyBomb,
-expend=WeaponExpend or AI.Task.WeaponExpend.ALL,
-attackQty=AttackQty or 1,
-direction=Direction and math.rad(Direction)or 0,
-groupAttack=GroupAttack and true or false,
-},
-}
-return DCSTask
-end
-function CONTROLLABLE:TaskRefueling()
-local DCSTask={
-id='Refueling',
-params={},
-}
-return DCSTask
-end
-function CONTROLLABLE:TaskRecoveryTanker(CarrierGroup,Speed,Altitude,LastWptNumber)
-local LastWptFlag=type(LastWptNumber)=="number"and true or false
-local DCSTask={
-id="RecoveryTanker",
-params={
-groupId=CarrierGroup:GetID(),
-speed=Speed,
-altitude=Altitude,
-lastWptIndexFlag=LastWptFlag,
-lastWptIndex=LastWptNumber
-}
-}
-return DCSTask
-end
-function CONTROLLABLE:TaskLandAtVec2(Vec2,Duration)
-local DCSTask={
-id='Land',
-params={
-point=Vec2,
-durationFlag=Duration and true or false,
-duration=Duration,
-},
-}
-return DCSTask
-end
-function CONTROLLABLE:TaskLandAtZone(Zone,Duration,RandomPoint)
-local Point=RandomPoint and Zone:GetRandomVec2()or Zone:GetVec2()
-local DCSTask=CONTROLLABLE.TaskLandAtVec2(self,Point,Duration)
-return DCSTask
-end
-function CONTROLLABLE:TaskFollow(FollowControllable,Vec3,LastWaypointIndex)
-self:F2({self.ControllableName,FollowControllable,Vec3,LastWaypointIndex})
-local LastWaypointIndexFlag=false
-local lastWptIndexFlagChangedManually=false
-if LastWaypointIndex then
-LastWaypointIndexFlag=true
-lastWptIndexFlagChangedManually=true
-end
-local DCSTask={
-id='Follow',
-params={
-groupId=FollowControllable:GetID(),
-pos=Vec3,
-lastWptIndexFlag=LastWaypointIndexFlag,
-lastWptIndex=LastWaypointIndex,
-lastWptIndexFlagChangedManually=lastWptIndexFlagChangedManually,
-},
-}
-self:T3({DCSTask})
-return DCSTask
-end
-function CONTROLLABLE:TaskGroundEscort(FollowControllable,LastWaypointIndex,OrbitDistance,TargetTypes)
-local DCSTask={
-id='GroundEscort',
-params={
-groupId=FollowControllable and FollowControllable:GetID()or nil,
-engagementDistMax=OrbitDistance or 2000,
-lastWptIndexFlag=LastWaypointIndex and true or false,
-lastWptIndex=LastWaypointIndex,
-targetTypes=TargetTypes or{"Ground vehicles"},
-lastWptIndexFlagChangedManually=true,
-},
-}
-return DCSTask
-end
-function CONTROLLABLE:TaskEscort(FollowControllable,Vec3,LastWaypointIndex,EngagementDistance,TargetTypes)
-local DCSTask={
-id='Escort',
-params={
-groupId=FollowControllable and FollowControllable:GetID()or nil,
-pos=Vec3,
-lastWptIndexFlag=LastWaypointIndex and true or false,
-lastWptIndex=LastWaypointIndex,
-engagementDistMax=EngagementDistance,
-targetTypes=TargetTypes or{"Air"},
-},
-}
-return DCSTask
-end
-function CONTROLLABLE:TaskFireAtPoint(Vec2,Radius,AmmoCount,WeaponType,Altitude,ASL)
-local DCSTask={
-id='FireAtPoint',
-params={
-point=Vec2,
-x=Vec2.x,
-y=Vec2.y,
-zoneRadius=Radius,
-radius=Radius,
-expendQty=1,
-expendQtyEnabled=false,
-alt_type=ASL and 0 or 1,
-},
-}
-if AmmoCount then
-DCSTask.params.expendQty=AmmoCount
-DCSTask.params.expendQtyEnabled=true
-end
-if Altitude then
-DCSTask.params.altitude=Altitude
-end
-if WeaponType then
-DCSTask.params.weaponType=WeaponType
-end
-return DCSTask
-end
-function CONTROLLABLE:TaskHold()
-local DCSTask={id='Hold',params={}}
-return DCSTask
-end
-function CONTROLLABLE:TaskFAC_AttackGroup(AttackGroup,WeaponType,Designation,Datalink,Frequency,Modulation,CallsignName,CallsignNumber)
-local DCSTask={
-id='FAC_AttackGroup',
-params={
-groupId=AttackGroup:GetID(),
-weaponType=WeaponType or ENUMS.WeaponFlag.AutoDCS,
-designation=Designation or"Auto",
-datalink=Datalink and Datalink or true,
-frequency=(Frequency or 133)*1000000,
-modulation=Modulation or radio.modulation.AM,
-callname=CallsignName,
-number=CallsignNumber,
-},
-}
-return DCSTask
-end
-function CONTROLLABLE:EnRouteTaskEngageTargets(Distance,TargetTypes,Priority)
-local DCSTask={
-id='EngageTargets',
-params={
-maxDistEnabled=Distance and true or false,
-maxDist=Distance,
-targetTypes=TargetTypes or{"Air"},
-priority=Priority or 0,
-},
-}
-return DCSTask
-end
-function CONTROLLABLE:EnRouteTaskEngageTargetsInZone(Vec2,Radius,TargetTypes,Priority)
-local DCSTask={
-id='EngageTargetsInZone',
-params={
-point=Vec2,
-zoneRadius=Radius,
-targetTypes=TargetTypes or{"Air"},
-priority=Priority or 0
-},
-}
-return DCSTask
-end
-function CONTROLLABLE:EnRouteTaskAntiShip(TargetTypes,Priority)
-local DCSTask={
-id='EngageTargets',
-key="AntiShip",
-params={
-targetTypes=TargetTypes or{"Ships"},
-priority=Priority or 0
-}
-}
-return DCSTask
-end
-function CONTROLLABLE:EnRouteTaskSEAD(TargetTypes,Priority)
-local DCSTask={
-id='EngageTargets',
-key="SEAD",
-params={
-targetTypes=TargetTypes or{"Air Defence"},
-priority=Priority or 0
-}
-}
-return DCSTask
-end
-function CONTROLLABLE:EnRouteTaskCAP(TargetTypes,Priority)
-local DCSTask={
-id='EngageTargets',
-key="CAP",
-enabled=true,
-params={
-targetTypes=TargetTypes or{"Air"},
-priority=Priority or 0
-}
-}
-return DCSTask
-end
-function CONTROLLABLE:EnRouteTaskEngageGroup(AttackGroup,Priority,WeaponType,WeaponExpend,AttackQty,Direction,Altitude,AttackQtyLimit)
-local DCSTask={
-id='EngageGroup',
-params={
-groupId=AttackGroup:GetID(),
-weaponType=WeaponType,
-expend=WeaponExpend or"Auto",
-directionEnabled=Direction and true or false,
-direction=Direction,
-altitudeEnabled=Altitude and true or false,
-altitude=Altitude,
-attackQtyLimit=AttackQty and true or false,
-attackQty=AttackQty,
-priority=Priority or 1,
-},
-}
-return DCSTask
-end
-function CONTROLLABLE:EnRouteTaskEngageUnit(EngageUnit,Priority,GroupAttack,WeaponExpend,AttackQty,Direction,Altitude,Visible,ControllableAttack)
-local DCSTask={
-id='EngageUnit',
-params={
-unitId=EngageUnit:GetID(),
-priority=Priority or 1,
-groupAttack=GroupAttack and GroupAttack or false,
-visible=Visible and Visible or false,
-expend=WeaponExpend or"Auto",
-directionEnabled=Direction and true or false,
-direction=Direction and math.rad(Direction)or nil,
-altitudeEnabled=Altitude and true or false,
-altitude=Altitude,
-attackQtyLimit=AttackQty and true or false,
-attackQty=AttackQty,
-controllableAttack=ControllableAttack,
-},
-}
-return DCSTask
-end
-function CONTROLLABLE:EnRouteTaskAWACS()
-local DCSTask={
-id='AWACS',
-params={},
-}
-return DCSTask
-end
-function CONTROLLABLE:EnRouteTaskTanker()
-local DCSTask={
-id='Tanker',
-params={},
-}
-return DCSTask
-end
-function CONTROLLABLE:EnRouteTaskEWR()
-local DCSTask={
-id='EWR',
-params={},
-}
-return DCSTask
-end
-function CONTROLLABLE:EnRouteTaskFAC_EngageGroup(AttackGroup,Priority,WeaponType,Designation,Datalink,Frequency,Modulation,CallsignID,CallsignNumber)
-local DCSTask={
-id='FAC_EngageGroup',
-params={
-groupId=AttackGroup:GetID(),
-weaponType=WeaponType or"Auto",
-designation=Designation,
-datalink=Datalink and Datalink or false,
-frequency=(Frequency or 133)*1000000,
-modulation=Modulation or radio.modulation.AM,
-callname=CallsignID,
-number=CallsignNumber,
-priority=Priority or 0,
-},
-}
-return DCSTask
-end
-function CONTROLLABLE:EnRouteTaskFAC(Frequency,Modulation,CallsignID,CallsignNumber,Priority)
-local DCSTask={
-id='FAC',
-params={
-frequency=(Frequency or 133)*1000000,
-modulation=Modulation or radio.modulation.AM,
-callname=CallsignID,
-number=CallsignNumber,
-priority=Priority or 0
-}
-}
-return DCSTask
-end
-function CONTROLLABLE:TaskFunction(FunctionString,...)
-local DCSScript={}
-DCSScript[#DCSScript+1]="local MissionControllable = GROUP:Find( ... ) "
-if arg and arg.n>0 then
-local ArgumentKey='_'..tostring(arg):match("table: (.*)")
-self:SetState(self,ArgumentKey,arg)
-DCSScript[#DCSScript+1]="local Arguments = MissionControllable:GetState( MissionControllable, '"..ArgumentKey.."' ) "
-DCSScript[#DCSScript+1]=FunctionString.."( MissionControllable, unpack( Arguments ) )"
-else
-DCSScript[#DCSScript+1]=FunctionString.."( MissionControllable )"
-end
-local DCSTask=self:TaskWrappedAction(self:CommandDoScript(table.concat(DCSScript)))
-return DCSTask
-end
-function CONTROLLABLE:TaskMission(TaskMission)
-local DCSTask={
-id='Mission',
-params={
-TaskMission,
-},
-}
-return DCSTask
-end
-do
-function CONTROLLABLE:PatrolRoute()
-local PatrolGroup=self
-if not self:IsInstanceOf("GROUP")then
-PatrolGroup=self:GetGroup()
-end
-self:F({PatrolGroup=PatrolGroup:GetName()})
-if PatrolGroup:IsGround()or PatrolGroup:IsShip()then
-local Waypoints=PatrolGroup:GetTemplateRoutePoints()
-local FromCoord=PatrolGroup:GetCoordinate()
-local depth=0
-local IsSub=false
-if PatrolGroup:IsShip()then
-local navalvec3=FromCoord:GetVec3()
-if navalvec3.y<0 then
-depth=navalvec3.y
-IsSub=true
-end
-end
-local Waypoint=Waypoints[1]
-local Speed=Waypoint.speed or(20/3.6)
-local From=FromCoord:WaypointGround(Speed)
-if IsSub then
-From=FromCoord:WaypointNaval(Speed,Waypoint.alt)
-end
-table.insert(Waypoints,1,From)
-local TaskRoute=PatrolGroup:TaskFunction("CONTROLLABLE.PatrolRoute")
-self:F({Waypoints=Waypoints})
-local Waypoint=Waypoints[#Waypoints]
-PatrolGroup:SetTaskWaypoint(Waypoint,TaskRoute)
-PatrolGroup:Route(Waypoints)
-end
-end
-function CONTROLLABLE:PatrolRouteRandom(Speed,Formation,ToWaypoint)
-local PatrolGroup=self
-if not self:IsInstanceOf("GROUP")then
-PatrolGroup=self:GetGroup()
-end
-self:F({PatrolGroup=PatrolGroup:GetName()})
-if PatrolGroup:IsGround()or PatrolGroup:IsShip()then
-local Waypoints=PatrolGroup:GetTemplateRoutePoints()
-local FromCoord=PatrolGroup:GetCoordinate()
-local FromWaypoint=1
-if ToWaypoint then
-FromWaypoint=ToWaypoint
-end
-local depth=0
-local IsSub=false
-if PatrolGroup:IsShip()then
-local navalvec3=FromCoord:GetVec3()
-if navalvec3.y<0 then
-depth=navalvec3.y
-IsSub=true
-end
-end
-local ToWaypoint
-repeat
-ToWaypoint=math.random(1,#Waypoints)
-until(ToWaypoint~=FromWaypoint)
-self:F({FromWaypoint=FromWaypoint,ToWaypoint=ToWaypoint})
-local Waypoint=Waypoints[ToWaypoint]
-local ToCoord=COORDINATE:NewFromVec2({x=Waypoint.x,y=Waypoint.y})
-local Route={}
-if IsSub then
-Route[#Route+1]=FromCoord:WaypointNaval(Speed,depth)
-Route[#Route+1]=ToCoord:WaypointNaval(Speed,Waypoint.alt)
-else
-Route[#Route+1]=FromCoord:WaypointGround(Speed,Formation)
-Route[#Route+1]=ToCoord:WaypointGround(Speed,Formation)
-end
-local TaskRouteToZone=PatrolGroup:TaskFunction("CONTROLLABLE.PatrolRouteRandom",Speed,Formation,ToWaypoint)
-PatrolGroup:SetTaskWaypoint(Route[#Route],TaskRouteToZone)
-PatrolGroup:Route(Route,1)
-end
-end
-function CONTROLLABLE:PatrolZones(ZoneList,Speed,Formation,DelayMin,DelayMax)
-if type(ZoneList)~="table"then
-ZoneList={ZoneList}
-end
-local PatrolGroup=self
-if not self:IsInstanceOf("GROUP")then
-PatrolGroup=self:GetGroup()
-end
-DelayMin=DelayMin or 1
-if not DelayMax or DelayMaxLengthDirect*10)or(LengthRoad/LengthOnRoad*100<5))
-self:T(string.format("Length on road = %.3f km",LengthOnRoad/1000))
-self:T(string.format("Length directly = %.3f km",LengthDirect/1000))
-self:T(string.format("Length fraction = %.3f km",LengthOnRoad/LengthDirect))
-self:T(string.format("Length only road = %.3f km",LengthRoad/1000))
-self:T(string.format("Length off road = %.3f km",LengthOffRoad/1000))
-self:T(string.format("Percent on road = %.1f",LengthRoad/LengthOnRoad*100))
-end
-local route={}
-local canroad=false
-if GotPath and LengthRoad and LengthDirect>2000 then
-if LongRoad and Shortcut then
-table.insert(route,FromCoordinate:WaypointGround(Speed,OffRoadFormation))
-table.insert(route,ToCoordinate:WaypointGround(Speed,OffRoadFormation))
-else
-table.insert(route,FromCoordinate:WaypointGround(Speed,OffRoadFormation))
-table.insert(route,PathOnRoad[2]:WaypointGround(Speed,"On Road"))
-table.insert(route,PathOnRoad[#PathOnRoad-1]:WaypointGround(Speed,"On Road"))
-local dist=ToCoordinate:Get2DDistance(PathOnRoad[#PathOnRoad-1])
-if dist>10 then
-table.insert(route,ToCoordinate:WaypointGround(Speed,OffRoadFormation))
-table.insert(route,ToCoordinate:GetRandomCoordinateInRadius(10,5):WaypointGround(5,OffRoadFormation))
-table.insert(route,ToCoordinate:GetRandomCoordinateInRadius(10,5):WaypointGround(5,OffRoadFormation))
-end
-end
-canroad=true
-else
-table.insert(route,FromCoordinate:WaypointGround(Speed,OffRoadFormation))
-table.insert(route,ToCoordinate:WaypointGround(Speed,OffRoadFormation))
-end
-if WaypointFunction then
-local N=#route
-for n,waypoint in pairs(route)do
-waypoint.task={}
-waypoint.task.id="ComboTask"
-waypoint.task.params={}
-waypoint.task.params.tasks={self:TaskFunction("CONTROLLABLE.___PassingWaypoint",n,N,WaypointFunction,unpack(WaypointFunctionArguments or{}))}
-end
-end
-return route,canroad
-end
-function CONTROLLABLE:TaskGroundOnRailRoads(ToCoordinate,Speed,WaypointFunction,WaypointFunctionArguments)
-self:F2({ToCoordinate=ToCoordinate,Speed=Speed})
-Speed=Speed or 20
-local FromCoordinate=self:GetCoordinate()
-local PathOnRail,LengthOnRail=FromCoordinate:GetPathOnRoad(ToCoordinate,false,true)
-self:T(string.format("Length on railroad = %.3f km",LengthOnRail/1000))
-local route={}
-if PathOnRail then
-table.insert(route,PathOnRail[1]:WaypointGround(Speed,"On Railroad"))
-table.insert(route,PathOnRail[2]:WaypointGround(Speed,"On Railroad"))
-end
-if WaypointFunction then
-local N=#route
-for n,waypoint in pairs(route)do
-waypoint.task={}
-waypoint.task.id="ComboTask"
-waypoint.task.params={}
-waypoint.task.params.tasks={self:TaskFunction("CONTROLLABLE.___PassingWaypoint",n,N,WaypointFunction,unpack(WaypointFunctionArguments or{}))}
-end
-end
-return route
-end
-function CONTROLLABLE.___PassingWaypoint(controllable,n,N,waypointfunction,...)
-waypointfunction(controllable,n,N,...)
-end
-function CONTROLLABLE:RouteAirTo(ToCoordinate,AltType,Type,Action,Speed,DelaySeconds)
-local FromCoordinate=self:GetCoordinate()
-local FromWP=FromCoordinate:WaypointAir()
-local ToWP=ToCoordinate:WaypointAir(AltType,Type,Action,Speed)
-self:Route({FromWP,ToWP},DelaySeconds)
-return self
-end
-function CONTROLLABLE:TaskRouteToZone(Zone,Randomize,Speed,Formation)
-self:F2(Zone)
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local ControllablePoint=self:GetVec2()
-local PointFrom={}
-PointFrom.x=ControllablePoint.x
-PointFrom.y=ControllablePoint.y
-PointFrom.type="Turning Point"
-PointFrom.action=Formation or"Cone"
-PointFrom.speed=20/3.6
-local PointTo={}
-local ZonePoint
-if Randomize then
-ZonePoint=Zone:GetRandomVec2()
-else
-ZonePoint=Zone:GetVec2()
-end
-PointTo.x=ZonePoint.x
-PointTo.y=ZonePoint.y
-PointTo.type="Turning Point"
-if Formation then
-PointTo.action=Formation
-else
-PointTo.action="Cone"
-end
-if Speed then
-PointTo.speed=Speed
-else
-PointTo.speed=20/3.6
-end
-local Points={PointFrom,PointTo}
-self:T3(Points)
-self:Route(Points)
-return self
-end
-return nil
-end
-function CONTROLLABLE:TaskRouteToVec2(Vec2,Speed,Formation)
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local ControllablePoint=self:GetVec2()
-local PointFrom={}
-PointFrom.x=ControllablePoint.x
-PointFrom.y=ControllablePoint.y
-PointFrom.type="Turning Point"
-PointFrom.action=Formation or"Cone"
-PointFrom.speed=20/3.6
-local PointTo={}
-PointTo.x=Vec2.x
-PointTo.y=Vec2.y
-PointTo.type="Turning Point"
-if Formation then
-PointTo.action=Formation
-else
-PointTo.action="Cone"
-end
-if Speed then
-PointTo.speed=Speed
-else
-PointTo.speed=20/3.6
-end
-local Points={PointFrom,PointTo}
-self:T3(Points)
-self:Route(Points)
-return self
-end
-return nil
-end
-end
-function CONTROLLABLE:CommandDoScript(DoScript)
-local DCSDoScript={
-id="Script",
-params={
-command=DoScript,
-},
-}
-self:T3(DCSDoScript)
-return DCSDoScript
-end
-function CONTROLLABLE:GetTaskMission()
-self:F2(self.ControllableName)
-return UTILS.DeepCopy(_DATABASE.Templates.Controllables[self.ControllableName].Template)
-end
-function CONTROLLABLE:GetTaskRoute()
-self:F2(self.ControllableName)
-return UTILS.DeepCopy(_DATABASE.Templates.Controllables[self.ControllableName].Template.route.points)
-end
-function CONTROLLABLE:CopyRoute(Begin,End,Randomize,Radius)
-self:F2({Begin,End})
-local Points={}
-local ControllableName=string.match(self:GetName(),".*#")
-if ControllableName then
-ControllableName=ControllableName:sub(1,-2)
-else
-ControllableName=self:GetName()
-end
-self:T3({ControllableName})
-local Template=_DATABASE.Templates.Controllables[ControllableName].Template
-if Template then
-if not Begin then
-Begin=0
-end
-if not End then
-End=0
-end
-for TPointID=Begin+1,#Template.route.points-End do
-if Template.route.points[TPointID]then
-Points[#Points+1]=UTILS.DeepCopy(Template.route.points[TPointID])
-if Randomize then
-if not Radius then
-Radius=500
-end
-Points[#Points].x=Points[#Points].x+math.random(Radius*-1,Radius)
-Points[#Points].y=Points[#Points].y+math.random(Radius*-1,Radius)
-end
-end
-end
-return Points
-else
-error("Template not found for Controllable : "..ControllableName)
-end
-return nil
-end
-function CONTROLLABLE:GetDetectedTargets(DetectVisual,DetectOptical,DetectRadar,DetectIRST,DetectRWR,DetectDLINK)
-self:F2(self.ControllableName)
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local DetectionVisual=(DetectVisual and DetectVisual==true)and Controller.Detection.VISUAL or nil
-local DetectionOptical=(DetectOptical and DetectOptical==true)and Controller.Detection.OPTICAL or nil
-local DetectionRadar=(DetectRadar and DetectRadar==true)and Controller.Detection.RADAR or nil
-local DetectionIRST=(DetectIRST and DetectIRST==true)and Controller.Detection.IRST or nil
-local DetectionRWR=(DetectRWR and DetectRWR==true)and Controller.Detection.RWR or nil
-local DetectionDLINK=(DetectDLINK and DetectDLINK==true)and Controller.Detection.DLINK or nil
-local Params={}
-if DetectionVisual then
-Params[#Params+1]=DetectionVisual
-end
-if DetectionOptical then
-Params[#Params+1]=DetectionOptical
-end
-if DetectionRadar then
-Params[#Params+1]=DetectionRadar
-end
-if DetectionIRST then
-Params[#Params+1]=DetectionIRST
-end
-if DetectionRWR then
-Params[#Params+1]=DetectionRWR
-end
-if DetectionDLINK then
-Params[#Params+1]=DetectionDLINK
-end
-self:T2({DetectionVisual,DetectionOptical,DetectionRadar,DetectionIRST,DetectionRWR,DetectionDLINK})
-return self:_GetController():getDetectedTargets(Params[1],Params[2],Params[3],Params[4],Params[5],Params[6])
-end
-return nil
-end
-function CONTROLLABLE:IsTargetDetected(DCSObject,DetectVisual,DetectOptical,DetectRadar,DetectIRST,DetectRWR,DetectDLINK)
-self:F2(self.ControllableName)
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local DetectionVisual=(DetectVisual and DetectVisual==true)and Controller.Detection.VISUAL or nil
-local DetectionOptical=(DetectOptical and DetectOptical==true)and Controller.Detection.OPTICAL or nil
-local DetectionRadar=(DetectRadar and DetectRadar==true)and Controller.Detection.RADAR or nil
-local DetectionIRST=(DetectIRST and DetectIRST==true)and Controller.Detection.IRST or nil
-local DetectionRWR=(DetectRWR and DetectRWR==true)and Controller.Detection.RWR or nil
-local DetectionDLINK=(DetectDLINK and DetectDLINK==true)and Controller.Detection.DLINK or nil
-local Controller=self:_GetController()
-local TargetIsDetected,TargetIsVisible,TargetLastTime,TargetKnowType,TargetKnowDistance,TargetLastPos,TargetLastVelocity
-=Controller:isTargetDetected(DCSObject,DetectionVisual,DetectionOptical,DetectionRadar,DetectionIRST,DetectionRWR,DetectionDLINK)
-return TargetIsDetected,TargetIsVisible,TargetLastTime,TargetKnowType,TargetKnowDistance,TargetLastPos,TargetLastVelocity
-end
-return nil
-end
-function CONTROLLABLE:IsUnitDetected(Unit,DetectVisual,DetectOptical,DetectRadar,DetectIRST,DetectRWR,DetectDLINK)
-self:F2(self.ControllableName)
-if Unit and Unit:IsAlive()then
-return self:IsTargetDetected(Unit:GetDCSObject(),DetectVisual,DetectOptical,DetectRadar,DetectIRST,DetectRWR,DetectDLINK)
-end
-return nil
-end
-function CONTROLLABLE:IsGroupDetected(Group,DetectVisual,DetectOptical,DetectRadar,DetectIRST,DetectRWR,DetectDLINK)
-self:F2(self.ControllableName)
-if Group and Group:IsAlive()then
-for _,_unit in pairs(Group:GetUnits())do
-local unit=_unit
-if unit and unit:IsAlive()then
-local isdetected=self:IsUnitDetected(unit,DetectVisual,DetectOptical,DetectRadar,DetectIRST,DetectRWR,DetectDLINK)
-if isdetected then
-return true
-end
-end
-end
-return false
-end
-return nil
-end
-function CONTROLLABLE:GetDetectedUnitSet(DetectVisual,DetectOptical,DetectRadar,DetectIRST,DetectRWR,DetectDLINK)
-local detectedtargets=self:GetDetectedTargets(DetectVisual,DetectOptical,DetectRadar,DetectIRST,DetectRWR,DetectDLINK)
-local unitset=SET_UNIT:New()
-for DetectionObjectID,Detection in pairs(detectedtargets or{})do
-local DetectedObject=Detection.object
-if DetectedObject and DetectedObject:isExist()and DetectedObject.id_<50000000 then
-local unit=UNIT:Find(DetectedObject)
-if unit and unit:IsAlive()then
-if not unitset:FindUnit(unit:GetName())then
-unitset:AddUnit(unit)
-end
-end
-end
-end
-return unitset
-end
-function CONTROLLABLE:GetDetectedGroupSet(DetectVisual,DetectOptical,DetectRadar,DetectIRST,DetectRWR,DetectDLINK)
-local detectedtargets=self:GetDetectedTargets(DetectVisual,DetectOptical,DetectRadar,DetectIRST,DetectRWR,DetectDLINK)
-local groupset=SET_GROUP:New()
-for DetectionObjectID,Detection in pairs(detectedtargets or{})do
-local DetectedObject=Detection.object
-if DetectedObject and DetectedObject:isExist()and DetectedObject.id_<50000000 then
-local unit=UNIT:Find(DetectedObject)
-if unit and unit:IsAlive()then
-local group=unit:GetGroup()
-if group and not groupset:FindGroup(group:GetName())then
-groupset:AddGroup(group)
-end
-end
-end
-end
-return groupset
-end
-function CONTROLLABLE:SetOption(OptionID,OptionValue)
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-Controller:setOption(OptionID,OptionValue)
-return self
-end
-return nil
-end
-function CONTROLLABLE:OptionROE(ROEvalue)
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-if self:IsAir()then
-Controller:setOption(AI.Option.Air.id.ROE,ROEvalue)
-elseif self:IsGround()then
-Controller:setOption(AI.Option.Ground.id.ROE,ROEvalue)
-elseif self:IsShip()then
-Controller:setOption(AI.Option.Naval.id.ROE,ROEvalue)
-end
-return self
-end
-return nil
-end
-function CONTROLLABLE:OptionROEHoldFirePossible()
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-if self:IsAir()or self:IsGround()or self:IsShip()then
-return true
-end
-return false
-end
-return nil
-end
-function CONTROLLABLE:OptionROEHoldFire()
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-if self:IsAir()then
-Controller:setOption(AI.Option.Air.id.ROE,AI.Option.Air.val.ROE.WEAPON_HOLD)
-elseif self:IsGround()then
-Controller:setOption(AI.Option.Ground.id.ROE,AI.Option.Ground.val.ROE.WEAPON_HOLD)
-elseif self:IsShip()then
-Controller:setOption(AI.Option.Naval.id.ROE,AI.Option.Naval.val.ROE.WEAPON_HOLD)
-end
-return self
-end
-return nil
-end
-function CONTROLLABLE:OptionROEReturnFirePossible()
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-if self:IsAir()or self:IsGround()or self:IsShip()then
-return true
-end
-return false
-end
-return nil
-end
-function CONTROLLABLE:OptionROEReturnFire()
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-if self:IsAir()then
-Controller:setOption(AI.Option.Air.id.ROE,AI.Option.Air.val.ROE.RETURN_FIRE)
-elseif self:IsGround()then
-Controller:setOption(AI.Option.Ground.id.ROE,AI.Option.Ground.val.ROE.RETURN_FIRE)
-elseif self:IsShip()then
-Controller:setOption(AI.Option.Naval.id.ROE,AI.Option.Naval.val.ROE.RETURN_FIRE)
-end
-return self
-end
-return nil
-end
-function CONTROLLABLE:OptionROEOpenFirePossible()
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-if self:IsAir()or self:IsGround()or self:IsShip()then
-return true
-end
-return false
-end
-return nil
-end
-function CONTROLLABLE:OptionROEOpenFire()
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-if self:IsAir()then
-Controller:setOption(AI.Option.Air.id.ROE,AI.Option.Air.val.ROE.OPEN_FIRE)
-elseif self:IsGround()then
-Controller:setOption(AI.Option.Ground.id.ROE,AI.Option.Ground.val.ROE.OPEN_FIRE)
-elseif self:IsShip()then
-Controller:setOption(AI.Option.Naval.id.ROE,AI.Option.Naval.val.ROE.OPEN_FIRE)
-end
-return self
-end
-return nil
-end
-function CONTROLLABLE:OptionROEOpenFireWeaponFreePossible()
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-if self:IsAir()then
-return true
-end
-return false
-end
-return nil
-end
-function CONTROLLABLE:OptionROEOpenFireWeaponFree()
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-if self:IsAir()then
-Controller:setOption(AI.Option.Air.id.ROE,AI.Option.Air.val.ROE.OPEN_FIRE_WEAPON_FREE)
-end
-return self
-end
-return nil
-end
-function CONTROLLABLE:OptionROEWeaponFreePossible()
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-if self:IsAir()then
-return true
-end
-return false
-end
-return nil
-end
-function CONTROLLABLE:OptionROEWeaponFree()
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-if self:IsAir()then
-Controller:setOption(AI.Option.Air.id.ROE,AI.Option.Air.val.ROE.WEAPON_FREE)
-end
-return self
-end
-return nil
-end
-function CONTROLLABLE:OptionROTNoReactionPossible()
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-if self:IsAir()then
-return true
-end
-return false
-end
-return nil
-end
-function CONTROLLABLE:OptionROTNoReaction()
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-if self:IsAir()then
-Controller:setOption(AI.Option.Air.id.REACTION_ON_THREAT,AI.Option.Air.val.REACTION_ON_THREAT.NO_REACTION)
-end
-return self
-end
-return nil
-end
-function CONTROLLABLE:OptionROT(ROTvalue)
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-if self:IsAir()then
-Controller:setOption(AI.Option.Air.id.REACTION_ON_THREAT,ROTvalue)
-end
-return self
-end
-return nil
-end
-function CONTROLLABLE:OptionROTPassiveDefensePossible()
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-if self:IsAir()then
-return true
-end
-return false
-end
-return nil
-end
-function CONTROLLABLE:OptionROTPassiveDefense()
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-if self:IsAir()then
-Controller:setOption(AI.Option.Air.id.REACTION_ON_THREAT,AI.Option.Air.val.REACTION_ON_THREAT.PASSIVE_DEFENCE)
-end
-return self
-end
-return nil
-end
-function CONTROLLABLE:OptionROTEvadeFirePossible()
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-if self:IsAir()then
-return true
-end
-return false
-end
-return nil
-end
-function CONTROLLABLE:OptionROTEvadeFire()
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-if self:IsAir()then
-Controller:setOption(AI.Option.Air.id.REACTION_ON_THREAT,AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE)
-end
-return self
-end
-return nil
-end
-function CONTROLLABLE:OptionROTVerticalPossible()
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-if self:IsAir()then
-return true
-end
-return false
-end
-return nil
-end
-function CONTROLLABLE:OptionROTVertical()
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-if self:IsAir()then
-Controller:setOption(AI.Option.Air.id.REACTION_ON_THREAT,AI.Option.Air.val.REACTION_ON_THREAT.BYPASS_AND_ESCAPE)
-end
-return self
-end
-return nil
-end
-function CONTROLLABLE:OptionAlarmStateAuto()
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-if self:IsGround()then
-Controller:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.AUTO)
-elseif self:IsShip()then
-Controller:setOption(9,0)
-end
-return self
-end
-return nil
-end
-function CONTROLLABLE:OptionAlarmStateGreen()
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-if self:IsGround()then
-Controller:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN)
-elseif self:IsShip()then
-Controller:setOption(9,1)
-end
-return self
-end
-return nil
-end
-function CONTROLLABLE:OptionAlarmStateRed()
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-if self:IsGround()then
-Controller:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.RED)
-elseif self:IsShip()then
-Controller:setOption(9,2)
-end
-return self
-end
-return nil
-end
-function CONTROLLABLE:OptionRTBBingoFuel(RTB)
-self:F2({self.ControllableName})
-if RTB==nil then
-RTB=true
-end
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-if self:IsAir()then
-Controller:setOption(AI.Option.Air.id.RTB_ON_BINGO,RTB)
-end
-return self
-end
-return nil
-end
-function CONTROLLABLE:OptionRTBAmmo(WeaponsFlag)
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-if self:IsAir()then
-Controller:setOption(AI.Option.Air.id.RTB_ON_OUT_OF_AMMO,WeaponsFlag)
-end
-return self
-end
-return nil
-end
-function CONTROLLABLE:OptionAllowJettisonWeaponsOnThreat()
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-if self:IsAir()then
-Controller:setOption(AI.Option.Air.id.PROHIBIT_JETT,false)
-end
-return self
-end
-return nil
-end
-function CONTROLLABLE:OptionKeepWeaponsOnThreat()
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-if self:IsAir()then
-Controller:setOption(AI.Option.Air.id.PROHIBIT_JETT,true)
-end
-return self
-end
-return nil
-end
-function CONTROLLABLE:OptionProhibitAfterburner(Prohibit)
-self:F2({self.ControllableName})
-if Prohibit==nil then
-Prohibit=true
-end
-if self:IsAir()then
-self:SetOption(AI.Option.Air.id.PROHIBIT_AB,Prohibit)
-end
-return self
-end
-function CONTROLLABLE:OptionECM(ECMvalue)
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-if self:IsAir()then
-Controller:setOption(AI.Option.Air.id.ECM_USING,ECMvalue or 1)
-end
-end
-return self
-end
-function CONTROLLABLE:OptionECM_Never()
-self:F2({self.ControllableName})
-self:OptionECM(0)
-return self
-end
-function CONTROLLABLE:OptionECM_OnlyLockByRadar()
-self:F2({self.ControllableName})
-self:OptionECM(1)
-return self
-end
-function CONTROLLABLE:OptionECM_DetectedLockByRadar()
-self:F2({self.ControllableName})
-self:OptionECM(2)
-return self
-end
-function CONTROLLABLE:OptionECM_AlwaysOn()
-self:F2({self.ControllableName})
-self:OptionECM(3)
-return self
-end
-function CONTROLLABLE:WayPointInitialize(WayPoints)
-self:F({WayPoints})
-if WayPoints then
-self.WayPoints=WayPoints
-else
-self.WayPoints=self:GetTaskRoute()
-end
-return self
-end
-function CONTROLLABLE:GetWayPoints()
-self:F()
-if self.WayPoints then
-return self.WayPoints
-end
-return nil
-end
-function CONTROLLABLE:WayPointFunction(WayPoint,WayPointIndex,WayPointFunction,...)
-self:F2({WayPoint,WayPointIndex,WayPointFunction})
-table.insert(self.WayPoints[WayPoint].task.params.tasks,WayPointIndex)
-self.WayPoints[WayPoint].task.params.tasks[WayPointIndex]=self:TaskFunction(WayPointFunction,arg)
-return self
-end
-function CONTROLLABLE:WayPointExecute(WayPoint,WaitTime)
-self:F({WayPoint,WaitTime})
-if not WayPoint then
-WayPoint=1
-end
-for TaskPointID=1,WayPoint-1 do
-table.remove(self.WayPoints,1)
-end
-self:T3(self.WayPoints)
-self:SetTask(self:TaskRoute(self.WayPoints),WaitTime)
-return self
-end
-function CONTROLLABLE:IsAirPlane()
-self:F2()
-local DCSObject=self:GetDCSObject()
-if DCSObject then
-local Category=DCSObject:getDesc().category
-return Category==Unit.Category.AIRPLANE
-end
-return nil
-end
-function CONTROLLABLE:IsHelicopter()
-self:F2()
-local DCSObject=self:GetDCSObject()
-if DCSObject then
-local Category=DCSObject:getDesc().category
-return Category==Unit.Category.HELICOPTER
-end
-return nil
-end
-function CONTROLLABLE:OptionRestrictBurner(RestrictBurner)
-self:F2({self.ControllableName})
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-if Controller then
-if RestrictBurner==true then
-if self:IsAir()then
-Controller:setOption(16,true)
-end
-else
-if self:IsAir()then
-Controller:setOption(16,false)
-end
-end
-end
-end
-end
-function CONTROLLABLE:OptionAAAttackRange(range)
-self:F2({self.ControllableName})
-local range=range or 3
-if range<0 or range>4 then
-range=3
-end
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-if Controller then
-if self:IsAir()then
-self:SetOption(AI.Option.Air.id.MISSILE_ATTACK,range)
-end
-end
-return self
-end
-return nil
-end
-function CONTROLLABLE:OptionEngageRange(EngageRange)
-self:F2({self.ControllableName})
-EngageRange=EngageRange or 100
-if EngageRange<0 or EngageRange>100 then
-EngageRange=100
-end
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-if Controller then
-if self:IsGround()then
-self:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,EngageRange)
-end
-end
-return self
-end
-return nil
-end
-function CONTROLLABLE:RelocateGroundRandomInRadius(speed,radius,onroad,shortcut,formation)
-self:F2({self.ControllableName})
-local _coord=self:GetCoordinate()
-local _radius=radius or 500
-local _speed=speed or 20
-local _tocoord=_coord:GetRandomCoordinateInRadius(_radius,100)
-local _onroad=onroad or true
-local _grptsk={}
-local _candoroad=false
-local _shortcut=shortcut or false
-local _formation=formation or"Off Road"
-if onroad then
-_grptsk,_candoroad=self:TaskGroundOnRoad(_tocoord,_speed,_formation,_shortcut)
-self:Route(_grptsk,5)
-else
-self:TaskRouteToVec2(_tocoord:GetVec2(),_speed,_formation)
-end
-return self
-end
-function CONTROLLABLE:OptionDisperseOnAttack(Seconds)
-self:F2({self.ControllableName})
-local seconds=Seconds or 0
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-if Controller then
-if self:IsGround()then
-self:SetOption(AI.Option.Ground.id.DISPERSE_ON_ATTACK,seconds)
-end
-end
-return self
-end
-return nil
-end
-function CONTROLLABLE:IsSubmarine()
-self:F2()
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-local UnitDescriptor=DCSUnit:getDesc()
-if UnitDescriptor.attributes["Submarines"]==true then
-return true
-else
-return false
-end
-end
-return nil
-end
-function CONTROLLABLE:SetSpeed(Speed,Keep)
-self:F2({self.ControllableName})
-local speed=Speed or 5
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-if Controller then
-Controller:setSpeed(speed,Keep)
-end
-end
-return self
-end
-function CONTROLLABLE:SetAltitude(Altitude,Keep,AltType)
-self:F2({self.ControllableName})
-local altitude=Altitude or 1000
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=self:_GetController()
-if Controller then
-if self:IsAir()then
-Controller:setAltitude(altitude,Keep,AltType)
-end
-end
-end
-return self
-end
-function CONTROLLABLE:TaskAerobatics()
-local DCSTaskAerobatics={
-id="Aerobatics",
-params={
-["maneuversSequency"]={},
-},
-["enabled"]=true,
-["auto"]=false,
-}
-return DCSTaskAerobatics
-end
-function CONTROLLABLE:TaskAerobaticsCandle(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately)
-local maxrepeats=10
-if Repeats>maxrepeats then maxrepeats=Repeats end
-local usesmoke=UseSmoke and 1 or 0
-local startimmediately=StartImmediately and 1 or 0
-local CandleTask={
-["name"]="CANDLE",
-["params"]={
-["RepeatQty"]={
-["max_v"]=maxrepeats,
-["min_v"]=1,
-["order"]=1,
-["value"]=Repeats or 1,
-},
-["InitAltitude"]={
-["order"]=2,
-["value"]=InitAltitude or 0,
-},
-["InitSpeed"]={
-["order"]=3,
-["value"]=InitSpeed or 0,
-},
-["UseSmoke"]={
-["order"]=4,
-["value"]=usesmoke,
-},
-["StartImmediatly"]={
-["order"]=5,
-["value"]=startimmediately,
-}
-}
-}
-table.insert(TaskAerobatics.params["maneuversSequency"],CandleTask)
-return TaskAerobatics
-end
-function CONTROLLABLE:TaskAerobaticsEdgeFlight(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,FlightTime,Side)
-local maxrepeats=10
-local maxflight=200
-if Repeats>maxrepeats then maxrepeats=Repeats end
-local usesmoke=UseSmoke and 1 or 0
-local startimmediately=StartImmediately and 1 or 0
-local flighttime=FlightTime or 10
-if flighttime>200 then maxflight=flighttime end
-local EdgeTask={
-["name"]="EDGE_FLIGHT",
-["params"]={
-["RepeatQty"]={
-["max_v"]=maxrepeats,
-["min_v"]=1,
-["order"]=1,
-["value"]=Repeats or 1,
-},
-["InitAltitude"]={
-["order"]=2,
-["value"]=InitAltitude or 0,
-},
-["InitSpeed"]={
-["order"]=3,
-["value"]=InitSpeed or 0,
-},
-["UseSmoke"]={
-["order"]=4,
-["value"]=usesmoke,
-},
-["StartImmediatly"]={
-["order"]=5,
-["value"]=startimmediately,
-},
-["FlightTime"]={
-["max_v"]=maxflight,
-["min_v"]=1,
-["order"]=6,
-["step"]=0.1,
-["value"]=flighttime or 10,
-},
-["SIDE"]={
-["order"]=7,
-["value"]=Side or 0,
-},
-}
-}
-table.insert(TaskAerobatics.params["maneuversSequency"],EdgeTask)
-return TaskAerobatics
-end
-function CONTROLLABLE:TaskAerobaticsWingoverFlight(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,FlightTime)
-local maxrepeats=10
-local maxflight=200
-if Repeats>maxrepeats then maxrepeats=Repeats end
-local usesmoke=UseSmoke and 1 or 0
-local startimmediately=StartImmediately and 1 or 0
-local flighttime=FlightTime or 10
-if flighttime>200 then maxflight=flighttime end
-local WingoverTask={
-["name"]="WINGOVER_FLIGHT",
-["params"]={
-["RepeatQty"]={
-["max_v"]=maxrepeats,
-["min_v"]=1,
-["order"]=1,
-["value"]=Repeats or 1,
-},
-["InitAltitude"]={
-["order"]=2,
-["value"]=InitAltitude or 0,
-},
-["InitSpeed"]={
-["order"]=3,
-["value"]=InitSpeed or 0,
-},
-["UseSmoke"]={
-["order"]=4,
-["value"]=usesmoke,
-},
-["StartImmediatly"]={
-["order"]=5,
-["value"]=startimmediately,
-},
-["FlightTime"]={
-["max_v"]=maxflight,
-["min_v"]=1,
-["order"]=6,
-["step"]=0.1,
-["value"]=flighttime or 10,
-},
-}
-}
-table.insert(TaskAerobatics.params["maneuversSequency"],WingoverTask)
-return TaskAerobatics
-end
-function CONTROLLABLE:TaskAerobaticsLoop(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately)
-local maxrepeats=10
-if Repeats>maxrepeats then maxrepeats=Repeats end
-local usesmoke=UseSmoke and 1 or 0
-local startimmediately=StartImmediately and 1 or 0
-local LoopTask={
-["name"]="LOOP",
-["params"]={
-["RepeatQty"]={
-["max_v"]=maxrepeats,
-["min_v"]=1,
-["order"]=1,
-["value"]=Repeats or 1,
-},
-["InitAltitude"]={
-["order"]=2,
-["value"]=InitAltitude or 0,
-},
-["InitSpeed"]={
-["order"]=3,
-["value"]=InitSpeed or 0,
-},
-["UseSmoke"]={
-["order"]=4,
-["value"]=usesmoke,
-},
-["StartImmediatly"]={
-["order"]=5,
-["value"]=startimmediately,
-}
-}
-}
-table.insert(TaskAerobatics.params["maneuversSequency"],LoopTask)
-return TaskAerobatics
-end
-function CONTROLLABLE:TaskAerobaticsHorizontalEight(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,Side,RollDeg)
-local maxrepeats=10
-if Repeats>maxrepeats then maxrepeats=Repeats end
-local usesmoke=UseSmoke and 1 or 0
-local startimmediately=StartImmediately and 1 or 0
-local LoopTask={
-["name"]="HORIZONTAL_EIGHT",
-["params"]={
-["RepeatQty"]={
-["max_v"]=maxrepeats,
-["min_v"]=1,
-["order"]=1,
-["value"]=Repeats or 1,
-},
-["InitAltitude"]={
-["order"]=2,
-["value"]=InitAltitude or 0,
-},
-["InitSpeed"]={
-["order"]=3,
-["value"]=InitSpeed or 0,
-},
-["UseSmoke"]={
-["order"]=4,
-["value"]=usesmoke,
-},
-["StartImmediatly"]={
-["order"]=5,
-["value"]=startimmediately,
-},
-["SIDE"]={
-["order"]=6,
-["value"]=Side or 0,
-},
-["ROLL1"]={
-["order"]=7,
-["value"]=RollDeg or 60,
-},
-["ROLL2"]={
-["order"]=8,
-["value"]=RollDeg or 60,
-},
-}
-}
-table.insert(TaskAerobatics.params["maneuversSequency"],LoopTask)
-return TaskAerobatics
-end
-function CONTROLLABLE:TaskAerobaticsHammerhead(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,Side)
-local maxrepeats=10
-if Repeats>maxrepeats then maxrepeats=Repeats end
-local usesmoke=UseSmoke and 1 or 0
-local startimmediately=StartImmediately and 1 or 0
-local Task={
-["name"]="HUMMERHEAD",
-["params"]={
-["RepeatQty"]={
-["max_v"]=maxrepeats,
-["min_v"]=1,
-["order"]=1,
-["value"]=Repeats or 1,
-},
-["InitAltitude"]={
-["order"]=2,
-["value"]=InitAltitude or 0,
-},
-["InitSpeed"]={
-["order"]=3,
-["value"]=InitSpeed or 0,
-},
-["UseSmoke"]={
-["order"]=4,
-["value"]=usesmoke,
-},
-["StartImmediatly"]={
-["order"]=5,
-["value"]=startimmediately,
-},
-["SIDE"]={
-["order"]=6,
-["value"]=Side or 0,
-},
-}
-}
-table.insert(TaskAerobatics.params["maneuversSequency"],Task)
-return TaskAerobatics
-end
-function CONTROLLABLE:TaskAerobaticsSkewedLoop(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,Side,RollDeg)
-local maxrepeats=10
-if Repeats>maxrepeats then maxrepeats=Repeats end
-local usesmoke=UseSmoke and 1 or 0
-local startimmediately=StartImmediately and 1 or 0
-local Task={
-["name"]="SKEWED_LOOP",
-["params"]={
-["RepeatQty"]={
-["max_v"]=maxrepeats,
-["min_v"]=1,
-["order"]=1,
-["value"]=Repeats or 1,
-},
-["InitAltitude"]={
-["order"]=2,
-["value"]=InitAltitude or 0,
-},
-["InitSpeed"]={
-["order"]=3,
-["value"]=InitSpeed or 0,
-},
-["UseSmoke"]={
-["order"]=4,
-["value"]=usesmoke,
-},
-["StartImmediatly"]={
-["order"]=5,
-["value"]=startimmediately,
-},
-["ROLL"]={
-["order"]=6,
-["value"]=RollDeg or 60,
-},
-["SIDE"]={
-["order"]=7,
-["value"]=Side or 0,
-},
-}
-}
-table.insert(TaskAerobatics.params["maneuversSequency"],Task)
-return TaskAerobatics
-end
-function CONTROLLABLE:TaskAerobaticsTurn(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,Side,RollDeg,Pull,Angle)
-local maxrepeats=10
-if Repeats>maxrepeats then maxrepeats=Repeats end
-local usesmoke=UseSmoke and 1 or 0
-local startimmediately=StartImmediately and 1 or 0
-local Task={
-["name"]="TURN",
-["params"]={
-["RepeatQty"]={
-["max_v"]=maxrepeats,
-["min_v"]=1,
-["order"]=1,
-["value"]=Repeats or 1,
-},
-["InitAltitude"]={
-["order"]=2,
-["value"]=InitAltitude or 0,
-},
-["InitSpeed"]={
-["order"]=3,
-["value"]=InitSpeed or 0,
-},
-["UseSmoke"]={
-["order"]=4,
-["value"]=usesmoke,
-},
-["StartImmediatly"]={
-["order"]=5,
-["value"]=startimmediately,
-},
-["Ny_req"]={
-["order"]=6,
-["value"]=Pull or 2,
-},
-["ROLL"]={
-["order"]=7,
-["value"]=RollDeg or 60,
-},
-["SECTOR"]={
-["order"]=8,
-["value"]=Angle or 180,
-},
-["SIDE"]={
-["order"]=9,
-["value"]=Side or 0,
-},
-}
-}
-table.insert(TaskAerobatics.params["maneuversSequency"],Task)
-return TaskAerobatics
-end
-function CONTROLLABLE:TaskAerobaticsDive(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,Angle,FinalAltitude)
-local maxrepeats=10
-local angle=Angle
-if angle<15 then angle=15 elseif angle>90 then angle=90 end
-if Repeats>maxrepeats then maxrepeats=Repeats end
-local usesmoke=UseSmoke and 1 or 0
-local startimmediately=StartImmediately and 1 or 0
-local Task={
-["name"]="DIVE",
-["params"]={
-["RepeatQty"]={
-["max_v"]=maxrepeats,
-["min_v"]=1,
-["order"]=1,
-["value"]=Repeats or 1,
-},
-["InitAltitude"]={
-["order"]=2,
-["value"]=InitAltitude or 5000,
-},
-["InitSpeed"]={
-["order"]=3,
-["value"]=InitSpeed or 0,
-},
-["UseSmoke"]={
-["order"]=4,
-["value"]=usesmoke,
-},
-["StartImmediatly"]={
-["order"]=5,
-["value"]=startimmediately,
-},
-["Angle"]={
-["max_v"]=90,
-["min_v"]=15,
-["order"]=6,
-["step"]=5,
-["value"]=angle or 45,
-},
-["FinalAltitude"]={
-["order"]=7,
-["value"]=FinalAltitude or 1000,
-},
-}
-}
-table.insert(TaskAerobatics.params["maneuversSequency"],Task)
-return TaskAerobatics
-end
-function CONTROLLABLE:TaskAerobaticsMilitaryTurn(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately)
-local maxrepeats=10
-if Repeats>maxrepeats then maxrepeats=Repeats end
-local usesmoke=UseSmoke and 1 or 0
-local startimmediately=StartImmediately and 1 or 0
-local Task={
-["name"]="MILITARY_TURN",
-["params"]={
-["RepeatQty"]={
-["max_v"]=maxrepeats,
-["min_v"]=1,
-["order"]=1,
-["value"]=Repeats or 1,
-},
-["InitAltitude"]={
-["order"]=2,
-["value"]=InitAltitude or 0,
-},
-["InitSpeed"]={
-["order"]=3,
-["value"]=InitSpeed or 0,
-},
-["UseSmoke"]={
-["order"]=4,
-["value"]=usesmoke,
-},
-["StartImmediatly"]={
-["order"]=5,
-["value"]=startimmediately,
-}
-}
-}
-table.insert(TaskAerobatics.params["maneuversSequency"],Task)
-return TaskAerobatics
-end
-function CONTROLLABLE:TaskAerobaticsImmelmann(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately)
-local maxrepeats=10
-if Repeats>maxrepeats then maxrepeats=Repeats end
-local usesmoke=UseSmoke and 1 or 0
-local startimmediately=StartImmediately and 1 or 0
-local Task={
-["name"]="IMMELMAN",
-["params"]={
-["RepeatQty"]={
-["max_v"]=maxrepeats,
-["min_v"]=1,
-["order"]=1,
-["value"]=Repeats or 1,
-},
-["InitAltitude"]={
-["order"]=2,
-["value"]=InitAltitude or 0,
-},
-["InitSpeed"]={
-["order"]=3,
-["value"]=InitSpeed or 0,
-},
-["UseSmoke"]={
-["order"]=4,
-["value"]=usesmoke,
-},
-["StartImmediatly"]={
-["order"]=5,
-["value"]=startimmediately,
-}
-}
-}
-table.insert(TaskAerobatics.params["maneuversSequency"],Task)
-return TaskAerobatics
-end
-function CONTROLLABLE:TaskAerobaticsStraightFlight(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,FlightTime)
-local maxrepeats=10
-if Repeats>maxrepeats then maxrepeats=Repeats end
-local maxflight=200
-if Repeats>maxrepeats then maxrepeats=Repeats end
-local flighttime=FlightTime or 10
-if flighttime>200 then maxflight=flighttime end
-local usesmoke=UseSmoke and 1 or 0
-local startimmediately=StartImmediately and 1 or 0
-local Task={
-["name"]="STRAIGHT_FLIGHT",
-["params"]={
-["RepeatQty"]={
-["max_v"]=maxrepeats,
-["min_v"]=1,
-["order"]=1,
-["value"]=Repeats or 1,
-},
-["InitAltitude"]={
-["order"]=2,
-["value"]=InitAltitude or 0,
-},
-["InitSpeed"]={
-["order"]=3,
-["value"]=InitSpeed or 0,
-},
-["UseSmoke"]={
-["order"]=4,
-["value"]=usesmoke,
-},
-["StartImmediatly"]={
-["order"]=5,
-["value"]=startimmediately,
-},
-["FlightTime"]={
-["max_v"]=maxflight,
-["min_v"]=1,
-["order"]=6,
-["step"]=0.1,
-["value"]=flighttime or 10,
-},
-}
-}
-table.insert(TaskAerobatics.params["maneuversSequency"],Task)
-return TaskAerobatics
-end
-function CONTROLLABLE:TaskAerobaticsClimb(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,Angle,FinalAltitude)
-local maxrepeats=10
-if Repeats>maxrepeats then maxrepeats=Repeats end
-local usesmoke=UseSmoke and 1 or 0
-local startimmediately=StartImmediately and 1 or 0
-local Task={
-["name"]="CLIMB",
-["params"]={
-["RepeatQty"]={
-["max_v"]=maxrepeats,
-["min_v"]=1,
-["order"]=1,
-["value"]=Repeats or 1,
-},
-["InitAltitude"]={
-["order"]=2,
-["value"]=InitAltitude or 0,
-},
-["InitSpeed"]={
-["order"]=3,
-["value"]=InitSpeed or 0,
-},
-["UseSmoke"]={
-["order"]=4,
-["value"]=usesmoke,
-},
-["StartImmediatly"]={
-["order"]=5,
-["value"]=startimmediately,
-},
-["Angle"]={
-["max_v"]=90,
-["min_v"]=15,
-["order"]=6,
-["step"]=5,
-["value"]=Angle or 45,
-},
-["FinalAltitude"]={
-["order"]=7,
-["value"]=FinalAltitude or 5000,
-},
-}
-}
-table.insert(TaskAerobatics.params["maneuversSequency"],Task)
-return TaskAerobatics
-end
-function CONTROLLABLE:TaskAerobaticsSpiral(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,TurnAngle,Roll,Side,UpDown,Angle)
-local maxrepeats=10
-if Repeats>maxrepeats then maxrepeats=Repeats end
-local usesmoke=UseSmoke and 1 or 0
-local startimmediately=StartImmediately and 1 or 0
-local updown=UpDown and 1 or 0
-local side=Side and 1 or 0
-local Task={
-["name"]="SPIRAL",
-["params"]={
-["RepeatQty"]={
-["max_v"]=maxrepeats,
-["min_v"]=1,
-["order"]=1,
-["value"]=Repeats or 1,
-},
-["InitAltitude"]={
-["order"]=2,
-["value"]=InitAltitude or 0,
-},
-["InitSpeed"]={
-["order"]=3,
-["value"]=InitSpeed or 0,
-},
-["UseSmoke"]={
-["order"]=4,
-["value"]=usesmoke,
-},
-["StartImmediatly"]={
-["order"]=5,
-["value"]=startimmediately,
-},
-["SECTOR"]={
-["order"]=6,
-["value"]=TurnAngle or 360,
-},
-["ROLL"]={
-["order"]=7,
-["value"]=Roll or 60,
-},
-["SIDE"]={
-["order"]=8,
-["value"]=side or 0,
-},
-["UPDOWN"]={
-["order"]=9,
-["value"]=updown or 0,
-},
-["Angle"]={
-["max_v"]=90,
-["min_v"]=15,
-["order"]=10,
-["step"]=5,
-["value"]=Angle or 45,
-},
-}
-}
-table.insert(TaskAerobatics.params["maneuversSequency"],Task)
-return TaskAerobatics
-end
-function CONTROLLABLE:TaskAerobaticsSplitS(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,FinalSpeed)
-local maxrepeats=10
-if Repeats>maxrepeats then maxrepeats=Repeats end
-local maxflight=200
-if Repeats>maxrepeats then maxrepeats=Repeats end
-local finalspeed=FinalSpeed or 500
-local usesmoke=UseSmoke and 1 or 0
-local startimmediately=StartImmediately and 1 or 0
-local Task={
-["name"]="SPLIT_S",
-["params"]={
-["RepeatQty"]={
-["max_v"]=maxrepeats,
-["min_v"]=1,
-["order"]=1,
-["value"]=Repeats or 1,
-},
-["InitAltitude"]={
-["order"]=2,
-["value"]=InitAltitude or 0,
-},
-["InitSpeed"]={
-["order"]=3,
-["value"]=InitSpeed or 0,
-},
-["UseSmoke"]={
-["order"]=4,
-["value"]=usesmoke,
-},
-["StartImmediatly"]={
-["order"]=5,
-["value"]=startimmediately,
-},
-["FinalSpeed"]={
-["order"]=6,
-["value"]=finalspeed,
-},
-}
-}
-table.insert(TaskAerobatics.params["maneuversSequency"],Task)
-return TaskAerobatics
-end
-function CONTROLLABLE:TaskAerobaticsAileronRoll(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,Side,RollRate,TurnAngle,FixAngle)
-local maxrepeats=10
-if Repeats>maxrepeats then maxrepeats=Repeats end
-local maxflight=200
-if Repeats>maxrepeats then maxrepeats=Repeats end
-local usesmoke=UseSmoke and 1 or 0
-local startimmediately=StartImmediately and 1 or 0
-local Task={
-["name"]="AILERON_ROLL",
-["params"]={
-["RepeatQty"]={
-["max_v"]=maxrepeats,
-["min_v"]=1,
-["order"]=1,
-["value"]=Repeats or 1,
-},
-["InitAltitude"]={
-["order"]=2,
-["value"]=InitAltitude or 0,
-},
-["InitSpeed"]={
-["order"]=3,
-["value"]=InitSpeed or 0,
-},
-["UseSmoke"]={
-["order"]=4,
-["value"]=usesmoke,
-},
-["StartImmediatly"]={
-["order"]=5,
-["value"]=startimmediately,
-},
-["SIDE"]={
-["order"]=6,
-["value"]=Side or 0,
-},
-["RollRate"]={
-["max_v"]=450,
-["min_v"]=15,
-["order"]=7,
-["step"]=5,
-["value"]=RollRate or 90,
-},
-["SECTOR"]={
-["order"]=8,
-["value"]=TurnAngle or 360,
-},
-["FIXSECTOR"]={
-["max_v"]=180,
-["min_v"]=0,
-["order"]=9,
-["step"]=5,
-["value"]=FixAngle or 0,
-},
-}
-}
-table.insert(TaskAerobatics.params["maneuversSequency"],Task)
-return TaskAerobatics
-end
-function CONTROLLABLE:TaskAerobaticsForcedTurn(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,TurnAngle,Side,FlightTime,MinSpeed)
-local maxrepeats=10
-local flighttime=FlightTime or 30
-local maxtime=200
-if flighttime>200 then maxtime=flighttime end
-if Repeats>maxrepeats then maxrepeats=Repeats end
-local usesmoke=UseSmoke and 1 or 0
-local startimmediately=StartImmediately and 1 or 0
-local Task={
-["name"]="FORCED_TURN",
-["params"]={
-["RepeatQty"]={
-["max_v"]=maxrepeats,
-["min_v"]=1,
-["order"]=1,
-["value"]=Repeats or 1,
-},
-["InitAltitude"]={
-["order"]=2,
-["value"]=InitAltitude or 0,
-},
-["InitSpeed"]={
-["order"]=3,
-["value"]=InitSpeed or 0,
-},
-["UseSmoke"]={
-["order"]=4,
-["value"]=usesmoke,
-},
-["StartImmediatly"]={
-["order"]=5,
-["value"]=startimmediately,
-},
-["SECTOR"]={
-["order"]=6,
-["value"]=TurnAngle or 360,
-},
-["SIDE"]={
-["order"]=7,
-["value"]=Side or 0,
-},
-["FlightTime"]={
-["max_v"]=maxtime or 200,
-["min_v"]=0,
-["order"]=8,
-["step"]=0.1,
-["value"]=flighttime or 30,
-},
-["MinSpeed"]={
-["max_v"]=3000,
-["min_v"]=30,
-["order"]=9,
-["step"]=10,
-["value"]=MinSpeed or 250,
-},
-}
-}
-table.insert(TaskAerobatics.params["maneuversSequency"],Task)
-return TaskAerobatics
-end
-function CONTROLLABLE:TaskAerobaticsBarrelRoll(TaskAerobatics,Repeats,InitAltitude,InitSpeed,UseSmoke,StartImmediately,Side,RollRate,TurnAngle)
-local maxrepeats=10
-if Repeats>maxrepeats then maxrepeats=Repeats end
-local usesmoke=UseSmoke and 1 or 0
-local startimmediately=StartImmediately and 1 or 0
-local Task={
-["name"]="BARREL_ROLL",
-["params"]={
-["RepeatQty"]={
-["max_v"]=maxrepeats,
-["min_v"]=1,
-["order"]=1,
-["value"]=Repeats or 1,
-},
-["InitAltitude"]={
-["order"]=2,
-["value"]=InitAltitude or 0,
-},
-["InitSpeed"]={
-["order"]=3,
-["value"]=InitSpeed or 0,
-},
-["UseSmoke"]={
-["order"]=4,
-["value"]=usesmoke,
-},
-["StartImmediatly"]={
-["order"]=5,
-["value"]=startimmediately,
-},
-["SIDE"]={
-["order"]=6,
-["value"]=Side or 0,
-},
-["RollRate"]={
-["max_v"]=450,
-["min_v"]=15,
-["order"]=7,
-["step"]=5,
-["value"]=RollRate or 90,
-},
-["SECTOR"]={
-["order"]=8,
-["value"]=TurnAngle or 360,
-},
-}
-}
-table.insert(TaskAerobatics.params["maneuversSequency"],Task)
-return TaskAerobatics
-end
-function CONTROLLABLE:PatrolRaceTrack(Point1,Point2,Altitude,Speed,Formation,AGL,Delay)
-local PatrolGroup=self
-if not self:IsInstanceOf("GROUP")then
-PatrolGroup=self:GetGroup()
-end
-local delay=Delay or 1
-self:F({PatrolGroup=PatrolGroup:GetName()})
-if PatrolGroup:IsAir()then
-if Formation then
-PatrolGroup:SetOption(AI.Option.Air.id.FORMATION,Formation)
-end
-local FromCoord=PatrolGroup:GetCoordinate()
-local ToCoord=Point1:GetCoordinate()
-if Altitude then
-local asl=true
-if AGL then asl=false end
-FromCoord:SetAltitude(Altitude,asl)
-ToCoord:SetAltitude(Altitude,asl)
-end
-local Route={}
-Route[#Route+1]=FromCoord:WaypointAir(AltType,COORDINATE.WaypointType.TurningPoint,COORDINATE.WaypointAction.TurningPoint,Speed,true,nil,DCSTasks,description,timeReFuAr)
-Route[#Route+1]=ToCoord:WaypointAir(AltType,COORDINATE.WaypointType.TurningPoint,COORDINATE.WaypointAction.TurningPoint,Speed,true,nil,DCSTasks,description,timeReFuAr)
-local TaskRouteToZone=PatrolGroup:TaskFunction("CONTROLLABLE.PatrolRaceTrack",Point2,Point1,Altitude,Speed,Formation,Delay)
-PatrolGroup:SetTaskWaypoint(Route[#Route],TaskRouteToZone)
-PatrolGroup:Route(Route,Delay)
-end
-return self
-end
-GROUP={
-ClassName="GROUP",
-}
-GROUP.Takeoff={
-Air=1,
-Runway=2,
-Hot=3,
-Cold=4,
-}
-GROUPTEMPLATE={}
-GROUPTEMPLATE.Takeoff={
-[GROUP.Takeoff.Air]={"Turning Point","Turning Point"},
-[GROUP.Takeoff.Runway]={"TakeOff","From Runway"},
-[GROUP.Takeoff.Hot]={"TakeOffParkingHot","From Parking Area Hot"},
-[GROUP.Takeoff.Cold]={"TakeOffParking","From Parking Area"}
-}
-GROUP.Attribute={
-AIR_TRANSPORTPLANE="Air_TransportPlane",
-AIR_AWACS="Air_AWACS",
-AIR_FIGHTER="Air_Fighter",
-AIR_BOMBER="Air_Bomber",
-AIR_TANKER="Air_Tanker",
-AIR_TRANSPORTHELO="Air_TransportHelo",
-AIR_ATTACKHELO="Air_AttackHelo",
-AIR_UAV="Air_UAV",
-AIR_OTHER="Air_OtherAir",
-GROUND_APC="Ground_APC",
-GROUND_TRUCK="Ground_Truck",
-GROUND_INFANTRY="Ground_Infantry",
-GROUND_IFV="Ground_IFV",
-GROUND_ARTILLERY="Ground_Artillery",
-GROUND_TANK="Ground_Tank",
-GROUND_TRAIN="Ground_Train",
-GROUND_EWR="Ground_EWR",
-GROUND_AAA="Ground_AAA",
-GROUND_SAM="Ground_SAM",
-GROUND_OTHER="Ground_OtherGround",
-NAVAL_AIRCRAFTCARRIER="Naval_AircraftCarrier",
-NAVAL_WARSHIP="Naval_WarShip",
-NAVAL_ARMEDSHIP="Naval_ArmedShip",
-NAVAL_UNARMEDSHIP="Naval_UnarmedShip",
-NAVAL_OTHER="Naval_OtherNaval",
-OTHER_UNKNOWN="Other_Unknown",
-}
-function GROUP:NewTemplate(GroupTemplate,CoalitionSide,CategoryID,CountryID)
-local GroupName=GroupTemplate.name
-_DATABASE:_RegisterGroupTemplate(GroupTemplate,CoalitionSide,CategoryID,CountryID,GroupName)
-local self=BASE:Inherit(self,CONTROLLABLE:New(GroupName))
-self.GroupName=GroupName
-if not _DATABASE.GROUPS[GroupName]then
-_DATABASE.GROUPS[GroupName]=self
-end
-self:SetEventPriority(4)
-return self
-end
-function GROUP:Register(GroupName)
-local self=BASE:Inherit(self,CONTROLLABLE:New(GroupName))
-self.GroupName=GroupName
-self:SetEventPriority(4)
-return self
-end
-function GROUP:Find(DCSGroup)
-local GroupName=DCSGroup:getName()
-local GroupFound=_DATABASE:FindGroup(GroupName)
-return GroupFound
-end
-function GROUP:FindByName(GroupName)
-local GroupFound=_DATABASE:FindGroup(GroupName)
-return GroupFound
-end
-function GROUP:FindByMatching(Pattern)
-local GroupFound=nil
-for name,group in pairs(_DATABASE.GROUPS)do
-if string.match(name,Pattern)then
-GroupFound=group
-break
-end
-end
-return GroupFound
-end
-function GROUP:FindAllByMatching(Pattern)
-local GroupsFound={}
-for name,group in pairs(_DATABASE.GROUPS)do
-if string.match(name,Pattern)then
-GroupsFound[#GroupsFound+1]=group
-end
-end
-return GroupsFound
-end
-function GROUP:GetDCSObject()
-local DCSGroup=Group.getByName(self.GroupName)
-if DCSGroup then
-return DCSGroup
-end
-return nil
-end
-function GROUP:GetPositionVec3()
-self:F2(self.PositionableName)
-local DCSPositionable=self:GetDCSObject()
-if DCSPositionable then
-local unit=DCSPositionable:getUnits()[1]
-if unit then
-local PositionablePosition=unit:getPosition().p
-self:T3(PositionablePosition)
-return PositionablePosition
-end
-end
-return nil
-end
-function GROUP:IsAlive()
-self:F2(self.GroupName)
-local DCSGroup=self:GetDCSObject()
-if DCSGroup then
-if DCSGroup:isExist()then
-local DCSUnit=DCSGroup:getUnit(1)
-if DCSUnit then
-local GroupIsAlive=DCSUnit:isActive()
-self:T3(GroupIsAlive)
-return GroupIsAlive
-end
-end
-end
-return nil
-end
-function GROUP:IsActive()
-self:F2(self.GroupName)
-local DCSGroup=self:GetDCSObject()
-if DCSGroup then
-local unit=DCSGroup:getUnit(1)
-if unit then
-local GroupIsActive=unit:isActive()
-return GroupIsActive
-end
-end
-return nil
-end
-function GROUP:Destroy(GenerateEvent,delay)
-self:F2(self.GroupName)
-if delay and delay>0 then
-self:ScheduleOnce(delay,GROUP.Destroy,self,GenerateEvent)
-else
-local DCSGroup=self:GetDCSObject()
-if DCSGroup then
-for Index,UnitData in pairs(DCSGroup:getUnits())do
-if GenerateEvent and GenerateEvent==true then
-if self:IsAir()then
-self:CreateEventCrash(timer.getTime(),UnitData)
-else
-self:CreateEventDead(timer.getTime(),UnitData)
-end
-elseif GenerateEvent==false then
-else
-self:CreateEventRemoveUnit(timer.getTime(),UnitData)
-end
-end
-USERFLAG:New(self:GetName()):Set(100)
-DCSGroup:destroy()
-DCSGroup=nil
-end
-end
-return nil
-end
-function GROUP:GetCategory()
-self:F2(self.GroupName)
-local DCSGroup=self:GetDCSObject()
-if DCSGroup then
-local GroupCategory=DCSGroup:getCategory()
-self:T3(GroupCategory)
-return GroupCategory
-end
-return nil
-end
-function GROUP:GetCategoryName()
-self:F2(self.GroupName)
-local DCSGroup=self:GetDCSObject()
-if DCSGroup then
-local CategoryNames={
-[Group.Category.AIRPLANE]="Airplane",
-[Group.Category.HELICOPTER]="Helicopter",
-[Group.Category.GROUND]="Ground Unit",
-[Group.Category.SHIP]="Ship",
-[Group.Category.TRAIN]="Train",
-}
-local GroupCategory=DCSGroup:getCategory()
-self:T3(GroupCategory)
-return CategoryNames[GroupCategory]
-end
-return nil
-end
-function GROUP:GetCoalition()
-self:F2(self.GroupName)
-local DCSGroup=self:GetDCSObject()
-if DCSGroup then
-local GroupCoalition=DCSGroup:getCoalition()
-self:T3(GroupCoalition)
-return GroupCoalition
-end
-return nil
-end
-function GROUP:GetCountry()
-self:F2(self.GroupName)
-local DCSGroup=self:GetDCSObject()
-if DCSGroup then
-local GroupCountry=DCSGroup:getUnit(1):getCountry()
-self:T3(GroupCountry)
-return GroupCountry
-end
-return nil
-end
-function GROUP:HasAttribute(attribute,all)
-local _units=self:GetUnits()
-if _units then
-local _allhave=true
-local _onehas=false
-for _,_unit in pairs(_units)do
-local _unit=_unit
-if _unit then
-local _hastit=_unit:HasAttribute(attribute)
-if _hastit==true then
-_onehas=true
-else
-_allhave=false
-end
-end
-end
-if all==true then
-return _allhave
-else
-return _onehas
-end
-end
-return nil
-end
-function GROUP:GetSpeedMax()
-self:F2(self.GroupName)
-local DCSGroup=self:GetDCSObject()
-if DCSGroup then
-local Units=self:GetUnits()
-local speedmax=nil
-for _,unit in pairs(Units)do
-local unit=unit
-local speed=unit:GetSpeedMax()
-if speedmax==nil or speed0 then
-self:ScheduleOnce(delay,GROUP.Activate,self)
-else
-trigger.action.activateGroup(self:GetDCSObject())
-end
-return self
-end
-function GROUP:GetTypeName()
-self:F2(self.GroupName)
-local DCSGroup=self:GetDCSObject()
-if DCSGroup then
-local GroupTypeName=DCSGroup:getUnit(1):getTypeName()
-self:T3(GroupTypeName)
-return(GroupTypeName)
-end
-return nil
-end
-function GROUP:GetNatoReportingName()
-self:F2(self.GroupName)
-local DCSGroup=self:GetDCSObject()
-if DCSGroup then
-local GroupTypeName=DCSGroup:getUnit(1):getTypeName()
-self:T3(GroupTypeName)
-return UTILS.GetReportingName(GroupTypeName)
-end
-return"Bogey"
-end
-function GROUP:GetPlayerName()
-self:F2(self.GroupName)
-local DCSGroup=self:GetDCSObject()
-if DCSGroup then
-local PlayerName=DCSGroup:getUnit(1):getPlayerName()
-self:T3(PlayerName)
-return(PlayerName)
-end
-return nil
-end
-function GROUP:GetCallsign()
-self:F2(self.GroupName)
-local DCSGroup=self:GetDCSObject()
-if DCSGroup then
-local GroupCallSign=DCSGroup:getUnit(1):getCallsign()
-self:T3(GroupCallSign)
-return GroupCallSign
-end
-BASE:E({"Cannot GetCallsign",Positionable=self,Alive=self:IsAlive()})
-return nil
-end
-function GROUP:GetVec2()
-local Unit=self:GetUnit(1)
-if Unit then
-local vec2=Unit:GetVec2()
-return vec2
-end
-end
-function GROUP:GetVec3()
-local unit=self:GetUnit(1)
-if unit then
-local vec3=unit:GetVec3()
-return vec3
-end
-self:E("ERROR: Cannot get Vec3 of group "..tostring(self.GroupName))
-return nil
-end
-function GROUP:GetAverageVec3()
-local units=self:GetUnits()or{}
-local x=0;local y=0;local z=0;local n=0
-for _,unit in pairs(units)do
-local vec3=nil
-if unit and unit:IsAlive()then
-vec3=unit:GetVec3()
-end
-if vec3 then
-x=x+vec3.x
-y=y+vec3.y
-z=z+vec3.z
-n=n+1
-end
-end
-if n>0 then
-local Vec3={x=x/n,y=y/n,z=z/n}
-return Vec3
-end
-return nil
-end
-function GROUP:GetPointVec2()
-self:F2(self.GroupName)
-local FirstUnit=self:GetUnit(1)
-if FirstUnit then
-local FirstUnitPointVec2=FirstUnit:GetPointVec2()
-self:T3(FirstUnitPointVec2)
-return FirstUnitPointVec2
-end
-BASE:E({"Cannot GetPointVec2",Group=self,Alive=self:IsAlive()})
-return nil
-end
-function GROUP:GetAverageCoordinate()
-local vec3=self:GetAverageVec3()
-if vec3 then
-local coord=COORDINATE:NewFromVec3(vec3)
-local Heading=self:GetHeading()
-coord.Heading=Heading
-return coord
-else
-BASE:E({"Cannot GetAverageCoordinate",Group=self,Alive=self:IsAlive()})
-return nil
-end
-end
-function GROUP:GetCoordinate()
-local Units=self:GetUnits()or{}
-for _,_unit in pairs(Units)do
-local FirstUnit=_unit
-if FirstUnit then
-local FirstUnitCoordinate=FirstUnit:GetCoordinate()
-if FirstUnitCoordinate then
-local Heading=self:GetHeading()
-FirstUnitCoordinate.Heading=Heading
-return FirstUnitCoordinate
-end
-end
-end
-BASE:E({"Cannot GetCoordinate",Group=self,Alive=self:IsAlive()})
-end
-function GROUP:GetRandomVec3(Radius)
-self:F2(self.GroupName)
-local FirstUnit=self:GetUnit(1)
-if FirstUnit then
-local FirstUnitRandomPointVec3=FirstUnit:GetRandomVec3(Radius)
-self:T3(FirstUnitRandomPointVec3)
-return FirstUnitRandomPointVec3
-end
-BASE:E({"Cannot GetRandomVec3",Group=self,Alive=self:IsAlive()})
-return nil
-end
-function GROUP:GetHeading()
-self:F2(self.GroupName)
-self:F2(self.GroupName)
-local GroupSize=self:GetSize()
-local HeadingAccumulator=0
-local n=0
-local Units=self:GetUnits()
-if GroupSize then
-for _,unit in pairs(Units)do
-if unit and unit:IsAlive()then
-HeadingAccumulator=HeadingAccumulator+unit:GetHeading()
-n=n+1
-end
-end
-return math.floor(HeadingAccumulator/n)
-end
-BASE:E({"Cannot GetHeading",Group=self,Alive=self:IsAlive()})
-return nil
-end
-function GROUP:GetFuelMin()
-self:F3(self.ControllableName)
-if not self:GetDCSObject()then
-BASE:E({"Cannot GetFuel",Group=self,Alive=self:IsAlive()})
-return 0
-end
-local min=65535
-local unit=nil
-local tmp=nil
-for UnitID,UnitData in pairs(self:GetUnits())do
-if UnitData and UnitData:IsAlive()then
-tmp=UnitData:GetFuel()
-if tmpGroupVelocityMax then
-GroupVelocityMax=UnitVelocity
-end
-end
-return GroupVelocityMax
-end
-return nil
-end
-function GROUP:GetMinHeight()
-self:F2()
-local DCSGroup=self:GetDCSObject()
-if DCSGroup then
-local GroupHeightMin=999999999
-for Index,UnitData in pairs(DCSGroup:getUnits())do
-local UnitData=UnitData
-local UnitHeight=UnitData:getPoint()
-if UnitHeightGroupHeightMax then
-GroupHeightMax=UnitHeight
-end
-end
-return GroupHeightMax
-end
-return nil
-end
-function GROUP:GetTemplate()
-local GroupName=self:GetName()
-return UTILS.DeepCopy(_DATABASE:GetGroupTemplate(GroupName))
-end
-function GROUP:GetTemplateRoutePoints()
-local GroupName=self:GetName()
-return UTILS.DeepCopy(_DATABASE:GetGroupTemplate(GroupName).route.points)
-end
-function GROUP:SetTemplateControlled(Template,Controlled)
-Template.uncontrolled=not Controlled
-return Template
-end
-function GROUP:SetTemplateCountry(Template,CountryID)
-Template.CountryID=CountryID
-return Template
-end
-function GROUP:SetTemplateCoalition(Template,CoalitionID)
-Template.CoalitionID=CoalitionID
-return Template
-end
-function GROUP:InitHeading(Heading)
-self.InitRespawnHeading=Heading
-return self
-end
-function GROUP:InitHeight(Height)
-self.InitRespawnHeight=Height
-return self
-end
-function GROUP:InitZone(Zone)
-self.InitRespawnZone=Zone
-return self
-end
-function GROUP:InitRandomizePositionZone(PositionZone)
-self.InitRespawnRandomizePositionZone=PositionZone
-self.InitRespawnRandomizePositionInner=nil
-self.InitRespawnRandomizePositionOuter=nil
-return self
-end
-function GROUP:InitRandomizePositionRadius(OuterRadius,InnerRadius)
-self.InitRespawnRandomizePositionZone=nil
-self.InitRespawnRandomizePositionOuter=OuterRadius
-self.InitRespawnRandomizePositionInner=InnerRadius
-return self
-end
-function GROUP:InitCoordinate(coordinate)
-self:F({coordinate=coordinate})
-self.InitCoord=coordinate
-return self
-end
-function GROUP:InitRadioCommsOnOff(switch)
-self:F({switch=switch})
-if switch==true or switch==nil then
-self.InitRespawnRadio=true
-else
-self.InitRespawnRadio=false
-end
-return self
-end
-function GROUP:InitRadioFrequency(frequency)
-self:F({frequency=frequency})
-self.InitRespawnFreq=frequency
-return self
-end
-function GROUP:InitRadioModulation(modulation)
-self:F({modulation=modulation})
-if modulation and modulation:lower()=="fm"then
-self.InitRespawnModu=radio.modulation.FM
-else
-self.InitRespawnModu=radio.modulation.AM
-end
-return self
-end
-function GROUP:InitModex(modex)
-self:F({modex=modex})
-if modex then
-self.InitRespawnModex=tonumber(modex)
-end
-return self
-end
-function GROUP:Respawn(Template,Reset)
-Template=Template or self:GetTemplate()
-local function _Heading(course)
-local h
-if course<=180 then
-h=math.rad(course)
-else
-h=-math.rad(360-course)
-end
-return h
-end
-if self:IsAlive()then
-local Zone=self.InitRespawnZone
-local Vec3=Zone and Zone:GetVec3()or self:GetVec3()
-local From={x=Template.x,y=Template.y}
-Template.x=Vec3.x
-Template.y=Vec3.z
-self:F(#Template.units)
-if Reset==true then
-for UnitID,UnitData in pairs(self:GetUnits())do
-local GroupUnit=UnitData
-self:F(GroupUnit:GetName())
-if GroupUnit:IsAlive()then
-self:I("FF Alive")
-local GroupUnitVec3=GroupUnit:GetVec3()
-if Zone then
-if self.InitRespawnRandomizePositionZone then
-GroupUnitVec3=Zone:GetRandomVec3()
-else
-if self.InitRespawnRandomizePositionInner and self.InitRespawnRandomizePositionOuter then
-GroupUnitVec3=POINT_VEC3:NewFromVec2(From):GetRandomPointVec3InRadius(self.InitRespawnRandomizePositionsOuter,self.InitRespawnRandomizePositionsInner)
-else
-GroupUnitVec3=Zone:GetVec3()
-end
-end
-end
-if self.InitCoord then
-GroupUnitVec3=self.InitCoord:GetVec3()
-end
-Template.units[UnitID].alt=self.InitRespawnHeight and self.InitRespawnHeight or GroupUnitVec3.y
-if Zone then
-Template.units[UnitID].x=(Template.units[UnitID].x-From.x)+GroupUnitVec3.x
-Template.units[UnitID].y=(Template.units[UnitID].y-From.y)+GroupUnitVec3.z
-else
-Template.units[UnitID].x=GroupUnitVec3.x
-Template.units[UnitID].y=GroupUnitVec3.z
-end
-Template.units[UnitID].heading=_Heading(self.InitRespawnHeading and self.InitRespawnHeading or GroupUnit:GetHeading())
-Template.units[UnitID].psi=-Template.units[UnitID].heading
-self:F({UnitID,Template.units[UnitID],Template.units[UnitID]})
-end
-end
-elseif Reset==false then
-for UnitID,TemplateUnitData in pairs(Template.units)do
-self:F("Reset")
-local GroupUnitVec3={x=TemplateUnitData.x,y=TemplateUnitData.alt,z=TemplateUnitData.y}
-if Zone then
-if self.InitRespawnRandomizePositionZone then
-GroupUnitVec3=Zone:GetRandomVec3()
-else
-if self.InitRespawnRandomizePositionInner and self.InitRespawnRandomizePositionOuter then
-GroupUnitVec3=POINT_VEC3:NewFromVec2(From):GetRandomPointVec3InRadius(self.InitRespawnRandomizePositionsOuter,self.InitRespawnRandomizePositionsInner)
-else
-GroupUnitVec3=Zone:GetVec3()
-end
-end
-end
-if self.InitCoord then
-GroupUnitVec3=self.InitCoord:GetVec3()
-end
-Template.units[UnitID].alt=self.InitRespawnHeight and self.InitRespawnHeight or GroupUnitVec3.y
-Template.units[UnitID].x=(Template.units[UnitID].x-From.x)+GroupUnitVec3.x
-Template.units[UnitID].y=(Template.units[UnitID].y-From.y)+GroupUnitVec3.z
-Template.units[UnitID].heading=self.InitRespawnHeading and self.InitRespawnHeading or TemplateUnitData.heading
-self:F({UnitID,Template.units[UnitID],Template.units[UnitID]})
-end
-else
-local units=self:GetUnits()
-for UnitID,Unit in pairs(Template.units)do
-for _,_unit in pairs(units)do
-local unit=_unit
-if unit:GetName()==Unit.name then
-local coord=unit:GetCoordinate()
-local heading=unit:GetHeading()
-Unit.x=coord.x
-Unit.y=coord.z
-Unit.alt=coord.y
-Unit.heading=math.rad(heading)
-Unit.psi=-Unit.heading
-end
-end
-end
-end
-end
-if self.InitRespawnModex then
-for UnitID=1,#Template.units do
-Template.units[UnitID].onboard_num=string.format("%03d",self.InitRespawnModex+(UnitID-1))
-end
-end
-if self.InitRespawnRadio then
-Template.communication=self.InitRespawnRadio
-end
-if self.InitRespawnFreq then
-Template.frequency=self.InitRespawnFreq
-end
-if self.InitRespawnModu then
-Template.modulation=self.InitRespawnModu
-end
-self:Destroy(false)
-self:T({Template=Template})
-_DATABASE:Spawn(Template)
-self:ResetEvents()
-return self
-end
-function GROUP:RespawnAtCurrentAirbase(SpawnTemplate,Takeoff,Uncontrolled)
-self:F2({SpawnTemplate,Takeoff,Uncontrolled})
-if self and self:IsAlive()then
-local airbase=self:GetCoordinate():GetClosestAirbase()
-if airbase then
-self:F2("Closest airbase = "..airbase:GetName())
-else
-self:E("ERROR: could not find closest airbase!")
-return nil
-end
-Takeoff=Takeoff or SPAWN.Takeoff.Hot
-local AirbaseCoord=airbase:GetCoordinate()
-SpawnTemplate=SpawnTemplate or self:GetTemplate()
-if SpawnTemplate then
-local SpawnPoint=SpawnTemplate.route.points[1]
-SpawnPoint.linkUnit=nil
-SpawnPoint.helipadId=nil
-SpawnPoint.airdromeId=nil
-local AirbaseID=airbase:GetID()
-local AirbaseCategory=airbase:GetAirbaseCategory()
-if AirbaseCategory==Airbase.Category.SHIP or AirbaseCategory==Airbase.Category.HELIPAD then
-SpawnPoint.linkUnit=AirbaseID
-SpawnPoint.helipadId=AirbaseID
-elseif AirbaseCategory==Airbase.Category.AIRDROME then
-SpawnPoint.airdromeId=AirbaseID
-end
-SpawnPoint.type=GROUPTEMPLATE.Takeoff[Takeoff][1]
-SpawnPoint.action=GROUPTEMPLATE.Takeoff[Takeoff][2]
-local units=self:GetUnits()
-local x
-local y
-for UnitID=1,#units do
-local unit=units[UnitID]
-local Parkingspot,TermialID,Distance=unit:GetCoordinate():GetClosestParkingSpot(airbase)
-self:T2(string.format("Closest parking spot distance = %s, terminal ID=%s",tostring(Distance),tostring(TermialID)))
-local uc=unit:GetCoordinate()
-SpawnTemplate.units[UnitID].x=uc.x
-SpawnTemplate.units[UnitID].y=uc.z
-SpawnTemplate.units[UnitID].alt=uc.y
-SpawnTemplate.units[UnitID].parking=TermialID
-SpawnTemplate.units[UnitID].parking_id=nil
-end
-SpawnPoint.x=SpawnTemplate.units[1].x
-SpawnPoint.y=SpawnTemplate.units[1].y
-SpawnPoint.alt=SpawnTemplate.units[1].alt
-SpawnTemplate.x=SpawnTemplate.units[1].x
-SpawnTemplate.y=SpawnTemplate.units[1].y
-SpawnTemplate.uncontrolled=Uncontrolled
-if self.InitRespawnRadio then
-SpawnTemplate.communication=self.InitRespawnRadio
-end
-if self.InitRespawnFreq then
-SpawnTemplate.frequency=self.InitRespawnFreq
-end
-if self.InitRespawnModu then
-SpawnTemplate.modulation=self.InitRespawnModu
-end
-self:Destroy(false)
-_DATABASE:Spawn(SpawnTemplate)
-self:ResetEvents()
-return self
-end
-else
-self:E("WARNING: GROUP is not alive!")
-end
-return nil
-end
-function GROUP:GetTaskMission()
-self:F2(self.GroupName)
-return UTILS.DeepCopy(_DATABASE.Templates.Groups[self.GroupName].Template)
-end
-function GROUP:GetTaskRoute()
-self:F2(self.GroupName)
-return UTILS.DeepCopy(_DATABASE.Templates.Groups[self.GroupName].Template.route.points)
-end
-function GROUP:CopyRoute(Begin,End,Randomize,Radius)
-self:F2({Begin,End})
-local Points={}
-local GroupName=string.match(self:GetName(),".*#")
-if GroupName then
-GroupName=GroupName:sub(1,-2)
-else
-GroupName=self:GetName()
-end
-self:T3({GroupName})
-local Template=_DATABASE.Templates.Groups[GroupName].Template
-if Template then
-if not Begin then
-Begin=0
-end
-if not End then
-End=0
-end
-for TPointID=Begin+1,#Template.route.points-End do
-if Template.route.points[TPointID]then
-Points[#Points+1]=UTILS.DeepCopy(Template.route.points[TPointID])
-if Randomize then
-if not Radius then
-Radius=500
-end
-Points[#Points].x=Points[#Points].x+math.random(Radius*-1,Radius)
-Points[#Points].y=Points[#Points].y+math.random(Radius*-1,Radius)
-end
-end
-end
-return Points
-else
-error("Template not found for Group : "..GroupName)
-end
-return nil
-end
-function GROUP:CalculateThreatLevelA2G()
-local MaxThreatLevelA2G=0
-for UnitName,UnitData in pairs(self:GetUnits())do
-local ThreatUnit=UnitData
-local ThreatLevelA2G=ThreatUnit:GetThreatLevel()
-if ThreatLevelA2G>MaxThreatLevelA2G then
-MaxThreatLevelA2G=ThreatLevelA2G
-end
-end
-self:T3(MaxThreatLevelA2G)
-return MaxThreatLevelA2G
-end
-function GROUP:GetThreatLevel()
-local threatlevelMax=0
-for UnitName,UnitData in pairs(self:GetUnits())do
-local ThreatUnit=UnitData
-local threatlevel=ThreatUnit:GetThreatLevel()
-if threatlevel>threatlevelMax then
-threatlevelMax=threatlevel
-end
-end
-return threatlevelMax
-end
-function GROUP:InAir()
-self:F2(self.GroupName)
-local DCSGroup=self:GetDCSObject()
-if DCSGroup then
-local DCSUnit=DCSGroup:getUnit(1)
-if DCSUnit then
-local GroupInAir=DCSGroup:getUnit(1):inAir()
-self:T3(GroupInAir)
-return GroupInAir
-end
-end
-return nil
-end
-function GROUP:IsAirborne(AllUnits)
-self:F2(self.GroupName)
-local units=self:GetUnits()
-if units then
-if AllUnits then
-for _,_unit in pairs(units)do
-local unit=_unit
-if unit then
-local inair=unit:InAir()
-if not inair then
-return false
-end
-end
-end
-return true
-else
-for _,_unit in pairs(units)do
-local unit=_unit
-if unit then
-local inair=unit:InAir()
-if inair then
-return true
-end
-end
-return false
-end
-end
-end
-return nil
-end
-function GROUP:GetDCSDesc(n)
-n=n or 1
-local unit=self:GetUnit(n)
-if unit and unit:IsAlive()~=nil then
-local desc=unit:GetDesc()
-return desc
-end
-return nil
-end
-function GROUP:GetAttribute()
-local attribute=GROUP.Attribute.OTHER_UNKNOWN
-if self then
-local transportplane=self:HasAttribute("Transports")and self:HasAttribute("Planes")
-local awacs=self:HasAttribute("AWACS")
-local fighter=self:HasAttribute("Fighters")or self:HasAttribute("Interceptors")or self:HasAttribute("Multirole fighters")or(self:HasAttribute("Bombers")and not self:HasAttribute("Strategic bombers"))
-local bomber=self:HasAttribute("Strategic bombers")
-local tanker=self:HasAttribute("Tankers")
-local uav=self:HasAttribute("UAVs")
-local transporthelo=self:HasAttribute("Transport helicopters")
-local attackhelicopter=self:HasAttribute("Attack helicopters")
-local apc=self:HasAttribute("APC")
-local truck=self:HasAttribute("Trucks")and self:GetCategory()==Group.Category.GROUND
-local infantry=self:HasAttribute("Infantry")
-local artillery=self:HasAttribute("Artillery")
-local tank=self:HasAttribute("Old Tanks")or self:HasAttribute("Modern Tanks")or self:HasAttribute("Tanks")
-local aaa=self:HasAttribute("AAA")and(not self:HasAttribute("SAM elements"))
-local ewr=self:HasAttribute("EWR")
-local ifv=self:HasAttribute("IFV")
-local sam=self:HasAttribute("SAM elements")or self:HasAttribute("Optical Tracker")
-local train=self:GetCategory()==Group.Category.TRAIN
-local aircraftcarrier=self:HasAttribute("Aircraft Carriers")
-local warship=self:HasAttribute("Heavy armed ships")
-local armedship=self:HasAttribute("Armed ships")
-local unarmedship=self:HasAttribute("Unarmed ships")
-if fighter then
-attribute=GROUP.Attribute.AIR_FIGHTER
-elseif bomber then
-attribute=GROUP.Attribute.AIR_BOMBER
-elseif awacs then
-attribute=GROUP.Attribute.AIR_AWACS
-elseif transportplane then
-attribute=GROUP.Attribute.AIR_TRANSPORTPLANE
-elseif tanker then
-attribute=GROUP.Attribute.AIR_TANKER
-elseif attackhelicopter then
-attribute=GROUP.Attribute.AIR_ATTACKHELO
-elseif transporthelo then
-attribute=GROUP.Attribute.AIR_TRANSPORTHELO
-elseif uav then
-attribute=GROUP.Attribute.AIR_UAV
-elseif ewr then
-attribute=GROUP.Attribute.GROUND_EWR
-elseif sam then
-attribute=GROUP.Attribute.GROUND_SAM
-elseif aaa then
-attribute=GROUP.Attribute.GROUND_AAA
-elseif artillery then
-attribute=GROUP.Attribute.GROUND_ARTILLERY
-elseif tank then
-attribute=GROUP.Attribute.GROUND_TANK
-elseif ifv then
-attribute=GROUP.Attribute.GROUND_IFV
-elseif apc then
-attribute=GROUP.Attribute.GROUND_APC
-elseif infantry then
-attribute=GROUP.Attribute.GROUND_INFANTRY
-elseif truck then
-attribute=GROUP.Attribute.GROUND_TRUCK
-elseif train then
-attribute=GROUP.Attribute.GROUND_TRAIN
-elseif aircraftcarrier then
-attribute=GROUP.Attribute.NAVAL_AIRCRAFTCARRIER
-elseif warship then
-attribute=GROUP.Attribute.NAVAL_WARSHIP
-elseif armedship then
-attribute=GROUP.Attribute.NAVAL_ARMEDSHIP
-elseif unarmedship then
-attribute=GROUP.Attribute.NAVAL_UNARMEDSHIP
-else
-if self:IsGround()then
-attribute=GROUP.Attribute.GROUND_OTHER
-elseif self:IsShip()then
-attribute=GROUP.Attribute.NAVAL_OTHER
-elseif self:IsAir()then
-attribute=GROUP.Attribute.AIR_OTHER
-else
-attribute=GROUP.Attribute.OTHER_UNKNOWN
-end
-end
-end
-return attribute
-end
-do
-function GROUP:RouteRTB(RTBAirbase,Speed)
-self:F({RTBAirbase:GetName(),Speed})
-local DCSGroup=self:GetDCSObject()
-if DCSGroup then
-if RTBAirbase then
-local Speed=Speed or self:GetSpeedMax()*0.8
-local coord=self:GetCoordinate()
-local PointFrom=coord:WaypointAirTurningPoint(nil,Speed)
-local PointLanding=RTBAirbase:GetCoordinate():WaypointAirLanding(Speed,RTBAirbase)
-local Points={PointFrom,PointLanding}
-self:T3(Points)
-local Template=self:GetTemplate()
-Template.route.points=Points
-self:Respawn(Template,true)
-self:Route(Points)
-else
-self:ClearTasks()
-end
-end
-return self
-end
-end
-function GROUP:OnReSpawn(ReSpawnFunction)
-self.ReSpawnFunction=ReSpawnFunction
-end
-do
-function GROUP:HandleEvent(Event,EventFunction,...)
-self:EventDispatcher():OnEventForGroup(self:GetName(),EventFunction,self,Event,...)
-return self
-end
-function GROUP:UnHandleEvent(Event)
-self:EventDispatcher():RemoveEvent(self,Event)
-return self
-end
-function GROUP:ResetEvents()
-self:EventDispatcher():Reset(self)
-for UnitID,UnitData in pairs(self:GetUnits())do
-UnitData:ResetEvents()
-end
-return self
-end
-end
-do
-function GROUP:GetPlayerNames()
-local HasPlayers=false
-local PlayerNames={}
-local Units=self:GetUnits()
-for UnitID,UnitData in pairs(Units)do
-local Unit=UnitData
-local PlayerName=Unit:GetPlayerName()
-if PlayerName and PlayerName~=""then
-PlayerNames=PlayerNames or{}
-table.insert(PlayerNames,PlayerName)
-HasPlayers=true
-end
-end
-if HasPlayers==true then
-self:F2(PlayerNames)
-return PlayerNames
-end
-return nil
-end
-function GROUP:GetPlayerCount()
-local PlayerCount=0
-local Units=self:GetUnits()
-for UnitID,UnitData in pairs(Units or{})do
-local Unit=UnitData
-local PlayerName=Unit:GetPlayerName()
-if PlayerName and PlayerName~=""then
-PlayerCount=PlayerCount+1
-end
-end
-return PlayerCount
-end
-end
-function GROUP:EnableEmission(switch)
-self:F2(self.GroupName)
-local switch=switch or false
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-DCSUnit:enableEmission(switch)
-end
-return self
-end
-function GROUP:SetCommandInvisible(switch)
-self:F2(self.GroupName)
-if switch==nil then
-switch=false
-end
-local SetInvisible={id='SetInvisible',params={value=switch}}
-self:SetCommand(SetInvisible)
-return self
-end
-function GROUP:SetCommandImmortal(switch)
-self:F2(self.GroupName)
-if switch==nil then
-switch=false
-end
-local SetImmortal={id='SetImmortal',params={value=switch}}
-self:SetCommand(SetImmortal)
-return self
-end
-function GROUP:GetSkill()
-self:F2(self.GroupName)
-local unit=self:GetUnit(1)
-local name=unit:GetName()
-local skill=_DATABASE.Templates.Units[name].Template.skill or"Random"
-return skill
-end
-function GROUP:GetHighestThreat()
-local units=self:GetUnits()
-if units then
-local threat=nil;local maxtl=0
-for _,_unit in pairs(units or{})do
-local unit=_unit
-if unit and unit:IsAlive()then
-local tl=unit:GetThreatLevel()
-if tl>maxtl then
-maxtl=tl
-threat=unit
-end
-end
-end
-return threat,maxtl
-end
-return nil,nil
-end
-function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations)
-local callsign="Ghost 1"
-if self:IsAlive()then
-local IsPlayer=self:IsPlayer()
-local shortcallsign=self:GetCallsign()or"unknown91"
-local callsignroot=string.match(shortcallsign,'(%a+)')or"Ghost"
-local groupname=self:GetName()
-local callnumber=string.match(shortcallsign,"(%d+)$")or"91"
-local callnumbermajor=string.char(string.byte(callnumber,1))
-local callnumberminor=string.char(string.byte(callnumber,2))
-local personalized=false
-if CallsignTranslations and CallsignTranslations[callsignroot]then
-callsignroot=CallsignTranslations[callsignroot]
-elseif IsPlayer and string.find(groupname,"#")then
-if Keepnumber then
-shortcallsign=string.match(groupname,"#(.+)")or"Ghost 111"
-else
-shortcallsign=string.match(groupname,"#%s*([%a]+)")or"Ghost"
-end
-personalized=true
-elseif IsPlayer and string.find(self:GetPlayerName(),"|")then
-shortcallsign=string.match(self:GetPlayerName(),"|%s*([%a]+)")or string.match(self:GetPlayerName(),"|%s*([%d]+)")or"Ghost"
-personalized=true
-end
-if personalized then
-shortcallsign=string.gsub(shortcallsign,"^%s*","")
-shortcallsign=string.gsub(shortcallsign,"%s*$","")
-if Keepnumber then
-return shortcallsign
-elseif ShortCallsign then
-callsign=shortcallsign.." "..callnumbermajor
-else
-callsign=shortcallsign.." "..callnumbermajor.." "..callnumberminor
-end
-return callsign
-end
-if ShortCallsign then
-callsign=callsignroot.." "..callnumbermajor
-else
-callsign=callsignroot.." "..callnumbermajor.." "..callnumberminor
-end
-end
-return callsign
-end
-function GROUP:SetAsRecoveryTanker(CarrierGroup,Speed,ToKIAS,Altitude,Delay,LastWaypoint)
-local speed=ToKIAS==true and UTILS.KnotsToAltKIAS(Speed,Altitude)or Speed
-speed=UTILS.KnotsToMps(speed)
-local alt=UTILS.FeetToMeters(Altitude)
-local delay=Delay or 1
-local task=self:TaskRecoveryTanker(CarrierGroup,speed,alt,LastWaypoint)
-self:SetTask(task,delay)
-local tankertask=self:EnRouteTaskTanker()
-self:PushTask(tankertask,delay+2)
-return self
-end
-function GROUP:GetGroupSTN()
-local tSTN={}
-local units=self:GetUnits()
-local gname=self:GetName()
-gname=string.gsub(gname,"(#%d+)$","")
-local report=REPORT:New()
-report:Add("Link16 S/TN Report")
-report:Add("Group: "..gname)
-report:Add("==================")
-for _,_unit in pairs(units)do
-local unit=_unit
-if unit and unit:IsAlive()then
-local STN,VCL,VCN,Lead=unit:GetSTN()
-local name=unit:GetName()
-tSTN[name]={
-STN=STN,
-VCL=VCL,
-VCN=VCN,
-Lead=Lead,
-}
-local lead=Lead==true and"(*)"or""
-report:Add(string.format("| %s%s %s %s",tostring(VCL),tostring(VCN),tostring(STN),lead))
-end
-end
-report:Add("==================")
-local text=report:Text()
-return tSTN,text
-end
-IDENTIFIABLE={
-ClassName="IDENTIFIABLE",
-IdentifiableName="",
-}
-local _CategoryName={
-[Unit.Category.AIRPLANE]="Airplane",
-[Unit.Category.HELICOPTER]="Helicopter",
-[Unit.Category.GROUND_UNIT]="Ground Identifiable",
-[Unit.Category.SHIP]="Ship",
-[Unit.Category.STRUCTURE]="Structure",
-}
-function IDENTIFIABLE:New(IdentifiableName)
-local self=BASE:Inherit(self,OBJECT:New(IdentifiableName))
-self:F2(IdentifiableName)
-self.IdentifiableName=IdentifiableName
-return self
-end
-function IDENTIFIABLE:IsAlive()
-self:F3(self.IdentifiableName)
-local DCSIdentifiable=self:GetDCSObject()
-if DCSIdentifiable then
-local IdentifiableIsAlive=DCSIdentifiable:isExist()
-return IdentifiableIsAlive
-end
-return false
-end
-function IDENTIFIABLE:GetName()
-local IdentifiableName=self.IdentifiableName
-return IdentifiableName
-end
-function IDENTIFIABLE:GetTypeName()
-self:F2(self.IdentifiableName)
-local DCSIdentifiable=self:GetDCSObject()
-if DCSIdentifiable then
-local IdentifiableTypeName=DCSIdentifiable:getTypeName()
-self:T3(IdentifiableTypeName)
-return IdentifiableTypeName
-end
-self:F(self.ClassName.." "..self.IdentifiableName.." not found!")
-return nil
-end
-function IDENTIFIABLE:GetCategory()
-self:F2(self.ObjectName)
-local DCSObject=self:GetDCSObject()
-if DCSObject then
-local ObjectCategory=DCSObject:getCategory()
-self:T3(ObjectCategory)
-return ObjectCategory
-end
-return nil
-end
-function IDENTIFIABLE:GetCategoryName()
-local DCSIdentifiable=self:GetDCSObject()
-if DCSIdentifiable then
-local IdentifiableCategoryName=_CategoryName[self:GetDesc().category]
-return IdentifiableCategoryName
-end
-self:F(self.ClassName.." "..self.IdentifiableName.." not found!")
-return nil
-end
-function IDENTIFIABLE:GetCoalition()
-self:F2(self.IdentifiableName)
-local DCSIdentifiable=self:GetDCSObject()
-if DCSIdentifiable then
-local IdentifiableCoalition=DCSIdentifiable:getCoalition()
-self:T3(IdentifiableCoalition)
-return IdentifiableCoalition
-end
-self:F(self.ClassName.." "..self.IdentifiableName.." not found!")
-return nil
-end
-function IDENTIFIABLE:GetCoalitionName()
-self:F2(self.IdentifiableName)
-local DCSIdentifiable=self:GetDCSObject()
-if DCSIdentifiable then
-local IdentifiableCoalition=DCSIdentifiable:getCoalition()
-self:T3(IdentifiableCoalition)
-return UTILS.GetCoalitionName(IdentifiableCoalition)
-end
-self:F(self.ClassName.." "..self.IdentifiableName.." not found!")
-return nil
-end
-function IDENTIFIABLE:GetCountry()
-self:F2(self.IdentifiableName)
-local DCSIdentifiable=self:GetDCSObject()
-if DCSIdentifiable then
-local IdentifiableCountry=DCSIdentifiable:getCountry()
-self:T3(IdentifiableCountry)
-return IdentifiableCountry
-end
-self:F(self.ClassName.." "..self.IdentifiableName.." not found!")
-return nil
-end
-function IDENTIFIABLE:GetCountryName()
-self:F2(self.IdentifiableName)
-local countryid=self:GetCountry()
-for name,id in pairs(country.id)do
-if countryid==id then
-return name
-end
-end
-end
-function IDENTIFIABLE:GetDesc()
-self:F2(self.IdentifiableName)
-local DCSIdentifiable=self:GetDCSObject()
-if DCSIdentifiable then
-local IdentifiableDesc=DCSIdentifiable:getDesc()
-self:T2(IdentifiableDesc)
-return IdentifiableDesc
-end
-self:F(self.ClassName.." "..self.IdentifiableName.." not found!")
-return nil
-end
-function IDENTIFIABLE:HasAttribute(AttributeName)
-self:F2(self.IdentifiableName)
-local DCSIdentifiable=self:GetDCSObject()
-if DCSIdentifiable then
-local IdentifiableHasAttribute=DCSIdentifiable:hasAttribute(AttributeName)
-self:T2(IdentifiableHasAttribute)
-return IdentifiableHasAttribute
-end
-self:F(self.ClassName.." "..self.IdentifiableName.." not found!")
-return nil
-end
-function IDENTIFIABLE:GetCallsign()
-return''
-end
-function IDENTIFIABLE:GetThreatLevel()
-return 0,"Scenery"
-end
-MARKER={
-ClassName="MARKER",
-Debug=false,
-lid=nil,
-mid=nil,
-coordinate=nil,
-text=nil,
-message=nil,
-readonly=nil,
-coalition=nil,
-}
-_MARKERID=0
-MARKER.version="0.1.1"
-function MARKER:New(Coordinate,Text)
-local self=BASE:Inherit(self,FSM:New())
-self.coordinate=UTILS.DeepCopy(Coordinate)
-self.text=Text
-self.readonly=false
-self.message=""
-_MARKERID=_MARKERID+1
-self.myid=_MARKERID
-self.lid=string.format("Marker #%d | ",self.myid)
-self:SetStartState("Invisible")
-self:AddTransition("Invisible","Added","Visible")
-self:AddTransition("Visible","Removed","Invisible")
-self:AddTransition("*","Changed","*")
-self:AddTransition("*","TextUpdate","*")
-self:AddTransition("*","CoordUpdate","*")
-self:HandleEvent(EVENTS.MarkAdded)
-self:HandleEvent(EVENTS.MarkRemoved)
-self:HandleEvent(EVENTS.MarkChange)
-return self
-end
-function MARKER:ReadOnly()
-self.readonly=true
-return self
-end
-function MARKER:ReadWrite()
-self.readonly=false
-return self
-end
-function MARKER:Message(Text)
-self.message=Text or""
-return self
-end
-function MARKER:ToAll(Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,MARKER.ToAll,self)
-else
-self.toall=true
-self.tocoalition=nil
-self.coalition=nil
-self.togroup=nil
-self.groupname=nil
-self.groupid=nil
-if self.shown then
-self:Remove()
-end
-self.mid=UTILS.GetMarkID()
-trigger.action.markToAll(self.mid,self.text,self.coordinate:GetVec3(),self.readonly,self.message)
-end
-return self
-end
-function MARKER:ToCoalition(Coalition,Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,MARKER.ToCoalition,self,Coalition)
-else
-self.coalition=Coalition
-self.tocoalition=true
-self.toall=false
-self.togroup=false
-self.groupname=nil
-self.groupid=nil
-if self.shown then
-self:Remove()
-end
-self.mid=UTILS.GetMarkID()
-trigger.action.markToCoalition(self.mid,self.text,self.coordinate:GetVec3(),self.coalition,self.readonly,self.message)
-end
-return self
-end
-function MARKER:ToBlue(Delay)
-self:ToCoalition(coalition.side.BLUE,Delay)
-return self
-end
-function MARKER:ToRed(Delay)
-self:ToCoalition(coalition.side.RED,Delay)
-return self
-end
-function MARKER:ToNeutral(Delay)
-self:ToCoalition(coalition.side.NEUTRAL,Delay)
-return self
-end
-function MARKER:ToGroup(Group,Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,MARKER.ToGroup,self,Group)
-else
-if Group and Group:IsAlive()~=nil then
-self.groupid=Group:GetID()
-if self.groupid then
-self.groupname=Group:GetName()
-self.togroup=true
-self.tocoalition=nil
-self.coalition=nil
-self.toall=nil
-if self.shown then
-self:Remove()
-end
-self.mid=UTILS.GetMarkID()
-trigger.action.markToGroup(self.mid,self.text,self.coordinate:GetVec3(),self.groupid,self.readonly,self.message)
-end
-else
-end
-end
-return self
-end
-function MARKER:UpdateText(Text,Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,MARKER.UpdateText,self,Text)
-else
-self.text=tostring(Text)
-self:Refresh()
-self:TextUpdate(tostring(Text))
-end
-return self
-end
-function MARKER:UpdateCoordinate(Coordinate,Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,MARKER.UpdateCoordinate,self,Coordinate)
-else
-self.coordinate=Coordinate
-self:Refresh()
-self:CoordUpdate(Coordinate)
-end
-return self
-end
-function MARKER:Refresh(Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,MARKER.Refresh,self)
-else
-if self.toall then
-self:ToAll()
-elseif self.tocoalition then
-self:ToCoalition(self.coalition)
-elseif self.togroup then
-local group=GROUP:FindByName(self.groupname)
-self:ToGroup(group)
-else
-self:E(self.lid.."ERROR: unknown To in :Refresh()!")
-end
-end
-return self
-end
-function MARKER:Remove(Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,MARKER.Remove,self)
-else
-if self.shown then
-trigger.action.removeMark(self.mid)
-end
-end
-return self
-end
-function MARKER:GetCoordinate()
-return self.coordinate
-end
-function MARKER:GetText()
-return self.text
-end
-function MARKER:SetText(Text)
-self.text=Text and tostring(Text)or""
-return self
-end
-function MARKER:IsVisible()
-return self:Is("Visible")
-end
-function MARKER:IsInvisible()
-return self:Is("Invisible")
-end
-function MARKER:OnEventMarkAdded(EventData)
-if EventData and EventData.MarkID then
-local MarkID=EventData.MarkID
-self:T3(self.lid..string.format("Captured event MarkAdded for Mark ID=%s",tostring(MarkID)))
-if MarkID==self.mid then
-self.shown=true
-self:Added(EventData)
-end
-end
-end
-function MARKER:OnEventMarkRemoved(EventData)
-if EventData and EventData.MarkID then
-local MarkID=EventData.MarkID
-local MarkID=EventData.MarkID
-self:T3(self.lid..string.format("Captured event MarkRemoved for Mark ID=%s",tostring(MarkID)))
-if MarkID==self.mid then
-self.shown=false
-self:Removed(EventData)
-end
-end
-end
-function MARKER:OnEventMarkChange(EventData)
-if EventData and EventData.MarkID then
-local MarkID=EventData.MarkID
-self:T3(self.lid..string.format("Captured event MarkChange for Mark ID=%s",tostring(MarkID)))
-if MarkID==self.mid then
-local MarkID=EventData.MarkID
-self:T3(self.lid..string.format("Captured event MarkChange for Mark ID=%s",tostring(MarkID)))
-if MarkID==self.mid then
-self.text=tostring(EventData.MarkText)
-self:Changed(EventData)
-end
-end
-end
-end
-function MARKER:onafterAdded(From,Event,To,EventData)
-local text=string.format("Captured event MarkAdded for myself:\n")
-text=text..string.format("Marker ID = %s\n",tostring(EventData.MarkID))
-text=text..string.format("Coalition = %s\n",tostring(EventData.MarkCoalition))
-text=text..string.format("Group ID = %s\n",tostring(EventData.MarkGroupID))
-text=text..string.format("Initiator = %s\n",EventData.IniUnit and EventData.IniUnit:GetName()or"Nobody")
-text=text..string.format("Coordinate = %s\n",EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS()or"Nowhere")
-text=text..string.format("Text: \n%s",tostring(EventData.MarkText))
-self:T2(self.lid..text)
-end
-function MARKER:onafterRemoved(From,Event,To,EventData)
-local text=string.format("Captured event MarkRemoved for myself:\n")
-text=text..string.format("Marker ID = %s\n",tostring(EventData.MarkID))
-text=text..string.format("Coalition = %s\n",tostring(EventData.MarkCoalition))
-text=text..string.format("Group ID = %s\n",tostring(EventData.MarkGroupID))
-text=text..string.format("Initiator = %s\n",EventData.IniUnit and EventData.IniUnit:GetName()or"Nobody")
-text=text..string.format("Coordinate = %s\n",EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS()or"Nowhere")
-text=text..string.format("Text: \n%s",tostring(EventData.MarkText))
-self:T2(self.lid..text)
-end
-function MARKER:onafterChanged(From,Event,To,EventData)
-local text=string.format("Captured event MarkChange for myself:\n")
-text=text..string.format("Marker ID = %s\n",tostring(EventData.MarkID))
-text=text..string.format("Coalition = %s\n",tostring(EventData.MarkCoalition))
-text=text..string.format("Group ID = %s\n",tostring(EventData.MarkGroupID))
-text=text..string.format("Initiator = %s\n",EventData.IniUnit and EventData.IniUnit:GetName()or"Nobody")
-text=text..string.format("Coordinate = %s\n",EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS()or"Nowhere")
-text=text..string.format("Text: \n%s",tostring(EventData.MarkText))
-self:T2(self.lid..text)
-end
-function MARKER:onafterTextUpdate(From,Event,To,Text)
-self:T(self.lid..string.format("New Marker Text:\n%s",Text))
-end
-function MARKER:onafterCoordUpdate(From,Event,To,Coordinate)
-self:T(self.lid..string.format("New Marker Coordinate in LL DMS: %s",Coordinate:ToStringLLDMS()))
-end
-OBJECT={
-ClassName="OBJECT",
-ObjectName="",
-}
-function OBJECT:New(ObjectName)
-local self=BASE:Inherit(self,BASE:New())
-self:F2(ObjectName)
-self.ObjectName=ObjectName
-return self
-end
-function OBJECT:GetID()
-local DCSObject=self:GetDCSObject()
-if DCSObject then
-local ObjectID=DCSObject:getID()
-return ObjectID
-end
-self:E({"Cannot GetID",Name=self.ObjectName,Class=self:GetClassName()})
-return nil
-end
-function OBJECT:Destroy()
-local DCSObject=self:GetDCSObject()
-if DCSObject then
-DCSObject:destroy(false)
-return true
-end
-self:E({"Cannot Destroy",Name=self.ObjectName,Class=self:GetClassName()})
-return nil
-end
-POSITIONABLE={
-ClassName="POSITIONABLE",
-PositionableName="",
-coordinate=nil,
-pointvec3=nil,
-}
-POSITIONABLE.__={}
-POSITIONABLE.__.Cargo={}
-function POSITIONABLE:New(PositionableName)
-local self=BASE:Inherit(self,IDENTIFIABLE:New(PositionableName))
-self.PositionableName=PositionableName
-return self
-end
-function POSITIONABLE:Destroy(GenerateEvent)
-self:F2(self.ObjectName)
-local DCSObject=self:GetDCSObject()
-if DCSObject then
-local UnitGroup=self:GetGroup()
-local UnitGroupName=UnitGroup:GetName()
-self:F({UnitGroupName=UnitGroupName})
-if GenerateEvent and GenerateEvent==true then
-if self:IsAir()then
-self:CreateEventCrash(timer.getTime(),DCSObject)
-else
-self:CreateEventDead(timer.getTime(),DCSObject)
-end
-elseif GenerateEvent==false then
-else
-self:CreateEventRemoveUnit(timer.getTime(),DCSObject)
-end
-USERFLAG:New(UnitGroupName):Set(100)
-DCSObject:destroy()
-end
-return nil
-end
-function POSITIONABLE:GetDCSObject()
-return nil
-end
-function POSITIONABLE:GetPosition()
-self:F2(self.PositionableName)
-local DCSPositionable=self:GetDCSObject()
-if DCSPositionable then
-local PositionablePosition=DCSPositionable:getPosition()
-self:T3(PositionablePosition)
-return PositionablePosition
-end
-BASE:E({"Cannot GetPosition",Positionable=self,Alive=self:IsAlive()})
-return nil
-end
-function POSITIONABLE:GetOrientation()
-local position=self:GetPosition()
-if position then
-return position.x,position.y,position.z
-else
-BASE:E({"Cannot GetOrientation",Positionable=self,Alive=self:IsAlive()})
-return nil,nil,nil
-end
-end
-function POSITIONABLE:GetOrientationX()
-local position=self:GetPosition()
-if position then
-return position.x
-else
-BASE:E({"Cannot GetOrientationX",Positionable=self,Alive=self:IsAlive()})
-return nil
-end
-end
-function POSITIONABLE:GetOrientationY()
-local position=self:GetPosition()
-if position then
-return position.y
-else
-BASE:E({"Cannot GetOrientationY",Positionable=self,Alive=self:IsAlive()})
-return nil
-end
-end
-function POSITIONABLE:GetOrientationZ()
-local position=self:GetPosition()
-if position then
-return position.z
-else
-BASE:E({"Cannot GetOrientationZ",Positionable=self,Alive=self:IsAlive()})
-return nil
-end
-end
-function POSITIONABLE:GetPositionVec3()
-self:F2(self.PositionableName)
-local DCSPositionable=self:GetDCSObject()
-if DCSPositionable then
-local PositionablePosition=DCSPositionable:getPosition().p
-self:T3(PositionablePosition)
-return PositionablePosition
-end
-BASE:E({"Cannot GetPositionVec3",Positionable=self,Alive=self:IsAlive()})
-return nil
-end
-function POSITIONABLE:GetVec3()
-local DCSPositionable=self:GetDCSObject()
-if DCSPositionable then
-local vec3=DCSPositionable:getPoint()
-return vec3
-end
-self:E({"Cannot get the Positionable DCS Object for GetVec3",Positionable=self,Alive=self:IsAlive()})
-return nil
-end
-function POSITIONABLE:GetVec2()
-local DCSPositionable=self:GetDCSObject()
-if DCSPositionable then
-local Vec3=DCSPositionable:getPoint()
-return{x=Vec3.x,y=Vec3.z}
-end
-self:E({"Cannot GetVec2",Positionable=self,Alive=self:IsAlive()})
-return nil
-end
-function POSITIONABLE:GetPointVec2()
-self:F2(self.PositionableName)
-local DCSPositionable=self:GetDCSObject()
-if DCSPositionable then
-local PositionableVec3=DCSPositionable:getPosition().p
-local PositionablePointVec2=POINT_VEC2:NewFromVec3(PositionableVec3)
-return PositionablePointVec2
-end
-self:E({"Cannot GetPointVec2",Positionable=self,Alive=self:IsAlive()})
-return nil
-end
-function POSITIONABLE:GetPointVec3()
-local DCSPositionable=self:GetDCSObject()
-if DCSPositionable then
-local PositionableVec3=self:GetPositionVec3()
-if false and self.pointvec3 then
-self.pointvec3.x=PositionableVec3.x
-self.pointvec3.y=PositionableVec3.y
-self.pointvec3.z=PositionableVec3.z
-else
-self.pointvec3=POINT_VEC3:NewFromVec3(PositionableVec3)
-end
-return self.pointvec3
-end
-BASE:E({"Cannot GetPointVec3",Positionable=self,Alive=self:IsAlive()})
-return nil
-end
-function POSITIONABLE:GetCoord()
-local DCSPositionable=self:GetDCSObject()
-if DCSPositionable then
-local PositionableVec3=self:GetVec3()
-if self.coordinate then
-self.coordinate:UpdateFromVec3(PositionableVec3)
-else
-self.coordinate=COORDINATE:NewFromVec3(PositionableVec3)
-end
-return self.coordinate
-end
-BASE:E({"Cannot GetCoordinate",Positionable=self,Alive=self:IsAlive()})
-return nil
-end
-function POSITIONABLE:GetCoordinate()
-local DCSPositionable=self:GetDCSObject()
-if DCSPositionable then
-local PositionableVec3=self:GetVec3()
-local coord=COORDINATE:NewFromVec3(PositionableVec3)
-local heading=self:GetHeading()
-coord.Heading=heading
-return coord
-end
-self:E({"Cannot GetCoordinate",Positionable=self,Alive=self:IsAlive()})
-return nil
-end
-function POSITIONABLE:Explode(power,delay)
-power=power or 100
-if delay and delay>0 then
-self:ScheduleOnce(delay,POSITIONABLE.Explode,self,power,0)
-else
-local coord=self:GetCoord()
-if coord then
-coord:Explosion(power)
-end
-end
-return self
-end
-function POSITIONABLE:GetOffsetCoordinate(x,y,z)
-x=x or 0
-y=y or 0
-z=z or 0
-local X=self:GetOrientationX()
-local Y=self:GetOrientationY()
-local Z=self:GetOrientationZ()
-local A={x=x,y=y,z=z}
-local x={x=X.x*A.x,y=X.y*A.x,z=X.z*A.x}
-local y={x=Y.x*A.y,y=Y.y*A.y,z=Y.z*A.y}
-local z={x=Z.x*A.z,y=Z.y*A.z,z=Z.z*A.z}
-local a={x=x.x+y.x+z.x,y=x.y+y.y+z.y,z=x.z+y.z+z.z}
-local u=self:GetVec3()
-local v={x=a.x+u.x,y=a.y+u.y,z=a.z+u.z}
-local coord=COORDINATE:NewFromVec3(v)
-return coord
-end
-function POSITIONABLE:GetRelativeCoordinate(x,y,z)
-x=x or 0
-y=y or 0
-z=z or 0
-local selfPos=self:GetVec3()
-local X=self:GetOrientationX()
-local Y=self:GetOrientationY()
-local Z=self:GetOrientationZ()
-local off={
-x=x-selfPos.x,
-y=y-selfPos.y,
-z=z-selfPos.z
-}
-local res={x=0,y=0,z=0}
-local mat={
-{X.x,Y.x,Z.x,off.x},
-{X.y,Y.y,Z.y,off.y},
-{X.z,Y.z,Z.z,off.z}
-}
-local m=3
-local n=4
-local h=1
-local k=1
-while h<=m and k<=n do
-local v_max=math.abs(mat[h][k])
-local i_max=h
-for i=h,m,1 do
-local value=math.abs(mat[i][k])
-if value>v_max then
-i_max=i
-v_max=value
-end
-end
-if mat[i_max][k]==0 then
-k=k+1
-else
-local tmp=mat[h]
-mat[h]=mat[i_max]
-mat[i_max]=tmp
-for i=h+1,m,1 do
-local f=mat[i][k]/mat[h][k]
-mat[i][k]=0
-for j=k+1,n,1 do
-mat[i][j]=mat[i][j]-f*mat[h][j]
-end
-end
-h=h+1
-k=k+1
-end
-end
-res.z=mat[3][4]/mat[3][3]
-res.y=(mat[2][4]-res.z*mat[2][3])/mat[2][2]
-res.x=(mat[1][4]-res.y*mat[1][2]-res.z*mat[1][3])/mat[1][1]
-local coord=COORDINATE:NewFromVec3(res)
-return coord
-end
-function POSITIONABLE:GetRandomVec3(Radius)
-self:F2(self.PositionableName)
-local DCSPositionable=self:GetDCSObject()
-if DCSPositionable then
-local PositionablePointVec3=DCSPositionable:getPosition().p
-if Radius then
-local PositionableRandomVec3={}
-local angle=math.random()*math.pi*2
-PositionableRandomVec3.x=PositionablePointVec3.x+math.cos(angle)*math.random()*Radius
-PositionableRandomVec3.y=PositionablePointVec3.y
-PositionableRandomVec3.z=PositionablePointVec3.z+math.sin(angle)*math.random()*Radius
-self:T3(PositionableRandomVec3)
-return PositionableRandomVec3
-else
-self:F("Radius is nil, returning the PointVec3 of the POSITIONABLE",PositionablePointVec3)
-return PositionablePointVec3
-end
-end
-BASE:E({"Cannot GetRandomVec3",Positionable=self,Alive=self:IsAlive()})
-return nil
-end
-function POSITIONABLE:GetBoundingBox()
-self:F2()
-local DCSPositionable=self:GetDCSObject()
-if DCSPositionable then
-local PositionableDesc=DCSPositionable:getDesc()
-if PositionableDesc then
-local PositionableBox=PositionableDesc.box
-return PositionableBox
-end
-end
-BASE:E({"Cannot GetBoundingBox",Positionable=self,Alive=self:IsAlive()})
-return nil
-end
-function POSITIONABLE:GetObjectSize()
-local box=self:GetBoundingBox()
-if box then
-local x=box.max.x+math.abs(box.min.x)
-local y=box.max.y+math.abs(box.min.y)
-local z=box.max.z+math.abs(box.min.z)
-return math.max(x,z),x,y,z
-end
-return 0,0,0,0
-end
-function POSITIONABLE:GetBoundingRadius(MinDist)
-self:F2()
-local Box=self:GetBoundingBox()
-local boxmin=MinDist or 0
-if Box then
-local X=Box.max.x-Box.min.x
-local Z=Box.max.z-Box.min.z
-local CX=X/2
-local CZ=Z/2
-return math.max(math.max(CX,CZ),boxmin)
-end
-BASE:E({"Cannot GetBoundingRadius",Positionable=self,Alive=self:IsAlive()})
-return nil
-end
-function POSITIONABLE:GetAltitude()
-self:F2()
-local DCSPositionable=self:GetDCSObject()
-if DCSPositionable then
-local PositionablePointVec3=DCSPositionable:getPoint()
-return PositionablePointVec3.y
-end
-BASE:E({"Cannot GetAltitude",Positionable=self,Alive=self:IsAlive()})
-return nil
-end
-function POSITIONABLE:IsAboveRunway()
-self:F2(self.PositionableName)
-local DCSPositionable=self:GetDCSObject()
-if DCSPositionable then
-local Vec2=self:GetVec2()
-local SurfaceType=land.getSurfaceType(Vec2)
-local IsAboveRunway=SurfaceType==land.SurfaceType.RUNWAY
-self:T2(IsAboveRunway)
-return IsAboveRunway
-end
-BASE:E({"Cannot IsAboveRunway",Positionable=self,Alive=self:IsAlive()})
-return nil
-end
-function POSITIONABLE:GetSize()
-local DCSObject=self:GetDCSObject()
-if DCSObject then
-return 1
-else
-return 0
-end
-end
-function POSITIONABLE:GetHeading()
-local DCSPositionable=self:GetDCSObject()
-if DCSPositionable then
-local PositionablePosition=DCSPositionable:getPosition()
-if PositionablePosition then
-local PositionableHeading=math.atan2(PositionablePosition.x.z,PositionablePosition.x.x)
-if PositionableHeading<0 then
-PositionableHeading=PositionableHeading+2*math.pi
-end
-PositionableHeading=PositionableHeading*180/math.pi
-return PositionableHeading
-end
-end
-self:E({"Cannot GetHeading",Positionable=self,Alive=self:IsAlive()})
-return nil
-end
-function POSITIONABLE:IsAir()
-self:F2()
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-local UnitDescriptor=DCSUnit:getDesc()
-self:T3({UnitDescriptor.category,Unit.Category.AIRPLANE,Unit.Category.HELICOPTER})
-local IsAirResult=(UnitDescriptor.category==Unit.Category.AIRPLANE)or(UnitDescriptor.category==Unit.Category.HELICOPTER)
-self:T3(IsAirResult)
-return IsAirResult
-end
-self:E({"Cannot check IsAir",Positionable=self,Alive=self:IsAlive()})
-return nil
-end
-function POSITIONABLE:IsGround()
-self:F2()
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-local UnitDescriptor=DCSUnit:getDesc()
-self:T3({UnitDescriptor.category,Unit.Category.GROUND_UNIT})
-local IsGroundResult=(UnitDescriptor.category==Unit.Category.GROUND_UNIT)
-self:T3(IsGroundResult)
-return IsGroundResult
-end
-self:E({"Cannot check IsGround",Positionable=self,Alive=self:IsAlive()})
-return nil
-end
-function POSITIONABLE:IsShip()
-self:F2()
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-local UnitDescriptor=DCSUnit:getDesc()
-self:T3({UnitDescriptor.category,Unit.Category.SHIP})
-local IsShipResult=(UnitDescriptor.category==Unit.Category.SHIP)
-self:T3(IsShipResult)
-return IsShipResult
-end
-self:E({"Cannot check IsShip",Positionable=self,Alive=self:IsAlive()})
-return nil
-end
-function POSITIONABLE:IsSubmarine()
-self:F2()
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-local UnitDescriptor=DCSUnit:getDesc()
-if UnitDescriptor.attributes["Submarines"]==true then
-return true
-else
-return false
-end
-end
-self:E({"Cannot check IsSubmarine",Positionable=self,Alive=self:IsAlive()})
-return nil
-end
-function POSITIONABLE:InAir()
-self:F2(self.PositionableName)
-return nil
-end
-function POSITIONABLE:GetVelocity()
-self:F2(self.PositionableName)
-local DCSPositionable=self:GetDCSObject()
-if DCSPositionable then
-local Velocity=VELOCITY:New(self)
-return Velocity
-end
-BASE:E({"Cannot GetVelocity",Positionable=self,Alive=self:IsAlive()})
-return nil
-end
-function POSITIONABLE:GetVelocityVec3()
-self:F2(self.PositionableName)
-local DCSPositionable=self:GetDCSObject()
-if DCSPositionable and DCSPositionable:isExist()then
-local PositionableVelocityVec3=DCSPositionable:getVelocity()
-self:T3(PositionableVelocityVec3)
-return PositionableVelocityVec3
-end
-BASE:E({"Cannot GetVelocityVec3",Positionable=self,Alive=self:IsAlive()})
-return nil
-end
-function POSITIONABLE:GetRelativeVelocity(Positionable)
-self:F2(self.PositionableName)
-local v1=self:GetVelocityVec3()
-local v2=Positionable:GetVelocityVec3()
-local vtot=UTILS.VecAdd(v1,v2)
-return UTILS.VecNorm(vtot)
-end
-function POSITIONABLE:GetHeight()
-self:F2(self.PositionableName)
-local DCSPositionable=self:GetDCSObject()
-if DCSPositionable and DCSPositionable:isExist()then
-local PositionablePosition=DCSPositionable:getPosition()
-if PositionablePosition then
-local PositionableHeight=PositionablePosition.p.y
-self:T2(PositionableHeight)
-return PositionableHeight
-end
-end
-return nil
-end
-function POSITIONABLE:GetVelocityKMH()
-self:F2(self.PositionableName)
-local DCSPositionable=self:GetDCSObject()
-if DCSPositionable and DCSPositionable:isExist()then
-local VelocityVec3=self:GetVelocityVec3()
-local Velocity=(VelocityVec3.x^2+VelocityVec3.y^2+VelocityVec3.z^2)^0.5
-local Velocity=Velocity*3.6
-self:T3(Velocity)
-return Velocity
-end
-return 0
-end
-function POSITIONABLE:GetVelocityMPS()
-self:F2(self.PositionableName)
-local DCSPositionable=self:GetDCSObject()
-if DCSPositionable and DCSPositionable:isExist()then
-local VelocityVec3=self:GetVelocityVec3()
-local Velocity=(VelocityVec3.x^2+VelocityVec3.y^2+VelocityVec3.z^2)^0.5
-self:T3(Velocity)
-return Velocity
-end
-return 0
-end
-function POSITIONABLE:GetVelocityKNOTS()
-self:F2(self.PositionableName)
-return UTILS.MpsToKnots(self:GetVelocityMPS())
-end
-function POSITIONABLE:GetAirspeedTrue()
-local tas=0
-local coord=self:GetCoord()
-if coord then
-local alt=coord.y
-local wvec3=coord:GetWindVec3(alt,false)
-local vvec3=self:GetVelocityVec3()
-local tasvec3=UTILS.VecSubstract(vvec3,wvec3)
-tas=UTILS.VecNorm(tasvec3)
-end
-return tas
-end
-function POSITIONABLE:GetAirspeedIndicated(oatcorr)
-local tas=self:GetAirspeedTrue()
-local altitude=self:GetAltitude()
-local ias=UTILS.TasToIas(tas,altitude,oatcorr)
-return ias
-end
-function POSITIONABLE:GetGroundSpeed()
-local gs=0
-local vel=self:GetVelocityVec3()
-if vel then
-local vec2={x=vel.x,y=vel.z}
-gs=UTILS.Vec2Norm(vel)
-end
-return gs
-end
-function POSITIONABLE:GetAoA()
-local unitpos=self:GetPosition()
-if unitpos then
-local unitvel=self:GetVelocityVec3()
-if unitvel and UTILS.VecNorm(unitvel)~=0 then
-local wind=self:GetCoordinate():GetWindWithTurbulenceVec3()
-unitvel.x=unitvel.x-wind.x
-unitvel.y=unitvel.y-wind.y
-unitvel.z=unitvel.z-wind.z
-local AxialVel={}
-AxialVel.x=UTILS.VecDot(unitpos.x,unitvel)
-AxialVel.y=UTILS.VecDot(unitpos.y,unitvel)
-AxialVel.z=UTILS.VecDot(unitpos.z,unitvel)
-local AoA=math.acos(UTILS.VecDot({x=1,y=0,z=0},{x=AxialVel.x,y=AxialVel.y,z=0})/UTILS.VecNorm({x=AxialVel.x,y=AxialVel.y,z=0}))
-if AxialVel.y>0 then
-AoA=-AoA
-end
-return math.deg(AoA)
-end
-end
-return nil
-end
-function POSITIONABLE:GetClimbAngle()
-local unitpos=self:GetPosition()
-if unitpos then
-local unitvel=self:GetVelocityVec3()
-if unitvel and UTILS.VecNorm(unitvel)~=0 then
-local angle=math.asin(unitvel.y/UTILS.VecNorm(unitvel))
-return math.deg(angle)
-else
-return 0
-end
-end
-return nil
-end
-function POSITIONABLE:GetPitch()
-local unitpos=self:GetPosition()
-if unitpos then
-return math.deg(math.asin(unitpos.x.y))
-end
-return nil
-end
-function POSITIONABLE:GetRoll()
-local unitpos=self:GetPosition()
-if unitpos then
-local cp=UTILS.VecCross(unitpos.x,{x=0,y=1,z=0})
-local dp=UTILS.VecDot(cp,unitpos.z)
-local Roll=math.acos(dp/(UTILS.VecNorm(cp)*UTILS.VecNorm(unitpos.z)))
-if unitpos.z.y>0 then
-Roll=-Roll
-end
-return math.deg(Roll)
-end
-return nil
-end
-function POSITIONABLE:GetYaw()
-local unitpos=self:GetPosition()
-if unitpos then
-local unitvel=self:GetVelocityVec3()
-if unitvel and UTILS.VecNorm(unitvel)~=0 then
-local AxialVel={}
-AxialVel.x=UTILS.VecDot(unitpos.x,unitvel)
-AxialVel.y=UTILS.VecDot(unitpos.y,unitvel)
-AxialVel.z=UTILS.VecDot(unitpos.z,unitvel)
-local Yaw=math.acos(UTILS.VecDot({x=1,y=0,z=0},{x=AxialVel.x,y=0,z=AxialVel.z})/UTILS.VecNorm({x=AxialVel.x,y=0,z=AxialVel.z}))
-if AxialVel.z>0 then
-Yaw=-Yaw
-end
-return Yaw
-end
-end
-return nil
-end
-function POSITIONABLE:GetMessageText(Message,Name)
-local DCSObject=self:GetDCSObject()
-if DCSObject then
-local Callsign=string.format("%s",((Name~=""and Name)or self:GetCallsign()~=""and self:GetCallsign())or self:GetName())
-local MessageText=string.format("%s - %s",Callsign,Message)
-return MessageText
-end
-return nil
-end
-function POSITIONABLE:GetMessage(Message,Duration,Name)
-local DCSObject=self:GetDCSObject()
-if DCSObject then
-local MessageText=self:GetMessageText(Message,Name)
-return MESSAGE:New(MessageText,Duration)
-end
-return nil
-end
-function POSITIONABLE:GetMessageType(Message,MessageType,Name)
-local DCSObject=self:GetDCSObject()
-if DCSObject then
-local MessageText=self:GetMessageText(Message,Name)
-return MESSAGE:NewType(MessageText,MessageType)
-end
-return nil
-end
-function POSITIONABLE:MessageToAll(Message,Duration,Name)
-self:F2({Message,Duration})
-local DCSObject=self:GetDCSObject()
-if DCSObject then
-self:GetMessage(Message,Duration,Name):ToAll()
-end
-return nil
-end
-function POSITIONABLE:MessageToCoalition(Message,Duration,MessageCoalition,Name)
-self:F2({Message,Duration})
-local Name=Name or""
-local DCSObject=self:GetDCSObject()
-if DCSObject then
-self:GetMessage(Message,Duration,Name):ToCoalition(MessageCoalition)
-end
-return nil
-end
-function POSITIONABLE:MessageTypeToCoalition(Message,MessageType,MessageCoalition,Name)
-self:F2({Message,MessageType})
-local Name=Name or""
-local DCSObject=self:GetDCSObject()
-if DCSObject then
-self:GetMessageType(Message,MessageType,Name):ToCoalition(MessageCoalition)
-end
-return nil
-end
-function POSITIONABLE:MessageToRed(Message,Duration,Name)
-self:F2({Message,Duration})
-local DCSObject=self:GetDCSObject()
-if DCSObject then
-self:GetMessage(Message,Duration,Name):ToRed()
-end
-return nil
-end
-function POSITIONABLE:MessageToBlue(Message,Duration,Name)
-self:F2({Message,Duration})
-local DCSObject=self:GetDCSObject()
-if DCSObject then
-self:GetMessage(Message,Duration,Name):ToBlue()
-end
-return nil
-end
-function POSITIONABLE:MessageToClient(Message,Duration,Client,Name)
-self:F2({Message,Duration})
-local DCSObject=self:GetDCSObject()
-if DCSObject then
-self:GetMessage(Message,Duration,Name):ToClient(Client)
-end
-return nil
-end
-function POSITIONABLE:MessageToUnit(Message,Duration,MessageUnit,Name)
-self:F2({Message,Duration})
-local DCSObject=self:GetDCSObject()
-if DCSObject then
-if DCSObject:isExist()then
-if MessageUnit:IsAlive()then
-self:GetMessage(Message,Duration,Name):ToUnit(MessageUnit)
-else
-BASE:E({"Message not sent to Unit; Unit is not alive...",Message=Message,MessageUnit=MessageUnit})
-end
-else
-BASE:E({"Message not sent to Unit; Positionable is not alive ...",Message=Message,Positionable=self,MessageUnit=MessageUnit})
-end
-end
-end
-function POSITIONABLE:MessageToGroup(Message,Duration,MessageGroup,Name)
-self:F2({Message,Duration})
-local DCSObject=self:GetDCSObject()
-if DCSObject then
-if DCSObject:isExist()then
-if MessageGroup:IsAlive()then
-self:GetMessage(Message,Duration,Name):ToGroup(MessageGroup)
-else
-BASE:E({"Message not sent to Group; Group is not alive...",Message=Message,MessageGroup=MessageGroup})
-end
-else
-BASE:E({
-"Message not sent to Group; Positionable is not alive ...",
-Message=Message,
-Positionable=self,
-MessageGroup=MessageGroup
-})
-end
-end
-return nil
-end
-function POSITIONABLE:MessageToUnit(Message,Duration,MessageUnit,Name)
-self:F2({Message,Duration})
-local DCSObject=self:GetDCSObject()
-if DCSObject then
-if DCSObject:isExist()then
-if MessageUnit:IsAlive()then
-self:GetMessage(Message,Duration,Name):ToUnit(MessageUnit)
-else
-BASE:E({"Message not sent to Unit; Unit is not alive...",Message=Message,MessageUnit=MessageUnit})
-end
-else
-BASE:E({"Message not sent to Unit; Positionable is not alive ...",Message=Message,Positionable=self,MessageUnit=MessageUnit})
-end
-end
-return nil
-end
-function POSITIONABLE:MessageTypeToGroup(Message,MessageType,MessageGroup,Name)
-self:F2({Message,MessageType})
-local DCSObject=self:GetDCSObject()
-if DCSObject then
-if DCSObject:isExist()then
-self:GetMessageType(Message,MessageType,Name):ToGroup(MessageGroup)
-end
-end
-return nil
-end
-function POSITIONABLE:MessageToSetGroup(Message,Duration,MessageSetGroup,Name)
-self:F2({Message,Duration})
-local DCSObject=self:GetDCSObject()
-if DCSObject then
-if DCSObject:isExist()then
-MessageSetGroup:ForEachGroupAlive(function(MessageGroup)
-self:GetMessage(Message,Duration,Name):ToGroup(MessageGroup)
-end)
-end
-end
-return nil
-end
-function POSITIONABLE:MessageToSetUnit(Message,Duration,MessageSetUnit,Name)
-self:F2({Message,Duration})
-local DCSObject=self:GetDCSObject()
-if DCSObject then
-if DCSObject:isExist()then
-MessageSetUnit:ForEachUnit(
-function(MessageGroup)
-self:GetMessage(Message,Duration,Name):ToUnit(MessageGroup)
-end
-)
-end
-end
-return nil
-end
-function POSITIONABLE:MessageToSetUnit(Message,Duration,MessageSetUnit,Name)
-self:F2({Message,Duration})
-local DCSObject=self:GetDCSObject()
-if DCSObject then
-if DCSObject:isExist()then
-MessageSetUnit:ForEachUnit(
-function(MessageGroup)
-self:GetMessage(Message,Duration,Name):ToUnit(MessageGroup)
-end
-)
-end
-end
-return nil
-end
-function POSITIONABLE:Message(Message,Duration,Name)
-self:F2({Message,Duration})
-local DCSObject=self:GetDCSObject()
-if DCSObject then
-self:GetMessage(Message,Duration,Name):ToGroup(self)
-end
-return nil
-end
-function POSITIONABLE:GetRadio()
-self:F2(self)
-return RADIO:New(self)
-end
-function POSITIONABLE:GetBeacon()
-self:F2(self)
-return BEACON:New(self)
-end
-function POSITIONABLE:LaseUnit(Target,LaserCode,Duration)
-self:F2()
-LaserCode=LaserCode or math.random(1000,9999)
-local RecceDcsUnit=self:GetDCSObject()
-local TargetVec3=Target:GetVec3()
-self:F("building spot")
-self.Spot=SPOT:New(self)
-self.Spot:LaseOn(Target,LaserCode,Duration)
-self.LaserCode=LaserCode
-return self.Spot
-end
-function POSITIONABLE:LaseCoordinate(Coordinate,LaserCode,Duration)
-self:F2()
-LaserCode=LaserCode or math.random(1000,9999)
-self.Spot=SPOT:New(self)
-self.Spot:LaseOnCoordinate(Coordinate,LaserCode,Duration)
-self.LaserCode=LaserCode
-return self.Spot
-end
-function POSITIONABLE:LaseOff()
-self:F2()
-if self.Spot then
-self.Spot:LaseOff()
-self.Spot=nil
-end
-return self
-end
-function POSITIONABLE:IsLasing()
-self:F2()
-local Lasing=false
-if self.Spot then
-Lasing=self.Spot:IsLasing()
-end
-return Lasing
-end
-function POSITIONABLE:GetSpot()
-return self.Spot
-end
-function POSITIONABLE:GetLaserCode()
-return self.LaserCode
-end
-do
-function POSITIONABLE:AddCargo(Cargo)
-self.__.Cargo[Cargo]=Cargo
-return self
-end
-function POSITIONABLE:GetCargo()
-return self.__.Cargo
-end
-function POSITIONABLE:RemoveCargo(Cargo)
-self.__.Cargo[Cargo]=nil
-return self
-end
-function POSITIONABLE:HasCargo(Cargo)
-return self.__.Cargo[Cargo]
-end
-function POSITIONABLE:ClearCargo()
-self.__.Cargo={}
-end
-function POSITIONABLE:IsCargoEmpty()
-local IsEmpty=true
-for _,Cargo in pairs(self.__.Cargo)do
-IsEmpty=false
-break
-end
-return IsEmpty
-end
-function POSITIONABLE:CargoItemCount()
-local ItemCount=0
-for CargoName,Cargo in pairs(self.__.Cargo)do
-ItemCount=ItemCount+Cargo:GetCount()
-end
-return ItemCount
-end
-function POSITIONABLE:GetTroopCapacity()
-local DCSunit=self:GetDCSObject()
-local capacity=DCSunit:getDescentCapacity()
-return capacity
-end
-function POSITIONABLE:GetCargoBayFreeWeight()
-if not self.__.CargoBayWeightLimit then
-self:SetCargoBayWeightLimit()
-end
-local CargoWeight=0
-for CargoName,Cargo in pairs(self.__.Cargo)do
-CargoWeight=CargoWeight+Cargo:GetWeight()
-end
-return self.__.CargoBayWeightLimit-CargoWeight
-end
-POSITIONABLE.DefaultInfantryWeight=95
-POSITIONABLE.CargoBayCapacityValues={
-["Air"]={
-["C_130"]=70000,
-},
-["Naval"]={
-["Type_071"]=245000,
-["LHA_Tarawa"]=500000,
-["Ropucha_class"]=150000,
-["Dry_cargo_ship_1"]=70000,
-["Dry_cargo_ship_2"]=70000,
-["Higgins_boat"]=3700,
-["USS_Samuel_Chase"]=25000,
-["LST_Mk2"]=2100000,
-["speedboat"]=500,
-["Seawise_Giant"]=261000000,
-},
-["Ground"]={
-["AAV7"]=25*POSITIONABLE.DefaultInfantryWeight,
-["Bedford_MWD"]=8*POSITIONABLE.DefaultInfantryWeight,
-["Blitz_36_6700A"]=10*POSITIONABLE.DefaultInfantryWeight,
-["BMD_1"]=9*POSITIONABLE.DefaultInfantryWeight,
-["BMP_1"]=8*POSITIONABLE.DefaultInfantryWeight,
-["BMP_2"]=7*POSITIONABLE.DefaultInfantryWeight,
-["BMP_3"]=8*POSITIONABLE.DefaultInfantryWeight,
-["Boman"]=25*POSITIONABLE.DefaultInfantryWeight,
-["BTR_80"]=9*POSITIONABLE.DefaultInfantryWeight,
-["BTR_82A"]=9*POSITIONABLE.DefaultInfantryWeight,
-["BTR_D"]=12*POSITIONABLE.DefaultInfantryWeight,
-["Cobra"]=8*POSITIONABLE.DefaultInfantryWeight,
-["Land_Rover_101_FC"]=11*POSITIONABLE.DefaultInfantryWeight,
-["Land_Rover_109_S3"]=7*POSITIONABLE.DefaultInfantryWeight,
-["LAV_25"]=6*POSITIONABLE.DefaultInfantryWeight,
-["M_2_Bradley"]=6*POSITIONABLE.DefaultInfantryWeight,
-["M1043_HMMWV_Armament"]=4*POSITIONABLE.DefaultInfantryWeight,
-["M1045_HMMWV_TOW"]=4*POSITIONABLE.DefaultInfantryWeight,
-["M1126_Stryker_ICV"]=9*POSITIONABLE.DefaultInfantryWeight,
-["M1134_Stryker_ATGM"]=9*POSITIONABLE.DefaultInfantryWeight,
-["M2A1_halftrack"]=9*POSITIONABLE.DefaultInfantryWeight,
-["M_113"]=9*POSITIONABLE.DefaultInfantryWeight,
-["Marder"]=6*POSITIONABLE.DefaultInfantryWeight,
-["MCV_80"]=9*POSITIONABLE.DefaultInfantryWeight,
-["MLRS_FDDM"]=4*POSITIONABLE.DefaultInfantryWeight,
-["MTLB"]=25*POSITIONABLE.DefaultInfantryWeight,
-["GAZ_66"]=8*POSITIONABLE.DefaultInfantryWeight,
-["GAZ_3307"]=12*POSITIONABLE.DefaultInfantryWeight,
-["GAZ_3308"]=14*POSITIONABLE.DefaultInfantryWeight,
-["Grad_FDDM"]=6*POSITIONABLE.DefaultInfantryWeight,
-["KAMAZ_Truck"]=12*POSITIONABLE.DefaultInfantryWeight,
-["KrAZ6322"]=12*POSITIONABLE.DefaultInfantryWeight,
-["M_818"]=12*POSITIONABLE.DefaultInfantryWeight,
-["Tigr_233036"]=6*POSITIONABLE.DefaultInfantryWeight,
-["TPZ"]=10*POSITIONABLE.DefaultInfantryWeight,
-["UAZ_469"]=4*POSITIONABLE.DefaultInfantryWeight,
-["Ural_375"]=12*POSITIONABLE.DefaultInfantryWeight,
-["Ural_4320_31"]=14*POSITIONABLE.DefaultInfantryWeight,
-["Ural_4320_APA_5D"]=10*POSITIONABLE.DefaultInfantryWeight,
-["Ural_4320T"]=14*POSITIONABLE.DefaultInfantryWeight,
-["ZBD04A"]=7*POSITIONABLE.DefaultInfantryWeight,
-["VAB_Mephisto"]=8*POSITIONABLE.DefaultInfantryWeight,
-["tt_KORD"]=6*POSITIONABLE.DefaultInfantryWeight,
-["tt_DSHK"]=6*POSITIONABLE.DefaultInfantryWeight,
-["HL_KORD"]=6*POSITIONABLE.DefaultInfantryWeight,
-["HL_DSHK"]=6*POSITIONABLE.DefaultInfantryWeight,
-["CCKW_353"]=16*POSITIONABLE.DefaultInfantryWeight,
-}
-}
-function POSITIONABLE:SetCargoBayWeightLimit(WeightLimit)
-if WeightLimit then
-self.__.CargoBayWeightLimit=WeightLimit
-elseif self.__.CargoBayWeightLimit~=nil then
-else
-local Desc=self:GetDesc()
-self:F({Desc=Desc})
-local TypeName=Desc.typeName or"Unknown Type"
-TypeName=string.gsub(TypeName,"[%p%s]","_")
-if self:IsAir()then
-local Weights=POSITIONABLE.CargoBayCapacityValues.Air
-local massMax=Desc.massMax or 0
-local maxTakeoff=Weights[TypeName]
-if maxTakeoff then
-massMax=maxTakeoff
-end
-local massEmpty=Desc.massEmpty or 0
-local massFuelMax=Desc.fuelMassMax or 0
-local relFuel=math.min(self:GetFuel()or 1.0,1.0)
-local massFuel=massFuelMax*relFuel
-local CargoWeight=massMax-(massEmpty+massFuel)
-self:T(string.format("Setting Cargo bay weight limit [%s]=%d kg (Mass max=%d, empty=%d, fuelMax=%d kg (rel=%.3f), fuel=%d kg",TypeName,CargoWeight,massMax,massEmpty,massFuelMax,relFuel,massFuel))
-self.__.CargoBayWeightLimit=CargoWeight
-elseif self:IsShip()then
-local Weights=POSITIONABLE.CargoBayCapacityValues.Naval
-self.__.CargoBayWeightLimit=(Weights[TypeName]or 50000)
-else
-local Weights=POSITIONABLE.CargoBayCapacityValues.Ground
-local CargoBayWeightLimit=(Weights[TypeName]or 0)
-self.__.CargoBayWeightLimit=CargoBayWeightLimit
-end
-end
-self:F({CargoBayWeightLimit=self.__.CargoBayWeightLimit})
-end
-function POSITIONABLE:GetCargoBayWeightLimit()
-if self.__.CargoBayWeightLimit==nil then
-self:SetCargoBayWeightLimit()
-end
-return self.__.CargoBayWeightLimit
-end
-end
-function POSITIONABLE:Flare(FlareColor)
-self:F2()
-trigger.action.signalFlare(self:GetVec3(),FlareColor,0)
-end
-function POSITIONABLE:FlareWhite()
-self:F2()
-trigger.action.signalFlare(self:GetVec3(),trigger.flareColor.White,0)
-end
-function POSITIONABLE:FlareYellow()
-self:F2()
-trigger.action.signalFlare(self:GetVec3(),trigger.flareColor.Yellow,0)
-end
-function POSITIONABLE:FlareGreen()
-self:F2()
-trigger.action.signalFlare(self:GetVec3(),trigger.flareColor.Green,0)
-end
-function POSITIONABLE:FlareRed()
-self:F2()
-local Vec3=self:GetVec3()
-if Vec3 then
-trigger.action.signalFlare(Vec3,trigger.flareColor.Red,0)
-end
-end
-function POSITIONABLE:Smoke(SmokeColor,Range,AddHeight)
-self:F2()
-if Range then
-local Vec3=self:GetRandomVec3(Range)
-Vec3.y=Vec3.y+AddHeight or 0
-trigger.action.smoke(Vec3,SmokeColor)
-else
-local Vec3=self:GetVec3()
-Vec3.y=Vec3.y+AddHeight or 0
-trigger.action.smoke(self:GetVec3(),SmokeColor)
-end
-end
-function POSITIONABLE:SmokeGreen()
-self:F2()
-trigger.action.smoke(self:GetVec3(),trigger.smokeColor.Green)
-end
-function POSITIONABLE:SmokeRed()
-self:F2()
-trigger.action.smoke(self:GetVec3(),trigger.smokeColor.Red)
-end
-function POSITIONABLE:SmokeWhite()
-self:F2()
-trigger.action.smoke(self:GetVec3(),trigger.smokeColor.White)
-end
-function POSITIONABLE:SmokeOrange()
-self:F2()
-trigger.action.smoke(self:GetVec3(),trigger.smokeColor.Orange)
-end
-function POSITIONABLE:SmokeBlue()
-self:F2()
-trigger.action.smoke(self:GetVec3(),trigger.smokeColor.Blue)
-end
-function POSITIONABLE:IsInZone(Zone)
-self:F2({self.PositionableName,Zone})
-if self:IsAlive()then
-local IsInZone=Zone:IsVec3InZone(self:GetVec3())
-return IsInZone
-end
-return false
-end
-function POSITIONABLE:IsNotInZone(Zone)
-self:F2({self.PositionableName,Zone})
-if self:IsAlive()then
-local IsNotInZone=not Zone:IsVec3InZone(self:GetVec3())
-return IsNotInZone
-else
-return false
-end
-end
-SCENERY={
-ClassName="SCENERY",
-}
-function SCENERY:Register(SceneryName,SceneryObject)
-local self=BASE:Inherit(self,POSITIONABLE:New(SceneryName))
-self.SceneryName=tostring(SceneryName)
-self.SceneryObject=SceneryObject
-if self.SceneryObject then
-self.Life0=self.SceneryObject:getLife()
-else
-self.Life0=0
-end
-self.Properties={}
-return self
-end
-function SCENERY:GetProperty(PropertyName)
-return self.Properties[PropertyName]
-end
-function SCENERY:GetAllProperties()
-return self.Properties
-end
-function SCENERY:SetProperty(PropertyName,PropertyValue)
-self.Properties[PropertyName]=PropertyValue
-return self
-end
-function SCENERY:GetName()
-return self.SceneryName
-end
-function SCENERY:GetDCSObject()
-return self.SceneryObject
-end
-function SCENERY:GetLife()
-local life=0
-if self.SceneryObject then
-life=self.SceneryObject:getLife()
-if life>self.Life0 then
-self.Life0=math.floor(life*1.2)
-end
-end
-return life
-end
-function SCENERY:GetLife0()
-return self.Life0 or 0
-end
-function SCENERY:IsAlive()
-return self:GetLife()>=1 and true or false
-end
-function SCENERY:IsDead()
-return self:GetLife()<1 and true or false
-end
-function SCENERY:GetThreatLevel()
-return 0,"Scenery"
-end
-function SCENERY:FindByName(Name,Coordinate,Radius,Role)
-local radius=Radius or 100
-local name=Name or"unknown"
-local scenery=nil
-local function SceneryScan(scoordinate,sradius,sname)
-if scoordinate~=nil then
-local Vec2=scoordinate:GetVec2()
-local scanzone=ZONE_RADIUS:New("Zone-"..sname,Vec2,sradius,true)
-scanzone:Scan({Object.Category.SCENERY})
-local scanned=scanzone:GetScannedSceneryObjects()
-local rscenery=nil
-for _,_scenery in pairs(scanned)do
-local scenery=_scenery
-if tostring(scenery.SceneryName)==tostring(sname)then
-rscenery=scenery
-if Role then rscenery:SetProperty("ROLE",Role)end
-break
-end
-end
-return rscenery
-end
-return nil
-end
-if Coordinate then
-scenery=SceneryScan(Coordinate,radius,name)
-end
-return scenery
-end
-function SCENERY:FindByNameInZone(Name,Zone,Radius)
-local radius=Radius or 100
-local name=Name or"unknown"
-if type(Zone)=="string"then
-Zone=ZONE:FindByName(Zone)
-end
-local coordinate=Zone:GetCoordinate()
-return self:FindByName(Name,coordinate,Radius,Zone:GetProperty("ROLE"))
-end
-function SCENERY:FindByZoneName(ZoneName)
-local zone=ZoneName
-if type(ZoneName)=="string"then
-zone=ZONE:FindByName(ZoneName)
-end
-local _id=zone:GetProperty('OBJECT ID')
-if not _id then
-BASE:E("**** Zone without object ID: "..ZoneName.." | Type: "..tostring(zone.ClassName))
-if string.find(zone.ClassName,"POLYGON")then
-zone:Scan({Object.Category.SCENERY})
-local scanned=zone:GetScannedSceneryObjects()
-for _,_scenery in(scanned)do
-local scenery=_scenery
-if scenery:IsAlive()then
-local role=zone:GetProperty("ROLE")
-if role then scenery:SetProperty("ROLE",role)end
-return scenery
-end
-end
-return nil
-else
-return self:FindByName(_id,zone:GetCoordinate(),nil,zone:GetProperty("ROLE"))
-end
-else
-return self:FindByName(_id,zone:GetCoordinate(),nil,zone:GetProperty("ROLE"))
-end
-end
-function SCENERY:FindAllByZoneName(ZoneName)
-local zone=ZoneName
-if type(ZoneName)=="string"then
-zone=ZONE:FindByName(ZoneName)
-end
-local _id=zone:GetProperty('OBJECT ID')
-if not _id then
-zone:Scan({Object.Category.SCENERY})
-local scanned=zone:GetScannedSceneryObjects()
-if#scanned>0 then
-return scanned
-else
-return nil
-end
-else
-local obj=self:FindByName(_id,zone:GetCoordinate(),nil,zone:GetProperty("ROLE"))
-if obj then
-return{obj}
-else
-return nil
-end
-end
-end
-function SCENERY:Destroy()
-return self
-end
-STATIC={
-ClassName="STATIC",
-}
-function STATIC:Register(StaticName)
-local self=BASE:Inherit(self,POSITIONABLE:New(StaticName))
-self.StaticName=StaticName
-local DCSStatic=StaticObject.getByName(self.StaticName)
-if DCSStatic then
-local Life0=DCSStatic:getLife()or 1
-self.Life0=Life0
-end
-return self
-end
-function STATIC:GetLife0()
-return self.Life0 or 1
-end
-function STATIC:GetLife()
-local DCSStatic=StaticObject.getByName(self.StaticName)
-if DCSStatic then
-return DCSStatic:getLife()or 1
-end
-return nil
-end
-function STATIC:Find(DCSStatic)
-local StaticName=DCSStatic:getName()
-local StaticFound=_DATABASE:FindStatic(StaticName)
-return StaticFound
-end
-function STATIC:FindByName(StaticName,RaiseError)
-local StaticFound=_DATABASE:FindStatic(StaticName)
-self.StaticName=StaticName
-if StaticFound then
-return StaticFound
-end
-if RaiseError==nil or RaiseError==true then
-error("STATIC not found for: "..StaticName)
-end
-return nil
-end
-function STATIC:Destroy(GenerateEvent)
-self:F2(self.ObjectName)
-local DCSObject=self:GetDCSObject()
-if DCSObject then
-local StaticName=DCSObject:getName()
-self:F({StaticName=StaticName})
-if GenerateEvent and GenerateEvent==true then
-if self:IsAir()then
-self:CreateEventCrash(timer.getTime(),DCSObject)
-else
-self:CreateEventDead(timer.getTime(),DCSObject)
-end
-elseif GenerateEvent==false then
-else
-self:CreateEventRemoveUnit(timer.getTime(),DCSObject)
-end
-DCSObject:destroy()
-return true
-end
-return nil
-end
-function STATIC:GetDCSObject()
-local DCSStatic=StaticObject.getByName(self.StaticName)
-if DCSStatic then
-return DCSStatic
-end
-return nil
-end
-function STATIC:GetUnits()
-self:F2({self.StaticName})
-local DCSStatic=self:GetDCSObject()
-local Statics={}
-if DCSStatic then
-Statics[1]=STATIC:Find(DCSStatic)
-self:T3(Statics)
-return Statics
-end
-return nil
-end
-function STATIC:GetThreatLevel()
-return 1,"Static"
-end
-function STATIC:SpawnAt(Coordinate,Heading,Delay)
-Heading=Heading or 0
-if Delay and Delay>0 then
-SCHEDULER:New(nil,self.SpawnAt,{self,Coordinate,Heading},Delay)
-else
-local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName)
-SpawnStatic:SpawnFromPointVec2(Coordinate,Heading,self.StaticName)
-end
-return self
-end
-function STATIC:ReSpawn(CountryID,Delay)
-if Delay and Delay>0 then
-SCHEDULER:New(nil,self.ReSpawn,{self,CountryID},Delay)
-else
-CountryID=CountryID or self:GetCountry()
-local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName,CountryID)
-SpawnStatic:Spawn(nil,self.StaticName)
-end
-return self
-end
-function STATIC:ReSpawnAt(Coordinate,Heading,Delay)
-if Delay and Delay>0 then
-SCHEDULER:New(nil,self.ReSpawnAt,{self,Coordinate,Heading},Delay)
-else
-local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName,self:GetCountry())
-SpawnStatic:SpawnFromCoordinate(Coordinate,Heading,self.StaticName)
-end
-return self
-end
-UNIT={
-ClassName="UNIT",
-UnitName=nil,
-GroupName=nil,
-DCSUnit=nil,
-}
-function UNIT:Register(UnitName)
-local self=BASE:Inherit(self,CONTROLLABLE:New(UnitName))
-self.UnitName=UnitName
-local unit=Unit.getByName(self.UnitName)
-if unit then
-local group=unit:getGroup()
-if group then
-self.GroupName=group:getName()
-end
-self.DCSUnit=unit
-end
-self:SetEventPriority(3)
-return self
-end
-function UNIT:Find(DCSUnit)
-if DCSUnit then
-local UnitName=DCSUnit:getName()
-local UnitFound=_DATABASE:FindUnit(UnitName)
-return UnitFound
-end
-return nil
-end
-function UNIT:FindByName(UnitName)
-local UnitFound=_DATABASE:FindUnit(UnitName)
-return UnitFound
-end
-function UNIT:FindByMatching(Pattern)
-local GroupFound=nil
-for name,group in pairs(_DATABASE.UNITS)do
-if string.match(name,Pattern)then
-GroupFound=group
-break
-end
-end
-return GroupFound
-end
-function UNIT:FindAllByMatching(Pattern)
-local GroupsFound={}
-for name,group in pairs(_DATABASE.UNITS)do
-if string.match(name,Pattern)then
-GroupsFound[#GroupsFound+1]=group
-end
-end
-return GroupsFound
-end
-function UNIT:Name()
-return self.UnitName
-end
-function UNIT:GetDCSObject()
-local DCSUnit=Unit.getByName(self.UnitName)
-if DCSUnit then
-return DCSUnit
-end
-return nil
-end
-function UNIT:GetAltitude(FromGround)
-local DCSUnit=Unit.getByName(self.UnitName)
-if DCSUnit then
-local altitude=0
-local point=DCSUnit:getPoint()
-altitude=point.y
-if FromGround then
-local land=land.getHeight({x=point.x,y=point.z})or 0
-altitude=altitude-land
-end
-return altitude
-end
-return nil
-end
-function UNIT:ReSpawnAt(Coordinate,Heading)
-self:T(self:Name())
-local SpawnGroupTemplate=UTILS.DeepCopy(_DATABASE:GetGroupTemplateFromUnitName(self:Name()))
-self:T(SpawnGroupTemplate)
-local SpawnGroup=self:GetGroup()
-self:T({SpawnGroup=SpawnGroup})
-if SpawnGroup then
-local Vec3=SpawnGroup:GetVec3()
-SpawnGroupTemplate.x=Coordinate.x
-SpawnGroupTemplate.y=Coordinate.z
-self:F(#SpawnGroupTemplate.units)
-for UnitID,UnitData in pairs(SpawnGroup:GetUnits()or{})do
-local GroupUnit=UnitData
-self:F(GroupUnit:GetName())
-if GroupUnit:IsAlive()then
-local GroupUnitVec3=GroupUnit:GetVec3()
-local GroupUnitHeading=GroupUnit:GetHeading()
-SpawnGroupTemplate.units[UnitID].alt=GroupUnitVec3.y
-SpawnGroupTemplate.units[UnitID].x=GroupUnitVec3.x
-SpawnGroupTemplate.units[UnitID].y=GroupUnitVec3.z
-SpawnGroupTemplate.units[UnitID].heading=GroupUnitHeading
-self:F({UnitID,SpawnGroupTemplate.units[UnitID],SpawnGroupTemplate.units[UnitID]})
-end
-end
-end
-for UnitTemplateID,UnitTemplateData in pairs(SpawnGroupTemplate.units)do
-self:T({UnitTemplateData.name,self:Name()})
-SpawnGroupTemplate.units[UnitTemplateID].unitId=nil
-if UnitTemplateData.name==self:Name()then
-self:T("Adjusting")
-SpawnGroupTemplate.units[UnitTemplateID].alt=Coordinate.y
-SpawnGroupTemplate.units[UnitTemplateID].x=Coordinate.x
-SpawnGroupTemplate.units[UnitTemplateID].y=Coordinate.z
-SpawnGroupTemplate.units[UnitTemplateID].heading=Heading
-self:F({UnitTemplateID,SpawnGroupTemplate.units[UnitTemplateID],SpawnGroupTemplate.units[UnitTemplateID]})
-else
-self:F(SpawnGroupTemplate.units[UnitTemplateID].name)
-local GroupUnit=UNIT:FindByName(SpawnGroupTemplate.units[UnitTemplateID].name)
-if GroupUnit and GroupUnit:IsAlive()then
-local GroupUnitVec3=GroupUnit:GetVec3()
-local GroupUnitHeading=GroupUnit:GetHeading()
-UnitTemplateData.alt=GroupUnitVec3.y
-UnitTemplateData.x=GroupUnitVec3.x
-UnitTemplateData.y=GroupUnitVec3.z
-UnitTemplateData.heading=GroupUnitHeading
-else
-if SpawnGroupTemplate.units[UnitTemplateID].name~=self:Name()then
-self:T("nilling")
-SpawnGroupTemplate.units[UnitTemplateID].delete=true
-end
-end
-end
-end
-local i=1
-while i<=#SpawnGroupTemplate.units do
-local UnitTemplateData=SpawnGroupTemplate.units[i]
-self:T(UnitTemplateData.name)
-if UnitTemplateData.delete then
-table.remove(SpawnGroupTemplate.units,i)
-else
-i=i+1
-end
-end
-SpawnGroupTemplate.groupId=nil
-self:T(SpawnGroupTemplate)
-_DATABASE:Spawn(SpawnGroupTemplate)
-end
-function UNIT:IsActive()
-self:F2(self.UnitName)
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-local UnitIsActive=DCSUnit:isActive()
-return UnitIsActive
-end
-return nil
-end
-function UNIT:IsExist()
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-local exists=DCSUnit:isExist()
-return exists
-end
-return nil
-end
-function UNIT:IsAlive()
-self:F3(self.UnitName)
-local DCSUnit=self:GetDCSObject()
-if DCSUnit and DCSUnit:isExist()then
-local UnitIsAlive=DCSUnit:isActive()
-return UnitIsAlive
-end
-return nil
-end
-function UNIT:IsDead()
-return not self:IsAlive()
-end
-function UNIT:GetCallsign()
-self:F2(self.UnitName)
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-local UnitCallSign=DCSUnit:getCallsign()
-if UnitCallSign==""then
-UnitCallSign=DCSUnit:getName()
-end
-return UnitCallSign
-end
-self:F(self.ClassName.." "..self.UnitName.." not found!")
-return nil
-end
-function UNIT:IsPlayer()
-local group=self:GetGroup()
-if not group then return false end
-local units=group:GetTemplate().units
-for _,unit in pairs(units)do
-if unit.name==self:GetName()and(unit.skill=="Client"or unit.skill=="Player")then
-return true
-end
-end
-return false
-end
-function UNIT:GetPlayerName()
-self:F(self.UnitName)
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-local PlayerName=DCSUnit:getPlayerName()
-return PlayerName
-end
-return nil
-end
-function UNIT:IsClient()
-if _DATABASE.CLIENTS[self.UnitName]then
-return true
-end
-return false
-end
-function UNIT:GetClient()
-local client=_DATABASE.CLIENTS[self.UnitName]
-if client then
-return client
-end
-return nil
-end
-function UNIT:GetNatoReportingName()
-local typename=self:GetTypeName()
-return UTILS.GetReportingName(typename)
-end
-function UNIT:GetNumber()
-self:F2(self.UnitName)
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-local UnitNumber=DCSUnit:getNumber()
-return UnitNumber
-end
-return nil
-end
-function UNIT:GetSpeedMax()
-self:F2(self.UnitName)
-local Desc=self:GetDesc()
-if Desc then
-local SpeedMax=Desc.speedMax
-return SpeedMax*3.6
-end
-return 0
-end
-function UNIT:GetRange()
-self:F2(self.UnitName)
-local Desc=self:GetDesc()
-if Desc then
-local Range=Desc.range
-if Range then
-Range=Range*1000
-else
-Range=10000000
-end
-return Range
-end
-return nil
-end
-function UNIT:IsRefuelable()
-self:F2(self.UnitName)
-local refuelable=self:HasAttribute("Refuelable")
-local system=nil
-local Desc=self:GetDesc()
-if Desc and Desc.tankerType then
-system=Desc.tankerType
-end
-return refuelable,system
-end
-function UNIT:IsTanker()
-self:F2(self.UnitName)
-local tanker=self:HasAttribute("Tankers")
-local system=nil
-if tanker then
-local Desc=self:GetDesc()
-if Desc and Desc.tankerType then
-system=Desc.tankerType
-end
-local typename=self:GetTypeName()
-if typename=="IL-78M"then
-system=1
-elseif typename=="KC130"or typename=="KC130J"then
-system=1
-elseif typename=="KC135BDA"then
-system=1
-elseif typename=="KC135MPRS"then
-system=1
-elseif typename=="S-3B Tanker"then
-system=1
-elseif typename=="KC_10_Extender"then
-system=1
-elseif typename=="KC_10_Extender_D"then
-system=0
-end
-end
-return tanker,system
-end
-function UNIT:IsAmmoSupply()
-local typename=self:GetTypeName()
-if typename=="M 818"then
-return true
-elseif typename=="Ural-375"then
-return true
-elseif typename=="ZIL-135"then
-return true
-end
-return false
-end
-function UNIT:IsFuelSupply()
-local typename=self:GetTypeName()
-if typename=="M978 HEMTT Tanker"then
-return true
-elseif typename=="ATMZ-5"then
-return true
-elseif typename=="ATMZ-10"then
-return true
-elseif typename=="ATZ-5"then
-return true
-end
-return false
-end
-function UNIT:GetGroup()
-self:F2(self.UnitName)
-local UnitGroup=GROUP:FindByName(self.GroupName)
-if UnitGroup then
-return UnitGroup
-else
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-local grp=DCSUnit:getGroup()
-if grp then
-local UnitGroup=GROUP:FindByName(grp:getName())
-return UnitGroup
-end
-end
-end
-return nil
-end
-function UNIT:GetPrefix()
-self:F2(self.UnitName)
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-local UnitPrefix=string.match(self.UnitName,".*#"):sub(1,-2)
-self:T3(UnitPrefix)
-return UnitPrefix
-end
-return nil
-end
-function UNIT:GetAmmo()
-self:F2(self.UnitName)
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-local UnitAmmo=DCSUnit:getAmmo()
-return UnitAmmo
-end
-return nil
-end
-function UNIT:SetUnitInternalCargo(mass)
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-trigger.action.setUnitInternalCargo(DCSUnit:getName(),mass)
-end
-return self
-end
-function UNIT:GetAmmunition()
-local nammo=0
-local nshells=0
-local nrockets=0
-local nmissiles=0
-local nbombs=0
-local narti=0
-local unit=self
-local ammotable=unit:GetAmmo()
-if ammotable then
-local weapons=#ammotable
-for w=1,weapons do
-local Nammo=ammotable[w]["count"]
-local Tammo=ammotable[w]["desc"]["typeName"]
-local Category=ammotable[w].desc.category
-local MissileCategory=nil
-if Category==Weapon.Category.MISSILE then
-MissileCategory=ammotable[w].desc.missileCategory
-end
-if Category==Weapon.Category.SHELL then
-nshells=nshells+Nammo
-if ammotable[w].desc.warhead and ammotable[w].desc.warhead.explosiveMass and ammotable[w].desc.warhead.explosiveMass>0 then
-narti=narti+Nammo
-end
-elseif Category==Weapon.Category.ROCKET then
-nrockets=nrockets+Nammo
-elseif Category==Weapon.Category.BOMB then
-nbombs=nbombs+Nammo
-elseif Category==Weapon.Category.MISSILE then
-if MissileCategory==Weapon.MissileCategory.AAM then
-nmissiles=nmissiles+Nammo
-elseif MissileCategory==Weapon.MissileCategory.ANTI_SHIP then
-nmissiles=nmissiles+Nammo
-elseif MissileCategory==Weapon.MissileCategory.BM then
-nmissiles=nmissiles+Nammo
-elseif MissileCategory==Weapon.MissileCategory.OTHER then
-nmissiles=nmissiles+Nammo
-elseif MissileCategory==Weapon.MissileCategory.SAM then
-nmissiles=nmissiles+Nammo
-elseif MissileCategory==Weapon.MissileCategory.CRUISE then
-nmissiles=nmissiles+Nammo
-end
-end
-end
-end
-nammo=nshells+nrockets+nmissiles+nbombs
-return nammo,nshells,nrockets,nbombs,nmissiles,narti
-end
-function UNIT:GetSensors()
-self:F2(self.UnitName)
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-local UnitSensors=DCSUnit:getSensors()
-return UnitSensors
-end
-return nil
-end
-function UNIT:HasSensors(...)
-self:F2(arg)
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-local HasSensors=DCSUnit:hasSensors(unpack(arg))
-return HasSensors
-end
-return nil
-end
-function UNIT:HasSEAD()
-self:F2()
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-local UnitSEADAttributes=DCSUnit:getDesc().attributes
-local HasSEAD=false
-if UnitSEADAttributes["RADAR_BAND1_FOR_ARM"]and UnitSEADAttributes["RADAR_BAND1_FOR_ARM"]==true or
-UnitSEADAttributes["RADAR_BAND2_FOR_ARM"]and UnitSEADAttributes["RADAR_BAND2_FOR_ARM"]==true or
-UnitSEADAttributes["Optical Tracker"]and UnitSEADAttributes["Optical Tracker"]==true
-then
-HasSEAD=true
-end
-return HasSEAD
-end
-return nil
-end
-function UNIT:GetRadar()
-self:F2(self.UnitName)
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-local UnitRadarOn,UnitRadarObject=DCSUnit:getRadar()
-return UnitRadarOn,UnitRadarObject
-end
-return nil,nil
-end
-function UNIT:GetFuel()
-self:F3(self.UnitName)
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-local UnitFuel=DCSUnit:getFuel()
-return UnitFuel
-end
-return nil
-end
-function UNIT:GetUnits()
-self:F3({self.UnitName})
-local DCSUnit=self:GetDCSObject()
-local Units={}
-if DCSUnit then
-Units[1]=UNIT:Find(DCSUnit)
-self:T3(Units)
-return Units
-end
-return nil
-end
-function UNIT:GetLife()
-self:F2(self.UnitName)
-local DCSUnit=self:GetDCSObject()
-if DCSUnit and DCSUnit:isExist()then
-local UnitLife=DCSUnit:getLife()
-return UnitLife
-end
-return-1
-end
-function UNIT:GetLife0()
-self:F2(self.UnitName)
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-local UnitLife0=DCSUnit:getLife0()
-return UnitLife0
-end
-return 0
-end
-function UNIT:GetLifeRelative()
-self:F2(self.UnitName)
-if self and self:IsAlive()then
-local life0=self:GetLife0()
-local lifeN=self:GetLife()
-return lifeN/life0
-end
-return-1
-end
-function UNIT:GetDamageRelative()
-self:F2(self.UnitName)
-if self and self:IsAlive()then
-return 1-self:GetLifeRelative()
-end
-return 1
-end
-function UNIT:GetDrawArgumentValue(AnimationArgument)
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-local value=DCSUnit:getDrawArgumentValue(AnimationArgument or 0)
-return value
-end
-return 0
-end
-function UNIT:GetUnitCategory()
-self:F3(self.UnitName)
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-return DCSUnit:getDesc().category
-end
-return nil
-end
-function UNIT:GetCategoryName()
-self:F3(self.UnitName)
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-local CategoryNames={
-[Unit.Category.AIRPLANE]="Airplane",
-[Unit.Category.HELICOPTER]="Helicopter",
-[Unit.Category.GROUND_UNIT]="Ground Unit",
-[Unit.Category.SHIP]="Ship",
-[Unit.Category.STRUCTURE]="Structure",
-}
-local UnitCategory=DCSUnit:getDesc().category
-self:T3(UnitCategory)
-return CategoryNames[UnitCategory]
-end
-return nil
-end
-function UNIT:GetThreatLevel()
-local ThreatLevel=0
-local ThreatText=""
-local Descriptor=self:GetDesc()
-if Descriptor then
-local Attributes=Descriptor.attributes
-if self:IsGround()then
-local ThreatLevels={
-"Unarmed",
-"Infantry",
-"Old Tanks & APCs",
-"Tanks & IFVs without ATGM",
-"Tanks & IFV with ATGM",
-"Modern Tanks",
-"AAA",
-"IR Guided SAMs",
-"SR SAMs",
-"MR SAMs",
-"LR SAMs"
-}
-if Attributes["LR SAM"]then ThreatLevel=10
-elseif Attributes["MR SAM"]then ThreatLevel=9
-elseif Attributes["SR SAM"]and
-not Attributes["IR Guided SAM"]then ThreatLevel=8
-elseif(Attributes["SR SAM"]or Attributes["MANPADS"])and
-Attributes["IR Guided SAM"]then ThreatLevel=7
-elseif Attributes["AAA"]then ThreatLevel=6
-elseif Attributes["Modern Tanks"]then ThreatLevel=5
-elseif(Attributes["Tanks"]or Attributes["IFV"])and
-Attributes["ATGM"]then ThreatLevel=4
-elseif(Attributes["Tanks"]or Attributes["IFV"])and
-not Attributes["ATGM"]then ThreatLevel=3
-elseif Attributes["Old Tanks"]or Attributes["APC"]or Attributes["Artillery"]then ThreatLevel=2
-elseif Attributes["Infantry"]or Attributes["EWR"]then ThreatLevel=1
-end
-ThreatText=ThreatLevels[ThreatLevel+1]
-end
-if self:IsAir()then
-local ThreatLevels={
-"Unarmed",
-"Tanker",
-"AWACS",
-"Transport Helicopter",
-"UAV",
-"Bomber",
-"Strategic Bomber",
-"Attack Helicopter",
-"Battleplane",
-"Multirole Fighter",
-"Fighter"
-}
-if Attributes["Fighters"]then ThreatLevel=10
-elseif Attributes["Multirole fighters"]then ThreatLevel=9
-elseif Attributes["Battleplanes"]then ThreatLevel=8
-elseif Attributes["Attack helicopters"]then ThreatLevel=7
-elseif Attributes["Strategic bombers"]then ThreatLevel=6
-elseif Attributes["Bombers"]then ThreatLevel=5
-elseif Attributes["UAVs"]then ThreatLevel=4
-elseif Attributes["Transport helicopters"]then ThreatLevel=3
-elseif Attributes["AWACS"]then ThreatLevel=2
-elseif Attributes["Tankers"]then ThreatLevel=1
-end
-ThreatText=ThreatLevels[ThreatLevel+1]
-end
-if self:IsShip()then
-local ThreatLevels={
-"Unarmed ship",
-"Light armed ships",
-"Corvettes",
-"",
-"Frigates",
-"",
-"Cruiser",
-"",
-"Destroyer",
-"",
-"Aircraft Carrier"
-}
-if Attributes["Aircraft Carriers"]then ThreatLevel=10
-elseif Attributes["Destroyers"]then ThreatLevel=8
-elseif Attributes["Cruisers"]then ThreatLevel=6
-elseif Attributes["Frigates"]then ThreatLevel=4
-elseif Attributes["Corvettes"]then ThreatLevel=2
-elseif Attributes["Light armed ships"]then ThreatLevel=1
-end
-ThreatText=ThreatLevels[ThreatLevel+1]
-end
-end
-return ThreatLevel,ThreatText
-end
-function UNIT:Explode(power,delay)
-power=power or 100
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-if delay and delay>0 then
-SCHEDULER:New(nil,self.Explode,{self,power},delay)
-else
-self:GetCoordinate():Explosion(power)
-end
-return self
-end
-return nil
-end
-function UNIT:OtherUnitInRadius(AwaitUnit,Radius)
-self:F2({self.UnitName,AwaitUnit.UnitName,Radius})
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-local UnitVec3=self:GetVec3()
-local AwaitUnitVec3=AwaitUnit:GetVec3()
-if(((UnitVec3.x-AwaitUnitVec3.x)^2+(UnitVec3.z-AwaitUnitVec3.z)^2)^0.5<=Radius)then
-self:T3("true")
-return true
-else
-self:T3("false")
-return false
-end
-end
-return nil
-end
-function UNIT:IsFriendly(FriendlyCoalition)
-self:F2()
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-local UnitCoalition=DCSUnit:getCoalition()
-self:T3({UnitCoalition,FriendlyCoalition})
-local IsFriendlyResult=(UnitCoalition==FriendlyCoalition)
-self:F(IsFriendlyResult)
-return IsFriendlyResult
-end
-return nil
-end
-function UNIT:IsShip()
-self:F2()
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-local UnitDescriptor=DCSUnit:getDesc()
-self:T3({UnitDescriptor.category,Unit.Category.SHIP})
-local IsShipResult=(UnitDescriptor.category==Unit.Category.SHIP)
-self:T3(IsShipResult)
-return IsShipResult
-end
-return nil
-end
-function UNIT:InAir(NoHeloCheck)
-self:F2(self.UnitName)
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-local UnitInAir=DCSUnit:inAir()
-local UnitCategory=DCSUnit:getDesc().category
-if UnitInAir==true and UnitCategory==Unit.Category.HELICOPTER and(not NoHeloCheck)then
-local VelocityVec3=DCSUnit:getVelocity()
-local Velocity=UTILS.VecNorm(VelocityVec3)
-local Coordinate=DCSUnit:getPoint()
-local LandHeight=land.getHeight({x=Coordinate.x,y=Coordinate.z})
-local Height=Coordinate.y-LandHeight
-if Velocity<1 and Height<=60 then
-UnitInAir=false
-end
-end
-self:T3(UnitInAir)
-return UnitInAir
-end
-return nil
-end
-do
-function UNIT:HandleEvent(EventID,EventFunction)
-self:EventDispatcher():OnEventForUnit(self:GetName(),EventFunction,self,EventID)
-return self
-end
-function UNIT:UnHandleEvent(EventID)
-self:EventDispatcher():RemoveEvent(self,EventID)
-return self
-end
-function UNIT:ResetEvents()
-self:EventDispatcher():Reset(self)
-return self
-end
-end
-do
-function UNIT:IsDetected(TargetUnit)
-local TargetIsDetected,TargetIsVisible,TargetLastTime,TargetKnowType,TargetKnowDistance,TargetLastPos,TargetLastVelocity=self:IsTargetDetected(TargetUnit:GetDCSObject())
-return TargetIsDetected
-end
-function UNIT:IsLOS(TargetUnit)
-local IsLOS=self:GetPointVec3():IsLOS(TargetUnit:GetPointVec3())
-return IsLOS
-end
-function UNIT:KnowUnit(TargetUnit,TypeKnown,DistanceKnown)
-if TypeKnown~=false then
-TypeKnown=true
-end
-if DistanceKnown~=false then
-DistanceKnown=true
-end
-local DCSControllable=self:GetDCSObject()
-if DCSControllable then
-local Controller=DCSControllable:getController()
-if Controller then
-local object=TargetUnit:GetDCSObject()
-if object then
-self:I(string.format("Unit %s now knows target unit %s. Type known=%s, distance known=%s",self:GetName(),TargetUnit:GetName(),tostring(TypeKnown),tostring(DistanceKnown)))
-Controller:knowTarget(object,TypeKnown,DistanceKnown)
-end
-end
-end
-end
-end
-function UNIT:GetTemplate()
-local group=self:GetGroup()
-local name=self:GetName()
-if group then
-local template=group:GetTemplate()
-if template then
-for _,unit in pairs(template.units)do
-if unit.name==name then
-return UTILS.DeepCopy(unit)
-end
-end
-end
-end
-return nil
-end
-function UNIT:GetTemplatePayload()
-local unit=self:GetTemplate()
-if unit then
-return unit.payload
-end
-return nil
-end
-function UNIT:GetTemplatePylons()
-local payload=self:GetTemplatePayload()
-if payload then
-return payload.pylons
-end
-return nil
-end
-function UNIT:GetTemplateFuel()
-local payload=self:GetTemplatePayload()
-if payload then
-return payload.fuel
-end
-return nil
-end
-function UNIT:EnableEmission(switch)
-self:F2(self.UnitName)
-local switch=switch or false
-local DCSUnit=self:GetDCSObject()
-if DCSUnit then
-DCSUnit:enableEmission(switch)
-end
-return self
-end
-function UNIT:GetSkill()
-self:F2(self.UnitName)
-local name=self.UnitName
-local skill=_DATABASE.Templates.Units[name].Template.skill or"Random"
-return skill
-end
-function UNIT:GetSTN()
-self:F2(self.UnitName)
-local STN=nil
-local VCL=nil
-local VCN=nil
-local FGL=false
-local template=self:GetTemplate()
-if template.AddPropAircraft then
-if template.AddPropAircraft.STN_L16 then
-STN=template.AddPropAircraft.STN_L16
-elseif template.AddPropAircraft.SADL_TN then
-STN=template.AddPropAircraft.SADL_TN
-end
-VCN=template.AddPropAircraft.VoiceCallsignNumber
-VCL=template.AddPropAircraft.VoiceCallsignLabel
-end
-if template.datalinks and template.datalinks.Link16 and template.datalinks.Link16.settings then
-FGL=template.datalinks.Link16.settings.flightLead
-end
-if template.datalinks and template.datalinks.SADL and template.datalinks.SADL.settings then
-FGL=template.datalinks.SADL.settings.flightLead
-end
-return STN,VCL,VCN,FGL
-end
-WEAPON={
-ClassName="WEAPON",
-verbose=0,
-}
-WEAPON.version="0.1.0"
-function WEAPON:New(WeaponObject)
-if WeaponObject==nil then
-env.error("ERROR: Weapon object does NOT exist")
-return nil
-end
-local self=BASE:Inherit(self,POSITIONABLE:New("Weapon"))
-self.weapon=WeaponObject
-self.desc=WeaponObject:getDesc()
-self.category=self.desc.category
-if self:IsMissile()and self.desc.missileCategory then
-self.categoryMissile=self.desc.missileCategory
-end
-self.typeName=WeaponObject:getTypeName()or"Unknown Type"
-self.name=WeaponObject:getName()
-self.coalition=WeaponObject:getCoalition()
-self.country=WeaponObject:getCountry()
-self.launcher=WeaponObject:getLauncher()
-self.launcherName="Unknown Launcher"
-if self.launcher then
-self.launcherName=self.launcher:getName()
-self.launcherUnit=UNIT:Find(self.launcher)
-end
-self.coordinate=COORDINATE:NewFromVec3(self.launcher:getPoint())
-self.lid=string.format("[%s] %s | ",self.typeName,self.name)
-if self.launcherUnit then
-self.releaseHeading=self.launcherUnit:GetHeading()
-self.releaseAltitudeASL=self.launcherUnit:GetAltitude()
-self.releaseAltitudeAGL=self.launcherUnit:GetAltitude(true)
-self.releaseCoordinate=self.launcherUnit:GetCoordinate()
-self.releasePitch=self.launcherUnit:GetPitch()
-end
-self:SetTimeStepTrack()
-self:SetDistanceInterceptPoint()
-local text=string.format("Weapon v%s\nName=%s, TypeName=%s, Category=%s, Coalition=%d, Country=%d, Launcher=%s",
-self.version,self.name,self.typeName,self.category,self.coalition,self.country,self.launcherName)
-self:T(self.lid..text)
-self:T2(self.desc)
-return self
-end
-function WEAPON:SetVerbosity(VerbosityLevel)
-self.verbose=VerbosityLevel or 0
-return self
-end
-function WEAPON:SetTimeStepTrack(TimeStep)
-self.dtTrack=TimeStep or 0.01
-return self
-end
-function WEAPON:SetDistanceInterceptPoint(Distance)
-self.distIP=Distance or 50
-return self
-end
-function WEAPON:SetMarkImpact(Switch)
-if Switch==false then
-self.impactMark=false
-else
-self.impactMark=true
-end
-return self
-end
-function WEAPON:SetSmokeImpact(Switch,SmokeColor)
-if Switch==false then
-self.impactSmoke=false
-else
-self.impactSmoke=true
-end
-self.impactSmokeColor=SmokeColor or SMOKECOLOR.Red
-return self
-end
-function WEAPON:SetFuncTrack(FuncTrack,...)
-self.trackFunc=FuncTrack
-self.trackArg=arg or{}
-return self
-end
-function WEAPON:SetFuncImpact(FuncImpact,...)
-self.impactFunc=FuncImpact
-self.impactArg=arg or{}
-return self
-end
-function WEAPON:GetLauncher()
-return self.launcherUnit
-end
-function WEAPON:GetTarget()
-local target=nil
-if self.weapon then
-local object=self.weapon:getTarget()
-if object then
-local category=Object.getCategory(object)
-local name=object:getName()
-self:T(self.lid..string.format("Got Target Object %s, category=%d",object:getName(),category))
-if category==Object.Category.UNIT then
-target=UNIT:FindByName(name)
-elseif category==Object.Category.STATIC then
-target=STATIC:FindByName(name,false)
-elseif category==Object.Category.SCENERY then
-self:E(self.lid..string.format("ERROR: Scenery target not implemented yet!"))
-else
-self:E(self.lid..string.format("ERROR: Object category=%d is not implemented yet!",category))
-end
-end
-end
-return target
-end
-function WEAPON:GetTargetDistance(ConversionFunction)
-local target=self:GetTarget()
-local distance=nil
-if target then
-local tv3=target:GetVec3()
-local wv3=self:GetVec3()
-if tv3 and wv3 then
-distance=UTILS.VecDist3D(tv3,wv3)
-if ConversionFunction then
-distance=ConversionFunction(distance)
-end
-end
-end
-return distance
-end
-function WEAPON:GetTargetName()
-local target=self:GetTarget()
-local name="None"
-if target then
-name=target:GetName()
-end
-return name
-end
-function WEAPON:GetVelocityVec3()
-local Vvec3=nil
-if self.weapon then
-Vvec3=self.weapon:getVelocity()
-end
-return Vvec3
-end
-function WEAPON:GetSpeed(ConversionFunction)
-local speed=nil
-if self.weapon then
-local v=self:GetVelocityVec3()
-speed=UTILS.VecNorm(v)
-if ConversionFunction then
-speed=ConversionFunction(speed)
-end
-end
-return speed
-end
-function WEAPON:GetVec3()
-local vec3=nil
-if self.weapon then
-vec3=self.weapon:getPoint()
-end
-return vec3
-end
-function WEAPON:GetVec2()
-local vec3=self:GetVec3()
-if vec3 then
-local vec2={x=vec3.x,y=vec3.z}
-return vec2
-end
-return nil
-end
-function WEAPON:GetTypeName()
-return self.typeName
-end
-function WEAPON:GetCoalition()
-return self.coalition
-end
-function WEAPON:GetCountry()
-return self.country
-end
-function WEAPON:GetDCSObject()
-return self.weapon
-end
-function WEAPON:GetImpactVec3()
-return self.impactVec3
-end
-function WEAPON:GetImpactCoordinate()
-return self.impactCoord
-end
-function WEAPON:GetReleaseHeading(AccountForMagneticInclination)
-AccountForMagneticInclination=AccountForMagneticInclination or true
-if AccountForMagneticInclination then return UTILS.ClampAngle(self.releaseHeading-UTILS.GetMagneticDeclination())else return UTILS.ClampAngle(self.releaseHeading)end
-end
-function WEAPON:GetReleaseAltitudeASL()
-return self.releaseAltitudeASL
-end
-function WEAPON:GetReleaseAltitudeAGL()
-return self.releaseAltitudeAGL
-end
-function WEAPON:GetReleaseCoordinate()
-return self.releaseCoordinate
-end
-function WEAPON:GetReleasePitch()
-return self.releasePitch
-end
-function WEAPON:GetImpactHeading(AccountForMagneticInclination)
-AccountForMagneticInclination=AccountForMagneticInclination or true
-if AccountForMagneticInclination then return UTILS.ClampAngle(self.impactHeading-UTILS.GetMagneticDeclination())else return self.impactHeading end
-end
-function WEAPON:InAir()
-local inAir=nil
-if self.weapon then
-inAir=self.weapon:inAir()
-end
-return inAir
-end
-function WEAPON:IsExist()
-local isExist=nil
-if self.weapon then
-isExist=self.weapon:isExist()
-end
-return isExist
-end
-function WEAPON:IsBomb()
-return self.category==Weapon.Category.BOMB
-end
-function WEAPON:IsMissile()
-return self.category==Weapon.Category.MISSILE
-end
-function WEAPON:IsRocket()
-return self.category==Weapon.Category.ROCKET
-end
-function WEAPON:IsShell()
-return self.category==Weapon.Category.SHELL
-end
-function WEAPON:IsTorpedo()
-return self.category==Weapon.Category.TORPEDO
-end
-function WEAPON:Destroy(Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,WEAPON.Destroy,self,0)
-else
-if self.weapon then
-self:T(self.lid.."Destroying Weapon NOW!")
-self:StopTrack()
-self.weapon:destroy()
-end
-end
-return self
-end
-function WEAPON:StartTrack(Delay)
-Delay=math.max(Delay or 0.001,0.001)
-self:T(self.lid..string.format("Start tracking weapon in %.4f sec",Delay))
-self.trackScheduleID=timer.scheduleFunction(WEAPON._TrackWeapon,self,timer.getTime()+Delay)
-return self
-end
-function WEAPON:StopTrack(Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,WEAPON.StopTrack,self,0)
-else
-if self.trackScheduleID then
-timer.removeFunction(self.trackScheduleID)
-end
-end
-return self
-end
-function WEAPON:_TrackWeapon(time)
-if self.verbose>=20 then
-self:I(self.lid..string.format("Tracking at T=%.5f",time))
-end
-local status,pos3=pcall(
-function()
-local point=self.weapon:getPosition()
-return point
-end
-)
-if status then
-self.pos3=pos3
-self.vec3=UTILS.DeepCopy(self.pos3.p)
-self.coordinate:UpdateFromVec3(self.vec3)
-self.last_velocity=self.weapon:getVelocity()
-self.tracking=true
-if self.trackFunc then
-self.trackFunc(self,unpack(self.trackArg))
-end
-if self.verbose>=5 then
-local vec2={x=self.vec3.x,y=self.vec3.z}
-local height=land.getHeight(vec2)
-local agl=self.vec3.y-height
-local ip=self:_GetIP(self.distIP)
-local d=0
-if ip then
-d=UTILS.VecDist3D(self.vec3,ip)
-end
-self:I(self.lid..string.format("T=%.3f: Height=%.3f m AGL=%.3f m, dIP=%.3f",time,height,agl,d))
-end
-else
-local ip=self:_GetIP(self.distIP)
-if self.verbose>=10 and ip then
-self:I(self.lid.."Got intercept point!")
-local coord=COORDINATE:NewFromVec3(ip)
-coord:MarkToAll("Intercept point")
-coord:SmokeBlue()
-local d=UTILS.VecDist3D(ip,self.vec3)
-self:I(self.lid..string.format("FF d(ip, vec3)=%.3f meters",d))
-end
-self.impactVec3=ip or self.vec3
-self.impactCoord=COORDINATE:NewFromVec3(self.vec3)
-self.impactHeading=UTILS.VecHdg(self.last_velocity)
-if self.impactMark then
-self.impactCoord:MarkToAll(string.format("Impact point of weapon %s\ntype=%s\nlauncher=%s",self.name,self.typeName,self.launcherName))
-end
-if self.impactSmoke then
-self.impactCoord:Smoke(self.impactSmokeColor)
-end
-if self.impactFunc then
-self.impactFunc(self,unpack(self.impactArg or{}))
-end
-self.tracking=false
-end
-if self.tracking then
-if self.dtTrack and self.dtTrack>=0.00001 then
-return time+self.dtTrack
-else
-return nil
-end
-end
-return nil
-end
-function WEAPON:_GetIP(Distance)
-Distance=Distance or 50
-local ip=nil
-if Distance>0 and self.pos3 then
-ip=land.getIP(self.pos3.p,self.pos3.x,Distance or 20)
-end
-return ip
-end
-do
-NET={
-ClassName="NET",
-Version="0.1.3",
-BlockTime=600,
-BlockedPilots={},
-BlockedUCIDs={},
-BlockedSides={},
-BlockedSlots={},
-KnownPilots={},
-BlockMessage=nil,
-UnblockMessage=nil,
-lid=nil,
-}
-function NET:New()
-local self=BASE:Inherit(self,FSM:New())
-self.BlockTime=600
-self.BlockedPilots={}
-self.KnownPilots={}
-self:SetBlockMessage()
-self:SetUnblockMessage()
-self:SetStartState("Stopped")
-self:AddTransition("Stopped","Run","Running")
-self:AddTransition("*","PlayerJoined","*")
-self:AddTransition("*","PlayerLeft","*")
-self:AddTransition("*","PlayerDied","*")
-self:AddTransition("*","PlayerEjected","*")
-self:AddTransition("*","PlayerBlocked","*")
-self:AddTransition("*","PlayerUnblocked","*")
-self:AddTransition("*","Status","*")
-self:AddTransition("*","Stop","Stopped")
-self.lid=string.format("NET %s | ",self.Version)
-self:Run()
-return self
-end
-function NET:IsAnyBlocked(UCID,Name,PlayerID,PlayerSide,PlayerSlot)
-local blocked=false
-local TNow=timer.getTime()
-if UCID and self.BlockedUCIDs[UCID]and TNow3)then
-self:__PlayerJoined(1,client,name)
-self.KnownPilots[name]={
-name=name,
-ucid=ucid,
-id=PlayerID,
-side=PlayerSide,
-slot=PlayerSlot,
-timestamp=TNow,
-}
-end
-return self
-end
-end
-if data.id==EVENTS.PlayerLeaveUnit and self.KnownPilots[name]then
-self:T(self.lid.."Pilot Leaving: "..name.." | UCID: "..ucid)
-self:__PlayerLeft(1,data.IniUnit,name)
-self.KnownPilots[name]=false
-return self
-end
-if data.id==EVENTS.Ejection and self.KnownPilots[name]then
-self:T(self.lid.."Pilot Ejecting: "..name.." | UCID: "..ucid)
-self:__PlayerEjected(1,data.IniUnit,name)
-self.KnownPilots[name]=false
-return self
-end
-if(data.id==EVENTS.PilotDead or data.id==EVENTS.SelfKillPilot or data.id==EVENTS.Crash)and self.KnownPilots[name]then
-self:T(self.lid.."Pilot Dead: "..name.." | UCID: "..ucid)
-self:__PlayerDied(1,data.IniUnit,name)
-self.KnownPilots[name]=false
-return self
-end
-end
-return self
-end
-function NET:BlockPlayer(Client,PlayerName,Seconds,Message)
-self:T({PlayerName,Seconds,Message})
-local name=PlayerName
-if Client and(not PlayerName)then
-name=Client:GetPlayerName()
-elseif PlayerName then
-name=PlayerName
-else
-self:F(self.lid.."Block: No Client or PlayerName given or nothing found!")
-return self
-end
-local ucid=self:GetPlayerUCID(Client,name)
-local addon=Seconds or self.BlockTime
-self.BlockedPilots[name]=timer.getTime()+addon
-self.BlockedUCIDs[ucid]=timer.getTime()+addon
-local message=Message or self.BlockMessage
-if name then
-self:SendChatToPlayer(message,name)
-else
-self:SendChat(name..": "..message)
-end
-self:__PlayerBlocked(1,Client,name,Seconds)
-local PlayerID=self:GetPlayerIDByName(name)
-if PlayerID and tonumber(PlayerID)~=1 then
-local outcome=net.force_player_slot(tonumber(PlayerID),0,'')
-end
-return self
-end
-function NET:BlockPlayerSet(PlayerSet,Seconds,Message)
-self:T({PlayerSet.Set,Seconds,Message})
-local addon=Seconds or self.BlockTime
-local message=Message or self.BlockMessage
-for _,_client in pairs(PlayerSet.Set)do
-local name=_client:GetPlayerName()
-self:BlockPlayer(_client,name,addon,message)
-end
-return self
-end
-function NET:UnblockPlayerSet(PlayerSet,Message)
-self:T({PlayerSet.Set,Seconds,Message})
-local message=Message or self.UnblockMessage
-for _,_client in pairs(PlayerSet.Set)do
-local name=_client:GetPlayerName()
-self:UnblockPlayer(_client,name,message)
-end
-return self
-end
-function NET:BlockUCID(ucid,Seconds)
-self:T({ucid,Seconds})
-local addon=Seconds or self.BlockTime
-self.BlockedUCIDs[ucid]=timer.getTime()+addon
-return self
-end
-function NET:UnblockUCID(ucid)
-self:T({ucid})
-self.BlockedUCIDs[ucid]=nil
-return self
-end
-function NET:BlockSide(Side,Seconds)
-self:T({Side,Seconds})
-local addon=Seconds or self.BlockTime
-if Side==1 or Side==2 then
-self.BlockedSides[Side]=timer.getTime()+addon
-end
-return self
-end
-function NET:UnblockSide(Side,Seconds)
-self:T({Side,Seconds})
-local addon=Seconds or self.BlockTime
-if Side==1 or Side==2 then
-self.BlockedSides[Side]=nil
-end
-return self
-end
-function NET:BlockSlot(Slot,Seconds)
-self:T({Slot,Seconds})
-local addon=Seconds or self.BlockTime
-self.BlockedSlots[Slot]=timer.getTime()+addon
-return self
-end
-function NET:UnblockSlot(Slot)
-self:T({Slot})
-self.BlockedSlots[Slot]=nil
-return self
-end
-function NET:UnblockPlayer(Client,PlayerName,Message)
-local name=PlayerName
-if Client then
-name=Client:GetPlayerName()
-elseif PlayerName then
-name=PlayerName
-else
-self:F(self.lid.."Unblock: No PlayerName given or not found!")
-return self
-end
-local ucid=self:GetPlayerUCID(Client,name)
-self.BlockedPilots[name]=nil
-self.BlockedUCIDs[ucid]=nil
-local message=Message or self.UnblockMessage
-if name then
-self:SendChatToPlayer(message,name)
-else
-self:SendChat(name..": "..message)
-end
-self:__PlayerUnblocked(1,Client,name)
-return self
-end
-function NET:SetBlockMessage(Text)
-self.BlockMessage=Text or"You are blocked from joining. Wait time is: "..self.BlockTime.." seconds!"
-return self
-end
-function NET:SetBlockTime(Seconds)
-self.BlockTime=Seconds or 600
-return self
-end
-function NET:SetUnblockMessage(Text)
-self.UnblockMessage=Text or"You are unblocked now and can join again."
-return self
-end
-function NET:SendChat(Message,ToAll)
-if Message then
-net.send_chat(Message,ToAll)
-end
-return self
-end
-function NET:GetPlayerIDByName(Name)
-if not Name then return nil end
-local playerList=net.get_player_list()
-for i=1,#playerList do
-local playerName=net.get_name(i)
-if playerName==Name then
-return playerList[i]
-end
-end
-return nil
-end
-function NET:GetPlayerIDFromClient(Client)
-if Client then
-local name=Client:GetPlayerName()
-local id=self:GetPlayerIDByName(name)
-return id
-else
-return nil
-end
-end
-function NET:SendChatToClient(Message,ToClient,FromClient)
-local PlayerId=self:GetPlayerIDFromClient(ToClient)
-local FromId=self:GetPlayerIDFromClient(FromClient)
-if Message and PlayerId and FromId then
-net.send_chat_to(Message,tonumber(PlayerId),tonumber(FromId))
-elseif Message and PlayerId then
-net.send_chat_to(Message,tonumber(PlayerId))
-end
-return self
-end
-function NET:SendChatToPlayer(Message,ToPlayer,FromPlayer)
-local PlayerId=self:GetPlayerIDByName(ToPlayer)
-local FromId=self:GetPlayerIDByName(FromPlayer)
-if Message and PlayerId and FromId then
-net.send_chat_to(Message,tonumber(PlayerId),tonumber(FromId))
-elseif Message and PlayerId then
-net.send_chat_to(Message,tonumber(PlayerId))
-end
-return self
-end
-function NET:LoadMission(Path)
-local outcome=false
-if Path then
-outcome=net.load_mission(Path)
-end
-return outcome
-end
-function NET:LoadNextMission()
-local outcome=false
-outcome=net.load_next_mission()
-return outcome
-end
-function NET:GetPlayerList()
-local plist=nil
-plist=net.get_player_list()
-return plist
-end
-function NET:GetMyPlayerID()
-return net.get_my_player_id()
-end
-function NET:GetServerID()
-return net.get_server_id()
-end
-function NET:GetPlayerInfo(Client,Attribute)
-local PlayerID=self:GetPlayerIDFromClient(Client)
-if PlayerID then
-return net.get_player_info(tonumber(PlayerID),Attribute)
-else
-return nil
-end
-end
-function NET:GetPlayerUCID(Client,Name)
-local PlayerID=nil
-if Client then
-PlayerID=self:GetPlayerIDFromClient(Client)
-elseif Name then
-PlayerID=self:GetPlayerIDByName(Name)
-else
-self:E(self.lid.."Neither client nor name provided!")
-end
-local ucid=net.get_player_info(tonumber(PlayerID),'ucid')
-return ucid
-end
-function NET:Kick(Client,Message)
-local PlayerID=self:GetPlayerIDFromClient(Client)
-if PlayerID and tonumber(PlayerID)~=1 then
-return net.kick(tonumber(PlayerID),Message)
-else
-return false
-end
-end
-function NET:GetPlayerStatistic(Client,StatisticID)
-local PlayerID=self:GetPlayerIDFromClient(Client)
-local stats=StatisticID or 0
-if stats>7 or stats<0 then stats=0 end
-if PlayerID then
-return net.get_stat(tonumber(PlayerID),stats)
-else
-return nil
-end
-end
-function NET:GetName(Client)
-local PlayerID=self:GetPlayerIDFromClient(Client)
-if PlayerID then
-return net.get_name(tonumber(PlayerID))
-else
-return nil
-end
-end
-function NET:GetSlot(Client)
-local PlayerID=self:GetPlayerIDFromClient(Client)
-if PlayerID then
-local side,slot=net.get_slot(tonumber(PlayerID))
-return side,slot
-else
-return nil,nil
-end
-end
-function NET:ForceSlot(Client,SideID,SlotID)
-local PlayerID=self:GetPlayerIDFromClient(Client)
-if PlayerID and tonumber(PlayerID)~=1 then
-return net.force_player_slot(tonumber(PlayerID),SideID,SlotID or'')
-else
-return false
-end
-end
-function NET:ReturnToSpectators(Client)
-local outcome=self:ForceSlot(Client,0)
-return outcome
-end
-function NET.Lua2Json(Lua)
-return net.lua2json(Lua)
-end
-function NET.Lua2Json(Json)
-return net.json2lua(Json)
-end
-function NET:DoStringIn(State,DoString)
-return net.dostring_in(State,DoString)
-end
-function NET:Log(Message)
-net.log(Message)
-return self
-end
-function NET:GetKnownPilotData(Client,Name)
-local name=Name
-if Client and not Name then
-name=Client:GetPlayerName()
-end
-if name then
-return self.KnownPilots[name]
-else
-return nil
-end
-end
-function NET:onafterStatus(From,Event,To)
-self:T({From,Event,To})
-local function HouseHold(tavolo)
-local TNow=timer.getTime()
-for _,entry in pairs(tavolo)do
-if entry>=TNow then entry=nil end
-end
-end
-HouseHold(self.BlockedPilots)
-HouseHold(self.BlockedSides)
-HouseHold(self.BlockedSlots)
-HouseHold(self.BlockedUCIDs)
-if self:Is("Running")then
-self:__Status(-60)
-end
-return self
-end
-function NET:onafterRun(From,Event,To)
-self:T({From,Event,To})
-self:HandleEvent(EVENTS.PlayerEnterUnit,self._EventHandler)
-self:HandleEvent(EVENTS.PlayerEnterAircraft,self._EventHandler)
-self:HandleEvent(EVENTS.PlayerLeaveUnit,self._EventHandler)
-self:HandleEvent(EVENTS.PilotDead,self._EventHandler)
-self:HandleEvent(EVENTS.Ejection,self._EventHandler)
-self:HandleEvent(EVENTS.Crash,self._EventHandler)
-self:HandleEvent(EVENTS.SelfKillPilot,self._EventHandler)
-self:__Status(-10)
-end
-function NET:onafterStop(From,Event,To)
-self:T({From,Event,To})
-self:UnHandleEvent(EVENTS.PlayerEnterUnit)
-self:UnHandleEvent(EVENTS.PlayerEnterAircraft)
-self:UnHandleEvent(EVENTS.PlayerLeaveUnit)
-self:UnHandleEvent(EVENTS.PilotDead)
-self:UnHandleEvent(EVENTS.Ejection)
-self:UnHandleEvent(EVENTS.Crash)
-self:UnHandleEvent(EVENTS.SelfKillPilot)
-return self
-end
-end
-STORAGE={
-ClassName="STORAGE",
-verbose=0,
-}
-STORAGE.Liquid={
-JETFUEL=0,
-GASOLINE=1,
-MW50=2,
-DIESEL=3,
-}
-STORAGE.version="0.0.1"
-function STORAGE:New(AirbaseName)
-local self=BASE:Inherit(self,BASE:New())
-self.airbase=Airbase.getByName(AirbaseName)
-if Airbase.getWarehouse then
-self.warehouse=self.airbase:getWarehouse()
-end
-self.lid=string.format("STORAGE %s",AirbaseName)
-return self
-end
-function STORAGE:FindByName(AirbaseName)
-local storage=_DATABASE:FindStorage(AirbaseName)
-return storage
-end
-function STORAGE:SetVerbosity(VerbosityLevel)
-self.verbose=VerbosityLevel or 0
-return self
-end
-function STORAGE:AddItem(Name,Amount)
-self:T(self.lid..string.format("Adding %d items of %s",Amount,UTILS.OneLineSerialize(Name)))
-self.warehouse:addItem(Name,Amount)
-return self
-end
-function STORAGE:SetItem(Name,Amount)
-self:T(self.lid..string.format("Setting item %s to N=%d",UTILS.OneLineSerialize(Name),Amount))
-self.warehouse:setItem(Name,Amount)
-return self
-end
-function STORAGE:GetItemAmount(Name)
-local N=self.warehouse:getItemCount(Name)
-return N
-end
-function STORAGE:RemoveItem(Name,Amount)
-self:T(self.lid..string.format("Removing N=%d of item %s",Amount,Name))
-self.warehouse:removeItem(Name,Amount)
-return self
-end
-function STORAGE:AddLiquid(Type,Amount)
-self:T(self.lid..string.format("Adding %d liquids of %s",Amount,self:GetLiquidName(Type)))
-self.warehouse:addLiquid(Type,Amount)
-return self
-end
-function STORAGE:SetLiquid(Type,Amount)
-self:T(self.lid..string.format("Setting liquid %s to N=%d",self:GetLiquidName(Type),Amount))
-self.warehouse:setLiquidAmount(Type,Amount)
-return self
-end
-function STORAGE:RemoveLiquid(Type,Amount)
-self:T(self.lid..string.format("Removing N=%d of liquid %s",Amount,self:GetLiquidName(Type)))
-self.warehouse:removeLiquid(Type,Amount)
-return self
-end
-function STORAGE:GetLiquidAmount(Type)
-local N=self.warehouse:getLiquidAmount(Type)
-return N
-end
-function STORAGE:GetLiquidName(Type)
-local name="Unknown"
-if Type==STORAGE.Liquid.JETFUEL then
-name="Jet fuel"
-elseif Type==STORAGE.Liquid.GASOLINE then
-name="Aircraft gasoline"
-elseif Type==STORAGE.Liquid.MW50 then
-name="MW 50"
-elseif Type==STORAGE.Liquid.DIESEL then
-name="Diesel"
-else
-self:E(self.lid..string.format("ERROR: Unknown liquid type %s",tostring(Type)))
-end
-return name
-end
-function STORAGE:AddAmount(Type,Amount)
-if type(Type)=="number"then
-self:AddLiquid(Type,Amount)
-else
-self:AddItem(Type,Amount)
-end
-return self
-end
-function STORAGE:RemoveAmount(Type,Amount)
-if type(Type)=="number"then
-self:RemoveLiquid(Type,Amount)
-else
-self:RemoveItem(Type,Amount)
-end
-return self
-end
-function STORAGE:SetAmount(Type,Amount)
-if type(Type)=="number"then
-self:SetLiquid(Type,Amount)
-else
-self:SetItem(Type,Amount)
-end
-return self
-end
-function STORAGE:GetAmount(Type)
-local N=0
-if type(Type)=="number"then
-N=self:GetLiquidAmount(Type)
-else
-N=self:GetItemAmount(Type)
-end
-return N
-end
-function STORAGE:IsUnlimited(Type)
-local N=self:GetAmount(Type)
-local unlimited=false
-if N>0 then
-self:RemoveAmount(Type,1)
-local n=self:GetAmount(Type)
-unlimited=n==N
-if not unlimited then
-self:AddAmount(Type,1)
-end
-self:I(self.lid..string.format("Type=%s: unlimited=%s (N=%d n=%d)",tostring(Type),tostring(unlimited),N,n))
-end
-return unlimited
-end
-function STORAGE:IsLimited(Type)
-local limited=not self:IsUnlimited(Type)
-return limited
-end
-function STORAGE:IsUnlimitedAircraft()
-local unlimited=self:IsUnlimited("A-10C")
-return unlimited
-end
-function STORAGE:IsUnlimitedLiquids()
-local unlimited=self:IsUnlimited(STORAGE.Liquid.DIESEL)
-return unlimited
-end
-function STORAGE:IsUnlimitedWeapons()
-local unlimited=self:IsUnlimited(ENUMS.Storage.weapons.bombs.Mk_82)
-return unlimited
-end
-function STORAGE:IsLimitedAircraft()
-local limited=self:IsLimited("A-10C")
-return limited
-end
-function STORAGE:IsLimitedLiquids()
-local limited=self:IsLimited(STORAGE.Liquid.DIESEL)
-return limited
-end
-function STORAGE:IsLimitedWeapons()
-local limited=self:IsLimited(ENUMS.Storage.weapons.bombs.Mk_82)
-return limited
-end
-function STORAGE:GetInventory(Item)
-local inventory=self.warehouse:getInventory(Item)
-return inventory.aircraft,inventory.liquids,inventory.weapon
-end
-CARGOS={}
-do
-CARGO={
-ClassName="CARGO",
-Type=nil,
-Name=nil,
-Weight=nil,
-CargoObject=nil,
-CargoCarrier=nil,
-Representable=false,
-Slingloadable=false,
-Moveable=false,
-Containable=false,
-Reported={},
-}
-function CARGO:New(Type,Name,Weight,LoadRadius,NearRadius)
-local self=BASE:Inherit(self,FSM:New())
-self:F({Type,Name,Weight,LoadRadius,NearRadius})
-self:SetStartState("UnLoaded")
-self:AddTransition({"UnLoaded","Boarding"},"Board","Boarding")
-self:AddTransition("Boarding","Boarding","Boarding")
-self:AddTransition("Boarding","CancelBoarding","UnLoaded")
-self:AddTransition("Boarding","Load","Loaded")
-self:AddTransition("UnLoaded","Load","Loaded")
-self:AddTransition("Loaded","UnBoard","UnBoarding")
-self:AddTransition("UnBoarding","UnBoarding","UnBoarding")
-self:AddTransition("UnBoarding","UnLoad","UnLoaded")
-self:AddTransition("Loaded","UnLoad","UnLoaded")
-self:AddTransition("*","Damaged","Damaged")
-self:AddTransition("*","Destroyed","Destroyed")
-self:AddTransition("*","Respawn","UnLoaded")
-self:AddTransition("*","Reset","UnLoaded")
-self.Type=Type
-self.Name=Name
-self.Weight=Weight or 0
-self.CargoObject=nil
-self.CargoCarrier=nil
-self.Representable=false
-self.Slingloadable=false
-self.Moveable=false
-self.Containable=false
-self.CargoLimit=0
-self.LoadRadius=LoadRadius or 500
-self:SetDeployed(false)
-self.CargoScheduler=SCHEDULER:New()
-CARGOS[self.Name]=self
-return self
-end
-function CARGO:FindByName(CargoName)
-local CargoFound=_DATABASE:FindCargo(CargoName)
-return CargoFound
-end
-function CARGO:GetX()
-if self:IsLoaded()then
-return self.CargoCarrier:GetCoordinate().x
-else
-return self.CargoObject:GetCoordinate().x
-end
-end
-function CARGO:GetY()
-if self:IsLoaded()then
-return self.CargoCarrier:GetCoordinate().z
-else
-return self.CargoObject:GetCoordinate().z
-end
-end
-function CARGO:GetHeading()
-if self:IsLoaded()then
-return self.CargoCarrier:GetHeading()
-else
-return self.CargoObject:GetHeading()
-end
-end
-function CARGO:CanSlingload()
-return false
-end
-function CARGO:CanBoard()
-return true
-end
-function CARGO:CanUnboard()
-return true
-end
-function CARGO:CanLoad()
-return true
-end
-function CARGO:CanUnload()
-return true
-end
-function CARGO:Destroy()
-if self.CargoObject then
-self.CargoObject:Destroy()
-end
-self:Destroyed()
-end
-function CARGO:GetName()
-return self.Name
-end
-function CARGO:GetObject()
-if self:IsLoaded()then
-return self.CargoCarrier
-else
-return self.CargoObject
-end
-end
-function CARGO:GetObjectName()
-if self:IsLoaded()then
-return self.CargoCarrier:GetName()
-else
-return self.CargoObject:GetName()
-end
-end
-function CARGO:GetCount()
-return 1
-end
-function CARGO:GetType()
-return self.Type
-end
-function CARGO:GetTransportationMethod()
-return self.TransportationMethod
-end
-function CARGO:GetCoalition()
-if self:IsLoaded()then
-return self.CargoCarrier:GetCoalition()
-else
-return self.CargoObject:GetCoalition()
-end
-end
-function CARGO:GetCoordinate()
-return self.CargoObject:GetCoordinate()
-end
-function CARGO:IsDestroyed()
-return self:Is("Destroyed")
-end
-function CARGO:IsLoaded()
-return self:Is("Loaded")
-end
-function CARGO:IsLoadedInCarrier(Carrier)
-return self.CargoCarrier and self.CargoCarrier:GetName()==Carrier:GetName()
-end
-function CARGO:IsUnLoaded()
-return self:Is("UnLoaded")
-end
-function CARGO:IsBoarding()
-return self:Is("Boarding")
-end
-function CARGO:IsUnboarding()
-return self:Is("UnBoarding")
-end
-function CARGO:IsAlive()
-if self:IsLoaded()then
-return self.CargoCarrier:IsAlive()
-else
-return self.CargoObject:IsAlive()
-end
-end
-function CARGO:SetDeployed(Deployed)
-self.Deployed=Deployed
-end
-function CARGO:IsDeployed()
-return self.Deployed
-end
-function CARGO:Spawn(PointVec2)
-self:F()
-end
-function CARGO:Flare(FlareColor)
-if self:IsUnLoaded()then
-trigger.action.signalFlare(self.CargoObject:GetVec3(),FlareColor,0)
-end
-end
-function CARGO:FlareWhite()
-self:Flare(trigger.flareColor.White)
-end
-function CARGO:FlareYellow()
-self:Flare(trigger.flareColor.Yellow)
-end
-function CARGO:FlareGreen()
-self:Flare(trigger.flareColor.Green)
-end
-function CARGO:FlareRed()
-self:Flare(trigger.flareColor.Red)
-end
-function CARGO:Smoke(SmokeColor,Radius)
-if self:IsUnLoaded()then
-if Radius then
-trigger.action.smoke(self.CargoObject:GetRandomVec3(Radius),SmokeColor)
-else
-trigger.action.smoke(self.CargoObject:GetVec3(),SmokeColor)
-end
-end
-end
-function CARGO:SmokeGreen()
-self:Smoke(trigger.smokeColor.Green,Range)
-end
-function CARGO:SmokeRed()
-self:Smoke(trigger.smokeColor.Red,Range)
-end
-function CARGO:SmokeWhite()
-self:Smoke(trigger.smokeColor.White,Range)
-end
-function CARGO:SmokeOrange()
-self:Smoke(trigger.smokeColor.Orange,Range)
-end
-function CARGO:SmokeBlue()
-self:Smoke(trigger.smokeColor.Blue,Range)
-end
-function CARGO:SetLoadRadius(LoadRadius)
-self.LoadRadius=LoadRadius or 150
-end
-function CARGO:GetLoadRadius()
-return self.LoadRadius
-end
-function CARGO:IsInLoadRadius(Coordinate)
-self:F({Coordinate,LoadRadius=self.LoadRadius})
-local Distance=0
-if self:IsUnLoaded()then
-local CargoCoordinate=self.CargoObject:GetCoordinate()
-Distance=Coordinate:Get2DDistance(CargoCoordinate)
-self:T(Distance)
-if Distance<=self.LoadRadius then
-return true
-end
-end
-return false
-end
-function CARGO:IsInReportRadius(Coordinate)
-self:F({Coordinate})
-local Distance=0
-if self:IsUnLoaded()then
-Distance=Coordinate:Get2DDistance(self.CargoObject:GetCoordinate())
-self:T(Distance)
-if Distance<=self.LoadRadius then
-return true
-end
-end
-return false
-end
-function CARGO:IsNear(Coordinate,NearRadius)
-if self.CargoObject:IsAlive()then
-local Distance=Coordinate:Get2DDistance(self.CargoObject:GetCoordinate())
-if Distance<=NearRadius then
-return true
-end
-end
-return false
-end
-function CARGO:IsInZone(Zone)
-if self:IsLoaded()then
-return Zone:IsPointVec2InZone(self.CargoCarrier:GetPointVec2())
-else
-if self.CargoObject:GetSize()~=0 then
-return Zone:IsPointVec2InZone(self.CargoObject:GetPointVec2())
-else
-return false
-end
-end
-return nil
-end
-function CARGO:GetPointVec2()
-return self.CargoObject:GetPointVec2()
-end
-function CARGO:GetCoordinate()
-return self.CargoObject:GetCoordinate()
-end
-function CARGO:GetWeight()
-return self.Weight
-end
-function CARGO:SetWeight(Weight)
-self.Weight=Weight
-return self
-end
-function CARGO:GetVolume()
-return self.Volume
-end
-function CARGO:SetVolume(Volume)
-self.Volume=Volume
-return self
-end
-function CARGO:MessageToGroup(Message,CarrierGroup,Name)
-MESSAGE:New(Message,20,"Cargo "..self:GetName()):ToGroup(CarrierGroup)
-end
-function CARGO:Report(ReportText,Action,CarrierGroup)
-if not self.Reported[CarrierGroup]or not self.Reported[CarrierGroup][Action]then
-self.Reported[CarrierGroup]={}
-self.Reported[CarrierGroup][Action]=true
-self:MessageToGroup(ReportText,CarrierGroup)
-if self.ReportFlareColor then
-if not self.Reported[CarrierGroup]["Flaring"]then
-self:Flare(self.ReportFlareColor)
-self.Reported[CarrierGroup]["Flaring"]=true
-end
-end
-if self.ReportSmokeColor then
-if not self.Reported[CarrierGroup]["Smoking"]then
-self:Smoke(self.ReportSmokeColor)
-self.Reported[CarrierGroup]["Smoking"]=true
-end
-end
-end
-end
-function CARGO:ReportFlare(FlareColor)
-self.ReportFlareColor=FlareColor
-end
-function CARGO:ReportSmoke(SmokeColor)
-self.ReportSmokeColor=SmokeColor
-end
-function CARGO:ReportReset(Action,CarrierGroup)
-self.Reported[CarrierGroup][Action]=nil
-end
-function CARGO:ReportResetAll(CarrierGroup)
-self.Reported[CarrierGroup]=nil
-end
-function CARGO:RespawnOnDestroyed(RespawnDestroyed)
-if RespawnDestroyed then
-self.onenterDestroyed=function(self)
-self:Respawn()
-end
-else
-self.onenterDestroyed=nil
-end
-end
-end
-do
-CARGO_REPRESENTABLE={
-ClassName="CARGO_REPRESENTABLE"
-}
-function CARGO_REPRESENTABLE:New(CargoObject,Type,Name,LoadRadius,NearRadius)
-local self=BASE:Inherit(self,CARGO:New(Type,Name,0,LoadRadius,NearRadius))
-self:F({Type,Name,LoadRadius,NearRadius})
-local Desc=CargoObject:GetDesc()
-self:T({Desc=Desc})
-local Weight=math.random(80,120)
-if Desc then
-if Desc.typeName=="2B11 mortar"then
-Weight=210
-else
-Weight=Desc.massEmpty
-end
-end
-self:SetWeight(Weight)
-return self
-end
-function CARGO_REPRESENTABLE:Destroy()
-self:F({CargoName=self:GetName()})
-return self
-end
-function CARGO_REPRESENTABLE:RouteTo(ToPointVec2,Speed)
-self:F2(ToPointVec2)
-local Points={}
-local PointStartVec2=self.CargoObject:GetPointVec2()
-Points[#Points+1]=PointStartVec2:WaypointGround(Speed)
-Points[#Points+1]=ToPointVec2:WaypointGround(Speed)
-local TaskRoute=self.CargoObject:TaskRoute(Points)
-self.CargoObject:SetTask(TaskRoute,2)
-return self
-end
-function CARGO_REPRESENTABLE:MessageToGroup(Message,TaskGroup,Name)
-local CoordinateZone=ZONE_RADIUS:New("Zone",self:GetCoordinate():GetVec2(),500)
-CoordinateZone:Scan({Object.Category.UNIT})
-for _,DCSUnit in pairs(CoordinateZone:GetScannedUnits())do
-local NearUnit=UNIT:Find(DCSUnit)
-self:F({NearUnit=NearUnit})
-local NearUnitCoalition=NearUnit:GetCoalition()
-local CargoCoalition=self:GetCoalition()
-if NearUnitCoalition==CargoCoalition then
-local Attributes=NearUnit:GetDesc()
-self:F({Desc=Attributes})
-if NearUnit:HasAttribute("Trucks")then
-MESSAGE:New(Message,20,NearUnit:GetCallsign().." reporting - Cargo "..self:GetName()):ToGroup(TaskGroup)
-break
-end
-end
-end
-end
-end
-do
-CARGO_REPORTABLE={
-ClassName="CARGO_REPORTABLE"
-}
-function CARGO_REPORTABLE:New(Type,Name,Weight,LoadRadius,NearRadius)
-local self=BASE:Inherit(self,CARGO:New(Type,Name,Weight,LoadRadius,NearRadius))
-self:F({Type,Name,Weight,LoadRadius,NearRadius})
-return self
-end
-function CARGO_REPORTABLE:MessageToGroup(Message,TaskGroup,Name)
-MESSAGE:New(Message,20,"Cargo "..self:GetName().." reporting"):ToGroup(TaskGroup)
-end
-end
-do
-CARGO_PACKAGE={
-ClassName="CARGO_PACKAGE"
-}
-function CARGO_PACKAGE:New(CargoCarrier,Type,Name,Weight,LoadRadius,NearRadius)
-local self=BASE:Inherit(self,CARGO_REPRESENTABLE:New(CargoCarrier,Type,Name,Weight,LoadRadius,NearRadius))
-self:F({Type,Name,Weight,LoadRadius,NearRadius})
-self:T(CargoCarrier)
-self.CargoCarrier=CargoCarrier
-return self
-end
-function CARGO_PACKAGE:onafterOnBoard(From,Event,To,CargoCarrier,Speed,BoardDistance,LoadDistance,Angle)
-self:F()
-self.CargoInAir=self.CargoCarrier:InAir()
-self:T(self.CargoInAir)
-if not self.CargoInAir then
-local Points={}
-local StartPointVec2=self.CargoCarrier:GetPointVec2()
-local CargoCarrierHeading=CargoCarrier:GetHeading()
-local CargoDeployHeading=((CargoCarrierHeading+Angle)>=360)and(CargoCarrierHeading+Angle-360)or(CargoCarrierHeading+Angle)
-self:T({CargoCarrierHeading,CargoDeployHeading})
-local CargoDeployPointVec2=CargoCarrier:GetPointVec2():Translate(BoardDistance,CargoDeployHeading)
-Points[#Points+1]=StartPointVec2:WaypointGround(Speed)
-Points[#Points+1]=CargoDeployPointVec2:WaypointGround(Speed)
-local TaskRoute=self.CargoCarrier:TaskRoute(Points)
-self.CargoCarrier:SetTask(TaskRoute,1)
-end
-self:Boarded(CargoCarrier,Speed,BoardDistance,LoadDistance,Angle)
-end
-function CARGO_PACKAGE:IsNear(CargoCarrier)
-self:F()
-local CargoCarrierPoint=CargoCarrier:GetCoordinate()
-local Distance=CargoCarrierPoint:Get2DDistance(self.CargoCarrier:GetCoordinate())
-self:T(Distance)
-if Distance<=self.NearRadius then
-return true
-else
-return false
-end
-end
-function CARGO_PACKAGE:onafterOnBoarded(From,Event,To,CargoCarrier,Speed,BoardDistance,LoadDistance,Angle)
-self:F()
-if self:IsNear(CargoCarrier)then
-self:__Load(1,CargoCarrier,Speed,LoadDistance,Angle)
-else
-self:__Boarded(1,CargoCarrier,Speed,BoardDistance,LoadDistance,Angle)
-end
-end
-function CARGO_PACKAGE:onafterUnBoard(From,Event,To,CargoCarrier,Speed,UnLoadDistance,UnBoardDistance,Radius,Angle)
-self:F()
-self.CargoInAir=self.CargoCarrier:InAir()
-self:T(self.CargoInAir)
-if not self.CargoInAir then
-self:_Next(self.FsmP.UnLoad,UnLoadDistance,Angle)
-local Points={}
-local StartPointVec2=CargoCarrier:GetPointVec2()
-local CargoCarrierHeading=self.CargoCarrier:GetHeading()
-local CargoDeployHeading=((CargoCarrierHeading+Angle)>=360)and(CargoCarrierHeading+Angle-360)or(CargoCarrierHeading+Angle)
-self:T({CargoCarrierHeading,CargoDeployHeading})
-local CargoDeployPointVec2=StartPointVec2:Translate(UnBoardDistance,CargoDeployHeading)
-Points[#Points+1]=StartPointVec2:WaypointGround(Speed)
-Points[#Points+1]=CargoDeployPointVec2:WaypointGround(Speed)
-local TaskRoute=CargoCarrier:TaskRoute(Points)
-CargoCarrier:SetTask(TaskRoute,1)
-end
-self:__UnBoarded(1,CargoCarrier,Speed)
-end
-function CARGO_PACKAGE:onafterUnBoarded(From,Event,To,CargoCarrier,Speed)
-self:F()
-if self:IsNear(CargoCarrier)then
-self:__UnLoad(1,CargoCarrier,Speed)
-else
-self:__UnBoarded(1,CargoCarrier,Speed)
-end
-end
-function CARGO_PACKAGE:onafterLoad(From,Event,To,CargoCarrier,Speed,LoadDistance,Angle)
-self:F()
-self.CargoCarrier=CargoCarrier
-local StartPointVec2=self.CargoCarrier:GetPointVec2()
-local CargoCarrierHeading=self.CargoCarrier:GetHeading()
-local CargoDeployHeading=((CargoCarrierHeading+Angle)>=360)and(CargoCarrierHeading+Angle-360)or(CargoCarrierHeading+Angle)
-local CargoDeployPointVec2=StartPointVec2:Translate(LoadDistance,CargoDeployHeading)
-local Points={}
-Points[#Points+1]=StartPointVec2:WaypointGround(Speed)
-Points[#Points+1]=CargoDeployPointVec2:WaypointGround(Speed)
-local TaskRoute=self.CargoCarrier:TaskRoute(Points)
-self.CargoCarrier:SetTask(TaskRoute,1)
-end
-function CARGO_PACKAGE:onafterUnLoad(From,Event,To,CargoCarrier,Speed,Distance,Angle)
-self:F()
-local StartPointVec2=self.CargoCarrier:GetPointVec2()
-local CargoCarrierHeading=self.CargoCarrier:GetHeading()
-local CargoDeployHeading=((CargoCarrierHeading+Angle)>=360)and(CargoCarrierHeading+Angle-360)or(CargoCarrierHeading+Angle)
-local CargoDeployPointVec2=StartPointVec2:Translate(Distance,CargoDeployHeading)
-self.CargoCarrier=CargoCarrier
-local Points={}
-Points[#Points+1]=StartPointVec2:WaypointGround(Speed)
-Points[#Points+1]=CargoDeployPointVec2:WaypointGround(Speed)
-local TaskRoute=self.CargoCarrier:TaskRoute(Points)
-self.CargoCarrier:SetTask(TaskRoute,1)
-end
-end
-do
-CARGO_UNIT={
-ClassName="CARGO_UNIT"
-}
-function CARGO_UNIT:New(CargoUnit,Type,Name,LoadRadius,NearRadius)
-local self=BASE:Inherit(self,CARGO_REPRESENTABLE:New(CargoUnit,Type,Name,LoadRadius,NearRadius))
-self:T({Type=Type,Name=Name,LoadRadius=LoadRadius,NearRadius=NearRadius})
-self.CargoObject=CargoUnit
-self:SetEventPriority(5)
-return self
-end
-function CARGO_UNIT:onenterUnBoarding(From,Event,To,ToPointVec2,NearRadius)
-self:F({From,Event,To,ToPointVec2,NearRadius})
-local Angle=180
-local Speed=60
-local DeployDistance=9
-local RouteDistance=60
-if From=="Loaded"then
-if not self:IsDestroyed()then
-local CargoCarrier=self.CargoCarrier
-if CargoCarrier:IsAlive()then
-local CargoCarrierPointVec2=CargoCarrier:GetPointVec2()
-local CargoCarrierHeading=self.CargoCarrier:GetHeading()
-local CargoDeployHeading=((CargoCarrierHeading+Angle)>=360)and(CargoCarrierHeading+Angle-360)or(CargoCarrierHeading+Angle)
-local CargoRoutePointVec2=CargoCarrierPointVec2:Translate(RouteDistance,CargoDeployHeading)
-local FromDirectionVec3=CargoCarrierPointVec2:GetDirectionVec3(ToPointVec2 or CargoRoutePointVec2)
-local FromAngle=CargoCarrierPointVec2:GetAngleDegrees(FromDirectionVec3)
-local FromPointVec2=CargoCarrierPointVec2:Translate(DeployDistance,FromAngle)
-ToPointVec2=ToPointVec2 or CargoCarrierPointVec2:GetRandomCoordinateInRadius(NearRadius,DeployDistance)
-if self.CargoObject then
-if CargoCarrier:IsShip()then
-self.CargoObject:ReSpawnAt(ToPointVec2,CargoDeployHeading)
-else
-self.CargoObject:ReSpawnAt(FromPointVec2,CargoDeployHeading)
-end
-self:F({"CargoUnits:",self.CargoObject:GetGroup():GetName()})
-self.CargoCarrier=nil
-local Points={}
-Points[#Points+1]=FromPointVec2:WaypointGround(Speed,"Vee")
-Points[#Points+1]=ToPointVec2:WaypointGround(Speed,"Vee")
-local TaskRoute=self.CargoObject:TaskRoute(Points)
-self.CargoObject:SetTask(TaskRoute,1)
-self:__UnBoarding(1,ToPointVec2,NearRadius)
-end
-else
-self:Destroyed()
-end
-end
-end
-end
-function CARGO_UNIT:onleaveUnBoarding(From,Event,To,ToPointVec2,NearRadius)
-self:F({From,Event,To,ToPointVec2,NearRadius})
-local Angle=180
-local Speed=10
-local Distance=5
-if From=="UnBoarding"then
-return true
-end
-end
-function CARGO_UNIT:onafterUnBoarding(From,Event,To,ToPointVec2,NearRadius)
-self:F({From,Event,To,ToPointVec2,NearRadius})
-self.CargoInAir=self.CargoObject:InAir()
-self:T(self.CargoInAir)
-if not self.CargoInAir then
-end
-self:__UnLoad(1,ToPointVec2,NearRadius)
-end
-function CARGO_UNIT:onenterUnLoaded(From,Event,To,ToPointVec2)
-self:F({ToPointVec2,From,Event,To})
-local Angle=180
-local Speed=10
-local Distance=5
-if From=="Loaded"then
-local StartPointVec2=self.CargoCarrier:GetPointVec2()
-local CargoCarrierHeading=self.CargoCarrier:GetHeading()
-local CargoDeployHeading=((CargoCarrierHeading+Angle)>=360)and(CargoCarrierHeading+Angle-360)or(CargoCarrierHeading+Angle)
-local CargoDeployCoord=StartPointVec2:Translate(Distance,CargoDeployHeading)
-ToPointVec2=ToPointVec2 or COORDINATE:New(CargoDeployCoord.x,CargoDeployCoord.z)
-if self.CargoObject then
-self.CargoObject:ReSpawnAt(ToPointVec2,0)
-self.CargoCarrier=nil
-end
-end
-if self.OnUnLoadedCallBack then
-self.OnUnLoadedCallBack(self,unpack(self.OnUnLoadedParameters))
-self.OnUnLoadedCallBack=nil
-end
-end
-function CARGO_UNIT:onafterBoard(From,Event,To,CargoCarrier,NearRadius,...)
-self:F({From,Event,To,CargoCarrier,NearRadius=NearRadius})
-self.CargoInAir=self.CargoObject:InAir()
-local Desc=self.CargoObject:GetDesc()
-local MaxSpeed=Desc.speedMaxOffRoad
-local TypeName=Desc.typeName
-if not self.CargoInAir then
-local NearRadius=NearRadius or CargoCarrier:GetBoundingRadius()+5
-if self:IsNear(CargoCarrier:GetPointVec2(),NearRadius)then
-self:Load(CargoCarrier,NearRadius,...)
-else
-if MaxSpeed and MaxSpeed==0 or TypeName and TypeName=="Stinger comm"then
-self:Load(CargoCarrier,NearRadius,...)
-else
-local Speed=90
-local Angle=180
-local Distance=0
-local CargoCarrierPointVec2=CargoCarrier:GetPointVec2()
-local CargoCarrierHeading=CargoCarrier:GetHeading()
-local CargoDeployHeading=((CargoCarrierHeading+Angle)>=360)and(CargoCarrierHeading+Angle-360)or(CargoCarrierHeading+Angle)
-local CargoDeployPointVec2=CargoCarrierPointVec2:Translate(Distance,CargoDeployHeading)
-self.CargoObject:OptionAlarmStateGreen()
-local Points={}
-local PointStartVec2=self.CargoObject:GetPointVec2()
-Points[#Points+1]=PointStartVec2:WaypointGround(Speed)
-Points[#Points+1]=CargoDeployPointVec2:WaypointGround(Speed)
-local TaskRoute=self.CargoObject:TaskRoute(Points)
-self.CargoObject:SetTask(TaskRoute,2)
-self:__Boarding(-5,CargoCarrier,NearRadius,...)
-self.RunCount=0
-end
-end
-end
-end
-function CARGO_UNIT:onafterBoarding(From,Event,To,CargoCarrier,NearRadius,...)
-self:F({From,Event,To,CargoCarrier:GetName(),NearRadius=NearRadius})
-self:F({IsAlive=self.CargoObject:IsAlive()})
-if CargoCarrier and CargoCarrier:IsAlive()then
-if(CargoCarrier:IsAir()and not CargoCarrier:InAir())or true then
-local NearRadius=NearRadius or CargoCarrier:GetBoundingRadius(NearRadius)+5
-if self:IsNear(CargoCarrier:GetPointVec2(),NearRadius)then
-self:__Load(-1,CargoCarrier,...)
-else
-if self:IsNear(CargoCarrier:GetPointVec2(),20)then
-self:__Boarding(-1,CargoCarrier,NearRadius,...)
-self.RunCount=self.RunCount+1
-else
-self:__Boarding(-2,CargoCarrier,NearRadius,...)
-self.RunCount=self.RunCount+2
-end
-if self.RunCount>=40 then
-self.RunCount=0
-local Speed=90
-local Angle=180
-local Distance=0
-local CargoCarrierPointVec2=CargoCarrier:GetPointVec2()
-local CargoCarrierHeading=CargoCarrier:GetHeading()
-local CargoDeployHeading=((CargoCarrierHeading+Angle)>=360)and(CargoCarrierHeading+Angle-360)or(CargoCarrierHeading+Angle)
-local CargoDeployPointVec2=CargoCarrierPointVec2:Translate(Distance,CargoDeployHeading)
-self.CargoObject:OptionAlarmStateGreen()
-local Points={}
-local PointStartVec2=self.CargoObject:GetPointVec2()
-Points[#Points+1]=PointStartVec2:WaypointGround(Speed,"Off road")
-Points[#Points+1]=CargoDeployPointVec2:WaypointGround(Speed,"Off road")
-local TaskRoute=self.CargoObject:TaskRoute(Points)
-self.CargoObject:SetTask(TaskRoute,0.2)
-end
-end
-else
-self.CargoObject:MessageToGroup("Cancelling Boarding... Get back on the ground!",5,CargoCarrier:GetGroup(),self:GetName())
-self:CancelBoarding(CargoCarrier,NearRadius,...)
-self.CargoObject:SetCommand(self.CargoObject:CommandStopRoute(true))
-end
-else
-self:E("Something is wrong")
-end
-end
-function CARGO_UNIT:onenterLoaded(From,Event,To,CargoCarrier)
-self:F({From,Event,To,CargoCarrier})
-self.CargoCarrier=CargoCarrier
-if self.CargoObject then
-self.CargoObject:Destroy(false)
-end
-end
-function CARGO_UNIT:GetTransportationMethod()
-if self:IsLoaded()then
-return"for unboarding"
-else
-if self:IsUnLoaded()then
-return"for boarding"
-else
-if self:IsDeployed()then
-return"delivered"
-end
-end
-end
-return""
-end
-end
-do
-CARGO_SLINGLOAD={
-ClassName="CARGO_SLINGLOAD"
-}
-function CARGO_SLINGLOAD:New(CargoStatic,Type,Name,LoadRadius,NearRadius)
-local self=BASE:Inherit(self,CARGO_REPRESENTABLE:New(CargoStatic,Type,Name,nil,LoadRadius,NearRadius))
-self:F({Type,Name,NearRadius})
-self.CargoObject=CargoStatic
-_EVENTDISPATCHER:CreateEventNewCargo(self)
-self:HandleEvent(EVENTS.Dead,self.OnEventCargoDead)
-self:HandleEvent(EVENTS.Crash,self.OnEventCargoDead)
-self:HandleEvent(EVENTS.PlayerLeaveUnit,self.OnEventCargoDead)
-self:SetEventPriority(4)
-self.NearRadius=NearRadius or 25
-return self
-end
-function CARGO_SLINGLOAD:OnEventCargoDead(EventData)
-local Destroyed=false
-if self:IsDestroyed()or self:IsUnLoaded()then
-if self.CargoObject:GetName()==EventData.IniUnitName then
-if not self.NoDestroy then
-Destroyed=true
-end
-end
-end
-if Destroyed then
-self:I({"Cargo crate destroyed: "..self.CargoObject:GetName()})
-self:Destroyed()
-end
-end
-function CARGO_SLINGLOAD:CanSlingload()
-return true
-end
-function CARGO_SLINGLOAD:CanBoard()
-return false
-end
-function CARGO_SLINGLOAD:CanUnboard()
-return false
-end
-function CARGO_SLINGLOAD:CanLoad()
-return false
-end
-function CARGO_SLINGLOAD:CanUnload()
-return false
-end
-function CARGO_SLINGLOAD:IsInReportRadius(Coordinate)
-local Distance=0
-if self:IsUnLoaded()then
-Distance=Coordinate:Get2DDistance(self.CargoObject:GetCoordinate())
-if Distance<=self.LoadRadius then
-return true
-end
-end
-return false
-end
-function CARGO_SLINGLOAD:IsInLoadRadius(Coordinate)
-local Distance=0
-if self:IsUnLoaded()then
-Distance=Coordinate:Get2DDistance(self.CargoObject:GetCoordinate())
-if Distance<=self.NearRadius then
-return true
-end
-end
-return false
-end
-function CARGO_SLINGLOAD:GetCoordinate()
-return self.CargoObject:GetCoordinate()
-end
-function CARGO_SLINGLOAD:IsAlive()
-local Alive=true
-if self:IsLoaded()then
-Alive=Alive==true and self.CargoCarrier:IsAlive()
-else
-Alive=Alive==true and self.CargoObject:IsAlive()
-end
-return Alive
-end
-function CARGO_SLINGLOAD:RouteTo(Coordinate)
-end
-function CARGO_SLINGLOAD:IsNear(CargoCarrier,NearRadius)
-return self:IsNear(CargoCarrier:GetCoordinate(),NearRadius)
-end
-function CARGO_SLINGLOAD:Respawn()
-if self.CargoObject then
-self.CargoObject:ReSpawn()
-self:__Reset(-0.1)
-end
-end
-function CARGO_SLINGLOAD:onafterReset()
-if self.CargoObject then
-self:SetDeployed(false)
-self:SetStartState("UnLoaded")
-self.CargoCarrier=nil
-_EVENTDISPATCHER:CreateEventNewCargo(self)
-end
-end
-function CARGO_SLINGLOAD:GetTransportationMethod()
-if self:IsLoaded()then
-return"for sling loading"
-else
-if self:IsUnLoaded()then
-return"for sling loading"
-else
-if self:IsDeployed()then
-return"delivered"
-end
-end
-end
-return""
-end
-end
-do
-CARGO_CRATE={
-ClassName="CARGO_CRATE"
-}
-function CARGO_CRATE:New(CargoStatic,Type,Name,LoadRadius,NearRadius)
-local self=BASE:Inherit(self,CARGO_REPRESENTABLE:New(CargoStatic,Type,Name,nil,LoadRadius,NearRadius))
-self:F({Type,Name,NearRadius})
-self.CargoObject=CargoStatic
-_EVENTDISPATCHER:CreateEventNewCargo(self)
-self:HandleEvent(EVENTS.Dead,self.OnEventCargoDead)
-self:HandleEvent(EVENTS.Crash,self.OnEventCargoDead)
-self:HandleEvent(EVENTS.PlayerLeaveUnit,self.OnEventCargoDead)
-self:SetEventPriority(4)
-self.NearRadius=NearRadius or 25
-return self
-end
-function CARGO_CRATE:OnEventCargoDead(EventData)
-local Destroyed=false
-if self:IsDestroyed()or self:IsUnLoaded()or self:IsBoarding()then
-if self.CargoObject:GetName()==EventData.IniUnitName then
-if not self.NoDestroy then
-Destroyed=true
-end
-end
-else
-if self:IsLoaded()then
-local CarrierName=self.CargoCarrier:GetName()
-if CarrierName==EventData.IniDCSUnitName then
-MESSAGE:New("Cargo is lost from carrier "..CarrierName,15):ToAll()
-Destroyed=true
-self.CargoCarrier:ClearCargo()
-end
-end
-end
-if Destroyed then
-self:I({"Cargo crate destroyed: "..self.CargoObject:GetName()})
-self:Destroyed()
-end
-end
-function CARGO_CRATE:onenterUnLoaded(From,Event,To,ToPointVec2)
-local Angle=180
-local Speed=10
-local Distance=10
-if From=="Loaded"then
-local StartCoordinate=self.CargoCarrier:GetCoordinate()
-local CargoCarrierHeading=self.CargoCarrier:GetHeading()
-local CargoDeployHeading=((CargoCarrierHeading+Angle)>=360)and(CargoCarrierHeading+Angle-360)or(CargoCarrierHeading+Angle)
-local CargoDeployCoord=StartCoordinate:Translate(Distance,CargoDeployHeading)
-ToPointVec2=ToPointVec2 or COORDINATE:NewFromVec2({x=CargoDeployCoord.x,y=CargoDeployCoord.z})
-if self.CargoObject then
-self.CargoObject:ReSpawnAt(ToPointVec2,0)
-self.CargoCarrier=nil
-end
-end
-if self.OnUnLoadedCallBack then
-self.OnUnLoadedCallBack(self,unpack(self.OnUnLoadedParameters))
-self.OnUnLoadedCallBack=nil
-end
-end
-function CARGO_CRATE:onenterLoaded(From,Event,To,CargoCarrier)
-self.CargoCarrier=CargoCarrier
-if self.CargoObject then
-self:T("Destroying")
-self.NoDestroy=true
-self.CargoObject:Destroy(false)
-end
-end
-function CARGO_CRATE:CanBoard()
-return false
-end
-function CARGO_CRATE:CanUnboard()
-return false
-end
-function CARGO_CRATE:CanSlingload()
-return false
-end
-function CARGO_CRATE:IsInReportRadius(Coordinate)
-local Distance=0
-if self:IsUnLoaded()then
-Distance=Coordinate:Get2DDistance(self.CargoObject:GetCoordinate())
-if Distance<=self.LoadRadius then
-return true
-end
-end
-return false
-end
-function CARGO_CRATE:IsInLoadRadius(Coordinate)
-local Distance=0
-if self:IsUnLoaded()then
-Distance=Coordinate:Get2DDistance(self.CargoObject:GetCoordinate())
-if Distance<=self.NearRadius then
-return true
-end
-end
-return false
-end
-function CARGO_CRATE:GetCoordinate()
-return self.CargoObject:GetCoordinate()
-end
-function CARGO_CRATE:IsAlive()
-local Alive=true
-if self:IsLoaded()then
-Alive=Alive==true and self.CargoCarrier:IsAlive()
-else
-Alive=Alive==true and self.CargoObject:IsAlive()
-end
-return Alive
-end
-function CARGO_CRATE:RouteTo(Coordinate)
-self:F({Coordinate=Coordinate})
-end
-function CARGO_CRATE:IsNear(CargoCarrier,NearRadius)
-self:F({NearRadius=NearRadius})
-return self:IsNear(CargoCarrier:GetCoordinate(),NearRadius)
-end
-function CARGO_CRATE:Respawn()
-self:F({"Respawning crate "..self:GetName()})
-if self.CargoObject then
-self.CargoObject:ReSpawn()
-self:__Reset(-0.1)
-end
-end
-function CARGO_CRATE:onafterReset()
-self:F({"Reset crate "..self:GetName()})
-if self.CargoObject then
-self:SetDeployed(false)
-self:SetStartState("UnLoaded")
-self.CargoCarrier=nil
-_EVENTDISPATCHER:CreateEventNewCargo(self)
-end
-end
-function CARGO_CRATE:GetTransportationMethod()
-if self:IsLoaded()then
-return"for unloading"
-else
-if self:IsUnLoaded()then
-return"for loading"
-else
-if self:IsDeployed()then
-return"delivered"
-end
-end
-end
-return""
-end
-end
-do
-CARGO_GROUP={
-ClassName="CARGO_GROUP",
-}
-function CARGO_GROUP:New(CargoGroup,Type,Name,LoadRadius,NearRadius)
-local self=BASE:Inherit(self,CARGO_REPORTABLE:New(Type,Name,0,LoadRadius,NearRadius))
-self:F({Type,Name,LoadRadius})
-self.CargoSet=SET_CARGO:New()
-self.CargoGroup=CargoGroup
-self.Grouped=true
-self.CargoUnitTemplate={}
-self.NearRadius=NearRadius
-self:SetDeployed(false)
-local WeightGroup=0
-local VolumeGroup=0
-self.CargoGroup:Destroy()
-local GroupName=CargoGroup:GetName()
-self.CargoName=Name
-self.CargoTemplate=UTILS.DeepCopy(_DATABASE:GetGroupTemplate(GroupName))
-self.CargoTemplate.lateActivation=false
-self.GroupTemplate=UTILS.DeepCopy(self.CargoTemplate)
-self.GroupTemplate.name=self.CargoName.."#CARGO"
-self.GroupTemplate.groupId=nil
-self.GroupTemplate.units={}
-for UnitID,UnitTemplate in pairs(self.CargoTemplate.units)do
-UnitTemplate.name=UnitTemplate.name.."#CARGO"
-local CargoUnitName=UnitTemplate.name
-self.CargoUnitTemplate[CargoUnitName]=UnitTemplate
-self.GroupTemplate.units[#self.GroupTemplate.units+1]=self.CargoUnitTemplate[CargoUnitName]
-self.GroupTemplate.units[#self.GroupTemplate.units].unitId=nil
-local Unit=UNIT:Register(CargoUnitName)
-end
-self.CargoGroup=GROUP:NewTemplate(self.GroupTemplate,self.GroupTemplate.CoalitionID,self.GroupTemplate.CategoryID,self.GroupTemplate.CountryID)
-self.CargoObject=_DATABASE:Spawn(self.GroupTemplate)
-for CargoUnitID,CargoUnit in pairs(self.CargoObject:GetUnits())do
-local CargoUnitName=CargoUnit:GetName()
-local Cargo=CARGO_UNIT:New(CargoUnit,Type,CargoUnitName,LoadRadius,NearRadius)
-self.CargoSet:Add(CargoUnitName,Cargo)
-WeightGroup=WeightGroup+Cargo:GetWeight()
-end
-self:SetWeight(WeightGroup)
-self:T({"Weight Cargo",WeightGroup})
-_EVENTDISPATCHER:CreateEventNewCargo(self)
-self:HandleEvent(EVENTS.Dead,self.OnEventCargoDead)
-self:HandleEvent(EVENTS.Crash,self.OnEventCargoDead)
-self:HandleEvent(EVENTS.PlayerLeaveUnit,self.OnEventCargoDead)
-self:SetEventPriority(4)
-return self
-end
-function CARGO_GROUP:Respawn()
-self:F({"Respawning"})
-for CargoID,CargoData in pairs(self.CargoSet:GetSet())do
-local Cargo=CargoData
-Cargo:Destroy()
-Cargo:SetStartState("UnLoaded")
-end
-_DATABASE:Spawn(self.GroupTemplate)
-for CargoUnitID,CargoUnit in pairs(self.CargoObject:GetUnits())do
-local CargoUnitName=CargoUnit:GetName()
-local Cargo=CARGO_UNIT:New(CargoUnit,self.Type,CargoUnitName,self.LoadRadius)
-self.CargoSet:Add(CargoUnitName,Cargo)
-end
-self:SetDeployed(false)
-self:SetStartState("UnLoaded")
-end
-function CARGO_GROUP:Ungroup()
-if self.Grouped==true then
-self.Grouped=false
-self.CargoGroup:Destroy()
-for CargoUnitName,CargoUnit in pairs(self.CargoSet:GetSet())do
-local CargoUnit=CargoUnit
-if CargoUnit:IsUnLoaded()then
-local GroupTemplate=UTILS.DeepCopy(self.CargoTemplate)
-GroupTemplate.name=self.CargoName.."#CARGO#"..CargoUnitName
-GroupTemplate.groupId=nil
-if CargoUnit:IsUnLoaded()then
-GroupTemplate.units={}
-GroupTemplate.units[1]=self.CargoUnitTemplate[CargoUnitName]
-GroupTemplate.units[#GroupTemplate.units].unitId=nil
-GroupTemplate.units[#GroupTemplate.units].x=CargoUnit:GetX()
-GroupTemplate.units[#GroupTemplate.units].y=CargoUnit:GetY()
-GroupTemplate.units[#GroupTemplate.units].heading=CargoUnit:GetHeading()
-end
-local CargoGroup=GROUP:NewTemplate(GroupTemplate,GroupTemplate.CoalitionID,GroupTemplate.CategoryID,GroupTemplate.CountryID)
-_DATABASE:Spawn(GroupTemplate)
-end
-end
-self.CargoObject=nil
-end
-end
-function CARGO_GROUP:Regroup()
-self:F("Regroup")
-if self.Grouped==false then
-self.Grouped=true
-local GroupTemplate=UTILS.DeepCopy(self.CargoTemplate)
-GroupTemplate.name=self.CargoName.."#CARGO"
-GroupTemplate.groupId=nil
-GroupTemplate.units={}
-for CargoUnitName,CargoUnit in pairs(self.CargoSet:GetSet())do
-local CargoUnit=CargoUnit
-self:F({CargoUnit:GetName(),UnLoaded=CargoUnit:IsUnLoaded()})
-if CargoUnit:IsUnLoaded()then
-CargoUnit.CargoObject:Destroy()
-GroupTemplate.units[#GroupTemplate.units+1]=self.CargoUnitTemplate[CargoUnitName]
-GroupTemplate.units[#GroupTemplate.units].unitId=nil
-GroupTemplate.units[#GroupTemplate.units].x=CargoUnit:GetX()
-GroupTemplate.units[#GroupTemplate.units].y=CargoUnit:GetY()
-GroupTemplate.units[#GroupTemplate.units].heading=CargoUnit:GetHeading()
-end
-end
-self.CargoGroup=GROUP:NewTemplate(GroupTemplate,GroupTemplate.CoalitionID,GroupTemplate.CategoryID,GroupTemplate.CountryID)
-self:F({"Regroup",GroupTemplate})
-self.CargoObject=_DATABASE:Spawn(GroupTemplate)
-end
-end
-function CARGO_GROUP:OnEventCargoDead(EventData)
-self:E(EventData)
-local Destroyed=false
-if self:IsDestroyed()or self:IsUnLoaded()or self:IsBoarding()or self:IsUnboarding()then
-Destroyed=true
-for CargoID,CargoData in pairs(self.CargoSet:GetSet())do
-local Cargo=CargoData
-if Cargo:IsAlive()then
-Destroyed=false
-else
-Cargo:Destroyed()
-end
-end
-else
-local CarrierName=self.CargoCarrier:GetName()
-if CarrierName==EventData.IniDCSUnitName then
-MESSAGE:New("Cargo is lost from carrier "..CarrierName,15):ToAll()
-Destroyed=true
-self.CargoCarrier:ClearCargo()
-end
-end
-if Destroyed then
-self:Destroyed()
-self:E({"Cargo group destroyed"})
-end
-end
-function CARGO_GROUP:onafterBoard(From,Event,To,CargoCarrier,NearRadius,...)
-self:F({CargoCarrier.UnitName,From,Event,To,NearRadius=NearRadius})
-NearRadius=NearRadius or self.NearRadius
-self.CargoSet:ForEach(
-function(Cargo,...)
-self:F({"Board Unit",Cargo:GetName(),Cargo:IsDestroyed(),Cargo.CargoObject:IsAlive()})
-local CargoGroup=Cargo.CargoObject
-CargoGroup:OptionAlarmStateGreen()
-Cargo:__Board(1,CargoCarrier,NearRadius,...)
-end,...
-)
-self:__Boarding(-1,CargoCarrier,NearRadius,...)
-end
-function CARGO_GROUP:onafterLoad(From,Event,To,CargoCarrier,...)
-if From=="UnLoaded"then
-for CargoID,Cargo in pairs(self.CargoSet:GetSet())do
-if not Cargo:IsDestroyed()then
-Cargo:Load(CargoCarrier)
-end
-end
-end
-self.CargoCarrier=CargoCarrier
-self.CargoCarrier:AddCargo(self)
-end
-function CARGO_GROUP:onafterBoarding(From,Event,To,CargoCarrier,NearRadius,...)
-local Boarded=true
-local Cancelled=false
-local Dead=true
-self.CargoSet:Flush()
-for CargoID,Cargo in pairs(self.CargoSet:GetSet())do
-if not Cargo:is("Loaded")
-and(not Cargo:is("Destroyed"))then
-Boarded=false
-end
-if Cargo:is("UnLoaded")then
-Cancelled=true
-end
-if not Cargo:is("Destroyed")then
-Dead=false
-end
-end
-if not Dead then
-if not Cancelled then
-if not Boarded then
-self:__Boarding(-5,CargoCarrier,NearRadius,...)
-else
-self:F("Group Cargo is loaded")
-self:__Load(1,CargoCarrier,...)
-end
-else
-self:__CancelBoarding(1,CargoCarrier,NearRadius,...)
-end
-else
-self:__Destroyed(1,CargoCarrier,NearRadius,...)
-end
-end
-function CARGO_GROUP:onafterUnBoard(From,Event,To,ToPointVec2,NearRadius,...)
-self:F({From,Event,To,ToPointVec2,NearRadius})
-NearRadius=NearRadius or 25
-local Timer=1
-if From=="Loaded"then
-if self.CargoObject then
-self.CargoObject:Destroy()
-end
-self.CargoSet:ForEach(
-function(Cargo,NearRadius)
-if not Cargo:IsDestroyed()then
-local ToVec=nil
-if ToPointVec2==nil then
-ToVec=self.CargoCarrier:GetPointVec2():GetRandomPointVec2InRadius(2*NearRadius,NearRadius)
-else
-ToVec=ToPointVec2
-end
-Cargo:__UnBoard(Timer,ToVec,NearRadius)
-Timer=Timer+1
-end
-end,{NearRadius}
-)
-self:__UnBoarding(1,ToPointVec2,NearRadius,...)
-end
-end
-function CARGO_GROUP:onafterUnBoarding(From,Event,To,ToPointVec2,NearRadius,...)
-local Angle=180
-local Speed=10
-local Distance=5
-if From=="UnBoarding"then
-local UnBoarded=true
-for CargoID,Cargo in pairs(self.CargoSet:GetSet())do
-self:T({Cargo:GetName(),Cargo.current})
-if not Cargo:is("UnLoaded")and not Cargo:IsDestroyed()then
-UnBoarded=false
-end
-end
-if UnBoarded then
-self:__UnLoad(1,ToPointVec2,...)
-else
-self:__UnBoarding(1,ToPointVec2,NearRadius,...)
-end
-return false
-end
-end
-function CARGO_GROUP:onafterUnLoad(From,Event,To,ToPointVec2,...)
-if From=="Loaded"then
-self.CargoSet:ForEach(
-function(Cargo)
-local RandomVec2=nil
-if ToPointVec2 then
-RandomVec2=ToPointVec2:GetRandomPointVec2InRadius(20,10)
-end
-Cargo:UnBoard(RandomVec2)
-end
-)
-end
-self.CargoCarrier:RemoveCargo(self)
-self.CargoCarrier=nil
-end
-function CARGO_GROUP:GetCoordinate()
-local Cargo=self:GetFirstAlive()
-if Cargo then
-return Cargo.CargoObject:GetCoordinate()
-end
-return nil
-end
-function CARGO:GetX()
-local Cargo=self:GetFirstAlive()
-if Cargo then
-return Cargo:GetCoordinate().x
-end
-return nil
-end
-function CARGO:GetY()
-local Cargo=self:GetFirstAlive()
-if Cargo then
-return Cargo:GetCoordinate().z
-end
-return nil
-end
-function CARGO_GROUP:IsAlive()
-local Cargo=self:GetFirstAlive()
-return Cargo~=nil
-end
-function CARGO_GROUP:GetFirstAlive()
-local CargoFirstAlive=nil
-for _,Cargo in pairs(self.CargoSet:GetSet())do
-if not Cargo:IsDestroyed()then
-CargoFirstAlive=Cargo
-break
-end
-end
-return CargoFirstAlive
-end
-function CARGO_GROUP:GetCount()
-return self.CargoSet:Count()
-end
-function CARGO_GROUP:GetGroup(Cargo)
-local Cargo=Cargo or self:GetFirstAlive()
-return Cargo.CargoObject:GetGroup()
-end
-function CARGO_GROUP:RouteTo(Coordinate)
-self.CargoSet:ForEach(
-function(Cargo)
-Cargo.CargoObject:RouteGroundTo(Coordinate,10,"vee",0)
-end
-)
-end
-function CARGO_GROUP:IsNear(CargoCarrier,NearRadius)
-self:F({NearRadius=NearRadius})
-for _,Cargo in pairs(self.CargoSet:GetSet())do
-local Cargo=Cargo
-if Cargo:IsAlive()then
-if Cargo:IsNear(CargoCarrier:GetCoordinate(),NearRadius)then
-self:F("Near")
-return true
-end
-end
-end
-return nil
-end
-function CARGO_GROUP:IsInLoadRadius(Coordinate)
-local Cargo=self:GetFirstAlive()
-if Cargo then
-local Distance=0
-local CargoCoordinate
-if Cargo:IsLoaded()then
-CargoCoordinate=Cargo.CargoCarrier:GetCoordinate()
-else
-CargoCoordinate=Cargo.CargoObject:GetCoordinate()
-end
-if CargoCoordinate then
-Distance=Coordinate:Get2DDistance(CargoCoordinate)
-else
-return false
-end
-self:F({Distance=Distance,LoadRadius=self.LoadRadius})
-if Distance<=self.LoadRadius then
-return true
-else
-return false
-end
-end
-return nil
-end
-function CARGO_GROUP:IsInReportRadius(Coordinate)
-local Cargo=self:GetFirstAlive()
-if Cargo then
-self:F({Cargo})
-local Distance=0
-if Cargo:IsUnLoaded()then
-Distance=Coordinate:Get2DDistance(Cargo.CargoObject:GetCoordinate())
-if Distance<=self.LoadRadius then
-return true
-end
-end
-end
-return nil
-end
-function CARGO_GROUP:Flare(FlareColor)
-local Cargo=self.CargoSet:GetFirst()
-if Cargo then
-Cargo:Flare(FlareColor)
-end
-end
-function CARGO_GROUP:Smoke(SmokeColor,Radius)
-local Cargo=self.CargoSet:GetFirst()
-if Cargo then
-Cargo:Smoke(SmokeColor,Radius)
-end
-end
-function CARGO_GROUP:IsInZone(Zone)
-local Cargo=self.CargoSet:GetFirst()
-if Cargo then
-return Cargo:IsInZone(Zone)
-end
-return nil
-end
-function CARGO_GROUP:GetTransportationMethod()
-if self:IsLoaded()then
-return"for unboarding"
-else
-if self:IsUnLoaded()then
-return"for boarding"
-else
-if self:IsDeployed()then
-return"delivered"
-end
-end
-end
-return""
-end
-end
-AICSAR={
-ClassName="AICSAR",
-version="0.1.16",
-lid="",
-coalition=coalition.side.BLUE,
-template="",
-helotemplate="",
-alias="",
-farp=nil,
-farpzone=nil,
-maxdistance=UTILS.NMToMeters(50),
-pilotqueue={},
-pilotindex=0,
-helos={},
-verbose=false,
-rescuezoneradius=200,
-rescued={},
-autoonoff=true,
-playerset=nil,
-Messages={},
-SRS=nil,
-SRSRadio=false,
-SRSFrequency=243,
-SRSPath="\\",
-SRSModulation=radio.modulation.AM,
-SRSSoundPath=nil,
-SRSPort=5002,
-DCSRadio=false,
-DCSFrequency=243,
-DCSModulation=radio.modulation.AM,
-DCSRadioGroup=nil,
-limithelos=true,
-helonumber=3,
-gettext=nil,
-locale="en",
-SRSTTSRadio=false,
-SRSGoogle=false,
-SRSQ=nil,
-SRSPilot=nil,
-SRSPilotVoice=false,
-SRSOperator=nil,
-SRSOperatorVoice=false,
-PilotStore=nil,
-Speed=100,
-Altitude=1500,
-UseEventEject=false,
-Delay=100,
-}
-AICSAR.Messages={
-EN={
-INITIALOK="Roger, Pilot, we hear you. Stay where you are, a helo is on the way!",
-INITIALNOTOK="Sorry, Pilot. You're behind maximum operational distance! Good Luck!",
-PILOTDOWN="Mayday, mayday, mayday! Pilot down at ",
-PILOTKIA="Pilot KIA!",
-HELODOWN="CSAR Helo Down!",
-PILOTRESCUED="Pilot rescued!",
-PILOTINHELO="Pilot picked up!",
-},
-DE={
-INITIALOK="Copy, Pilot, wir hören Sie. Bleiben Sie, wo Sie sind!\nEin Hubschrauber sammelt Sie auf!",
-INITIALNOTOK="Verstehe, Pilot. Sie sind zu weit weg von uns.\nViel Glück!",
-PILOTDOWN="Mayday, mayday, mayday! Pilot abgestürzt: ",
-PILOTKIA="Pilot gefallen!",
-HELODOWN="CSAR Hubschrauber verloren!",
-PILOTRESCUED="Pilot gerettet!",
-PILOTINHELO="Pilot an Bord geholt!",
-},
-}
-AICSAR.RadioMessages={
-EN={
-INITIALOK="initialok.ogg",
-INITIALNOTOK="initialnotok.ogg",
-PILOTDOWN="pilotdown.ogg",
-PILOTKIA="pilotkia.ogg",
-HELODOWN="helodown.ogg",
-PILOTRESCUED="pilotrescued.ogg",
-PILOTINHELO="pilotinhelo.ogg",
-},
-}
-AICSAR.RadioLength={
-EN={
-INITIALOK=4.1,
-INITIALNOTOK=4.6,
-PILOTDOWN=2.6,
-PILOTKIA=1.1,
-HELODOWN=2.1,
-PILOTRESCUED=3.5,
-PILOTINHELO=2.6,
-},
-}
-function AICSAR:New(Alias,Coalition,Pilottemplate,Helotemplate,FARP,MASHZone)
-local self=BASE:Inherit(self,FSM:New())
-if Coalition and type(Coalition)=="string"then
-if Coalition=="blue"then
-self.coalition=coalition.side.BLUE
-self.coalitiontxt=Coalition
-elseif Coalition=="red"then
-self.coalition=coalition.side.RED
-self.coalitiontxt=Coalition
-elseif Coalition=="neutral"then
-self.coalition=coalition.side.NEUTRAL
-self.coalitiontxt=Coalition
-else
-self:E("ERROR: Unknown coalition in AICSAR!")
-end
-else
-self.coalition=Coalition
-self.coalitiontxt=string.lower(UTILS.GetCoalitionName(self.coalition))
-end
-if Alias then
-self.alias=tostring(Alias)
-else
-self.alias="Red Cross"
-if self.coalition then
-if self.coalition==coalition.side.RED then
-self.alias="IFRC"
-elseif self.coalition==coalition.side.BLUE then
-self.alias="CSAR"
-end
-end
-end
-self.template=Pilottemplate
-self.helotemplate=Helotemplate
-self.farp=FARP
-self.farpzone=MASHZone
-self.playerset=SET_CLIENT:New():FilterActive(true):FilterCategories("helicopter"):FilterStart()
-self.UseEventEject=false
-self.Delay=300
-self.SRS=nil
-self.SRSRadio=false
-self.SRSTTSRadio=false
-self.SRSGoogle=false
-self.SRSQ=nil
-self.SRSFrequency=243
-self.SRSPath="\\"
-self.SRSModulation=radio.modulation.AM
-self.SRSSoundPath=nil
-self.SRSPort=5002
-self.DCSRadio=false
-self.DCSFrequency=243
-self.DCSModulation=radio.modulation.AM
-self.DCSRadioGroup=nil
-self.DCSRadioQueue=nil
-self.MGRS_Accuracy=2
-self.limithelos=true
-self.helonumber=3
-self:InitLocalization()
-self.lid=string.format("%s (%s) | ",self.alias,self.coalition and UTILS.GetCoalitionName(self.coalition)or"unknown")
-self.PilotStore=FIFO:New()
-self:SetStartState("Stopped")
-self:AddTransition("Stopped","Start","Running")
-self:AddTransition("*","Status","*")
-self:AddTransition("*","PilotDown","*")
-self:AddTransition("*","PilotPickedUp","*")
-self:AddTransition("*","PilotUnloaded","*")
-self:AddTransition("*","PilotRescued","*")
-self:AddTransition("*","PilotKIA","*")
-self:AddTransition("*","HeloDown","*")
-self:AddTransition("*","HeloOnDuty","*")
-self:AddTransition("*","Stop","Stopped")
-self:HandleEvent(EVENTS.LandingAfterEjection,self._EventHandler)
-self:HandleEvent(EVENTS.Ejection,self._EjectEventHandler)
-self:__Start(math.random(2,5))
-local text=string.format("%sAICSAR Version %s Starting",self.lid,self.version)
-self:I(text)
-return self
-end
-function AICSAR:InitLocalization()
-self:T(self.lid.."InitLocalization")
-self.gettext=TEXTANDSOUND:New(self.ClassName,"en")
-self.gettext:AddEntry("en","INITIALOK",AICSAR.Messages.EN.INITIALOK,AICSAR.RadioMessages.EN.INITIALOK,AICSAR.RadioLength.INITIALOK)
-self.gettext:AddEntry("en","INITIALNOTOK",AICSAR.Messages.EN.INITIALNOTOK,AICSAR.RadioMessages.EN.INITIALNOTOK,AICSAR.RadioLength.EN.INITIALNOTOK)
-self.gettext:AddEntry("en","HELODOWN",AICSAR.Messages.EN.HELODOWN,AICSAR.RadioMessages.EN.HELODOWN,AICSAR.RadioLength.EN.HELODOWN)
-self.gettext:AddEntry("en","PILOTDOWN",AICSAR.Messages.EN.PILOTDOWN,AICSAR.RadioMessages.EN.PILOTDOWN,AICSAR.RadioLength.EN.PILOTDOWN)
-self.gettext:AddEntry("en","PILOTINHELO",AICSAR.Messages.EN.PILOTINHELO,AICSAR.RadioMessages.EN.PILOTINHELO,AICSAR.RadioLength.EN.PILOTINHELO)
-self.gettext:AddEntry("en","PILOTKIA",AICSAR.Messages.EN.PILOTKIA,AICSAR.RadioMessages.EN.PILOTKIA,AICSAR.RadioLength.EN.PILOTKIA)
-self.gettext:AddEntry("en","PILOTRESCUED",AICSAR.Messages.EN.PILOTRESCUED,AICSAR.RadioMessages.EN.PILOTRESCUED,AICSAR.RadioLength.EN.PILOTRESCUED)
-self.gettext:AddEntry("de","INITIALOK",AICSAR.Messages.DE.INITIALOK,AICSAR.RadioMessages.EN.INITIALOK,AICSAR.RadioLength.INITIALOK)
-self.gettext:AddEntry("de","INITIALNOTOK",AICSAR.Messages.DE.INITIALNOTOK,AICSAR.RadioMessages.EN.INITIALNOTOK,AICSAR.RadioLength.EN.INITIALNOTOK)
-self.gettext:AddEntry("de","HELODOWN",AICSAR.Messages.DE.HELODOWN,AICSAR.RadioMessages.EN.HELODOWN,AICSAR.RadioLength.EN.HELODOWN)
-self.gettext:AddEntry("de","PILOTDOWN",AICSAR.Messages.DE.PILOTDOWN,AICSAR.RadioMessages.EN.PILOTDOWN,AICSAR.RadioLength.EN.PILOTDOWN)
-self.gettext:AddEntry("de","PILOTINHELO",AICSAR.Messages.DE.PILOTINHELO,AICSAR.RadioMessages.EN.PILOTINHELO,AICSAR.RadioLength.EN.PILOTINHELO)
-self.gettext:AddEntry("de","PILOTKIA",AICSAR.Messages.DE.PILOTKIA,AICSAR.RadioMessages.EN.PILOTKIA,AICSAR.RadioLength.EN.PILOTKIA)
-self.gettext:AddEntry("de","PILOTRESCUED",AICSAR.Messages.DE.PILOTRESCUED,AICSAR.RadioMessages.EN.PILOTRESCUED,AICSAR.RadioLength.EN.PILOTRESCUED)
-self.locale="en"
-return self
-end
-function AICSAR:SetSRSRadio(OnOff,Path,Frequency,Modulation,SoundPath,Port)
-self:T(self.lid.."SetSRSRadio")
-self.SRSRadio=OnOff and true
-self.SRSTTSRadio=false
-self.SRSFrequency=Frequency or 243
-self.SRSPath=Path or"c:\\"
-self.SRS:SetLabel("ACSR")
-self.SRS:SetCoalition(self.coalition)
-self.SRSModulation=Modulation or radio.modulation.AM
-local soundpath=os.getenv('TMP').."\\DCS\\Mission\\l10n\\DEFAULT"
-self.SRSSoundPath=SoundPath or soundpath
-self.SRSPort=Port or 5002
-if OnOff then
-self.SRS=MSRS:New(Path,Frequency,Modulation)
-self.SRS:SetPort(self.SRSPort)
-end
-return self
-end
-function AICSAR:SetSRSTTSRadio(OnOff,Path,Frequency,Modulation,Port,Voice,Culture,Gender,GoogleCredentials)
-self:T(self.lid.."SetSRSTTSRadio")
-self.SRSTTSRadio=OnOff and true
-self.SRSRadio=false
-self.SRSFrequency=Frequency or 243
-self.SRSPath=Path or"C:\\Program Files\\DCS-SimpleRadio-Standalone"
-self.SRSModulation=Modulation or radio.modulation.AM
-self.SRSPort=Port or 5002
-if OnOff then
-self.SRS=MSRS:New(Path,Frequency,Modulation,1)
-self.SRS:SetPort(self.SRSPort)
-self.SRS:SetCoalition(self.coalition)
-self.SRS:SetLabel("ACSR")
-self.SRS:SetVoice(Voice)
-self.SRS:SetCulture(Culture)
-self.SRS:SetGender(Gender)
-if GoogleCredentials then
-self.SRS:SetGoogle(GoogleCredentials)
-self.SRSGoogle=true
-end
-self.SRSQ=MSRSQUEUE:New(self.alias)
-end
-return self
-end
-function AICSAR:SetPilotTTSVoice(Voice,Culture,Gender)
-self:T(self.lid.."SetPilotTTSVoice")
-self.SRSPilotVoice=true
-self.SRSPilot=MSRS:New(self.SRSPath,self.SRSFrequency,self.SRSModulation,1)
-self.SRSPilot:SetCoalition(self.coalition)
-self.SRSPilot:SetVoice(Voice)
-self.SRSPilot:SetCulture(Culture or"en-US")
-self.SRSPilot:SetGender(Gender or"male")
-self.SRSPilot:SetLabel("PILOT")
-if self.SRS.google then
-self.SRSPilot:SetGoogle(self.SRS.google)
-end
-return self
-end
-function AICSAR:SetOperatorTTSVoice(Voice,Culture,Gender)
-self:T(self.lid.."SetOperatorTTSVoice")
-self.SRSOperatorVoice=true
-self.SRSOperator=MSRS:New(self.SRSPath,self.SRSFrequency,self.SRSModulation,1)
-self.SRSOperator:SetCoalition(self.coalition)
-self.SRSOperator:SetVoice(Voice)
-self.SRSOperator:SetCulture(Culture or"en-GB")
-self.SRSOperator:SetGender(Gender or"female")
-self.SRSPilot:SetLabel("RESCUE")
-if self.SRS.google then
-self.SRSOperator:SetGoogle(self.SRS.google)
-end
-return self
-end
-function AICSAR:SetDCSRadio(OnOff,Frequency,Modulation,Group)
-self:T(self.lid.."SetDCSRadio")
-self:T(self.lid.."SetDCSRadio to "..tostring(OnOff))
-self.DCSRadio=OnOff and true
-self.DCSFrequency=Frequency or 243
-self.DCSModulation=Modulation or radio.modulation.AM
-self.DCSRadioGroup=Group
-if self.DCSRadio then
-self.DCSRadioQueue=RADIOQUEUE:New(Frequency,Modulation,"AI-CSAR")
-self.DCSRadioQueue:Start(5,5)
-self.DCSRadioQueue:SetRadioPower(1000)
-self.DCSRadioQueue:SetSenderCoordinate(Group:GetCoordinate())
-else
-if self.DCSRadioQueue then
-self.DCSRadioQueue:Stop()
-end
-end
-return self
-end
-function AICSAR:DCSRadioBroadcast(Soundfile,Duration,Subtitle)
-self:T(self.lid.."DCSRadioBroadcast")
-local radioqueue=self.DCSRadioQueue
-radioqueue:NewTransmission(Soundfile,Duration,nil,2,nil,Subtitle,10)
-return self
-end
-function AICSAR:_EjectEventHandler(EventData)
-local _event=EventData
-if _event.IniPlayerName then
-self.PilotStore:Push(_event.IniPlayerName)
-self:T(self.lid.."Pilot Ejected: ".._event.IniPlayerName)
-if self.UseEventEject then
-local _LandingPos=COORDINATE:NewFromVec3(_event.initiator:getPosition().p)
-local _country=_event.initiator:getCountry()
-local _coalition=coalition.getCountryCoalition(_country)
-local data=UTILS.DeepCopy(EventData)
-Unit.destroy(_event.initiator)
-self:ScheduleOnce(self.Delay,self._DelayedSpawnPilot,self,_LandingPos,_coalition)
-end
-end
-return self
-end
-function AICSAR:_DelayedSpawnPilot(_LandingPos,_coalition)
-local distancetofarp=_LandingPos:Get2DDistance(self.farp:GetCoordinate())
-local Text,Soundfile,Soundlength,Subtitle=self.gettext:GetEntry("PILOTDOWN",self.locale)
-local text=""
-local setting={}
-setting.MGRS_Accuracy=self.MGRS_Accuracy
-local location=_LandingPos:ToStringMGRS(setting)
-local msgtxt=Text..location.."!"
-location=string.gsub(location,"MGRS ","")
-location=string.gsub(location,"%s+","")
-location=string.gsub(location,"([%a%d])","%1;")
-location=string.gsub(location,"0","zero")
-location=string.gsub(location,"9","niner")
-location="MGRS;"..location
-if self.SRSGoogle then
-location=string.format("%s",location)
-end
-text=Text..location.."!"
-local ttstext=Text..location.."! Repeat! "..location
-if _coalition==self.coalition then
-if self.verbose then
-MESSAGE:New(msgtxt,15,"AICSAR"):ToCoalition(self.coalition)
-end
-if self.SRSRadio then
-local sound=SOUNDFILE:New(Soundfile,self.SRSSoundPath,Soundlength)
-sound:SetPlayWithSRS(true)
-self.SRS:PlaySoundFile(sound,2)
-elseif self.DCSRadio then
-self:DCSRadioBroadcast(Soundfile,Soundlength,text)
-elseif self.SRSTTSRadio then
-if self.SRSPilotVoice then
-self.SRSQ:NewTransmission(ttstext,nil,self.SRSPilot,nil,1)
-else
-self.SRSQ:NewTransmission(ttstext,nil,self.SRS,nil,1)
-end
-end
-end
-if _coalition==self.coalition and distancetofarp<=self.maxdistance then
-self:T(self.lid.."Spawning new Pilot")
-self.pilotindex=self.pilotindex+1
-local newpilot=SPAWN:NewWithAlias(self.template,string.format("%s-AICSAR-%d",self.template,self.pilotindex))
-newpilot:InitDelayOff()
-newpilot:OnSpawnGroup(
-function(grp)
-self.pilotqueue[self.pilotindex]=grp
-end
-)
-newpilot:SpawnFromCoordinate(_LandingPos)
-self:__PilotDown(2,_LandingPos,true)
-elseif _coalition==self.coalition and distancetofarp>self.maxdistance then
-self:T(self.lid.."Pilot out of reach")
-self:__PilotDown(2,_LandingPos,false)
-end
-return self
-end
-function AICSAR:_EventHandler(EventData,FromEject)
-self:T(self.lid.."OnEventLandingAfterEjection ID="..EventData.id)
-if self.autoonoff then
-if self.playerset:CountAlive()>0 then
-return self
-end
-end
-if self.UseEventEject and(not FromEject)then return self end
-local _event=EventData
-local _LandingPos=COORDINATE:NewFromVec3(_event.initiator:getPosition().p)
-local _country=_event.initiator:getCountry()
-local _coalition=coalition.getCountryCoalition(_country)
-local distancetofarp=_LandingPos:Get2DDistance(self.farp:GetCoordinate())
-local Text,Soundfile,Soundlength,Subtitle=self.gettext:GetEntry("PILOTDOWN",self.locale)
-local text=""
-local setting={}
-setting.MGRS_Accuracy=self.MGRS_Accuracy
-local location=_LandingPos:ToStringMGRS(setting)
-local msgtxt=Text..location.."!"
-location=string.gsub(location,"MGRS ","")
-location=string.gsub(location,"%s+","")
-location=string.gsub(location,"([%a%d])","%1;")
-location=string.gsub(location,"0","zero")
-location=string.gsub(location,"9","niner")
-location="MGRS;"..location
-if self.SRSGoogle then
-location=string.format("%s",location)
-end
-text=Text..location.."!"
-local ttstext=Text..location.."! Repeat! "..location
-if _coalition==self.coalition then
-if self.verbose then
-MESSAGE:New(msgtxt,15,"AICSAR"):ToCoalition(self.coalition)
-end
-if self.SRSRadio then
-local sound=SOUNDFILE:New(Soundfile,self.SRSSoundPath,Soundlength)
-sound:SetPlayWithSRS(true)
-self.SRS:PlaySoundFile(sound,2)
-elseif self.DCSRadio then
-self:DCSRadioBroadcast(Soundfile,Soundlength,text)
-elseif self.SRSTTSRadio then
-if self.SRSPilotVoice then
-self.SRSQ:NewTransmission(ttstext,nil,self.SRSPilot,nil,1)
-else
-self.SRSQ:NewTransmission(ttstext,nil,self.SRS,nil,1)
-end
-end
-end
-if _coalition==self.coalition and distancetofarp<=self.maxdistance then
-self:T(self.lid.."Spawning new Pilot")
-self.pilotindex=self.pilotindex+1
-local newpilot=SPAWN:NewWithAlias(self.template,string.format("%s-AICSAR-%d",self.template,self.pilotindex))
-newpilot:InitDelayOff()
-newpilot:OnSpawnGroup(
-function(grp)
-self.pilotqueue[self.pilotindex]=grp
-end
-)
-newpilot:SpawnFromCoordinate(_LandingPos)
-Unit.destroy(_event.initiator)
-self:__PilotDown(2,_LandingPos,true)
-elseif _coalition==self.coalition and distancetofarp>self.maxdistance then
-self:T(self.lid.."Pilot out of reach")
-self:__PilotDown(2,_LandingPos,false)
-end
-return self
-end
-function AICSAR:_GetFlight()
-self:T(self.lid.."_GetFlight")
-local newhelo=SPAWN:NewWithAlias(self.helotemplate,self.helotemplate..math.random(1,10000))
-:InitDelayOff()
-:InitUnControlled(true)
-:OnSpawnGroup(
-function(Group)
-self:__HeloOnDuty(1,Group)
-end
-)
-:Spawn()
-local nhelo=FLIGHTGROUP:New(newhelo)
-nhelo:SetHomebase(self.farp)
-nhelo:Activate()
-return nhelo
-end
-function AICSAR:_InitMission(Pilot,Index)
-self:T(self.lid.."_InitMission")
-local pickupzone=ZONE_GROUP:New(Pilot:GetName(),Pilot,self.rescuezoneradius)
-local opstransport=OPSTRANSPORT:New(Pilot,pickupzone,self.farpzone)
-local helo=self:_GetFlight()
-helo.AICSARReserved=true
-helo:SetDefaultAltitude(self.Altitude or 1500)
-helo:SetDefaultSpeed(self.Speed or 100)
-helo:AddOpsTransport(opstransport)
-local function AICPickedUp(Helo,Cargo,Index)
-self:__PilotPickedUp(2,Helo,Cargo,Index)
-end
-local function AICHeloDead(Helo,Index)
-self:__HeloDown(2,Helo,Index)
-end
-local function AICHeloUnloaded(Helo,OpsGroup)
-self:__PilotUnloaded(2,Helo,OpsGroup)
-end
-function helo:OnAfterLoading(From,Event,To)
-AICPickedUp(helo,helo:GetCargoGroups(),Index)
-helo:__LoadingDone(5)
-end
-function helo:OnAfterDead(From,Event,To)
-AICHeloDead(helo,Index)
-end
-function helo:OnAfterUnloaded(From,Event,To,OpsGroupCargo)
-AICHeloUnloaded(helo,OpsGroupCargo)
-helo:__UnloadingDone(5)
-end
-self.helos[Index]=helo
-return self
-end
-function AICSAR:_CheckInMashZone(Pilot)
-self:T(self.lid.."_CheckInMashZone")
-if Pilot:IsInZone(self.farpzone)then
-return true
-else
-return false
-end
-end
-function AICSAR:SetDefaultSpeed(Knots)
-self:T(self.lid.."SetDefaultSpeed")
-self.Speed=Knots or 100
-return self
-end
-function AICSAR:SetDefaultAltitude(Feet)
-self:T(self.lid.."SetDefaultAltitude")
-self.Altitude=Feet or 1500
-return self
-end
-function AICSAR:_CheckHelos()
-self:T(self.lid.."_CheckHelos")
-for _index,_helo in pairs(self.helos)do
-local helo=_helo
-if helo and helo.ClassName=="FLIGHTGROUP"then
-local state=helo:GetState()
-local name=helo:GetName()
-self:T("Helo group "..name.." in state "..state)
-if state=="Arrived"then
-helo:__Stop(5)
-self.helos[_index]=nil
-end
-else
-self.helos[_index]=nil
-end
-end
-return self
-end
-function AICSAR:_CountHelos()
-self:T(self.lid.."_CountHelos")
-local count=0
-for _index,_helo in pairs(self.helos)do
-count=count+1
-end
-return count
-end
-function AICSAR:_CheckQueue(OpsGroup)
-self:T(self.lid.."_CheckQueue")
-for _index,_pilot in pairs(self.pilotqueue)do
-local classname=_pilot.ClassName and _pilot.ClassName or"NONE"
-local name=_pilot.GroupName and _pilot.GroupName or"NONE"
-local playername="John Doe"
-local helocount=self:_CountHelos()
-if _pilot and _pilot.ClassName and _pilot.ClassName=="GROUP"then
-local flightgroup=self.helos[_index]
-if self:_CheckInMashZone(_pilot)then
-self:T("Pilot".._pilot.GroupName.." rescued!")
-if OpsGroup then
-OpsGroup:Despawn(10)
-else
-_pilot:Destroy(true,10)
-end
-self.pilotqueue[_index]=nil
-self.rescued[_index]=true
-if self.PilotStore:Count()>0 then
-playername=self.PilotStore:Pull()
-end
-self:__PilotRescued(2,playername)
-if flightgroup then
-flightgroup.AICSARReserved=false
-end
-end
-if not _pilot.AICSAR then
-if self.limithelos and helocount>=self.helonumber then
-break
-end
-_pilot.AICSAR={}
-_pilot.AICSAR.Status="Initiated"
-_pilot.AICSAR.Boarded=false
-self:_InitMission(_pilot,_index)
-break
-else
-if flightgroup then
-local state=flightgroup:GetState()
-_pilot.AICSAR.Status=state
-end
-end
-end
-end
-return self
-end
-function AICSAR:onafterStart(From,Event,To)
-self:T({From,Event,To})
-self:__Status(3)
-return self
-end
-function AICSAR:onafterStatus(From,Event,To)
-self:T({From,Event,To})
-self:_CheckHelos()
-self:__Status(30)
-return self
-end
-function AICSAR:onafterStop(From,Event,To)
-self:T({From,Event,To})
-self:UnHandleEvent(EVENTS.LandingAfterEjection)
-if self.DCSRadioQueue then
-self.DCSRadioQueue:Stop()
-end
-return self
-end
-function AICSAR:onafterPilotDown(From,Event,To,Coordinate,InReach)
-self:T({From,Event,To})
-local CoordinateText=Coordinate:ToStringMGRS()
-local inreach=tostring(InReach)
-if InReach then
-local text,Soundfile,Soundlength,Subtitle=self.gettext:GetEntry("INITIALOK",self.locale)
-self:T(text)
-if self.verbose then
-MESSAGE:New(text,15,"AICSAR"):ToCoalition(self.coalition)
-end
-if self.SRSRadio then
-local sound=SOUNDFILE:New(Soundfile,self.SRSSoundPath,Soundlength)
-sound:SetPlayWithSRS(true)
-self.SRS:PlaySoundFile(sound,2)
-elseif self.DCSRadio then
-self:DCSRadioBroadcast(Soundfile,Soundlength,text)
-elseif self.SRSTTSRadio then
-if self.SRSOperatorVoice then
-self.SRSQ:NewTransmission(text,nil,self.SRSOperator,nil,1)
-else
-self.SRSQ:NewTransmission(text,nil,self.SRS,nil,1)
-end
-end
-else
-local text,Soundfile,Soundlength,Subtitle=self.gettext:GetEntry("INITIALNOTOK",self.locale)
-self:T(text)
-if self.verbose then
-MESSAGE:New(text,15,"AICSAR"):ToCoalition(self.coalition)
-end
-if self.SRSRadio then
-local sound=SOUNDFILE:New(Soundfile,self.SRSSoundPath,Soundlength)
-sound:SetPlayWithSRS(true)
-self.SRS:PlaySoundFile(sound,2)
-elseif self.DCSRadio then
-self:DCSRadioBroadcast(Soundfile,Soundlength,text)
-elseif self.SRSTTSRadio then
-if self.SRSOperatorVoice then
-self.SRSQ:NewTransmission(text,nil,self.SRSOperator,nil,1)
-else
-self.SRSQ:NewTransmission(text,nil,self.SRS,nil,1)
-end
-end
-end
-self:_CheckQueue()
-return self
-end
-function AICSAR:onafterPilotKIA(From,Event,To)
-self:T({From,Event,To})
-local text,Soundfile,Soundlength,Subtitle=self.gettext:GetEntry("PILOTKIA",self.locale)
-if self.verbose then
-MESSAGE:New(text,15,"AICSAR"):ToCoalition(self.coalition)
-end
-if self.SRSRadio then
-local sound=SOUNDFILE:New(Soundfile,self.SRSSoundPath,Soundlength)
-sound:SetPlayWithSRS(true)
-self.SRS:PlaySoundFile(sound,2)
-elseif self.DCSRadio then
-self:DCSRadioBroadcast(Soundfile,Soundlength,text)
-elseif self.SRSTTSRadio then
-self.SRSQ:NewTransmission(text,nil,self.SRS,nil,1)
-end
-return self
-end
-function AICSAR:onafterHeloDown(From,Event,To,Helo,Index)
-self:T({From,Event,To})
-local text,Soundfile,Soundlength,Subtitle=self.gettext:GetEntry("HELODOWN",self.locale)
-if self.verbose then
-MESSAGE:New(text,15,"AICSAR"):ToCoalition(self.coalition)
-end
-if self.SRSRadio then
-local sound=SOUNDFILE:New(Soundfile,self.SRSSoundPath,Soundlength)
-sound:SetPlayWithSRS(true)
-self.SRS:PlaySoundFile(sound,2)
-elseif self.DCSRadio then
-self:DCSRadioBroadcast(Soundfile,Soundlength,text)
-elseif self.SRSTTSRadio then
-if self.SRSOperatorVoice then
-self.SRSQ:NewTransmission(text,nil,self.SRSOperator,nil,1)
-else
-self.SRSQ:NewTransmission(text,nil,self.SRS,nil,1)
-end
-end
-local findex=0
-local fhname=Helo:GetName()
-if Index and Index>0 then
-findex=Index
-else
-for _index,_helo in pairs(self.helos)do
-local helo=_helo
-local hname=helo:GetName()
-if fhname==hname then
-findex=_index
-break
-end
-end
-end
-if findex>0 and not self.rescued[findex]then
-local pilot=self.pilotqueue[findex]
-self.helos[findex]=nil
-if pilot.AICSAR.Boarded then
-self:T("Helo Down: Found DEAD Pilot ID "..findex.." with name "..pilot:GetName())
-self:__PilotKIA(2)
-self.pilotqueue[findex]=nil
-else
-self:T("Helo Down: Found ALIVE Pilot ID "..findex.." with name "..pilot:GetName())
-self:_InitMission(pilot,findex)
-end
-end
-return self
-end
-function AICSAR:onafterPilotRescued(From,Event,To,PilotName)
-self:T({From,Event,To})
-local text,Soundfile,Soundlength,Subtitle=self.gettext:GetEntry("PILOTRESCUED",self.locale)
-if self.verbose then
-MESSAGE:New(text,15,"AICSAR"):ToCoalition(self.coalition)
-end
-if self.SRSRadio then
-local sound=SOUNDFILE:New(Soundfile,self.SRSSoundPath,Soundlength)
-sound:SetPlayWithSRS(true)
-self.SRS:PlaySoundFile(sound,2)
-elseif self.DCSRadio then
-self:DCSRadioBroadcast(Soundfile,Soundlength,text)
-elseif self.SRSTTSRadio then
-self.SRSQ:NewTransmission(text,nil,self.SRS,nil,1)
-end
-return self
-end
-function AICSAR:onafterPilotUnloaded(From,Event,To,Helo,OpsGroup)
-self:T({From,Event,To})
-self:_CheckQueue(OpsGroup)
-return self
-end
-function AICSAR:onafterPilotPickedUp(From,Event,To,Helo,CargoTable,Index)
-self:T({From,Event,To})
-local text,Soundfile,Soundlength,Subtitle=self.gettext:GetEntry("PILOTINHELO",self.locale)
-if self.verbose then
-MESSAGE:New(text,15,"AICSAR"):ToCoalition(self.coalition)
-end
-if self.SRSRadio then
-local sound=SOUNDFILE:New(Soundfile,self.SRSSoundPath,Soundlength)
-sound:SetPlayWithSRS(true)
-self.SRS:PlaySoundFile(sound,2)
-elseif self.DCSRadio then
-self:DCSRadioBroadcast(Soundfile,Soundlength,text)
-elseif self.SRSTTSRadio then
-self.SRSQ:NewTransmission(text,nil,self.SRS,nil,1)
-end
-local findex=0
-local fhname=Helo:GetName()
-if Index and Index>0 then
-findex=Index
-else
-for _index,_helo in pairs(self.helos)do
-local helo=_helo
-local hname=helo:GetName()
-if fhname==hname then
-findex=_index
-break
-end
-end
-end
-if findex>0 then
-local pilot=self.pilotqueue[findex]
-self:T("Boarded: Found Pilot ID "..findex.." with name "..pilot:GetName())
-pilot.AICSAR.Boarded=true
-end
-return self
-end
-AMMOTRUCK={
-ClassName="AMMOTRUCK",
-lid="",
-version="0.0.12",
-alias="",
-debug=false,
-trucklist={},
-targetlist={},
-coalition=nil,
-truckset=nil,
-targetset=nil,
-remunitionqueue={},
-waitingtargets={},
-ammothreshold=5,
-remunidist=20000,
-monitor=-60,
-unloadtime=600,
-waitingtime=1800,
-routeonroad=true,
-reloads=5,
-}
-AMMOTRUCK.State={
-IDLE="idle",
-DRIVING="driving",
-ARRIVED="arrived",
-UNLOADING="unloading",
-RETURNING="returning",
-WAITING="waiting",
-RELOADING="reloading",
-OUTOFAMMO="outofammo",
-REQUESTED="requested",
-}
-function AMMOTRUCK:New(Truckset,Targetset,Coalition,Alias,Homezone)
-local self=BASE:Inherit(self,FSM:New())
-self.truckset=Truckset
-self.targetset=Targetset
-self.coalition=Coalition
-self.alias=Alias
-self.debug=false
-self.remunitionqueue={}
-self.trucklist={}
-self.targetlist={}
-self.ammothreshold=5
-self.remunidist=20000
-self.homezone=Homezone
-self.waitingtime=1800
-self.usearmygroup=false
-self.hasarmygroup=false
-self.lid=string.format("AMMOTRUCK %s | %s | ",self.version,self.alias)
-self:SetStartState("Stopped")
-self:AddTransition("Stopped","Start","Running")
-self:AddTransition("*","Monitor","*")
-self:AddTransition("*","RouteTruck","*")
-self:AddTransition("*","TruckArrived","*")
-self:AddTransition("*","TruckUnloading","*")
-self:AddTransition("*","TruckReturning","*")
-self:AddTransition("*","TruckHome","*")
-self:AddTransition("*","Stop","Stopped")
-self:__Start(math.random(5,10))
-self:I(self.lid.."Started")
-return self
-end
-function AMMOTRUCK:CheckDrivingTrucks(dataset)
-self:T(self.lid.." CheckDrivingTrucks")
-local data=dataset
-for _,_data in pairs(data)do
-local truck=_data
-local coord=truck.group:GetCoordinate()
-local tgtcoord=truck.targetcoordinate
-local dist=coord:Get2DDistance(tgtcoord)
-if dist<=150 then
-truck.statusquo=AMMOTRUCK.State.ARRIVED
-truck.timestamp=timer.getAbsTime()
-truck.coordinate=coord
-self:__TruckArrived(1,truck)
-end
-local Tnow=timer.getAbsTime()
-if Tnow-truck.timestamp>30 then
-local group=truck.group
-if self.usearmygroup then
-group=truck.group:GetGroup()
-end
-local currspeed=group:GetVelocityKMH()
-if truck.lastspeed then
-if truck.lastspeed==0 and currspeed==0 then
-self:T(truck.group:GetName().." Is not moving!")
-truck.timestamp=timer.getAbsTime()
-if self.routeonroad then
-group:RouteGroundOnRoad(truck.targetcoordinate,30,2,"Vee")
-else
-group:RouteGroundTo(truck.targetcoordinate,30,"Vee",2)
-end
-end
-truck.lastspeed=currspeed
-else
-truck.lastspeed=currspeed
-truck.timestamp=timer.getAbsTime()
-end
-self:I({truck=truck.group:GetName(),currspeed=currspeed,lastspeed=truck.lastspeed})
-end
-end
-return self
-end
-function AMMOTRUCK:GetAmmoStatus(Group)
-local ammotot,shells,rockets,bombs,missiles,narti=Group:GetAmmunition()
-return rockets+missiles+narti
-end
-function AMMOTRUCK:CheckWaitingTargets(dataset)
-self:T(self.lid.." CheckWaitingTargets")
-local data=dataset
-for _,_data in pairs(data)do
-local truck=_data
-local Tnow=timer.getAbsTime()
-local Tdiff=Tnow-truck.timestamp
-if Tdiff>self.waitingtime then
-local hasammo=self:GetAmmoStatus(truck.group)
-if hasammo<=self.ammothreshold then
-truck.statusquo=AMMOTRUCK.State.OUTOFAMMO
-else
-truck.statusquo=AMMOTRUCK.State.IDLE
-end
-end
-end
-return self
-end
-function AMMOTRUCK:CheckReturningTrucks(dataset)
-self:T(self.lid.." CheckReturningTrucks")
-local data=dataset
-local tgtcoord=self.homezone:GetCoordinate()
-local radius=self.homezone:GetRadius()
-for _,_data in pairs(data)do
-local truck=_data
-local coord=truck.group:GetCoordinate()
-local dist=coord:Get2DDistance(tgtcoord)
-self:T({name=truck.name,radius=radius,distance=dist})
-if dist<=radius then
-truck.statusquo=AMMOTRUCK.State.IDLE
-truck.timestamp=timer.getAbsTime()
-truck.coordinate=coord
-truck.reloads=self.reloads or 5
-self:__TruckHome(1,truck)
-end
-end
-return self
-end
-function AMMOTRUCK:FindTarget(name)
-self:T(self.lid.." FindTarget")
-local data=nil
-local dataset=self.targetlist
-for _,_entry in pairs(dataset)do
-local entry=_entry
-if entry.name==name then
-data=entry
-break
-end
-end
-return data
-end
-function AMMOTRUCK:FindTruck(name)
-self:T(self.lid.." FindTruck")
-local data=nil
-local dataset=self.trucklist
-for _,_entry in pairs(dataset)do
-local entry=_entry
-if entry.name==name then
-data=entry
-break
-end
-end
-return data
-end
-function AMMOTRUCK:CheckArrivedTrucks(dataset)
-self:T(self.lid.." CheckArrivedTrucks")
-local data=dataset
-for _,_data in pairs(data)do
-local truck=_data
-truck.statusquo=AMMOTRUCK.State.UNLOADING
-truck.timestamp=timer.getAbsTime()
-self:__TruckUnloading(2,truck)
-local aridata=self:FindTarget(truck.targetname)
-if aridata then
-aridata.statusquo=AMMOTRUCK.State.RELOADING
-aridata.timestamp=timer.getAbsTime()
-end
-end
-return self
-end
-function AMMOTRUCK:CheckUnloadingTrucks(dataset)
-self:T(self.lid.." CheckUnloadingTrucks")
-local data=dataset
-for _,_data in pairs(data)do
-local truck=_data
-local Tnow=timer.getAbsTime()
-local Tpassed=Tnow-truck.timestamp
-local hasammo=self:GetAmmoStatus(truck.targetgroup)
-if Tpassed>self.unloadtime and hasammo>self.ammothreshold then
-truck.statusquo=AMMOTRUCK.State.RETURNING
-truck.timestamp=timer.getAbsTime()
-self:__TruckReturning(2,truck)
-local aridata=self:FindTarget(truck.targetname)
-if aridata then
-aridata.statusquo=AMMOTRUCK.State.IDLE
-aridata.timestamp=timer.getAbsTime()
-end
-end
-end
-return self
-end
-function AMMOTRUCK:CheckTargetsAlive()
-self:T(self.lid.." CheckTargetsAlive")
-local arilist=self.targetlist
-for _,_ari in pairs(arilist)do
-local ari=_ari
-if ari.group and ari.group:IsAlive()then
-else
-self.targetlist[ari.name]=nil
-end
-end
-local aritable=self.targetset:GetSetObjects()
-for _,_ari in pairs(aritable)do
-local ari=_ari
-if ari and ari:IsAlive()and not self.targetlist[ari:GetName()]then
-local name=ari:GetName()
-local newari={}
-newari.name=name
-newari.group=ari
-newari.statusquo=AMMOTRUCK.State.IDLE
-newari.timestamp=timer.getAbsTime()
-newari.coordinate=ari:GetCoordinate()
-local hasammo=self:GetAmmoStatus(ari)
-newari.ammo=hasammo
-self.targetlist[name]=newari
-end
-end
-return self
-end
-function AMMOTRUCK:CheckTrucksAlive()
-self:T(self.lid.." CheckTrucksAlive")
-local trucklist=self.trucklist
-for _,_truck in pairs(trucklist)do
-local truck=_truck
-if truck.group and truck.group:IsAlive()then
-else
-local tgtname=truck.targetname
-local targetdata=self:FindTarget(tgtname)
-if targetdata then
-if targetdata.statusquo~=AMMOTRUCK.State.IDLE then
-targetdata.statusquo=AMMOTRUCK.State.IDLE
-end
-end
-self.trucklist[truck.name]=nil
-end
-end
-local trucktable=self.truckset:GetSetObjects()
-for _,_truck in pairs(trucktable)do
-local truck=_truck
-if truck and truck:IsAlive()and not self.trucklist[truck:GetName()]then
-local name=truck:GetName()
-local newtruck={}
-newtruck.name=name
-newtruck.group=truck
-if self.hasarmygroup then
-if truck.ClassName and truck.ClassName=="GROUP"then
-local trucker=ARMYGROUP:New(truck)
-trucker:Activate()
-newtruck.group=trucker
-end
-end
-newtruck.statusquo=AMMOTRUCK.State.IDLE
-newtruck.timestamp=timer.getAbsTime()
-newtruck.coordinate=truck:GetCoordinate()
-newtruck.reloads=self.reloads or 5
-self.trucklist[name]=newtruck
-end
-end
-return self
-end
-function AMMOTRUCK:onafterStart(From,Event,To)
-self:T({From,Event,To})
-if ARMYGROUP and self.usearmygroup then
-self.hasarmygroup=true
-else
-self.hasarmygroup=false
-end
-if self.debug then
-BASE:TraceOn()
-BASE:TraceClass("AMMOTRUCK")
-end
-self:CheckTargetsAlive()
-self:CheckTrucksAlive()
-self:__Monitor(-30)
-return self
-end
-function AMMOTRUCK:onafterMonitor(From,Event,To)
-self:T({From,Event,To})
-self:CheckTargetsAlive()
-self:CheckTrucksAlive()
-local remunition=false
-local remunitionqueue={}
-local waitingtargets={}
-for _,_ari in pairs(self.targetlist)do
-local data=_ari
-if data.group and data.group:IsAlive()then
-data.ammo=self:GetAmmoStatus(data.group)
-data.timestamp=timer.getAbsTime()
-local text=string.format("Ari %s | Ammo %d | State %s",data.name,data.ammo,data.statusquo)
-self:T(text)
-if data.ammo<=self.ammothreshold and(data.statusquo==AMMOTRUCK.State.IDLE or data.statusquo==AMMOTRUCK.State.OUTOFAMMO)then
-data.statusquo=AMMOTRUCK.State.OUTOFAMMO
-remunitionqueue[#remunitionqueue+1]=data
-remunition=true
-elseif data.statusquo==AMMOTRUCK.State.WAITING then
-waitingtargets[#waitingtargets+1]=data
-end
-else
-self.targetlist[data.name]=nil
-end
-end
-local idletrucks={}
-local drivingtrucks={}
-local unloadingtrucks={}
-local arrivedtrucks={}
-local returningtrucks={}
-local found=false
-for _,_truckdata in pairs(self.trucklist)do
-local data=_truckdata
-if data.group and data.group:IsAlive()then
-local text=string.format("Truck %s | State %s",data.name,data.statusquo)
-self:T(text)
-if data.statusquo==AMMOTRUCK.State.IDLE then
-idletrucks[#idletrucks+1]=data
-found=true
-elseif data.statusquo==AMMOTRUCK.State.DRIVING then
-drivingtrucks[#drivingtrucks+1]=data
-elseif data.statusquo==AMMOTRUCK.State.ARRIVED then
-arrivedtrucks[#arrivedtrucks+1]=data
-elseif data.statusquo==AMMOTRUCK.State.UNLOADING then
-unloadingtrucks[#unloadingtrucks+1]=data
-elseif data.statusquo==AMMOTRUCK.State.RETURNING then
-returningtrucks[#returningtrucks+1]=data
-if data.reloads>0 or data.reloads==-1 then
-idletrucks[#idletrucks+1]=data
-found=true
-end
-end
-else
-self.truckset[data.name]=nil
-end
-end
-local n=0
-if found and remunition then
-for _,_truckdata in pairs(idletrucks)do
-local truckdata=_truckdata
-local truckcoord=truckdata.group:GetCoordinate()
-for _,_aridata in pairs(remunitionqueue)do
-local aridata=_aridata
-local aricoord=aridata.coordinate
-local distance=truckcoord:Get2DDistance(aricoord)
-if distance<=self.remunidist and aridata.statusquo==AMMOTRUCK.State.OUTOFAMMO and n<=#idletrucks then
-n=n+1
-aridata.statusquo=AMMOTRUCK.State.REQUESTED
-self:__RouteTruck(n*5,truckdata,aridata)
-break
-end
-end
-end
-end
-if#drivingtrucks>0 then
-self:CheckDrivingTrucks(drivingtrucks)
-end
-if#arrivedtrucks>0 then
-self:CheckArrivedTrucks(arrivedtrucks)
-end
-if#unloadingtrucks>0 then
-self:CheckUnloadingTrucks(unloadingtrucks)
-end
-if#returningtrucks>0 then
-self:CheckReturningTrucks(returningtrucks)
-end
-if#waitingtargets>0 then
-self:CheckWaitingTargets(waitingtargets)
-end
-self:__Monitor(self.monitor)
-return self
-end
-function AMMOTRUCK:onafterRouteTruck(From,Event,To,Truckdata,Aridata)
-self:T({From,Event,To,Truckdata.name,Aridata.name})
-local truckdata=Truckdata
-local aridata=Aridata
-local tgtgrp=aridata.group
-local tgtzone=ZONE_GROUP:New(aridata.name,tgtgrp,30)
-local tgtcoord=tgtzone:GetRandomCoordinate(15)
-if self.hasarmygroup then
-local mission=AUFTRAG:NewONGUARD(tgtcoord)
-local oldmission=truckdata.group:GetMissionCurrent()
-if oldmission then oldmission:Cancel()end
-mission:SetTime(5)
-mission:SetTeleport(false)
-truckdata.group:AddMission(mission)
-elseif self.routeonroad then
-truckdata.group:RouteGroundOnRoad(tgtcoord,30)
-else
-truckdata.group:RouteGroundTo(tgtcoord,30)
-end
-truckdata.statusquo=AMMOTRUCK.State.DRIVING
-truckdata.targetgroup=tgtgrp
-truckdata.targetname=aridata.name
-truckdata.targetcoordinate=tgtcoord
-aridata.statusquo=AMMOTRUCK.State.WAITING
-aridata.timestamp=timer.getAbsTime()
-return self
-end
-function AMMOTRUCK:onafterTruckUnloading(From,Event,To,Truckdata)
-local m=MESSAGE:New("Truck "..Truckdata.name.." unloading!",15,"AmmoTruck"):ToCoalitionIf(self.coalition,self.debug)
-local truck=Truckdata
-local coord=truck.group:GetCoordinate()
-local heading=truck.group:GetHeading()
-heading=heading<180 and(360-heading)or(heading-180)
-local cid=self.coalition==coalition.side.BLUE and country.id.USA or country.id.RUSSIA
-cid=self.coalition==coalition.side.NEUTRAL and country.id.UN_PEACEKEEPERS or cid
-local ammo={}
-for i=1,5 do
-ammo[i]=SPAWNSTATIC:NewFromType("ammo_cargo","Cargos",cid)
-:InitCoordinate(coord:Translate((15+((i-1)*4)),heading))
-:Spawn(0,"AmmoCrate-"..math.random(1,10000))
-end
-local function destroyammo(ammo)
-for _,_crate in pairs(ammo)do
-_crate:Destroy(false)
-end
-end
-local scheduler=SCHEDULER:New(nil,destroyammo,{ammo},self.waitingtime)
-if truck.reloads~=-1 then
-truck.reloads=truck.reloads-1
-end
-return self
-end
-function AMMOTRUCK:onafterTruckReturning(From,Event,To,Truck)
-self:T({From,Event,To,Truck.name})
-local truckdata=Truck
-local tgtzone=self.homezone
-local tgtcoord=tgtzone:GetRandomCoordinate()
-if self.hasarmygroup then
-local mission=AUFTRAG:NewONGUARD(tgtcoord)
-local oldmission=truckdata.group:GetMissionCurrent()
-if oldmission then oldmission:Cancel()end
-mission:SetTime(5)
-mission:SetTeleport(false)
-truckdata.group:AddMission(mission)
-elseif self.routeonroad then
-truckdata.group:RouteGroundOnRoad(tgtcoord,30,1,"Cone")
-else
-truckdata.group:RouteGroundTo(tgtcoord,30,"Cone",1)
-end
-return self
-end
-function AMMOTRUCK:onafterStop(From,Event,To)
-self:T({From,Event,To})
-return self
-end
-ARTY={
-ClassName="ARTY",
-lid=nil,
-Debug=false,
-targets={},
-moves={},
-currentTarget=nil,
-currentMove=nil,
-Nammo0=0,
-Nshells0=0,
-Nrockets0=0,
-Nmissiles0=0,
-Nukes0=0,
-Nillu0=0,
-Nsmoke0=0,
-StatusInterval=10,
-WaitForShotTime=300,
-DCSdesc=nil,
-Type=nil,
-DisplayName=nil,
-groupname=nil,
-alias=nil,
-clusters={},
-ismobile=true,
-iscargo=false,
-cargogroup=nil,
-IniGroupStrength=0,
-IsArtillery=nil,
-RearmingDistance=100,
-RearmingGroup=nil,
-RearmingGroupSpeed=nil,
-RearmingGroupOnRoad=false,
-RearmingGroupCoord=nil,
-RearmingPlaceCoord=nil,
-RearmingArtyOnRoad=false,
-InitialCoord=nil,
-report=true,
-ammoshells={},
-ammorockets={},
-ammomissiles={},
-Nshots=0,
-minrange=300,
-maxrange=1000000,
-nukewarhead=75000,
-Nukes=nil,
-nukefire=false,
-nukefires=nil,
-nukerange=nil,
-Nillu=nil,
-illuPower=1000000,
-illuMinalt=500,
-illuMaxalt=1000,
-Nsmoke=nil,
-smokeColor=SMOKECOLOR.Red,
-relocateafterfire=false,
-relocateRmin=300,
-relocateRmax=800,
-markallow=false,
-markkey=nil,
-markreadonly=false,
-autorelocate=false,
-autorelocatemaxdist=50000,
-autorelocateonroad=false,
-coalition=nil,
-respawnafterdeath=false,
-respawndelay=nil
-}
-ARTY.WeaponType={
-Auto=1073741822,
-Cannon=805306368,
-Rockets=30720,
-CruiseMissile=2097152,
-TacticalNukes=666,
-IlluminationShells=667,
-SmokeShells=668,
-}
-ARTY.db={
-["2B11 mortar"]={
-minrange=500,
-maxrange=7000,
-reloadtime=30,
-},
-["SPH 2S1 Gvozdika"]={
-minrange=300,
-maxrange=15000,
-reloadtime=nil,
-},
-["SPH 2S19 Msta"]={
-minrange=300,
-maxrange=23500,
-reloadtime=nil,
-},
-["SPH 2S3 Akatsia"]={
-minrange=300,
-maxrange=17000,
-reloadtime=nil,
-},
-["SPH 2S9 Nona"]={
-minrange=500,
-maxrange=7000,
-reloadtime=nil,
-},
-["SPH M109 Paladin"]={
-minrange=300,
-maxrange=22000,
-reloadtime=nil,
-},
-["SpGH Dana"]={
-minrange=300,
-maxrange=18700,
-reloadtime=nil,
-},
-["MLRS BM-21 Grad"]={
-minrange=5000,
-maxrange=19000,
-reloadtime=420,
-},
-["MLRS 9K57 Uragan BM-27"]={
-minrange=11500,
-maxrange=35800,
-reloadtime=840,
-},
-["MLRS 9A52 Smerch"]={
-minrange=20000,
-maxrange=70000,
-reloadtime=2160,
-},
-["MLRS M270"]={
-minrange=10000,
-maxrange=32000,
-reloadtime=540,
-},
-}
-ARTY.version="1.3.0"
-function ARTY:New(group,alias)
-local self=BASE:Inherit(self,FSM_CONTROLLABLE:New())
-if type(group)=="string"then
-self.groupname=group
-group=GROUP:FindByName(group)
-if not group then
-self:E(string.format("ERROR: Requested ARTY group %s does not exist! (Has to be a MOOSE group.)",self.groupname))
-return nil
-end
-end
-if group then
-self:T(string.format("ARTY script version %s. Added group %s.",ARTY.version,group:GetName()))
-else
-self:E("ERROR: Requested ARTY group does not exist! (Has to be a MOOSE group.)")
-return nil
-end
-if not(group:IsGround()or group:IsShip())then
-self:E(string.format("ERROR: ARTY group %s has to be a GROUND or SHIP group!",group:GetName()))
-return nil
-end
-self:SetControllable(group)
-self.groupname=group:GetName()
-self.coalition=group:GetCoalition()
-if alias~=nil then
-self.alias=tostring(alias)
-else
-self.alias=self.groupname
-end
-self.lid=string.format("ARTY %s | ",self.alias)
-self.InitialCoord=group:GetCoordinate()
-local DCSgroup=Group.getByName(group:GetName())
-local DCSunit=DCSgroup:getUnit(1)
-self.DCSdesc=DCSunit:getDesc()
-self:T3(self.lid.."DCS descriptors for group "..group:GetName())
-for id,desc in pairs(self.DCSdesc)do
-self:T3({id=id,desc=desc})
-end
-self.SpeedMax=group:GetSpeedMax()
-if self.SpeedMax>1 then
-self.ismobile=true
-else
-self.ismobile=false
-end
-self.dtTrack=0.2
-self.Speed=self.SpeedMax*0.7
-self.DisplayName=self.DCSdesc.displayName
-self.IsArtillery=DCSunit:hasAttribute("Artillery")
-self.Type=group:GetTypeName()
-self.IniGroupStrength=#group:GetUnits()
-self:AddTransition("*","Start","CombatReady")
-self:AddTransition("CombatReady","OpenFire","Firing")
-self:AddTransition("Firing","CeaseFire","CombatReady")
-self:AddTransition("CombatReady","Winchester","OutOfAmmo")
-self:AddTransition({"CombatReady","OutOfAmmo"},"Rearm","Rearming")
-self:AddTransition("Rearming","Rearmed","Rearmed")
-self:AddTransition("*","Move","Moving")
-self:AddTransition("Moving","Arrived","Arrived")
-self:AddTransition("*","NewTarget","*")
-self:AddTransition("*","CombatReady","CombatReady")
-self:AddTransition("*","Status","*")
-self:AddTransition("*","NewMove","*")
-self:AddTransition("*","Dead","*")
-self:AddTransition("*","Respawn","CombatReady")
-self:AddTransition("*","Loaded","InTransit")
-self:AddTransition("InTransit","UnLoaded","CombatReady")
-self:AddTransition("Rearming","Arrived","Rearming")
-self:AddTransition("Rearming","Move","Rearming")
-return self
-end
-function ARTY:NewFromCargoGroup(cargogroup,alias)
-if cargogroup then
-BASE:T(string.format("ARTY script version %s. Added CARGO group %s.",ARTY.version,cargogroup:GetName()))
-else
-BASE:E("ERROR: Requested ARTY CARGO GROUP does not exist! (Has to be a MOOSE CARGO(!) group.)")
-return nil
-end
-local group=cargogroup:GetObject()
-local arty=ARTY:New(group,alias)
-arty.iscargo=true
-arty.cargogroup=cargogroup
-return arty
-end
-function ARTY:AssignTargetCoord(coord,prio,radius,nshells,maxengage,time,weapontype,name,unique)
-self:F({coord=coord,prio=prio,radius=radius,nshells=nshells,maxengage=maxengage,time=time,weapontype=weapontype,name=name,unique=unique})
-nshells=nshells or 5
-radius=radius or 100
-maxengage=maxengage or 1
-prio=prio or 50
-prio=math.max(1,prio)
-prio=math.min(100,prio)
-if unique==nil then
-unique=false
-end
-weapontype=weapontype or ARTY.WeaponType.Auto
-local text=nil
-if coord:IsInstanceOf("GROUP")then
-text="WARNING: ARTY:AssignTargetCoordinate(coord, ...) needs a COORDINATE object as first parameter - you gave a GROUP. Converting to COORDINATE..."
-coord=coord:GetCoordinate()
-elseif coord:IsInstanceOf("UNIT")then
-text="WARNING: ARTY:AssignTargetCoordinate(coord, ...) needs a COORDINATE object as first parameter - you gave a UNIT. Converting to COORDINATE..."
-coord=coord:GetCoordinate()
-elseif coord:IsInstanceOf("POSITIONABLE")then
-text="WARNING: ARTY:AssignTargetCoordinate(coord, ...) needs a COORDINATE object as first parameter - you gave a POSITIONABLE. Converting to COORDINATE..."
-coord=coord:GetCoordinate()
-elseif coord:IsInstanceOf("COORDINATE")then
-else
-text="ERROR: ARTY:AssignTargetCoordinate(coord, ...) needs a COORDINATE object as first parameter!"
-MESSAGE:New(text,30):ToAll()
-self:E(self.lid..text)
-return nil
-end
-if text~=nil then
-self:E(self.lid..text)
-end
-local _name=name or coord:ToStringLLDMS()
-local _unique=true
-_name,_unique=self:_CheckName(self.targets,_name,not unique)
-if unique==true and _unique==false then
-self:T(self.lid..string.format("%s: target %s should have a unique name but name was already given. Rejecting target!",self.groupname,_name))
-return nil
-end
-local _time
-if type(time)=="string"then
-_time=self:_ClockToSeconds(time)
-elseif type(time)=="number"then
-_time=timer.getAbsTime()+time
-else
-_time=timer.getAbsTime()
-end
-local _target={name=_name,coord=coord,radius=radius,nshells=nshells,engaged=0,underfire=false,prio=prio,maxengage=maxengage,time=_time,weapontype=weapontype}
-table.insert(self.targets,_target)
-self:__NewTarget(1,_target)
-return _name
-end
-function ARTY:AssignAttackGroup(group,prio,radius,nshells,maxengage,time,weapontype,name,unique)
-nshells=nshells or 5
-radius=radius or 100
-maxengage=maxengage or 1
-prio=prio or 50
-prio=math.max(1,prio)
-prio=math.min(100,prio)
-if unique==nil then
-unique=false
-end
-weapontype=weapontype or ARTY.WeaponType.Auto
-if type(group)=="string"then
-group=GROUP:FindByName(group)
-end
-if group and group:IsAlive()then
-local coord=group:GetCoordinate()
-local _name=group:GetName()
-local _unique=true
-_name,_unique=self:_CheckName(self.targets,_name,not unique)
-if unique==true and _unique==false then
-self:T(self.lid..string.format("%s: target %s should have a unique name but name was already given. Rejecting target!",self.groupname,_name))
-return nil
-end
-local _time
-if type(time)=="string"then
-_time=self:_ClockToSeconds(time)
-elseif type(time)=="number"then
-_time=timer.getAbsTime()+time
-else
-_time=timer.getAbsTime()
-end
-local target={}
-target.attackgroup=true
-target.name=_name
-target.coord=coord
-target.radius=radius
-target.nshells=nshells
-target.engaged=0
-target.underfire=false
-target.prio=prio
-target.time=_time
-target.maxengage=maxengage
-target.weapontype=weapontype
-table.insert(self.targets,target)
-self:__NewTarget(1,target)
-return _name
-else
-self:E("ERROR: Group does not exist!")
-end
-return nil
-end
-function ARTY:AssignMoveCoord(coord,time,speed,onroad,cancel,name,unique)
-self:F({coord=coord,time=time,speed=speed,onroad=onroad,cancel=cancel,name=name,unique=unique})
-if not self.ismobile then
-self:T(self.lid..string.format("%s: group is immobile. Rejecting move request!",self.groupname))
-return nil
-end
-if unique==nil then
-unique=false
-end
-local _name=name or coord:ToStringLLDMS()
-local _unique=true
-_name,_unique=self:_CheckName(self.moves,_name,not unique)
-if unique==true and _unique==false then
-self:T(self.lid..string.format("%s: move %s should have a unique name but name was already given. Rejecting move!",self.groupname,_name))
-return nil
-end
-if speed then
-speed=math.min(speed,self.SpeedMax)
-elseif self.Speed then
-speed=self.Speed
-else
-speed=self.SpeedMax*0.7
-end
-if onroad==nil then
-onroad=false
-end
-if cancel==nil then
-cancel=false
-end
-local _time
-if type(time)=="string"then
-_time=self:_ClockToSeconds(time)
-elseif type(time)=="number"then
-_time=timer.getAbsTime()+time
-else
-_time=timer.getAbsTime()
-end
-local _move={name=_name,coord=coord,time=_time,speed=speed,onroad=onroad,cancel=cancel}
-table.insert(self.moves,_move)
-return _name
-end
-function ARTY:SetAlias(alias)
-self:F({alias=alias})
-self.alias=tostring(alias)
-return self
-end
-function ARTY:AddToCluster(clusters)
-self:F({clusters=clusters})
-local names
-if type(clusters)=="table"then
-names=clusters
-elseif type(clusters)=="string"then
-names={clusters}
-else
-self:E(self.lid.."ERROR: Input parameter must be a string or a table in ARTY:AddToCluster()!")
-return
-end
-for _,cluster in pairs(names)do
-table.insert(self.clusters,cluster)
-end
-return self
-end
-function ARTY:SetMinFiringRange(range)
-self:F({range=range})
-self.minrange=range*1000 or 100
-return self
-end
-function ARTY:SetMaxFiringRange(range)
-self:F({range=range})
-self.maxrange=range*1000 or 1000*1000
-return self
-end
-function ARTY:SetStatusInterval(interval)
-self:F({interval=interval})
-self.StatusInterval=interval or 10
-return self
-end
-function ARTY:SetTrackInterval(interval)
-self.dtTrack=interval or 0.2
-return self
-end
-function ARTY:SetWaitForShotTime(waittime)
-self:F({waittime=waittime})
-self.WaitForShotTime=waittime or 300
-return self
-end
-function ARTY:SetRearmingDistance(distance)
-self:F({distance=distance})
-self.RearmingDistance=distance or 100
-return self
-end
-function ARTY:SetRearmingGroup(group)
-self:F({group=group})
-self.RearmingGroup=group
-return self
-end
-function ARTY:SetRearmingGroupSpeed(speed)
-self:F({speed=speed})
-self.RearmingGroupSpeed=speed
-return self
-end
-function ARTY:SetRearmingGroupOnRoad(onroad)
-self:F({onroad=onroad})
-if onroad==nil then
-onroad=true
-end
-self.RearmingGroupOnRoad=onroad
-return self
-end
-function ARTY:SetRearmingArtyOnRoad(onroad)
-self:F({onroad=onroad})
-if onroad==nil then
-onroad=true
-end
-self.RearmingArtyOnRoad=onroad
-return self
-end
-function ARTY:SetRearmingPlace(coord)
-self:F({coord=coord})
-self.RearmingPlaceCoord=coord
-return self
-end
-function ARTY:SetAutoRelocateToFiringRange(maxdistance,onroad)
-self:F({distance=maxdistance,onroad=onroad})
-self.autorelocate=true
-self.autorelocatemaxdist=maxdistance or 50
-self.autorelocatemaxdist=self.autorelocatemaxdist*1000
-if onroad==nil then
-onroad=false
-end
-self.autorelocateonroad=onroad
-return self
-end
-function ARTY:SetAutoRelocateAfterEngagement(rmax,rmin)
-self.relocateafterfire=true
-self.relocateRmax=rmax or 800
-self.relocateRmin=rmin or 300
-self.relocateRmin=math.min(self.relocateRmin,self.relocateRmax)
-return self
-end
-function ARTY:SetReportON()
-self.report=true
-return self
-end
-function ARTY:SetReportOFF()
-self.report=false
-return self
-end
-function ARTY:SetRespawnOnDeath(delay)
-self.respawnafterdeath=true
-self.respawndelay=delay
-return self
-end
-function ARTY:SetDebugON()
-self.Debug=true
-return self
-end
-function ARTY:SetDebugOFF()
-self.Debug=false
-return self
-end
-function ARTY:SetSpeed(speed)
-self.Speed=speed
-return self
-end
-function ARTY:RemoveTarget(name)
-self:F2(name)
-local id=self:_GetTargetIndexByName(name)
-if id then
-self:T(self.lid..string.format("Group %s: Removing target %s (id=%d).",self.groupname,name,id))
-table.remove(self.targets,id)
-if self.markallow then
-local batteryname,markTargetID,markMoveID=self:_GetMarkIDfromName(name)
-if batteryname==self.groupname and markTargetID~=nil then
-COORDINATE:RemoveMark(markTargetID)
-end
-end
-end
-self:T(self.lid..string.format("Group %s: Number of targets = %d.",self.groupname,#self.targets))
-end
-function ARTY:RemoveMove(name)
-self:F2(name)
-local id=self:_GetMoveIndexByName(name)
-if id then
-self:T(self.lid..string.format("Group %s: Removing move %s (id=%d).",self.groupname,name,id))
-table.remove(self.moves,id)
-if self.markallow then
-local batteryname,markTargetID,markMoveID=self:_GetMarkIDfromName(name)
-if batteryname==self.groupname and markMoveID~=nil then
-COORDINATE:RemoveMark(markMoveID)
-end
-end
-end
-self:T(self.lid..string.format("Group %s: Number of moves = %d.",self.groupname,#self.moves))
-end
-function ARTY:RemoveAllTargets()
-self:F2()
-for _,target in pairs(self.targets)do
-self:RemoveTarget(target.name)
-end
-end
-function ARTY:SetShellTypes(tableofnames)
-self:F2(tableofnames)
-self.ammoshells={}
-for _,_type in pairs(tableofnames)do
-table.insert(self.ammoshells,_type)
-end
-return self
-end
-function ARTY:SetRocketTypes(tableofnames)
-self:F2(tableofnames)
-self.ammorockets={}
-for _,_type in pairs(tableofnames)do
-table.insert(self.ammorockets,_type)
-end
-return self
-end
-function ARTY:SetMissileTypes(tableofnames)
-self:F2(tableofnames)
-self.ammomissiles={}
-for _,_type in pairs(tableofnames)do
-table.insert(self.ammomissiles,_type)
-end
-return self
-end
-function ARTY:SetTacNukeShells(n)
-self.Nukes=n
-return self
-end
-function ARTY:SetTacNukeWarhead(strength)
-self.nukewarhead=strength or 0.075
-self.nukewarhead=self.nukewarhead*1000*1000
-return self
-end
-function ARTY:SetIlluminationShells(n,power)
-self.Nillu=n
-self.illuPower=power or 1.0
-self.illuPower=self.illuPower*1000000
-return self
-end
-function ARTY:SetIlluminationMinMaxAlt(minalt,maxalt)
-self.illuMinalt=minalt or 500
-self.illuMaxalt=maxalt or 1000
-if self.illuMinalt>self.illuMaxalt then
-self.illuMinalt=self.illuMaxalt
-end
-return self
-end
-function ARTY:SetSmokeShells(n,color)
-self.Nsmoke=n
-self.smokeColor=color or SMOKECOLOR.Red
-return self
-end
-function ARTY:SetTacNukeFires(nfires,range)
-self.nukefire=true
-self.nukefires=nfires
-self.nukerange=range
-return self
-end
-function ARTY:SetMarkAssignmentsOn(key,readonly)
-self.markkey=key
-self.markallow=true
-if readonly==nil then
-self.markreadonly=false
-end
-return self
-end
-function ARTY:SetMarkTargetsOff()
-self.markallow=false
-self.markkey=nil
-return self
-end
-function ARTY:onafterStart(Controllable,From,Event,To)
-self:_EventFromTo("onafterStart",Event,From,To)
-local text=string.format("Started ARTY version %s for group %s.",ARTY.version,Controllable:GetName())
-self:I(self.lid..text)
-MESSAGE:New(text,5):ToAllIf(self.Debug)
-self.Nammo0,self.Nshells0,self.Nrockets0,self.Nmissiles0=self:GetAmmo(self.Debug)
-if self.nukerange==nil then
-self.nukerange=1500/75000*self.nukewarhead
-end
-if self.nukefires==nil then
-self.nukefires=20/1000/1000*self.nukerange*self.nukerange
-end
-if self.Nukes~=nil then
-self.Nukes0=math.min(self.Nukes,self.Nshells0)
-else
-self.Nukes=0
-self.Nukes0=0
-end
-if self.Nillu~=nil then
-self.Nillu0=math.min(self.Nillu,self.Nshells0)
-else
-self.Nillu=0
-self.Nillu0=0
-end
-if self.Nsmoke~=nil then
-self.Nsmoke0=math.min(self.Nsmoke,self.Nshells0)
-else
-self.Nsmoke=0
-self.Nsmoke0=0
-end
-local _dbproperties=self:_CheckDB(self.DisplayName)
-self:T({dbproperties=_dbproperties})
-if _dbproperties~=nil then
-for property,value in pairs(_dbproperties)do
-self:T({property=property,value=value})
-self[property]=value
-end
-end
-if not self.ismobile then
-self.RearmingPlaceCoord=nil
-self.relocateafterfire=false
-self.autorelocate=false
-end
-self.Speed=math.min(self.Speed,self.SpeedMax)
-if self.RearmingGroup then
-local speedmax=self.RearmingGroup:GetSpeedMax()
-self:T(self.lid..string.format("%s, rearming group %s max speed = %.1f km/h.",self.groupname,self.RearmingGroup:GetName(),speedmax))
-if self.RearmingGroupSpeed==nil then
-self.RearmingGroupSpeed=speedmax*0.5
-else
-self.RearmingGroupSpeed=math.min(self.RearmingGroupSpeed,self.RearmingGroup:GetSpeedMax())
-end
-else
-self.RearmingGroupSpeed=23
-end
-local text=string.format("\n******************************************************\n")
-text=text..string.format("Arty group = %s\n",self.groupname)
-text=text..string.format("Arty alias = %s\n",self.alias)
-text=text..string.format("Artillery attribute = %s\n",tostring(self.IsArtillery))
-text=text..string.format("Type = %s\n",self.Type)
-text=text..string.format("Display Name = %s\n",self.DisplayName)
-text=text..string.format("Number of units = %d\n",self.IniGroupStrength)
-text=text..string.format("Speed max = %d km/h\n",self.SpeedMax)
-text=text..string.format("Speed default = %d km/h\n",self.Speed)
-text=text..string.format("Is mobile = %s\n",tostring(self.ismobile))
-text=text..string.format("Is cargo = %s\n",tostring(self.iscargo))
-text=text..string.format("Min range = %.1f km\n",self.minrange/1000)
-text=text..string.format("Max range = %.1f km\n",self.maxrange/1000)
-text=text..string.format("Total ammo count = %d\n",self.Nammo0)
-text=text..string.format("Number of shells = %d\n",self.Nshells0)
-text=text..string.format("Number of rockets = %d\n",self.Nrockets0)
-text=text..string.format("Number of missiles = %d\n",self.Nmissiles0)
-text=text..string.format("Number of nukes = %d\n",self.Nukes0)
-text=text..string.format("Nuclear warhead = %d tons TNT\n",self.nukewarhead/1000)
-text=text..string.format("Nuclear demolition = %d m\n",self.nukerange)
-text=text..string.format("Nuclear fires = %d (active=%s)\n",self.nukefires,tostring(self.nukefire))
-text=text..string.format("Number of illum. = %d\n",self.Nillu0)
-text=text..string.format("Illuminaton Power = %.3f mcd\n",self.illuPower/1000000)
-text=text..string.format("Illuminaton Minalt = %d m\n",self.illuMinalt)
-text=text..string.format("Illuminaton Maxalt = %d m\n",self.illuMaxalt)
-text=text..string.format("Number of smoke = %d\n",self.Nsmoke0)
-text=text..string.format("Smoke color = %d\n",self.smokeColor)
-if self.RearmingGroup or self.RearmingPlaceCoord then
-text=text..string.format("Rearming safe dist. = %d m\n",self.RearmingDistance)
-end
-if self.RearmingGroup then
-text=text..string.format("Rearming group = %s\n",self.RearmingGroup:GetName())
-text=text..string.format("Rearming group speed= %d km/h\n",self.RearmingGroupSpeed)
-text=text..string.format("Rearming group roads= %s\n",tostring(self.RearmingGroupOnRoad))
-end
-if self.RearmingPlaceCoord then
-local dist=self.InitialCoord:Get2DDistance(self.RearmingPlaceCoord)
-text=text..string.format("Rearming coord dist = %d m\n",dist)
-text=text..string.format("Rearming ARTY roads = %s\n",tostring(self.RearmingArtyOnRoad))
-end
-text=text..string.format("Relocate after fire = %s\n",tostring(self.relocateafterfire))
-text=text..string.format("Relocate min dist. = %d m\n",self.relocateRmin)
-text=text..string.format("Relocate max dist. = %d m\n",self.relocateRmax)
-text=text..string.format("Auto move in range = %s\n",tostring(self.autorelocate))
-text=text..string.format("Auto move dist. max = %.1f km\n",self.autorelocatemaxdist/1000)
-text=text..string.format("Auto move on road = %s\n",tostring(self.autorelocateonroad))
-text=text..string.format("Marker assignments = %s\n",tostring(self.markallow))
-text=text..string.format("Marker auth. key = %s\n",tostring(self.markkey))
-text=text..string.format("Marker readonly = %s\n",tostring(self.markreadonly))
-text=text..string.format("Clusters:\n")
-for _,cluster in pairs(self.clusters)do
-text=text..string.format("- %s\n",tostring(cluster))
-end
-text=text..string.format("******************************************************\n")
-text=text..string.format("Targets:\n")
-for _,target in pairs(self.targets)do
-text=text..string.format("- %s\n",self:_TargetInfo(target))
-local possible=self:_CheckWeaponTypePossible(target)
-if not possible then
-self:E(self.lid..string.format("WARNING: Selected weapon type %s is not possible",self:_WeaponTypeName(target.weapontype)))
-end
-if self.Debug then
-local zone=ZONE_RADIUS:New(target.name,target.coord:GetVec2(),target.radius)
-zone:BoundZone(180,coalition.side.NEUTRAL)
-end
-end
-text=text..string.format("Moves:\n")
-for i=1,#self.moves do
-text=text..string.format("- %s\n",self:_MoveInfo(self.moves[i]))
-end
-text=text..string.format("******************************************************\n")
-text=text..string.format("Shell types:\n")
-for _,_type in pairs(self.ammoshells)do
-text=text..string.format("- %s\n",_type)
-end
-text=text..string.format("Rocket types:\n")
-for _,_type in pairs(self.ammorockets)do
-text=text..string.format("- %s\n",_type)
-end
-text=text..string.format("Missile types:\n")
-for _,_type in pairs(self.ammomissiles)do
-text=text..string.format("- %s\n",_type)
-end
-text=text..string.format("******************************************************")
-if self.Debug then
-self:I(self.lid..text)
-else
-self:T(self.lid..text)
-end
-self.Controllable:OptionROEHoldFire()
-self:HandleEvent(EVENTS.Shot)
-self:HandleEvent(EVENTS.Dead)
-if self.markallow then
-world.addEventHandler(self)
-end
-self:__Status(self.StatusInterval)
-end
-function ARTY:_CheckDB(displayname)
-for _type,_properties in pairs(ARTY.db)do
-self:T({type=_type,properties=_properties})
-if _type==displayname then
-self:T({type=_type,properties=_properties})
-return _properties
-end
-end
-return nil
-end
-function ARTY:_StatusReport(display)
-if display==nil then
-display=false
-end
-local Nammo,Nshells,Nrockets,Nmissiles=self:GetAmmo()
-local Nnukes=self.Nukes
-local Nillu=self.Nillu
-local Nsmoke=self.Nsmoke
-local Tnow=timer.getTime()
-local Clock=self:_SecondsToClock(timer.getAbsTime())
-local text=string.format("\n******************* STATUS ***************************\n")
-text=text..string.format("ARTY group = %s\n",self.groupname)
-text=text..string.format("Clock = %s\n",Clock)
-text=text..string.format("FSM state = %s\n",self:GetState())
-text=text..string.format("Total ammo count = %d\n",Nammo)
-text=text..string.format("Number of shells = %d\n",Nshells)
-text=text..string.format("Number of rockets = %d\n",Nrockets)
-text=text..string.format("Number of missiles = %d\n",Nmissiles)
-text=text..string.format("Number of nukes = %d\n",Nnukes)
-text=text..string.format("Number of illum. = %d\n",Nillu)
-text=text..string.format("Number of smoke = %d\n",Nsmoke)
-if self.currentTarget then
-text=text..string.format("Current Target = %s\n",tostring(self.currentTarget.name))
-text=text..string.format("Curr. Tgt assigned = %d\n",Tnow-self.currentTarget.Tassigned)
-else
-text=text..string.format("Current Target = %s\n","none")
-end
-text=text..string.format("Nshots curr. Target = %d\n",self.Nshots)
-text=text..string.format("Targets:\n")
-for i=1,#self.targets do
-text=text..string.format("- %s\n",self:_TargetInfo(self.targets[i]))
-end
-if self.currentMove then
-text=text..string.format("Current Move = %s\n",tostring(self.currentMove.name))
-else
-text=text..string.format("Current Move = %s\n","none")
-end
-text=text..string.format("Moves:\n")
-for i=1,#self.moves do
-text=text..string.format("- %s\n",self:_MoveInfo(self.moves[i]))
-end
-text=text..string.format("******************************************************")
-env.info(self.lid..text)
-MESSAGE:New(text,20):Clear():ToCoalitionIf(self.coalition,display)
-end
-function ARTY._FuncTrack(weapon,self,target)
-local _coord=weapon.coordinate
-local _dist=_coord:Get2DDistance(target.coord)
-local _destroyweapon=false
-self:T3(self.lid..string.format("ARTY %s weapon to target dist = %d m",self.groupname,_dist))
-if target.weapontype==ARTY.WeaponType.IlluminationShells then
-if _dist0
-local _trackillu=self.currentTarget.weapontype==ARTY.WeaponType.IlluminationShells and self.Nillu>0
-local _tracksmoke=self.currentTarget.weapontype==ARTY.WeaponType.SmokeShells and self.Nsmoke>0
-if _tracknuke or _trackillu or _tracksmoke then
-self:T(self.lid..string.format("ARTY %s: Tracking of weapon starts in two seconds.",self.groupname))
-local weapon=WEAPON:New(EventData.weapon)
-weapon:SetTimeStepTrack(self.dtTrack)
-local target=UTILS.DeepCopy(self.currentTarget)
-weapon:SetFuncTrack(ARTY._FuncTrack,self,target)
-weapon:SetFuncImpact(ARTY._FuncImpact,self,target)
-weapon:StartTrack(2)
-end
-local _nammo,_nshells,_nrockets,_nmissiles=self:GetAmmo()
-if self.currentTarget.weapontype==ARTY.WeaponType.TacticalNukes then
-self.Nukes=self.Nukes-1
-end
-if self.currentTarget.weapontype==ARTY.WeaponType.IlluminationShells then
-self.Nillu=self.Nillu-1
-end
-if self.currentTarget.weapontype==ARTY.WeaponType.SmokeShells then
-self.Nsmoke=self.Nsmoke-1
-end
-local _outofammo=false
-if _nammo==0 then
-self:T(self.lid..string.format("Group %s completely out of ammo.",self.groupname))
-_outofammo=true
-end
-local _partlyoutofammo=self:_CheckOutOfAmmo({self.currentTarget})
-local _weapontype=self:_WeaponTypeName(self.currentTarget.weapontype)
-self:T(self.lid..string.format("Group %s ammo: total=%d, shells=%d, rockets=%d, missiles=%d",self.groupname,_nammo,_nshells,_nrockets,_nmissiles))
-self:T(self.lid..string.format("Group %s uses weapontype %s for current target.",self.groupname,_weapontype))
-local _ceasefire=false
-local _relocate=false
-if self.Nshots>=self.currentTarget.nshells then
-local text=string.format("Group %s stop firing on target %s.",self.groupname,self.currentTarget.name)
-self:T(self.lid..text)
-MESSAGE:New(text,5):ToAllIf(self.Debug)
-_ceasefire=true
-_relocate=self.relocateafterfire
-end
-if _outofammo or _partlyoutofammo then
-_ceasefire=true
-end
-if _relocate then
-self:_Relocate()
-end
-if _ceasefire then
-self:CeaseFire(self.currentTarget)
-end
-else
-self:E(self.lid..string.format("WARNING: No current target for group %s?!",self.groupname))
-end
-end
-end
-end
-function ARTY:onEvent(Event)
-if Event==nil or Event.idx==nil then
-self:T3("Skipping onEvent. Event or Event.idx unknown.")
-return true
-end
-self:T2(string.format("Event captured = %s",tostring(self.groupname)))
-self:T2(string.format("Event id = %s",tostring(Event.id)))
-self:T2(string.format("Event time = %s",tostring(Event.time)))
-self:T2(string.format("Event idx = %s",tostring(Event.idx)))
-self:T2(string.format("Event coalition = %s",tostring(Event.coalition)))
-self:T2(string.format("Event group id = %s",tostring(Event.groupID)))
-self:T2(string.format("Event text = %s",tostring(Event.text)))
-if Event.initiator~=nil then
-local _unitname=Event.initiator:getName()
-self:T2(string.format("Event ini unit name = %s",tostring(_unitname)))
-end
-if Event.id==world.event.S_EVENT_MARK_ADDED then
-self:T2({event="S_EVENT_MARK_ADDED",battery=self.groupname,vec3=Event.pos})
-elseif Event.id==world.event.S_EVENT_MARK_CHANGE then
-self:T({event="S_EVENT_MARK_CHANGE",battery=self.groupname,vec3=Event.pos})
-self:_OnEventMarkChange(Event)
-elseif Event.id==world.event.S_EVENT_MARK_REMOVED then
-self:T2({event="S_EVENT_MARK_REMOVED",battery=self.groupname,vec3=Event.pos})
-self:_OnEventMarkRemove(Event)
-end
-end
-function ARTY:_OnEventMarkRemove(Event)
-local batterycoalition=self.coalition
-if Event.text~=nil and Event.text:find("BATTERY")then
-local _cancelmove=false
-local _canceltarget=false
-local _name=""
-local _id=nil
-if Event.text:find("Marked Relocation")then
-_cancelmove=true
-_name=self:_MarkMoveName(Event.idx)
-_id=self:_GetMoveIndexByName(_name)
-elseif Event.text:find("Marked Target")then
-_canceltarget=true
-_name=self:_MarkTargetName(Event.idx)
-_id=self:_GetTargetIndexByName(_name)
-else
-return
-end
-if _id==nil then
-return
-end
-if(batterycoalition==Event.coalition and self.markkey==nil)or self.markkey~=nil then
-local _validkey=self:_MarkerKeyAuthentification(Event.text)
-if _validkey then
-if _cancelmove then
-if self.currentMove and self.currentMove.name==_name then
-self.Controllable:ClearTasks()
-self:Arrived()
-else
-self:RemoveMove(_name)
-end
-elseif _canceltarget then
-if self.currentTarget and self.currentTarget.name==_name then
-self:CeaseFire(self.currentTarget)
-self:RemoveTarget(_name)
-else
-self:RemoveTarget(_name)
-end
-end
-end
-end
-end
-end
-function ARTY:_OnEventMarkChange(Event)
-if Event.text~=nil and Event.text:lower():find("arty")then
-local vec3={y=Event.pos.y,x=Event.pos.x,z=Event.pos.z}
-local _coord=COORDINATE:NewFromVec3(vec3)
-_coord.y=_coord:GetLandHeight()
-local batterycoalition=self.coalition
-local batteryname=self.groupname
-if(batterycoalition==Event.coalition and self.markkey==nil)or self.markkey~=nil then
-local _assign=self:_Markertext(Event.text)
-if _assign==nil or not(_assign.engage or _assign.move or _assign.request or _assign.cancel or _assign.set)then
-self:T(self.lid..string.format("WARNING: %s, no keyword ENGAGE, MOVE, REQUEST, CANCEL or SET in mark text! Command will not be executed. Text:\n%s",self.groupname,Event.text))
-return
-end
-local _assigned=false
-if _assign.everyone then
-_assigned=true
-else
-for _,bat in pairs(_assign.battery)do
-if self.groupname==bat then
-_assigned=true
-end
-end
-for _,alias in pairs(_assign.aliases)do
-if self.alias==alias then
-_assigned=true
-end
-end
-for _,bat in pairs(_assign.cluster)do
-for _,cluster in pairs(self.clusters)do
-if cluster==bat then
-_assigned=true
-end
-end
-end
-end
-if not _assigned then
-self:T3(self.lid..string.format("INFO: ARTY group %s was not addressed! Mark text:\n%s",self.groupname,Event.text))
-return
-else
-if self.Controllable and self.Controllable:IsAlive()then
-else
-self:T3(self.lid..string.format("INFO: ARTY group %s was addressed but is NOT alive! Mark text:\n%s",self.groupname,Event.text))
-return
-end
-end
-if _assign.coord then
-_coord=_assign.coord
-end
-local _validkey=self:_MarkerKeyAuthentification(Event.text)
-if _assign.request and _validkey then
-if _assign.requestammo then
-self:_MarkRequestAmmo()
-end
-if _assign.requestmoves then
-self:_MarkRequestMoves()
-end
-if _assign.requesttargets then
-self:_MarkRequestTargets()
-end
-if _assign.requeststatus then
-self:_MarkRequestStatus()
-end
-if _assign.requestrearming then
-self:Rearm()
-end
-return
-end
-if _assign.cancel and _validkey then
-if _assign.cancelmove and self.currentMove then
-self.Controllable:ClearTasks()
-self:Arrived()
-elseif _assign.canceltarget and self.currentTarget then
-self.currentTarget.engaged=self.currentTarget.engaged+1
-self:CeaseFire(self.currentTarget)
-elseif _assign.cancelrearm and self:is("Rearming")then
-local nammo=self:GetAmmo()
-if nammo>0 then
-self:Rearmed()
-else
-self:Winchester()
-end
-end
-return
-end
-if _assign.set and _validkey then
-if _assign.setrearmingplace and self.ismobile then
-self:SetRearmingPlace(_coord)
-_coord:RemoveMark(Event.idx)
-_coord:MarkToCoalition(string.format("Rearming place for battery %s",self.groupname),self.coalition,false,string.format("New rearming place for battery %s defined.",self.groupname))
-if self.Debug then
-_coord:SmokeOrange()
-end
-end
-if _assign.setrearminggroup then
-_coord:RemoveMark(Event.idx)
-local rearminggroupcoord=_assign.setrearminggroup:GetCoordinate()
-rearminggroupcoord:MarkToCoalition(string.format("Rearming group for battery %s",self.groupname),self.coalition,false,string.format("New rearming group for battery %s defined.",self.groupname))
-self:SetRearmingGroup(_assign.setrearminggroup)
-if self.Debug then
-rearminggroupcoord:SmokeOrange()
-end
-end
-return
-end
-if _validkey then
-_coord:RemoveMark(Event.idx)
-local _id=UTILS._MarkID+1
-if _assign.move then
-local _name=self:_MarkMoveName(_id)
-local text=string.format("%s, received new relocation assignment.",self.alias)
-text=text..string.format("\nCoordinates %s",_coord:ToStringLLDMS())
-MESSAGE:New(text,10):ToCoalitionIf(batterycoalition,self.report or self.Debug)
-local _movename=self:AssignMoveCoord(_coord,_assign.time,_assign.speed,_assign.onroad,_assign.movecanceltarget,_name,true)
-if _movename~=nil then
-local _mid=self:_GetMoveIndexByName(_movename)
-local _move=self.moves[_mid]
-local clock=tostring(self:_SecondsToClock(_move.time))
-local _markertext=_movename..string.format(", Time=%s, Speed=%d km/h, Use Roads=%s.",clock,_move.speed,tostring(_move.onroad))
-local _randomcoord=_coord:GetRandomCoordinateInRadius(100)
-_randomcoord:MarkToCoalition(_markertext,batterycoalition,self.markreadonly or _assign.readonly)
-else
-local text=string.format("%s, relocation not possible.",self.alias)
-MESSAGE:New(text,10):ToCoalitionIf(batterycoalition,self.report or self.Debug)
-end
-else
-local _name=self:_MarkTargetName(_id)
-local text=string.format("%s, received new target assignment.",self.alias)
-text=text..string.format("\nCoordinates %s",_coord:ToStringLLDMS())
-if _assign.time then
-text=text..string.format("\nTime %s",_assign.time)
-end
-if _assign.prio then
-text=text..string.format("\nPrio %d",_assign.prio)
-end
-if _assign.radius then
-text=text..string.format("\nRadius %d m",_assign.radius)
-end
-if _assign.nshells then
-text=text..string.format("\nShots %d",_assign.nshells)
-end
-if _assign.maxengage then
-text=text..string.format("\nEngagements %d",_assign.maxengage)
-end
-if _assign.weapontype then
-text=text..string.format("\nWeapon %s",self:_WeaponTypeName(_assign.weapontype))
-end
-MESSAGE:New(text,10):ToCoalitionIf(batterycoalition,self.report or self.Debug)
-local _targetname=self:AssignTargetCoord(_coord,_assign.prio,_assign.radius,_assign.nshells,_assign.maxengage,_assign.time,_assign.weapontype,_name,true)
-if _targetname~=nil then
-local _tid=self:_GetTargetIndexByName(_targetname)
-local _target=self.targets[_tid]
-local clock=tostring(self:_SecondsToClock(_target.time))
-local weapon=self:_WeaponTypeName(_target.weapontype)
-local _markertext=_targetname..string.format(", Priority=%d, Radius=%d m, Shots=%d, Engagements=%d, Weapon=%s, Time=%s",_target.prio,_target.radius,_target.nshells,_target.maxengage,weapon,clock)
-local _randomcoord=_coord:GetRandomCoordinateInRadius(250)
-_randomcoord:MarkToCoalition(_markertext,batterycoalition,self.markreadonly or _assign.readonly)
-end
-end
-end
-end
-end
-end
-function ARTY:OnEventDead(EventData)
-self:F(EventData)
-local _name=self.groupname
-if EventData and EventData.IniGroupName and EventData.IniGroupName==_name then
-local unitname=tostring(EventData.IniUnitName)
-self:T(self.lid..string.format("%s: Captured dead event for unit %s.",_name,unitname))
-self:Dead(unitname)
-end
-end
-function ARTY:onafterStatus(Controllable,From,Event,To)
-self:_EventFromTo("onafterStatus",Event,From,To)
-local nammo,nshells,nrockets,nmissiles=self:GetAmmo()
-if self.iscargo and self.cargogroup then
-if self.cargogroup:IsLoaded()and not self:is("InTransit")then
-self:T(self.lid..string.format("Group %s has been loaded into a carrier and is now transported.",self.alias))
-self:Loaded()
-elseif self.cargogroup:IsUnLoaded()then
-self:T(self.lid..string.format("Group %s has been unloaded from the carrier.",self.alias))
-self:UnLoaded()
-end
-end
-local fsmstate=self:GetState()
-self:T(self.lid..string.format("Status %s, Ammo total=%d: shells=%d [smoke=%d, illu=%d, nukes=%d*%.3f kT], rockets=%d, missiles=%d",fsmstate,nammo,nshells,self.Nsmoke,self.Nillu,self.Nukes,self.nukewarhead/1000000,nrockets,nmissiles))
-if self.Controllable and self.Controllable:IsAlive()then
-if self.Debug then
-self:_StatusReport()
-end
-if self:is("Moving")then
-self:T2(self.lid..string.format("%s: Moving",Controllable:GetName()))
-end
-if self:is("Rearming")then
-local _rearmed=self:_CheckRearmed()
-if _rearmed then
-self:T2(self.lid..string.format("%s: Rearming ==> Rearmed",Controllable:GetName()))
-self:Rearmed()
-end
-end
-if self:is("Rearmed")then
-local distance=self.Controllable:GetCoordinate():Get2DDistance(self.InitialCoord)
-self:T2(self.lid..string.format("%s: Rearmed. Distance ARTY to InitalCoord = %d m",Controllable:GetName(),distance))
-if distance<=self.RearmingDistance then
-self:T2(self.lid..string.format("%s: Rearmed ==> CombatReady",Controllable:GetName()))
-self:CombatReady()
-end
-end
-if self:is("Arrived")then
-self:T2(self.lid..string.format("%s: Arrived ==> CombatReady",Controllable:GetName()))
-self:CombatReady()
-end
-if self:is("Firing")then
-self:_CheckShootingStarted()
-end
-self:_CheckTargetsInRange()
-local notpossible={}
-for i=1,#self.targets do
-local _target=self.targets[i]
-local possible=self:_CheckWeaponTypePossible(_target)
-if not possible then
-table.insert(notpossible,_target.name)
-end
-end
-for _,targetname in pairs(notpossible)do
-self:E(self.lid..string.format("%s: Removing target %s because requested weapon is not possible with this type of unit.",self.groupname,targetname))
-self:RemoveTarget(targetname)
-end
-local _timedTarget=self:_CheckTimedTargets()
-local _normalTarget=self:_CheckNormalTargets()
-local _move=self:_CheckMoves()
-if _move then
-self:Move(_move)
-elseif _timedTarget then
-if self.currentTarget then
-self:CeaseFire(self.currentTarget)
-end
-self:OpenFire(_timedTarget)
-elseif _normalTarget then
-self:OpenFire(_normalTarget)
-end
-local gotsome=false
-if#self.targets>0 then
-for i=1,#self.targets do
-local _target=self.targets[i]
-if self:_CheckWeaponTypeAvailable(_target)>0 then
-gotsome=true
-end
-end
-else
-gotsome=true
-end
-if(nammo==0 or not gotsome)and not(self:is("Moving")or self:is("Rearming")or self:is("OutOfAmmo"))then
-self:Winchester()
-end
-if self:is("OutOfAmmo")then
-self:T2(self.lid..string.format("%s: OutOfAmmo ==> Rearm ==> Rearming",Controllable:GetName()))
-self:Rearm()
-end
-self:__Status(self.StatusInterval)
-elseif self.iscargo then
-if self.cargogroup and self.cargogroup:IsAlive()then
-if self:is("InTransit")then
-self:__Status(-5)
-end
-end
-else
-self:E(self.lid..string.format("Arty group %s is not alive!",self.groupname))
-end
-end
-function ARTY:onbeforeLoaded(Controllable,From,Event,To)
-if self.currentTarget then
-self:CeaseFire(self.currentTarget)
-end
-return true
-end
-function ARTY:onafterUnLoaded(Controllable,From,Event,To)
-self:CombatReady()
-end
-function ARTY:onenterCombatReady(Controllable,From,Event,To)
-self:_EventFromTo("onenterCombatReady",Event,From,To)
-self:T3(self.lid..string.format("onenterComabReady, from=%s, event=%s, to=%s",From,Event,To))
-end
-function ARTY:onbeforeOpenFire(Controllable,From,Event,To,target)
-self:_EventFromTo("onbeforeOpenFire",Event,From,To)
-if self.currentTarget then
-self:E(self.lid..string.format("ERROR: Group %s already has a target %s!",self.groupname,self.currentTarget.name))
-return false
-end
-if not self:_TargetInRange(target)then
-self:E(self.lid..string.format("ERROR: Group %s, target %s is out of range!",self.groupname,self.currentTarget.name))
-return false
-end
-local nfire=self:_CheckWeaponTypeAvailable(target)
-target.nshells=math.min(target.nshells,nfire)
-if target.nshells<1 then
-local text=string.format("%s, no ammo left to engage target %s with selected weapon type %s.")
-return false
-end
-return true
-end
-function ARTY:onafterOpenFire(Controllable,From,Event,To,target)
-self:_EventFromTo("onafterOpenFire",Event,From,To)
-local id=self:_GetTargetIndexByName(target.name)
-if id then
-self.targets[id].underfire=true
-self.currentTarget=target
-self.currentTarget.Tassigned=timer.getTime()
-end
-local range=Controllable:GetCoordinate():Get2DDistance(target.coord)
-local Nammo,Nshells,Nrockets,Nmissiles=self:GetAmmo()
-local nfire=Nammo
-local _type="shots"
-if target.weapontype==ARTY.WeaponType.Auto then
-nfire=Nammo
-_type="shots"
-elseif target.weapontype==ARTY.WeaponType.Cannon then
-nfire=Nshells
-_type="shells"
-elseif target.weapontype==ARTY.WeaponType.TacticalNukes then
-nfire=self.Nukes
-_type="nuclear shells"
-elseif target.weapontype==ARTY.WeaponType.IlluminationShells then
-nfire=self.Nillu
-_type="illumination shells"
-elseif target.weapontype==ARTY.WeaponType.SmokeShells then
-nfire=self.Nsmoke
-_type="smoke shells"
-elseif target.weapontype==ARTY.WeaponType.Rockets then
-nfire=Nrockets
-_type="rockets"
-elseif target.weapontype==ARTY.WeaponType.CruiseMissile then
-nfire=Nmissiles
-_type="cruise missiles"
-end
-target.nshells=math.min(target.nshells,nfire)
-local text=string.format("%s, opening fire on target %s with %d %s. Distance %.1f km.",Controllable:GetName(),target.name,target.nshells,_type,range/1000)
-self:T(self.lid..text)
-MESSAGE:New(text,10):ToCoalitionIf(self.coalition,self.report)
-if target.attackgroup then
-self:_AttackGroup(target)
-else
-self:_FireAtCoord(target.coord,target.radius,target.nshells,target.weapontype)
-end
-end
-function ARTY:onafterCeaseFire(Controllable,From,Event,To,target)
-self:_EventFromTo("onafterCeaseFire",Event,From,To)
-if target then
-local text=string.format("%s, ceasing fire on target %s.",Controllable:GetName(),target.name)
-self:T(self.lid..text)
-MESSAGE:New(text,10):ToCoalitionIf(self.coalition,self.report)
-local id=self:_GetTargetIndexByName(target.name)
-if id then
-if self.Nshots>0 then
-self.targets[id].engaged=self.targets[id].engaged+1
-self.targets[id].time=nil
-end
-self.targets[id].underfire=false
-end
-if target.engaged>=target.maxengage then
-self:RemoveTarget(target.name)
-end
-self.Controllable:OptionROEHoldFire()
-self.Controllable:ClearTasks()
-else
-self:E(self.lid..string.format("ERROR: No target in cease fire for group %s.",self.groupname))
-end
-self.Nshots=0
-self.currentTarget=nil
-end
-function ARTY:onafterWinchester(Controllable,From,Event,To)
-self:_EventFromTo("onafterWinchester",Event,From,To)
-local text=string.format("%s, winchester!",Controllable:GetName())
-self:T(self.lid..text)
-MESSAGE:New(text,10):ToCoalitionIf(self.coalition,self.report or self.Debug)
-end
-function ARTY:onbeforeRearm(Controllable,From,Event,To)
-self:_EventFromTo("onbeforeRearm",Event,From,To)
-local _rearmed=self:_CheckRearmed()
-if _rearmed then
-self:T(self.lid..string.format("%s, group is already armed to the teeth. Rearming request denied!",self.groupname))
-return false
-else
-self:T(self.lid..string.format("%s, group might be rearmed.",self.groupname))
-end
-if self.RearmingGroup and self.RearmingGroup:IsAlive()then
-return true
-elseif self.RearmingPlaceCoord then
-return true
-else
-return false
-end
-end
-function ARTY:onafterRearm(Controllable,From,Event,To)
-self:_EventFromTo("onafterRearm",Event,From,To)
-local coordARTY=self.Controllable:GetCoordinate()
-self.InitialCoord=coordARTY
-local coordRARM=nil
-if self.RearmingGroup then
-coordRARM=self.RearmingGroup:GetCoordinate()
-self.RearmingGroupCoord=coordRARM
-end
-if self.RearmingGroup and self.RearmingPlaceCoord and self.ismobile then
-local text=string.format("%s, %s, request rearming at rearming place.",Controllable:GetName(),self.RearmingGroup:GetName())
-self:T(self.lid..text)
-MESSAGE:New(text,10):ToCoalitionIf(self.coalition,self.report or self.Debug)
-local dA=coordARTY:Get2DDistance(self.RearmingPlaceCoord)
-local dR=coordRARM:Get2DDistance(self.RearmingPlaceCoord)
-if dA>self.RearmingDistance then
-local _tocoord=self:_VicinityCoord(self.RearmingPlaceCoord,self.RearmingDistance/4,self.RearmingDistance/2)
-self:AssignMoveCoord(_tocoord,nil,nil,self.RearmingArtyOnRoad,false,"REARMING MOVE TO REARMING PLACE",true)
-end
-if dR>self.RearmingDistance then
-local ToCoord=self:_VicinityCoord(self.RearmingPlaceCoord,self.RearmingDistance/4,self.RearmingDistance/2)
-self:_Move(self.RearmingGroup,ToCoord,self.RearmingGroupSpeed,self.RearmingGroupOnRoad)
-end
-elseif self.RearmingGroup then
-local text=string.format("%s, %s, request rearming.",Controllable:GetName(),self.RearmingGroup:GetName())
-self:T(self.lid..text)
-MESSAGE:New(text,10):ToCoalitionIf(self.coalition,self.report or self.Debug)
-local distance=coordARTY:Get2DDistance(coordRARM)
-if distance>self.RearmingDistance then
-self:_Move(self.RearmingGroup,self:_VicinityCoord(coordARTY),self.RearmingGroupSpeed,self.RearmingGroupOnRoad)
-end
-elseif self.RearmingPlaceCoord then
-local text=string.format("%s, moving to rearming place.",Controllable:GetName())
-self:T(self.lid..text)
-MESSAGE:New(text,10):ToCoalitionIf(self.coalition,self.report or self.Debug)
-local dA=coordARTY:Get2DDistance(self.RearmingPlaceCoord)
-if dA>self.RearmingDistance then
-local _tocoord=self:_VicinityCoord(self.RearmingPlaceCoord)
-self:AssignMoveCoord(_tocoord,nil,nil,self.RearmingArtyOnRoad,false,"REARMING MOVE TO REARMING PLACE",true)
-end
-end
-end
-function ARTY:onafterRearmed(Controllable,From,Event,To)
-self:_EventFromTo("onafterRearmed",Event,From,To)
-local text=string.format("%s, rearming complete.",Controllable:GetName())
-self:T(self.lid..text)
-MESSAGE:New(text,10):ToCoalitionIf(self.coalition,self.report or self.Debug)
-self.Nukes=self.Nukes0
-self.Nillu=self.Nillu0
-self.Nsmoke=self.Nsmoke0
-local dist=self.Controllable:GetCoordinate():Get2DDistance(self.InitialCoord)
-if dist>self.RearmingDistance then
-self:AssignMoveCoord(self.InitialCoord,nil,nil,self.RearmingArtyOnRoad,false,"REARMING MOVE REARMING COMPLETE",true)
-end
-if self.RearmingGroup and self.RearmingGroup:IsAlive()then
-local d=self.RearmingGroup:GetCoordinate():Get2DDistance(self.RearmingGroupCoord)
-if d>self.RearmingDistance then
-self:_Move(self.RearmingGroup,self.RearmingGroupCoord,self.RearmingGroupSpeed,self.RearmingGroupOnRoad)
-else
-self.RearmingGroup:ClearTasks()
-end
-end
-end
-function ARTY:_CheckRearmed()
-self:F2()
-local nammo,nshells,nrockets,nmissiles=self:GetAmmo()
-local units=self.Controllable:GetUnits()
-local nunits=0
-if units then
-nunits=#units
-end
-local FullAmmo=self.Nammo0*nunits/self.IniGroupStrength
-local _rearmpc=nammo/FullAmmo*100
-if _rearmpc>1 then
-local text=string.format("%s, rearming %d %% complete.",self.alias,_rearmpc)
-self:T(self.lid..text)
-MESSAGE:New(text,10):ToCoalitionIf(self.coalition,self.report or self.Debug)
-end
-if nammo>=FullAmmo then
-return true
-else
-return false
-end
-end
-function ARTY:onbeforeMove(Controllable,From,Event,To,move)
-self:_EventFromTo("onbeforeMove",Event,From,To)
-if not self.ismobile then
-return false
-end
-if self.currentTarget then
-if move.cancel then
-self:CeaseFire(self.currentTarget)
-else
-return false
-end
-end
-return true
-end
-function ARTY:onafterMove(Controllable,From,Event,To,move)
-self:_EventFromTo("onafterMove",Event,From,To)
-self.Controllable:OptionAlarmStateGreen()
-self.Controllable:OptionROEHoldFire()
-local _Speed=math.min(move.speed,self.SpeedMax)
-if self.Debug then
-move.coord:SmokeRed()
-end
-self.currentMove=move
-self:_Move(self.Controllable,move.coord,move.speed,move.onroad)
-end
-function ARTY:onafterArrived(Controllable,From,Event,To)
-self:_EventFromTo("onafterArrived",Event,From,To)
-self.Controllable:OptionAlarmStateAuto()
-local text=string.format("%s, arrived at destination.",Controllable:GetName())
-self:T(self.lid..text)
-MESSAGE:New(text,10):ToCoalitionIf(self.coalition,self.report or self.Debug)
-if self.currentMove then
-self:RemoveMove(self.currentMove.name)
-self.currentMove=nil
-end
-end
-function ARTY:onafterNewTarget(Controllable,From,Event,To,target)
-self:_EventFromTo("onafterNewTarget",Event,From,To)
-local text=string.format("Adding new target %s.",target.name)
-MESSAGE:New(text,5):ToAllIf(self.Debug)
-self:T(self.lid..text)
-end
-function ARTY:onafterNewMove(Controllable,From,Event,To,move)
-self:_EventFromTo("onafterNewTarget",Event,From,To)
-local text=string.format("Adding new move %s.",move.name)
-MESSAGE:New(text,5):ToAllIf(self.Debug)
-self:T(self.lid..text)
-end
-function ARTY:onafterDead(Controllable,From,Event,To,Unitname)
-self:_EventFromTo("onafterDead",Event,From,To)
-local nunits=self.Controllable:CountAliveUnits()
-local text=string.format("%s, our unit %s just died! %d units left.",self.groupname,Unitname,nunits)
-MESSAGE:New(text,5):ToAllIf(self.Debug)
-self:I(self.lid..text)
-if nunits==0 then
-if self.currentTarget then
-self:CeaseFire(self.currentTarget)
-end
-if self.respawnafterdeath then
-if not self.respawning then
-self.respawning=true
-self:__Respawn(self.respawndelay or 1)
-end
-else
-self:Stop()
-end
-end
-end
-function ARTY:onafterRespawn(Controllable,From,Event,To)
-self:_EventFromTo("onafterRespawn",Event,From,To)
-local group=self.Controllable
-self.Controllable=group:Respawn()
-self.respawning=false
-self:__Status(-1)
-end
-function ARTY:onafterStop(Controllable,From,Event,To)
-self:_EventFromTo("onafterStop",Event,From,To)
-self:I(self.lid..string.format("Stopping ARTY FSM for group %s.",tostring(Controllable:GetName())))
-if self.currentTarget then
-self:CeaseFire(self.currentTarget)
-end
-self:UnHandleEvent(EVENTS.Shot)
-self:UnHandleEvent(EVENTS.Dead)
-end
-function ARTY:_FireAtCoord(coord,radius,nshells,weapontype)
-self:F({coord=coord,radius=radius,nshells=nshells})
-local group=self.Controllable
-if weapontype==ARTY.WeaponType.TacticalNukes or weapontype==ARTY.WeaponType.IlluminationShells or weapontype==ARTY.WeaponType.SmokeShells then
-weapontype=ARTY.WeaponType.Cannon
-end
-group:OptionROEOpenFire()
-local vec2=coord:GetVec2()
-local fire=group:TaskFireAtPoint(vec2,radius,nshells,weapontype)
-group:SetTask(fire)
-end
-function ARTY:_AttackGroup(target)
-local group=self.Controllable
-local weapontype=target.weapontype
-if weapontype==ARTY.WeaponType.TacticalNukes or weapontype==ARTY.WeaponType.IlluminationShells or weapontype==ARTY.WeaponType.SmokeShells then
-weapontype=ARTY.WeaponType.Cannon
-end
-group:OptionROEOpenFire()
-local targetgroup=GROUP:FindByName(target.name)
-local fire=group:TaskAttackGroup(targetgroup,weapontype,AI.Task.WeaponExpend.ONE,1)
-group:SetTask(fire)
-end
-function ARTY:_NuclearBlast(_coord)
-local S0=self.nukewarhead
-local R0=self.nukerange
-local N0=self.nukefires
-_coord:Explosion(S0)
-_coord:BigSmokeAndFireHuge()
-local _fires={}
-for i=1,N0 do
-local _fire=_coord:GetRandomCoordinateInRadius(R0)
-local _dist=_fire:Get2DDistance(_coord)
-table.insert(_fires,{distance=_dist,coord=_fire})
-end
-local _sort=function(a,b)return a.distance_nmax
-if _gotit then
-self:AssignMoveCoord(_new,nil,nil,false,false,"RELOCATION MOVE AFTER FIRING")
-end
-end
-function ARTY:GetAmmo(display)
-self:F3({display=display})
-if display==nil then
-display=false
-end
-local nammo=0
-local nshells=0
-local nrockets=0
-local nmissiles=0
-local units=self.Controllable:GetUnits()
-if units==nil then
-return nammo,nshells,nrockets,nmissiles
-end
-for _,_unit in pairs(units)do
-local unit=_unit
-if unit then
-local text=string.format("ARTY group %s - unit %s:\n",self.groupname,unit:GetName())
-local ammotable=unit:GetAmmo()
-if ammotable~=nil then
-local weapons=#ammotable
-if display then
-self:I(self.lid..string.format("Number of weapons %d.",weapons))
-self:I({ammotable=ammotable})
-self:I(self.lid.."Ammotable:")
-for id,bla in pairs(ammotable)do
-self:I({id=id,ammo=bla})
-end
-end
-for w=1,weapons do
-local Nammo=ammotable[w]["count"]
-local Tammo=ammotable[w]["desc"]["typeName"]
-local _weaponString=self:_split(Tammo,"%.")
-local _weaponName=_weaponString[#_weaponString]
-local Category=ammotable[w].desc.category
-local MissileCategory=nil
-if Category==Weapon.Category.MISSILE then
-MissileCategory=ammotable[w].desc.missileCategory
-end
-local _gotshell=false
-if#self.ammoshells>0 then
-for _,_type in pairs(self.ammoshells)do
-if string.match(Tammo,_type)and Category==Weapon.Category.SHELL then
-_gotshell=true
-end
-end
-else
-if Category==Weapon.Category.SHELL then
-_gotshell=true
-end
-end
-local _gotrocket=false
-if#self.ammorockets>0 then
-for _,_type in pairs(self.ammorockets)do
-if string.match(Tammo,_type)and Category==Weapon.Category.ROCKET then
-_gotrocket=true
-end
-end
-else
-if Category==Weapon.Category.ROCKET then
-_gotrocket=true
-end
-end
-local _gotmissile=false
-if#self.ammomissiles>0 then
-for _,_type in pairs(self.ammomissiles)do
-if string.match(Tammo,_type)and Category==Weapon.Category.MISSILE then
-_gotmissile=true
-end
-end
-else
-if Category==Weapon.Category.MISSILE then
-_gotmissile=true
-end
-end
-if _gotshell then
-nshells=nshells+Nammo
-text=text..string.format("- %d shells of type %s\n",Nammo,_weaponName)
-elseif _gotrocket then
-nrockets=nrockets+Nammo
-text=text..string.format("- %d rockets of type %s\n",Nammo,_weaponName)
-elseif _gotmissile then
-if MissileCategory==Weapon.MissileCategory.CRUISE then
-nmissiles=nmissiles+Nammo
-end
-text=text..string.format("- %d %s missiles of type %s\n",Nammo,self:_MissileCategoryName(MissileCategory),_weaponName)
-else
-text=text..string.format("- %d unknown ammo of type %s (category=%d, missile category=%s)\n",Nammo,Tammo,Category,tostring(MissileCategory))
-end
-end
-end
-if display then
-self:I(self.lid..text)
-else
-self:T3(self.lid..text)
-end
-MESSAGE:New(text,10):ToAllIf(display)
-end
-end
-nammo=nshells+nrockets+nmissiles
-return nammo,nshells,nrockets,nmissiles
-end
-function ARTY:_MissileCategoryName(categorynumber)
-local cat="unknown"
-if categorynumber==Weapon.MissileCategory.AAM then
-cat="air-to-air"
-elseif categorynumber==Weapon.MissileCategory.SAM then
-cat="surface-to-air"
-elseif categorynumber==Weapon.MissileCategory.BM then
-cat="ballistic"
-elseif categorynumber==Weapon.MissileCategory.ANTI_SHIP then
-cat="anti-ship"
-elseif categorynumber==Weapon.MissileCategory.CRUISE then
-cat="cruise"
-elseif categorynumber==Weapon.MissileCategory.OTHER then
-cat="other"
-end
-return cat
-end
-function ARTY:_MarkerKeyAuthentification(text)
-local batterycoalition=self.coalition
-local mykey=nil
-if self.markkey~=nil then
-local keywords=self:_split(text,",")
-for _,key in pairs(keywords)do
-local s=self:_split(key," ")
-local val=s[2]
-if key:lower():find("key")then
-mykey=tonumber(val)
-self:T(self.lid..string.format("Authorisation Key=%s.",val))
-end
-end
-end
-local _validkey=true
-if self.markkey~=nil then
-_validkey=false
-if mykey~=nil then
-_validkey=self.markkey==mykey
-end
-self:T2(self.lid..string.format("%s, authkey=%s == %s=playerkey ==> valid=%s",self.groupname,tostring(self.markkey),tostring(mykey),tostring(_validkey)))
-local text=""
-if mykey==nil then
-text=string.format("%s, authorization required but did not receive a key!",self.alias)
-elseif _validkey==false then
-text=string.format("%s, authorization required but did receive an incorrect key (key=%s)!",self.alias,tostring(mykey))
-elseif _validkey==true then
-text=string.format("%s, authentification successful!",self.alias)
-end
-MESSAGE:New(text,10):ToCoalitionIf(batterycoalition,self.report or self.Debug)
-end
-return _validkey
-end
-function ARTY:_Markertext(text)
-self:F(text)
-local assignment={}
-assignment.battery={}
-assignment.aliases={}
-assignment.cluster={}
-assignment.everyone=false
-assignment.move=false
-assignment.engage=false
-assignment.request=false
-assignment.cancel=false
-assignment.set=false
-assignment.readonly=false
-assignment.movecanceltarget=false
-assignment.cancelmove=false
-assignment.canceltarget=false
-assignment.cancelrearm=false
-assignment.setrearmingplace=false
-assignment.setrearminggroup=false
-if text:lower():find("arty engage")or text:lower():find("arty attack")then
-assignment.engage=true
-elseif text:lower():find("arty move")or text:lower():find("arty relocate")then
-assignment.move=true
-elseif text:lower():find("arty request")then
-assignment.request=true
-elseif text:lower():find("arty cancel")then
-assignment.cancel=true
-elseif text:lower():find("arty set")then
-assignment.set=true
-else
-self:E(self.lid..'ERROR: Neither "ARTY ENGAGE" nor "ARTY MOVE" nor "ARTY RELOCATE" nor "ARTY REQUEST" nor "ARTY CANCEL" nor "ARTY SET" keyword specified!')
-return nil
-end
-local keywords=self:_split(text,",")
-self:T({keywords=keywords})
-for _,keyphrase in pairs(keywords)do
-local str=self:_split(keyphrase," ")
-local key=str[1]
-local val=str[2]
-self:T3(self.lid..string.format("%s, keyphrase = %s, key = %s, val = %s",self.groupname,tostring(keyphrase),tostring(key),tostring(val)))
-if key:lower():find("battery")then
-local v=self:_split(keyphrase,'"')
-for i=2,#v,2 do
-table.insert(assignment.battery,v[i])
-self:T2(self.lid..string.format("Key Battery=%s.",v[i]))
-end
-elseif key:lower():find("alias")then
-local v=self:_split(keyphrase,'"')
-for i=2,#v,2 do
-table.insert(assignment.aliases,v[i])
-self:T2(self.lid..string.format("Key Aliases=%s.",v[i]))
-end
-elseif key:lower():find("cluster")then
-local v=self:_split(keyphrase,'"')
-for i=2,#v,2 do
-table.insert(assignment.cluster,v[i])
-self:T2(self.lid..string.format("Key Cluster=%s.",v[i]))
-end
-elseif keyphrase:lower():find("everyone")or keyphrase:lower():find("all batteries")or keyphrase:lower():find("allbatteries")then
-assignment.everyone=true
-self:T(self.lid..string.format("Key Everyone=true."))
-elseif keyphrase:lower():find("irrevocable")or keyphrase:lower():find("readonly")then
-assignment.readonly=true
-self:T2(self.lid..string.format("Key Readonly=true."))
-elseif(assignment.engage or assignment.move)and key:lower():find("time")then
-if val:lower():find("now")then
-assignment.time=self:_SecondsToClock(timer.getTime0()+2)
-else
-assignment.time=val
-end
-self:T2(self.lid..string.format("Key Time=%s.",val))
-elseif assignment.engage and key:lower():find("shot")then
-assignment.nshells=tonumber(val)
-self:T(self.lid..string.format("Key Shot=%s.",val))
-elseif assignment.engage and key:lower():find("prio")then
-assignment.prio=tonumber(val)
-self:T2(string.format("Key Prio=%s.",val))
-elseif assignment.engage and key:lower():find("maxengage")then
-assignment.maxengage=tonumber(val)
-self:T2(self.lid..string.format("Key Maxengage=%s.",val))
-elseif assignment.engage and key:lower():find("radius")then
-assignment.radius=tonumber(val)
-self:T2(self.lid..string.format("Key Radius=%s.",val))
-elseif assignment.engage and key:lower():find("weapon")then
-if val:lower():find("cannon")then
-assignment.weapontype=ARTY.WeaponType.Cannon
-elseif val:lower():find("rocket")then
-assignment.weapontype=ARTY.WeaponType.Rockets
-elseif val:lower():find("missile")then
-assignment.weapontype=ARTY.WeaponType.CruiseMissile
-elseif val:lower():find("nuke")then
-assignment.weapontype=ARTY.WeaponType.TacticalNukes
-elseif val:lower():find("illu")then
-assignment.weapontype=ARTY.WeaponType.IlluminationShells
-elseif val:lower():find("smoke")then
-assignment.weapontype=ARTY.WeaponType.SmokeShells
-else
-assignment.weapontype=ARTY.WeaponType.Auto
-end
-self:T2(self.lid..string.format("Key Weapon=%s.",val))
-elseif(assignment.move or assignment.set)and key:lower():find("speed")then
-assignment.speed=tonumber(val)
-self:T2(self.lid..string.format("Key Speed=%s.",val))
-elseif(assignment.move or assignment.set)and(keyphrase:lower():find("on road")or keyphrase:lower():find("onroad")or keyphrase:lower():find("use road"))then
-assignment.onroad=true
-self:T2(self.lid..string.format("Key Onroad=true."))
-elseif assignment.move and(keyphrase:lower():find("cancel target")or keyphrase:lower():find("canceltarget"))then
-assignment.movecanceltarget=true
-self:T2(self.lid..string.format("Key Cancel Target (before move)=true."))
-elseif assignment.request and keyphrase:lower():find("rearm")then
-assignment.requestrearming=true
-self:T2(self.lid..string.format("Key Request Rearming=true."))
-elseif assignment.request and keyphrase:lower():find("ammo")then
-assignment.requestammo=true
-self:T2(self.lid..string.format("Key Request Ammo=true."))
-elseif assignment.request and keyphrase:lower():find("target")then
-assignment.requesttargets=true
-self:T2(self.lid..string.format("Key Request Targets=true."))
-elseif assignment.request and keyphrase:lower():find("status")then
-assignment.requeststatus=true
-self:T2(self.lid..string.format("Key Request Status=true."))
-elseif assignment.request and(keyphrase:lower():find("move")or keyphrase:lower():find("relocation"))then
-assignment.requestmoves=true
-self:T2(self.lid..string.format("Key Request Moves=true."))
-elseif assignment.cancel and(keyphrase:lower():find("engagement")or keyphrase:lower():find("attack")or keyphrase:lower():find("target"))then
-assignment.canceltarget=true
-self:T2(self.lid..string.format("Key Cancel Target=true."))
-elseif assignment.cancel and(keyphrase:lower():find("move")or keyphrase:lower():find("relocation"))then
-assignment.cancelmove=true
-self:T2(self.lid..string.format("Key Cancel Move=true."))
-elseif assignment.cancel and keyphrase:lower():find("rearm")then
-assignment.cancelrearm=true
-self:T2(self.lid..string.format("Key Cancel Rearm=true."))
-elseif assignment.set and keyphrase:lower():find("rearming place")then
-assignment.setrearmingplace=true
-self:T(self.lid..string.format("Key Set Rearming Place=true."))
-elseif assignment.set and keyphrase:lower():find("rearming group")then
-local v=self:_split(keyphrase,'"')
-local groupname=v[2]
-local group=GROUP:FindByName(groupname)
-if group and group:IsAlive()then
-assignment.setrearminggroup=group
-end
-self:T2(self.lid..string.format("Key Set Rearming Group = %s.",tostring(groupname)))
-elseif key:lower():find("lldms")then
-local _flat="%d+:%d+:%d+%s*[N,S]"
-local _flon="%d+:%d+:%d+%s*[W,E]"
-local _lat=keyphrase:match(_flat)
-local _lon=keyphrase:match(_flon)
-self:T2(self.lid..string.format("Key LLDMS: lat=%s, long=%s format=DMS",_lat,_lon))
-if _lat and _lon then
-local _latitude,_longitude=self:_LLDMS2DD(_lat,_lon)
-self:T2(self.lid..string.format("Key LLDMS: lat=%.3f, long=%.3f format=DD",_latitude,_longitude))
-if _latitude and _longitude then
-assignment.coord=COORDINATE:NewFromLLDD(_latitude,_longitude)
-end
-end
-end
-end
-return assignment
-end
-function ARTY:_MarkRequestAmmo()
-self:GetAmmo(true)
-end
-function ARTY:_MarkRequestStatus()
-self:_StatusReport(true)
-end
-function ARTY:_MarkRequestMoves()
-local text=string.format("%s, relocations:",self.groupname)
-if#self.moves>0 then
-for _,move in pairs(self.moves)do
-if self.currentMove and move.name==self.currentMove.name then
-text=text..string.format("\n- %s (current)",self:_MoveInfo(move))
-else
-text=text..string.format("\n- %s",self:_MoveInfo(move))
-end
-end
-else
-text=text..string.format("\n- no queued relocations")
-end
-MESSAGE:New(text,20):Clear():ToCoalition(self.coalition)
-end
-function ARTY:_MarkRequestTargets()
-local text=string.format("%s, targets:",self.groupname)
-if#self.targets>0 then
-for _,target in pairs(self.targets)do
-if self.currentTarget and target.name==self.currentTarget.name then
-text=text..string.format("\n- %s (current)",self:_TargetInfo(target))
-else
-text=text..string.format("\n- %s",self:_TargetInfo(target))
-end
-end
-else
-text=text..string.format("\n- no queued targets")
-end
-MESSAGE:New(text,20):Clear():ToCoalition(self.coalition)
-end
-function ARTY:_MarkTargetName(markerid)
-return string.format("BATTERY=%s, Marked Target ID=%d",self.groupname,markerid)
-end
-function ARTY:_MarkMoveName(markerid)
-return string.format("BATTERY=%s, Marked Relocation ID=%d",self.groupname,markerid)
-end
-function ARTY:_GetMarkIDfromName(name)
-local keywords=self:_split(name,",")
-local battery=nil
-local markTID=nil
-local markMID=nil
-for _,key in pairs(keywords)do
-local str=self:_split(key,"=")
-local par=str[1]
-local val=str[2]
-if par:find("BATTERY")then
-battery=val
-end
-if par:find("Marked Target ID")then
-markTID=tonumber(val)
-end
-if par:find("Marked Relocation ID")then
-markMID=tonumber(val)
-end
-end
-return battery,markTID,markMID
-end
-function ARTY:_SortTargetQueuePrio()
-self:F2()
-local function _sort(a,b)
-return(a.engaged_target.engaged and self:_TargetInRange(_target)and self:_CheckWeaponTypeAvailable(_target)>0 then
-self:T2(self.lid..string.format("Found NORMAL target %s",self:_TargetInfo(_target)))
-return _target
-end
-end
-return nil
-end
-function ARTY:_CheckTimedTargets()
-self:F3()
-local Tnow=timer.getAbsTime()
-self:_SortQueueTime(self.targets)
-if self:is("Rearming")then
-return nil
-end
-for i=1,#self.targets do
-local _target=self.targets[i]
-self:T3(self.lid..string.format("Check TIMED target %d: %s",i,self:_TargetInfo(_target)))
-if _target.time and Tnow>=_target.time and _target.underfire==false and self:_TargetInRange(_target)and self:_CheckWeaponTypeAvailable(_target)>0 then
-if self.currentTarget then
-if self.currentTarget.prio>_target.prio then
-self:T2(self.lid..string.format("Found TIMED HIGH PRIO target %s.",self:_TargetInfo(_target)))
-return _target
-end
-else
-self:T2(self.lid..string.format("Found TIMED target %s.",self:_TargetInfo(_target)))
-return _target
-end
-end
-end
-return nil
-end
-function ARTY:_CheckMoves()
-self:F3()
-local Tnow=timer.getAbsTime()
-self:_SortQueueTime(self.moves)
-local firing=false
-if self.currentTarget then
-firing=true
-end
-for i=1,#self.moves do
-local _move=self.moves[i]
-if string.find(_move.name,"REARMING MOVE")and((self.currentMove and self.currentMove.name~=_move.name)or self.currentMove==nil)then
-return _move
-elseif(Tnow>=_move.time)and(firing==false or _move.cancel)and(not self.currentMove)and(not self:is("Rearming"))then
-return _move
-end
-end
-return nil
-end
-function ARTY:_CheckShootingStarted()
-self:F2()
-if self.currentTarget then
-local Tnow=timer.getTime()
-local name=self.currentTarget.name
-local dt=Tnow-self.currentTarget.Tassigned
-if self.Nshots==0 then
-self:T(self.lid..string.format("%s, waiting for %d seconds for first shot on target %s.",self.groupname,dt,name))
-end
-if dt>self.WaitForShotTime and(self.Nshots==0 or self.currentTarget.nshells>=self.Nshots)then
-self:T(self.lid..string.format("%s, no shot event after %d seconds. Removing current target %s from list.",self.groupname,self.WaitForShotTime,name))
-self:CeaseFire(self.currentTarget)
-self:RemoveTarget(name)
-end
-end
-end
-function ARTY:_GetTargetIndexByName(name)
-self:F2(name)
-for i=1,#self.targets do
-local targetname=self.targets[i].name
-self:T3(self.lid..string.format("Have target with name %s. Index = %d",targetname,i))
-if targetname==name then
-self:T2(self.lid..string.format("Found target with name %s. Index = %d",name,i))
-return i
-end
-end
-self:T2(self.lid..string.format("WARNING: Target with name %s could not be found. (This can happen.)",name))
-return nil
-end
-function ARTY:_GetMoveIndexByName(name)
-self:F2(name)
-for i=1,#self.moves do
-local movename=self.moves[i].name
-self:T3(self.lid..string.format("Have move with name %s. Index = %d",movename,i))
-if movename==name then
-self:T2(self.lid..string.format("Found move with name %s. Index = %d",name,i))
-return i
-end
-end
-self:T2(self.lid..string.format("WARNING: Move with name %s could not be found. (This can happen.)",name))
-return nil
-end
-function ARTY:_CheckOutOfAmmo(targets)
-local _nammo,_nshells,_nrockets,_nmissiles=self:GetAmmo()
-local _partlyoutofammo=false
-for _,Target in pairs(targets)do
-if Target.weapontype==ARTY.WeaponType.Auto and _nammo==0 then
-self:T(self.lid..string.format("Group %s, auto weapon requested for target %s but all ammo is empty.",self.groupname,Target.name))
-_partlyoutofammo=true
-elseif Target.weapontype==ARTY.WeaponType.Cannon and _nshells==0 then
-self:T(self.lid..string.format("Group %s, cannons requested for target %s but shells empty.",self.groupname,Target.name))
-_partlyoutofammo=true
-elseif Target.weapontype==ARTY.WeaponType.TacticalNukes and self.Nukes<=0 then
-self:T(self.lid..string.format("Group %s, tactical nukes requested for target %s but nukes empty.",self.groupname,Target.name))
-_partlyoutofammo=true
-elseif Target.weapontype==ARTY.WeaponType.IlluminationShells and self.Nillu<=0 then
-self:T(self.lid..string.format("Group %s, illumination shells requested for target %s but illumination shells empty.",self.groupname,Target.name))
-_partlyoutofammo=true
-elseif Target.weapontype==ARTY.WeaponType.SmokeShells and self.Nsmoke<=0 then
-self:T(self.lid..string.format("Group %s, smoke shells requested for target %s but smoke shells empty.",self.groupname,Target.name))
-_partlyoutofammo=true
-elseif Target.weapontype==ARTY.WeaponType.Rockets and _nrockets==0 then
-self:T(self.lid..string.format("Group %s, rockets requested for target %s but rockets empty.",self.groupname,Target.name))
-_partlyoutofammo=true
-elseif Target.weapontype==ARTY.WeaponType.CruiseMissile and _nmissiles==0 then
-self:T(self.lid..string.format("Group %s, cruise missiles requested for target %s but all missiles empty.",self.groupname,Target.name))
-_partlyoutofammo=true
-end
-end
-return _partlyoutofammo
-end
-function ARTY:_CheckWeaponTypeAvailable(target)
-local Nammo,Nshells,Nrockets,Nmissiles=self:GetAmmo()
-local nfire=Nammo
-if target.weapontype==ARTY.WeaponType.Auto then
-nfire=Nammo
-elseif target.weapontype==ARTY.WeaponType.Cannon then
-nfire=Nshells
-elseif target.weapontype==ARTY.WeaponType.TacticalNukes then
-nfire=self.Nukes
-elseif target.weapontype==ARTY.WeaponType.IlluminationShells then
-nfire=self.Nillu
-elseif target.weapontype==ARTY.WeaponType.SmokeShells then
-nfire=self.Nsmoke
-elseif target.weapontype==ARTY.WeaponType.Rockets then
-nfire=Nrockets
-elseif target.weapontype==ARTY.WeaponType.CruiseMissile then
-nfire=Nmissiles
-end
-return nfire
-end
-function ARTY:_CheckWeaponTypePossible(target)
-local possible=false
-if target.weapontype==ARTY.WeaponType.Auto then
-possible=self.Nammo0>0
-elseif target.weapontype==ARTY.WeaponType.Cannon then
-possible=self.Nshells0>0
-elseif target.weapontype==ARTY.WeaponType.TacticalNukes then
-possible=self.Nukes0>0
-elseif target.weapontype==ARTY.WeaponType.IlluminationShells then
-possible=self.Nillu0>0
-elseif target.weapontype==ARTY.WeaponType.SmokeShells then
-possible=self.Nsmoke0>0
-elseif target.weapontype==ARTY.WeaponType.Rockets then
-possible=self.Nrockets0>0
-elseif target.weapontype==ARTY.WeaponType.CruiseMissile then
-possible=self.Nmissiles0>0
-end
-return possible
-end
-function ARTY:_CheckName(givennames,name,makeunique)
-self:F2({givennames=givennames,name=name})
-local newname=name
-local counter=1
-local n=1
-local nmax=100
-if makeunique==nil then
-makeunique=true
-end
-repeat
-local _unique=true
-for _,_target in pairs(givennames)do
-local _givenname=_target.name
-if _givenname==newname then
-_unique=false
-end
-self:T3(self.lid..string.format("%d: givenname = %s, newname=%s, unique = %s, makeunique = %s",n,tostring(_givenname),newname,tostring(_unique),tostring(makeunique)))
-end
-if _unique==false and makeunique==true then
-newname=string.format("%s #%02d",name,counter)
-counter=counter+1
-end
-if _unique==false and makeunique==false then
-self:T3(self.lid..string.format("Name %s is not unique. Return false.",tostring(newname)))
-return name,false
-end
-n=n+1
-until(_unique or n==nmax)
-self:T3(self.lid..string.format("Original name %s, new name = %s",name,newname))
-return newname,true
-end
-function ARTY:_TargetInRange(target,message)
-self:F3(target)
-if message==nil then
-message=false
-end
-self:T3({controllable=self.Controllable,targetcoord=target.coord})
-local _dist=self.Controllable:GetCoordinate():Get2DDistance(target.coord)
-local _inrange=true
-local _tooclose=false
-local _toofar=false
-local text=""
-if _distself.maxrange then
-_inrange=false
-_toofar=true
-text=string.format("%s, target is out of range. Distance of %.1f km is greater than max range of %.1f km.",self.alias,_dist/1000,self.maxrange/1000)
-end
-if not _inrange then
-self:T(self.lid..text)
-MESSAGE:New(text,5):ToCoalitionIf(self.coalition,(self.report and message)or(self.Debug and message))
-end
-local _remove=false
-if not(self.ismobile or self.iscargo)and _inrange==false then
-_remove=true
-end
-return _inrange,_toofar,_tooclose,_remove
-end
-function ARTY:_WeaponTypeName(tnumber)
-self:F2(tnumber)
-local name="unknown"
-if tnumber==ARTY.WeaponType.Auto then
-name="Auto"
-elseif tnumber==ARTY.WeaponType.Cannon then
-name="Cannons"
-elseif tnumber==ARTY.WeaponType.Rockets then
-name="Rockets"
-elseif tnumber==ARTY.WeaponType.CruiseMissile then
-name="Cruise Missiles"
-elseif tnumber==ARTY.WeaponType.TacticalNukes then
-name="Tactical Nukes"
-elseif tnumber==ARTY.WeaponType.IlluminationShells then
-name="Illumination Shells"
-elseif tnumber==ARTY.WeaponType.SmokeShells then
-name="Smoke Shells"
-end
-return name
-end
-function ARTY:_VicinityCoord(coord,rmin,rmax)
-self:F2({coord=coord,rmin=rmin,rmax=rmax})
-rmin=rmin or 20
-rmax=rmax or 80
-local vec2=coord:GetRandomVec2InRadius(rmax,rmin)
-local pops=COORDINATE:NewFromVec2(vec2)
-self:T3(self.lid..string.format("Vicinity distance = %d (rmin=%d, rmax=%d)",pops:Get2DDistance(coord),rmin,rmax))
-return pops
-end
-function ARTY:_EventFromTo(BA,Event,From,To)
-local text=string.format("%s: %s EVENT %s: %s --> %s",BA,self.groupname,Event,From,To)
-self:T3(self.lid..text)
-end
-function ARTY:_split(str,sep)
-self:F3({str=str,sep=sep})
-local result={}
-local regex=("([^%s]+)"):format(sep)
-for each in str:gmatch(regex)do
-table.insert(result,each)
-end
-return result
-end
-function ARTY:_TargetInfo(target)
-local clock=tostring(self:_SecondsToClock(target.time))
-local weapon=self:_WeaponTypeName(target.weapontype)
-local _underfire=tostring(target.underfire)
-return string.format("%s: prio=%d, radius=%d, nshells=%d, engaged=%d/%d, weapontype=%s, time=%s, underfire=%s, attackgroup=%s",
-target.name,target.prio,target.radius,target.nshells,target.engaged,target.maxengage,weapon,clock,_underfire,tostring(target.attackgroup))
-end
-function ARTY:_MoveInfo(move)
-self:F3(move)
-local _clock=self:_SecondsToClock(move.time)
-return string.format("%s: time=%s, speed=%d, onroad=%s, cancel=%s",move.name,_clock,move.speed,tostring(move.onroad),tostring(move.cancel))
-end
-function ARTY:_LLDMS2DD(l1,l2)
-self:F2(l1,l2)
-local _latlong={l1,l2}
-local _latitude=nil
-local _longitude=nil
-for _,ll in pairs(_latlong)do
-local _format="%d+:%d+:%d+"
-local _ldms=ll:match(_format)
-if _ldms then
-local _dms=self:_split(_ldms,":")
-local _deg=tonumber(_dms[1])
-local _min=tonumber(_dms[2])
-local _sec=tonumber(_dms[3])
-local function DMS2DD(d,m,s)
-return d+m/60+s/3600
-end
-if ll:match("N")then
-_latitude=DMS2DD(_deg,_min,_sec)
-elseif ll:match("S")then
-_latitude=-DMS2DD(_deg,_min,_sec)
-elseif ll:match("W")then
-_longitude=-DMS2DD(_deg,_min,_sec)
-elseif ll:match("E")then
-_longitude=DMS2DD(_deg,_min,_sec)
-end
-local text=string.format("DMS %02d Deg %02d min %02d sec",_deg,_min,_sec)
-self:T2(self.lid..text)
-end
-end
-local text=string.format("\nLatitude %s",tostring(_latitude))
-text=text..string.format("\nLongitude %s",tostring(_longitude))
-self:T2(self.lid..text)
-return _latitude,_longitude
-end
-function ARTY:_SecondsToClock(seconds)
-self:F3({seconds=seconds})
-if seconds==nil then
-return nil
-end
-local seconds=tonumber(seconds)
-local _seconds=seconds%(60*60*24)
-if seconds<=0 then
-return nil
-else
-local hours=string.format("%02.f",math.floor(_seconds/3600))
-local mins=string.format("%02.f",math.floor(_seconds/60-(hours*60)))
-local secs=string.format("%02.f",math.floor(_seconds-hours*3600-mins*60))
-local days=string.format("%d",seconds/(60*60*24))
-return hours..":"..mins..":"..secs.."+"..days
-end
-end
-function ARTY:_ClockToSeconds(clock)
-self:F3({clock=clock})
-if clock==nil then
-return nil
-end
-local seconds=0
-local dsplit=self:_split(clock,"+")
-if#dsplit>1 then
-seconds=seconds+tonumber(dsplit[2])*60*60*24
-end
-local tsplit=self:_split(dsplit[1],":")
-local i=1
-for _,time in ipairs(tsplit)do
-if i==1 then
-seconds=seconds+tonumber(time)*60*60
-elseif i==2 then
-seconds=seconds+tonumber(time)*60
-elseif i==3 then
-seconds=seconds+tonumber(time)
-end
-i=i+1
-end
-self:T3(self.lid..string.format("Clock %s = %d seconds",clock,seconds))
-return seconds
-end
-ATC_GROUND={
-ClassName="ATC_GROUND",
-SetClient=nil,
-Airbases=nil,
-AirbaseNames=nil,
-}
-function ATC_GROUND:New(Airbases,AirbaseList)
-local self=BASE:Inherit(self,BASE:New())
-self:T({self.ClassName,Airbases})
-self.Airbases=Airbases
-self.AirbaseList=AirbaseList
-self.SetClient=SET_CLIENT:New():FilterCategories("plane"):FilterStart()
-for AirbaseID,Airbase in pairs(self.Airbases)do
-if Airbase.ZoneBoundary then
-Airbase.ZoneBoundary=ZONE_POLYGON_BASE:New("Boundary "..AirbaseID,Airbase.ZoneBoundary)
-else
-Airbase.ZoneBoundary=_DATABASE:FindAirbase(AirbaseID):GetZone()
-end
-Airbase.ZoneRunways={}
-if Airbase.PointsRunways then
-for PointsRunwayID,PointsRunway in pairs(Airbase.PointsRunways)do
-Airbase.ZoneRunways[PointsRunwayID]=ZONE_POLYGON_BASE:New("Runway "..PointsRunwayID,PointsRunway)
-end
-end
-Airbase.Monitor=self.AirbaseList and false or true
-end
-for AirbaseID,AirbaseName in pairs(self.AirbaseList or{})do
-self.Airbases[AirbaseName].Monitor=true
-end
-self.SetClient:ForEachClient(
-function(Client)
-Client:SetState(self,"Speeding",false)
-Client:SetState(self,"Warnings",0)
-Client:SetState(self,"IsOffRunway",false)
-Client:SetState(self,"OffRunwayWarnings",0)
-Client:SetState(self,"Taxi",false)
-end
-)
-SSB=USERFLAG:New("SSB")
-SSB:Set(100)
-return self
-end
-function ATC_GROUND:SmokeRunways(SmokeColor)
-for AirbaseID,Airbase in pairs(self.Airbases)do
-for PointsRunwayID,PointsRunway in pairs(Airbase.PointsRunways)do
-Airbase.ZoneRunways[PointsRunwayID]:SmokeZone(SmokeColor)
-end
-end
-end
-function ATC_GROUND:SetKickSpeed(KickSpeed,Airbase)
-if not Airbase then
-self.KickSpeed=KickSpeed
-else
-self.Airbases[Airbase].KickSpeed=KickSpeed
-end
-return self
-end
-function ATC_GROUND:SetKickSpeedKmph(KickSpeed,Airbase)
-self:SetKickSpeed(UTILS.KmphToMps(KickSpeed),Airbase)
-return self
-end
-function ATC_GROUND:SetKickSpeedMiph(KickSpeedMiph,Airbase)
-self:SetKickSpeed(UTILS.MiphToMps(KickSpeedMiph),Airbase)
-return self
-end
-function ATC_GROUND:SetMaximumKickSpeed(MaximumKickSpeed,Airbase)
-if not Airbase then
-self.MaximumKickSpeed=MaximumKickSpeed
-else
-self.Airbases[Airbase].MaximumKickSpeed=MaximumKickSpeed
-end
-return self
-end
-function ATC_GROUND:SetMaximumKickSpeedKmph(MaximumKickSpeed,Airbase)
-self:SetMaximumKickSpeed(UTILS.KmphToMps(MaximumKickSpeed),Airbase)
-return self
-end
-function ATC_GROUND:SetMaximumKickSpeedMiph(MaximumKickSpeedMiph,Airbase)
-self:SetMaximumKickSpeed(UTILS.MiphToMps(MaximumKickSpeedMiph),Airbase)
-return self
-end
-function ATC_GROUND:_AirbaseMonitor()
-self.SetClient:ForEachClient(
-function(Client)
-if Client:IsAlive()then
-local IsOnGround=Client:InAir()==false
-for AirbaseID,AirbaseMeta in pairs(self.Airbases)do
-self:T(AirbaseID,AirbaseMeta.KickSpeed)
-if AirbaseMeta.Monitor==true and Client:IsInZone(AirbaseMeta.ZoneBoundary)then
-local NotInRunwayZone=true
-for ZoneRunwayID,ZoneRunway in pairs(AirbaseMeta.ZoneRunways)do
-NotInRunwayZone=(Client:IsNotInZone(ZoneRunway)==true)and NotInRunwayZone or false
-end
-if NotInRunwayZone then
-if IsOnGround then
-local Taxi=Client:GetState(self,"Taxi")
-self:T(Taxi)
-if Taxi==false then
-local Velocity=VELOCITY:New(AirbaseMeta.KickSpeed or self.KickSpeed)
-Client:Message("Welcome to "..AirbaseID..". The maximum taxiing speed is "..
-Velocity:ToString(),20,"ATC")
-Client:SetState(self,"Taxi",true)
-end
-local Velocity=VELOCITY_POSITIONABLE:New(Client)
-local IsAboveRunway=Client:IsAboveRunway()
-self:T(IsAboveRunway,IsOnGround)
-if IsOnGround then
-local Speeding=false
-if AirbaseMeta.MaximumKickSpeed then
-if Velocity:Get()>AirbaseMeta.MaximumKickSpeed then
-Speeding=true
-end
-else
-if Velocity:Get()>self.MaximumKickSpeed then
-Speeding=true
-end
-end
-if Speeding==true then
-MESSAGE:New("Penalty! Player "..Client:GetPlayerName()..
-" has been kicked, due to a severe airbase traffic rule violation ...",10,"ATC"):ToAll()
-Client:Destroy()
-Client:SetState(self,"Speeding",false)
-Client:SetState(self,"Warnings",0)
-end
-end
-if IsOnGround then
-local Speeding=false
-if AirbaseMeta.KickSpeed then
-if Velocity:Get()>AirbaseMeta.KickSpeed then
-Speeding=true
-end
-else
-if Velocity:Get()>self.KickSpeed then
-Speeding=true
-end
-end
-if Speeding==true then
-local IsSpeeding=Client:GetState(self,"Speeding")
-if IsSpeeding==true then
-local SpeedingWarnings=Client:GetState(self,"Warnings")
-self:T(SpeedingWarnings)
-if SpeedingWarnings<=3 then
-Client:Message("Warning "..SpeedingWarnings.."/3! Airbase traffic rule violation! Slow down now! Your speed is "..
-Velocity:ToString(),5,"ATC")
-Client:SetState(self,"Warnings",SpeedingWarnings+1)
-else
-MESSAGE:New("Penalty! Player "..Client:GetPlayerName().." has been kicked, due to a severe airbase traffic rule violation ...",10,"ATC"):ToAll()
-Client:Destroy()
-Client:SetState(self,"Speeding",false)
-Client:SetState(self,"Warnings",0)
-end
-else
-Client:Message("Attention! You are speeding on the taxiway, slow down! Your speed is "..
-Velocity:ToString(),5,"ATC")
-Client:SetState(self,"Speeding",true)
-Client:SetState(self,"Warnings",1)
-end
-else
-Client:SetState(self,"Speeding",false)
-Client:SetState(self,"Warnings",0)
-end
-end
-if IsOnGround and not IsAboveRunway then
-local IsOffRunway=Client:GetState(self,"IsOffRunway")
-if IsOffRunway==true then
-local OffRunwayWarnings=Client:GetState(self,"OffRunwayWarnings")
-self:T(OffRunwayWarnings)
-if OffRunwayWarnings<=3 then
-Client:Message("Warning "..OffRunwayWarnings.."/3! Airbase traffic rule violation! Get back on the taxi immediately!",5,"ATC")
-Client:SetState(self,"OffRunwayWarnings",OffRunwayWarnings+1)
-else
-MESSAGE:New("Penalty! Player "..Client:GetPlayerName().." has been kicked, due to a severe airbase traffic rule violation ...",10,"ATC"):ToAll()
-Client:Destroy()
-Client:SetState(self,"IsOffRunway",false)
-Client:SetState(self,"OffRunwayWarnings",0)
-end
-else
-Client:Message("Attention! You are off the taxiway. Get back on the taxiway immediately!",5,"ATC")
-Client:SetState(self,"IsOffRunway",true)
-Client:SetState(self,"OffRunwayWarnings",1)
-end
-else
-Client:SetState(self,"IsOffRunway",false)
-Client:SetState(self,"OffRunwayWarnings",0)
-end
-end
-else
-Client:SetState(self,"Speeding",false)
-Client:SetState(self,"Warnings",0)
-Client:SetState(self,"IsOffRunway",false)
-Client:SetState(self,"OffRunwayWarnings",0)
-local Taxi=Client:GetState(self,"Taxi")
-if Taxi==true then
-Client:Message("You have progressed to the runway ... Await take-off clearance ...",20,"ATC")
-Client:SetState(self,"Taxi",false)
-end
-end
-end
-end
-else
-Client:SetState(self,"Taxi",false)
-end
-end
-)
-return true
-end
-ATC_GROUND_UNIVERSAL={
-ClassName="ATC_GROUND_UNIVERSAL",
-Version="0.0.1",
-SetClient=nil,
-Airbases=nil,
-AirbaseList=nil,
-KickSpeed=nil,
-}
-function ATC_GROUND_UNIVERSAL:New(AirbaseList)
-local self=BASE:Inherit(self,BASE:New())
-self:T({self.ClassName})
-self.Airbases={}
-for _name,_ in pairs(_DATABASE.AIRBASES)do
-self.Airbases[_name]={}
-end
-self.AirbaseList=AirbaseList
-if not self.AirbaseList then
-self.AirbaseList={}
-for _name,_ in pairs(_DATABASE.AIRBASES)do
-self.AirbaseList[_name]=_name
-end
-end
-self.SetClient=SET_CLIENT:New():FilterCategories("plane"):FilterStart()
-for AirbaseID,Airbase in pairs(self.Airbases)do
-if Airbase.ZoneBoundary then
-Airbase.ZoneBoundary=ZONE_POLYGON_BASE:New("Boundary "..AirbaseID,Airbase.ZoneBoundary)
-else
-Airbase.ZoneBoundary=_DATABASE:FindAirbase(AirbaseID):GetZone()
-end
-Airbase.ZoneRunways=AIRBASE:FindByName(AirbaseID):GetRunways()
-Airbase.Monitor=self.AirbaseList and false or true
-end
-for AirbaseID,AirbaseName in pairs(self.AirbaseList or{})do
-self.Airbases[AirbaseName].Monitor=true
-end
-self.SetClient:ForEachClient(
-function(Client)
-Client:SetState(self,"Speeding",false)
-Client:SetState(self,"Warnings",0)
-Client:SetState(self,"IsOffRunway",false)
-Client:SetState(self,"OffRunwayWarnings",0)
-Client:SetState(self,"Taxi",false)
-end
-)
-SSB=USERFLAG:New("SSB")
-SSB:Set(100)
-self.KickSpeed=UTILS.KnotsToMps(10)
-self:SetMaximumKickSpeedMiph(30)
-return self
-end
-function ATC_GROUND_UNIVERSAL:SetAirbaseBoundaries(Airbase,Zone)
-self.Airbases[Airbase].ZoneBoundary=Zone
-return self
-end
-function ATC_GROUND_UNIVERSAL:SmokeRunways(SmokeColor)
-local SmokeColor=SmokeColor or SMOKECOLOR.Red
-for AirbaseID,Airbase in pairs(self.Airbases)do
-if Airbase.ZoneRunways then
-for _,_runwaydata in pairs(Airbase.ZoneRunways)do
-local runwaydata=_runwaydata
-runwaydata.zone:SmokeZone(SmokeColor)
-end
-end
-end
-return self
-end
-function ATC_GROUND_UNIVERSAL:DrawRunways(Color)
-local Color=Color or{1,0,0}
-for AirbaseID,Airbase in pairs(self.Airbases)do
-if Airbase.ZoneRunways then
-for _,_runwaydata in pairs(Airbase.ZoneRunways)do
-local runwaydata=_runwaydata
-runwaydata.zone:DrawZone(-1,Color)
-end
-end
-end
-return self
-end
-function ATC_GROUND_UNIVERSAL:DrawBoundaries(Color)
-local Color=Color or{1,0,0}
-for AirbaseID,Airbase in pairs(self.Airbases)do
-if Airbase.ZoneBoundary then
-Airbase.ZoneBoundary:DrawZone(-1,Color)
-end
-end
-return self
-end
-function ATC_GROUND_UNIVERSAL:SetKickSpeed(KickSpeed,Airbase)
-if not Airbase then
-self.KickSpeed=KickSpeed
-else
-self.Airbases[Airbase].KickSpeed=KickSpeed
-end
-return self
-end
-function ATC_GROUND_UNIVERSAL:SetKickSpeedKmph(KickSpeed,Airbase)
-self:SetKickSpeed(UTILS.KmphToMps(KickSpeed),Airbase)
-return self
-end
-function ATC_GROUND_UNIVERSAL:SetKickSpeedMiph(KickSpeedMiph,Airbase)
-self:SetKickSpeed(UTILS.MiphToMps(KickSpeedMiph),Airbase)
-return self
-end
-function ATC_GROUND_UNIVERSAL:SetMaximumKickSpeed(MaximumKickSpeed,Airbase)
-if not Airbase then
-self.MaximumKickSpeed=MaximumKickSpeed
-else
-self.Airbases[Airbase].MaximumKickSpeed=MaximumKickSpeed
-end
-return self
-end
-function ATC_GROUND_UNIVERSAL:SetMaximumKickSpeedKmph(MaximumKickSpeed,Airbase)
-self:SetMaximumKickSpeed(UTILS.KmphToMps(MaximumKickSpeed),Airbase)
-return self
-end
-function ATC_GROUND_UNIVERSAL:SetMaximumKickSpeedMiph(MaximumKickSpeedMiph,Airbase)
-self:SetMaximumKickSpeed(UTILS.MiphToMps(MaximumKickSpeedMiph),Airbase)
-return self
-end
-function ATC_GROUND_UNIVERSAL:_AirbaseMonitor()
-self:I("_AirbaseMonitor")
-self.SetClient:ForEachClient(
-function(Client)
-if Client:IsAlive()then
-local IsOnGround=Client:InAir()==false
-for AirbaseID,AirbaseMeta in pairs(self.Airbases)do
-self:T(AirbaseID,AirbaseMeta.KickSpeed)
-if AirbaseMeta.Monitor==true and Client:IsInZone(AirbaseMeta.ZoneBoundary)then
-local NotInRunwayZone=true
-if AirbaseMeta.ZoneRunways then
-for _,_runwaydata in pairs(AirbaseMeta.ZoneRunways)do
-local runwaydata=_runwaydata
-NotInRunwayZone=(Client:IsNotInZone(_runwaydata.zone)==true)and NotInRunwayZone or false
-end
-end
-if NotInRunwayZone then
-if IsOnGround then
-local Taxi=Client:GetState(self,"Taxi")
-self:T(Taxi)
-if Taxi==false then
-local Velocity=VELOCITY:New(AirbaseMeta.KickSpeed or self.KickSpeed)
-Client:Message("Welcome to "..AirbaseID..". The maximum taxiing speed is "..
-Velocity:ToString(),20,"ATC")
-Client:SetState(self,"Taxi",true)
-end
-local Velocity=VELOCITY_POSITIONABLE:New(Client)
-local IsAboveRunway=Client:IsAboveRunway()
-self:T({IsAboveRunway,IsOnGround,Velocity:Get()})
-if IsOnGround then
-local Speeding=false
-if AirbaseMeta.MaximumKickSpeed then
-if Velocity:Get()>AirbaseMeta.MaximumKickSpeed then
-Speeding=true
-end
-else
-if Velocity:Get()>self.MaximumKickSpeed then
-Speeding=true
-end
-end
-if Speeding==true then
-MESSAGE:New("Penalty! Player "..Client:GetPlayerName()..
-" has been kicked, due to a severe airbase traffic rule violation ...",10,"ATC"):ToAll()
-Client:Destroy()
-Client:SetState(self,"Speeding",false)
-Client:SetState(self,"Warnings",0)
-end
-end
-if IsOnGround then
-local Speeding=false
-if AirbaseMeta.KickSpeed then
-if Velocity:Get()>AirbaseMeta.KickSpeed then
-Speeding=true
-end
-else
-if Velocity:Get()>self.KickSpeed then
-Speeding=true
-end
-end
-if Speeding==true then
-local IsSpeeding=Client:GetState(self,"Speeding")
-if IsSpeeding==true then
-local SpeedingWarnings=Client:GetState(self,"Warnings")
-self:T(SpeedingWarnings)
-if SpeedingWarnings<=3 then
-Client:Message("Warning "..SpeedingWarnings.."/3! Airbase traffic rule violation! Slow down now! Your speed is "..
-Velocity:ToString(),5,"ATC")
-Client:SetState(self,"Warnings",SpeedingWarnings+1)
-else
-MESSAGE:New("Penalty! Player "..Client:GetPlayerName().." has been kicked, due to a severe airbase traffic rule violation ...",10,"ATC"):ToAll()
-Client:Destroy()
-Client:SetState(self,"Speeding",false)
-Client:SetState(self,"Warnings",0)
-end
-else
-Client:Message("Attention! You are speeding on the taxiway, slow down! Your speed is "..
-Velocity:ToString(),5,"ATC")
-Client:SetState(self,"Speeding",true)
-Client:SetState(self,"Warnings",1)
-end
-else
-Client:SetState(self,"Speeding",false)
-Client:SetState(self,"Warnings",0)
-end
-end
-if IsOnGround and not IsAboveRunway then
-local IsOffRunway=Client:GetState(self,"IsOffRunway")
-if IsOffRunway==true then
-local OffRunwayWarnings=Client:GetState(self,"OffRunwayWarnings")
-self:T(OffRunwayWarnings)
-if OffRunwayWarnings<=3 then
-Client:Message("Warning "..OffRunwayWarnings.."/3! Airbase traffic rule violation! Get back on the taxi immediately!",5,"ATC")
-Client:SetState(self,"OffRunwayWarnings",OffRunwayWarnings+1)
-else
-MESSAGE:New("Penalty! Player "..Client:GetPlayerName().." has been kicked, due to a severe airbase traffic rule violation ...",10,"ATC"):ToAll()
-Client:Destroy()
-Client:SetState(self,"IsOffRunway",false)
-Client:SetState(self,"OffRunwayWarnings",0)
-end
-else
-Client:Message("Attention! You are off the taxiway. Get back on the taxiway immediately!",5,"ATC")
-Client:SetState(self,"IsOffRunway",true)
-Client:SetState(self,"OffRunwayWarnings",1)
-end
-else
-Client:SetState(self,"IsOffRunway",false)
-Client:SetState(self,"OffRunwayWarnings",0)
-end
-end
-else
-Client:SetState(self,"Speeding",false)
-Client:SetState(self,"Warnings",0)
-Client:SetState(self,"IsOffRunway",false)
-Client:SetState(self,"OffRunwayWarnings",0)
-local Taxi=Client:GetState(self,"Taxi")
-if Taxi==true then
-Client:Message("You have progressed to the runway ... Await take-off clearance ...",20,"ATC")
-Client:SetState(self,"Taxi",false)
-end
-end
-end
-end
-else
-Client:SetState(self,"Taxi",false)
-end
-end
-)
-return true
-end
-function ATC_GROUND_UNIVERSAL:Start(RepeatScanSeconds)
-RepeatScanSeconds=RepeatScanSeconds or 0.05
-self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,RepeatScanSeconds)
-return self
-end
-ATC_GROUND_CAUCASUS={
-ClassName="ATC_GROUND_CAUCASUS",
-}
-function ATC_GROUND_CAUCASUS:New(AirbaseNames)
-local self=BASE:Inherit(self,ATC_GROUND_UNIVERSAL:New(AirbaseNames))
-self:SetKickSpeedKmph(50)
-self:SetMaximumKickSpeedKmph(150)
-return self
-end
-function ATC_GROUND_CAUCASUS:Start(RepeatScanSeconds)
-RepeatScanSeconds=RepeatScanSeconds or 0.05
-self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,RepeatScanSeconds)
-end
-ATC_GROUND_NEVADA={
-ClassName="ATC_GROUND_NEVADA",
-}
-function ATC_GROUND_NEVADA:New(AirbaseNames)
-local self=BASE:Inherit(self,ATC_GROUND_UNIVERSAL:New(AirbaseNames))
-self:SetKickSpeedKmph(50)
-self:SetMaximumKickSpeedKmph(150)
-return self
-end
-function ATC_GROUND_NEVADA:Start(RepeatScanSeconds)
-RepeatScanSeconds=RepeatScanSeconds or 0.05
-self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,RepeatScanSeconds)
-end
-ATC_GROUND_NORMANDY={
-ClassName="ATC_GROUND_NORMANDY",
-}
-function ATC_GROUND_NORMANDY:New(AirbaseNames)
-local self=BASE:Inherit(self,ATC_GROUND_UNIVERSAL:New(AirbaseNames))
-self:SetKickSpeedKmph(40)
-self:SetMaximumKickSpeedKmph(100)
-return self
-end
-function ATC_GROUND_NORMANDY:Start(RepeatScanSeconds)
-RepeatScanSeconds=RepeatScanSeconds or 0.05
-self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,RepeatScanSeconds)
-end
-ATC_GROUND_PERSIANGULF={
-ClassName="ATC_GROUND_PERSIANGULF",
-}
-function ATC_GROUND_PERSIANGULF:New(AirbaseNames)
-local self=BASE:Inherit(self,ATC_GROUND_UNIVERSAL:New(AirbaseNames))
-self:SetKickSpeedKmph(50)
-self:SetMaximumKickSpeedKmph(150)
-end
-function ATC_GROUND_PERSIANGULF:Start(RepeatScanSeconds)
-RepeatScanSeconds=RepeatScanSeconds or 0.05
-self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,RepeatScanSeconds)
-end
-ATC_GROUND_MARIANAISLANDS={
-ClassName="ATC_GROUND_MARIANAISLANDS",
-}
-function ATC_GROUND_MARIANAISLANDS:New(AirbaseNames)
-local self=BASE:Inherit(self,ATC_GROUND_UNIVERSAL:New(AirbaseNames))
-self:SetKickSpeedKmph(50)
-self:SetMaximumKickSpeedKmph(150)
-return self
-end
-function ATC_GROUND_MARIANAISLANDS:Start(RepeatScanSeconds)
-RepeatScanSeconds=RepeatScanSeconds or 0.05
-self.AirbaseMonitor=SCHEDULER:New(self,self._AirbaseMonitor,{self},0,RepeatScanSeconds)
-end
-AUTOLASE={
-ClassName="AUTOLASE",
-lid="",
-verbose=0,
-alias="",
-debug=false,
-}
-AUTOLASE.version="0.1.22"
-function AUTOLASE:New(RecceSet,Coalition,Alias,PilotSet)
-BASE:T({RecceSet,Coalition,Alias,PilotSet})
-local self=BASE:Inherit(self,BASE:New())
-if Coalition and type(Coalition)=="string"then
-if Coalition=="blue"then
-self.coalition=coalition.side.BLUE
-elseif Coalition=="red"then
-self.coalition=coalition.side.RED
-elseif Coalition=="neutral"then
-self.coalition=coalition.side.NEUTRAL
-else
-self:E("ERROR: Unknown coalition in AUTOLASE!")
-end
-end
-if Alias then
-self.alias=tostring(Alias)
-else
-self.alias="Lion"
-if self.coalition then
-if self.coalition==coalition.side.RED then
-self.alias="Wolf"
-elseif self.coalition==coalition.side.BLUE then
-self.alias="Fox"
-end
-end
-end
-local self=BASE:Inherit(self,INTEL:New(RecceSet,Coalition,Alias))
-self.RecceSet=RecceSet
-self.DetectVisual=true
-self.DetectOptical=true
-self.DetectRadar=true
-self.DetectIRST=true
-self.DetectRWR=true
-self.DetectDLINK=true
-self.LaserCodes=UTILS.GenerateLaserCodes()
-self.LaseDistance=5000
-self.LaseDuration=300
-self.GroupsByThreat={}
-self.UnitsByThreat={}
-self.RecceNames={}
-self.RecceLaserCode={}
-self.RecceSmokeColor={}
-self.RecceUnitNames={}
-self.maxlasing=4
-self.CurrentLasing={}
-self.lasingindex=0
-self.deadunitnotes={}
-self.usepilotset=false
-self.reporttimeshort=10
-self.reporttimelong=30
-self.smoketargets=false
-self.smokecolor=SMOKECOLOR.Red
-self.notifypilots=true
-self.targetsperrecce={}
-self.RecceUnits={}
-self.forcecooldown=true
-self.cooldowntime=60
-self.useSRS=false
-self.SRSPath=""
-self.SRSFreq=251
-self.SRSMod=radio.modulation.AM
-self.NoMenus=false
-self.minthreatlevel=0
-self.blacklistattributes={}
-self:SetLaserCodes({1688,1130,4785,6547,1465,4578})
-self.playermenus={}
-self.lid=string.format("AUTOLASE %s (%s) | ",self.alias,self.coalition and UTILS.GetCoalitionName(self.coalition)or"unknown")
-self:AddTransition("*","Monitor","*")
-self:AddTransition("*","Lasing","*")
-self:AddTransition("*","TargetLost","*")
-self:AddTransition("*","TargetDestroyed","*")
-self:AddTransition("*","RecceKIA","*")
-self:AddTransition("*","LaserTimeout","*")
-self:AddTransition("*","Cancel","*")
-if PilotSet then
-self.usepilotset=true
-self.pilotset=PilotSet
-self:HandleEvent(EVENTS.PlayerEnterAircraft,self._EventHandler)
-end
-self:SetClusterAnalysis(false,false)
-self:__Start(2)
-self:__Monitor(math.random(5,10))
-return self
-end
-function AUTOLASE:SetLaserCodes(LaserCodes)
-self.LaserCodes=(type(LaserCodes)=="table")and LaserCodes or{LaserCodes}
-return self
-end
-function AUTOLASE:SetPilotMenu()
-if self.usepilotset then
-local pilottable=self.pilotset:GetSetObjects()or{}
-for _,_unit in pairs(pilottable)do
-local Unit=_unit
-if Unit and Unit:IsAlive()then
-local Group=Unit:GetGroup()
-local unitname=Unit:GetName()
-if self.playermenus[unitname]then self.playermenus[unitname]:Remove()end
-local lasetopm=MENU_GROUP:New(Group,"Autolase",nil)
-self.playermenus[unitname]=lasetopm
-local lasemenu=MENU_GROUP_COMMAND:New(Group,"Status",lasetopm,self.ShowStatus,self,Group,Unit)
-local smoke=(self.smoketargets==true)and"off"or"on"
-local smoketext=string.format("Switch smoke targets to %s",smoke)
-local smokemenu=MENU_GROUP_COMMAND:New(Group,smoketext,lasetopm,self.SetSmokeTargets,self,(not self.smoketargets))
-for _,_grp in pairs(self.RecceSet.Set)do
-local grp=_grp
-local unit=grp:GetUnit(1)
-if unit and unit:IsAlive()then
-local name=unit:GetName()
-local mname=string.gsub(name,".%d+.%d+$","")
-local code=self:GetLaserCode(name)
-local unittop=MENU_GROUP:New(Group,"Change laser code for "..mname,lasetopm)
-for _,_code in pairs(self.LaserCodes)do
-local text=tostring(_code)
-if _code==code then text=text.."(*)"end
-local changemenu=MENU_GROUP_COMMAND:New(Group,text,unittop,self.SetRecceLaserCode,self,name,_code,true)
-end
-end
-end
-end
-end
-else
-if not self.NoMenus then
-self.Menu=MENU_COALITION_COMMAND:New(self.coalition,"Autolase",nil,self.ShowStatus,self)
-end
-end
-return self
-end
-function AUTOLASE:_EventHandler(EventData)
-self:SetPilotMenu()
-return self
-end
-function AUTOLASE:SetMinThreatLevel(Level)
-local level=Level or 0
-if level<0 or level>10 then level=0 end
-self.minthreatlevel=level
-return self
-end
-function AUTOLASE:AddBlackListAttributes(Attributes)
-local attributes=Attributes
-if type(attributes)~="table"then
-attributes={attributes}
-end
-for _,_attr in pairs(attributes)do
-table.insert(self.blacklistattributes,_attr)
-end
-return self
-end
-function AUTOLASE:GetLaserCode(RecceName)
-local code=1688
-if self.RecceLaserCode[RecceName]==nil then
-code=self.LaserCodes[math.random(#self.LaserCodes)]
-self.RecceLaserCode[RecceName]=code
-else
-code=self.RecceLaserCode[RecceName]
-end
-return code
-end
-function AUTOLASE:GetSmokeColor(RecceName)
-local color=self.smokecolor
-if self.RecceSmokeColor[RecceName]==nil then
-self.RecceSmokeColor[RecceName]=color
-else
-color=self.RecceSmokeColor[RecceName]
-end
-return color
-end
-function AUTOLASE:SetUsingSRS(OnOff,Path,Frequency,Modulation,Label,Gender,Culture,Port,Voice,Volume,PathToGoogleKey)
-if OnOff then
-self.useSRS=true
-self.SRSPath=Path or"C:\\Program Files\\DCS-SimpleRadio-Standalone"
-self.SRSFreq=Frequency or 271
-self.SRSMod=Modulation or radio.modulation.AM
-self.Gender=Gender or"male"
-self.Culture=Culture or"en-US"
-self.Port=Port or 5002
-self.Voice=Voice
-self.PathToGoogleKey=PathToGoogleKey
-self.Volume=Volume or 1.0
-self.Label=Label
-self.SRS=MSRS:New(self.SRSPath,self.SRSFreq,self.SRSMod,self.Volume)
-self.SRS:SetCoalition(self.coalition)
-self.SRS:SetLabel(self.MenuName or self.Name)
-self.SRS:SetGender(self.Gender)
-self.SRS:SetCulture(self.Culture)
-self.SRS:SetPort(self.Port)
-self.SRS:SetVoice(self.Voice)
-self.SRS:SetCoalition(self.coalition)
-if self.PathToGoogleKey then
-self.SRS:SetGoogle(self.PathToGoogleKey)
-end
-self.SRSQueue=MSRSQUEUE:New(self.alias)
-else
-self.useSRS=false
-self.SRS=nil
-self.SRSQueue=nil
-end
-return self
-end
-function AUTOLASE:SetMaxLasingTargets(Number)
-self.maxlasing=Number or 4
-return self
-end
-function AUTOLASE:SetNotifyPilots(OnOff)
-self.notifypilots=OnOff and true
-return self
-end
-function AUTOLASE:SetRecceLaserCode(RecceName,Code,Refresh)
-local code=Code or 1688
-self.RecceLaserCode[RecceName]=code
-if Refresh then
-self:SetPilotMenu()
-if self.notifypilots then
-if string.find(RecceName,"#")then
-RecceName=string.match(RecceName,"^(.*)#")
-end
-self:NotifyPilots(string.format("Code for %s set to: %d",RecceName,Code),15)
-end
-end
-return self
-end
-function AUTOLASE:SetRecceSmokeColor(RecceName,Color)
-local color=Color or self.smokecolor
-self.RecceSmokeColor[RecceName]=color
-return self
-end
-function AUTOLASE:SetLaserCoolDown(OnOff,Seconds)
-self.forcecooldown=OnOff and true
-self.cooldowntime=Seconds or 60
-return self
-end
-function AUTOLASE:SetReportingTimes(long,short)
-self.reporttimeshort=short or 10
-self.reporttimelong=long or 30
-return self
-end
-function AUTOLASE:SetLasingParameters(Distance,Duration)
-self.LaseDistance=Distance or 5000
-self.LaseDuration=Duration or 300
-return self
-end
-function AUTOLASE:SetSmokeTargets(OnOff,Color)
-self.smoketargets=OnOff
-self.smokecolor=Color or SMOKECOLOR.Red
-local smktxt=OnOff==true and"on"or"off"
-local Message="Smoking targets is now "..smktxt.."!"
-self:NotifyPilots(Message,10)
-return self
-end
-function AUTOLASE:GetLosFromUnit(Unit)
-local lasedistance=self.LaseDistance
-local unitheight=Unit:GetHeight()
-local coord=Unit:GetCoordinate()
-local landheight=coord:GetLandHeight()
-local asl=unitheight-landheight
-if asl>100 then
-local absquare=lasedistance^2+asl^2
-lasedistance=math.sqrt(absquare)
-end
-return lasedistance
-end
-function AUTOLASE:CleanCurrentLasing()
-local lasingtable=self.CurrentLasing
-local newtable={}
-local newreccecount={}
-local lasing=0
-for _ind,_entry in pairs(lasingtable)do
-local entry=_entry
-if not newreccecount[entry.reccename]then
-newreccecount[entry.reccename]=0
-end
-end
-for _,_recce in pairs(self.RecceSet:GetSetObjects())do
-local recce=_recce
-if recce and recce:IsAlive()then
-local unit=recce:GetUnit(1)
-local name=unit:GetName()
-if not self.RecceUnits[name]then
-self.RecceUnits[name]={name=name,unit=unit,cooldown=false,timestamp=timer.getAbsTime()}
-end
-end
-end
-for _ind,_entry in pairs(lasingtable)do
-local entry=_entry
-local valid=0
-local reccedead=false
-local unitdead=false
-local lostsight=false
-local timeout=false
-local Tnow=timer.getAbsTime()
-local recce=entry.lasingunit
-if recce and recce:IsAlive()then
-valid=valid+1
-else
-reccedead=true
-self:__RecceKIA(2,entry.reccename)
-end
-local unit=entry.lasedunit
-if unit and unit:IsAlive()==true then
-valid=valid+1
-else
-unitdead=true
-if not self.deadunitnotes[entry.unitname]then
-self.deadunitnotes[entry.unitname]=true
-self:__TargetDestroyed(2,entry.unitname,entry.reccename)
-end
-end
-if not reccedead and not unitdead then
-if self:CanLase(recce,unit)then
-valid=valid+1
-else
-lostsight=true
-entry.laserspot:LaseOff()
-self:__TargetLost(2,entry.unitname,entry.reccename)
-end
-end
-local timestamp=entry.timestamp
-if Tnow-timestamp=distance and threat>=self.minthreatlevel then
-table.insert(groupsbythreat,{contact.group,contact.threatlevel})
-self.RecceNames[contact.groupname]=contact.recce
-end
-end
-end
-self.GroupsByThreat=groupsbythreat
-if self.verbose>2 and lines>0 then
-local m=MESSAGE:New(report:Text(),self.reporttimeshort,"Autolase"):ToAll()
-end
-table.sort(self.GroupsByThreat,function(a,b)
-local aNum=a[2]
-local bNum=b[2]
-return aNum>bNum
-end)
-local unitsbythreat={}
-for _,_entry in pairs(self.GroupsByThreat)do
-local group=_entry[1]
-if group and group:IsAlive()then
-local units=group:GetUnits()
-local reccename=self.RecceNames[group:GetName()]
-for _,_unit in pairs(units)do
-local unit=_unit
-if unit and unit:IsAlive()then
-local threat=unit:GetThreatLevel()
-local coord=unit:GetCoordinate()
-if threat>=self.minthreatlevel then
-local unitname=unit:GetName()
-if unit:HasAttribute("RADAR_BAND1_FOR_ARM")or unit:HasAttribute("RADAR_BAND2_FOR_ARM")or unit:HasAttribute("Optical Tracker")then
-threat=11
-end
-table.insert(unitsbythreat,{unit,threat})
-self.RecceUnitNames[unitname]=reccename
-end
-end
-end
-end
-end
-self.UnitsByThreat=unitsbythreat
-table.sort(self.UnitsByThreat,function(a,b)
-local aNum=a[2]
-local bNum=b[2]
-return aNum>bNum
-end)
-local unitreport=REPORT:New("Detected Units")
-local lines=0
-for _,_entry in pairs(self.UnitsByThreat)do
-local threat=_entry[2]
-local unit=_entry[1]
-local unitname=unit:GetName()
-local text=string.format("Unit %s | Threatlevel %d | Detected by %s",unitname,threat,self.RecceUnitNames[unitname])
-unitreport:Add(text)
-lines=lines+1
-self:T(text)
-if self.debug then self:I(text)end
-end
-if self.verbose>2 and lines>0 then
-local m=MESSAGE:New(unitreport:Text(),self.reporttimeshort,"Autolase"):ToAll()
-end
-for _,_detectingunit in pairs(self.RecceUnits)do
-local reccename=_detectingunit.name
-local recce=_detectingunit.unit
-local reccecount=self.targetsperrecce[reccename]or 0
-local targets=0
-for _,_entry in pairs(self.UnitsByThreat)do
-local unit=_entry[1]
-local unitname=unit:GetName()
-local canlase=self:CanLase(recce,unit)
-if targets+reccecount10 then
-break
-end
-end
-MenuDesignate:Remove(MenuTime,self.DesignateName)
-MenuDesignate:Set()
-end
-function DESIGNATE:SetDesignateMenu()
-self.AttackSet:Flush(self)
-local Delay=1
-self.AttackSet:ForEachGroupAlive(
-function(AttackGroup)
-self:ScheduleOnce(Delay,self.SetMenu,self,AttackGroup)
-Delay=Delay+1
-end
-)
-return self
-end
-function DESIGNATE:MenuStatus(AttackGroup)
-self:F("Status")
-self:SendStatus(AttackGroup)
-end
-function DESIGNATE:MenuFlashStatus(AttackGroup,Flash)
-self:F("Flash Status")
-self.FlashStatusMenu[AttackGroup]=Flash
-self:SetDesignateMenu()
-end
-function DESIGNATE:MenuForget(Index)
-self:F("Forget")
-self.Designating[Index]=""
-self:SetDesignateMenu()
-end
-function DESIGNATE:MenuAutoLase(AutoLase)
-self:F("AutoLase")
-self:SetAutoLase(AutoLase,true)
-end
-function DESIGNATE:MenuSmoke(Index,Color)
-self:F("Designate through Smoke")
-if string.find(self.Designating[Index],"S")==nil then
-self.Designating[Index]=self.Designating[Index].."S"
-end
-self:Smoke(Index,Color)
-self:SetDesignateMenu()
-end
-function DESIGNATE:MenuIlluminate(Index)
-self:F("Designate through Illumination")
-if string.find(self.Designating[Index],"I",1,true)==nil then
-self.Designating[Index]=self.Designating[Index].."I"
-end
-self:__Illuminate(1,Index)
-self:SetDesignateMenu()
-end
-function DESIGNATE:MenuLaseOn(Index,Duration)
-self:F("Designate through Lase")
-self:__LaseOn(1,Index,Duration)
-self:SetDesignateMenu()
-end
-function DESIGNATE:MenuLaseCode(Index,Duration,LaserCode)
-self:F("Designate through Lase using "..LaserCode)
-self:__LaseOn(1,Index,Duration,LaserCode)
-self:SetDesignateMenu()
-end
-function DESIGNATE:MenuLaseOff(Index,Duration)
-self:F("Lasing off")
-self.Designating[Index]=string.gsub(self.Designating[Index],"L","")
-self:__LaseOff(1,Index)
-self:SetDesignateMenu()
-end
-function DESIGNATE:onafterLaseOn(From,Event,To,Index,Duration,LaserCode)
-if string.find(self.Designating[Index],"L",1,true)==nil then
-self.Designating[Index]=self.Designating[Index].."L"
-self.LaseStart=timer.getTime()
-self.LaseDuration=Duration
-self:Lasing(Index,Duration,LaserCode)
-end
-end
-function DESIGNATE:onafterLasing(From,Event,To,Index,Duration,LaserCodeRequested)
-local DetectedItem=self.Detection:GetDetectedItemByIndex(Index)
-local TargetSetUnit=self.Detection:GetDetectedItemSet(DetectedItem)
-local MarkingCount=0
-local MarkedTypes={}
-TargetSetUnit:Flush(self)
-for TargetUnit,RecceData in pairs(self.Recces)do
-local Recce=RecceData
-self:F({TargetUnit=TargetUnit,Recce=Recce:GetName()})
-if not Recce:IsLasing()then
-local LaserCode=Recce:GetLaserCode()
-self:F({ClearingLaserCode=LaserCode})
-self.LaserCodesUsed[LaserCode]=nil
-self.Recces[TargetUnit]=nil
-end
-end
-if LaserCodeRequested then
-for TargetUnit,RecceData in pairs(self.Recces)do
-local Recce=RecceData
-self:F({TargetUnit=TargetUnit,Recce=Recce:GetName()})
-if Recce:IsLasing()then
-Recce:LaseOff()
-local LaserCode=Recce:GetLaserCode()
-self:F({ClearingLaserCode=LaserCode})
-self.LaserCodesUsed[LaserCode]=nil
-self.Recces[TargetUnit]=nil
-break
-end
-end
-end
-if self.AutoLase or(not self.AutoLase and(self.LaseStart+Duration>=timer.getTime()))then
-TargetSetUnit:ForEachUnitPerThreatLevel(10,0,
-function(TargetUnit)
-self:F({TargetUnit=TargetUnit:GetName()})
-if MarkingCountself.AcceptRange then
-DetectionAccepted=false
-end
-if self.AcceptZones then
-local AnyZoneDetection=false
-for AcceptZoneID,AcceptZone in pairs(self.AcceptZones)do
-local AcceptZone=AcceptZone
-if AcceptZone:IsVec2InZone(DetectedObjectVec2)then
-AnyZoneDetection=true
-end
-end
-if not AnyZoneDetection then
-DetectionAccepted=false
-end
-end
-if self.RejectZones then
-for RejectZoneID,RejectZone in pairs(self.RejectZones)do
-local RejectZone=RejectZone
-if RejectZone:IsVec2InZone(DetectedObjectVec2)==true then
-DetectionAccepted=false
-end
-end
-end
-if self.RadarBlur then
-MESSAGE:New("Radar Blur",10):ToLogIf(self.debug):ToAllIf(self.verbose)
-local minheight=self.RadarBlurMinHeight or 250
-local thresheight=self.RadarBlurThresHeight or 90
-local thresblur=self.RadarBlurThresBlur or 85
-local dist=math.floor(Distance)
-if dist<=self.RadarBlurClosing then
-thresheight=(((dist*dist)/self.RadarBlurClosingSquare)*thresheight)
-thresblur=(((dist*dist)/self.RadarBlurClosingSquare)*thresblur)
-end
-local fheight=math.floor(math.random(1,10000)/100)
-local fblur=math.floor(math.random(1,10000)/100)
-local unit=UNIT:FindByName(DetectedObjectName)
-if unit and unit:IsAlive()then
-local AGL=unit:GetAltitude(true)
-MESSAGE:New("Unit "..DetectedObjectName.." is at "..math.floor(AGL).."m. Distance "..math.floor(Distance).."km.",10):ToLogIf(self.debug):ToAllIf(self.verbose)
-MESSAGE:New(string.format("fheight = %d/%d | fblur = %d/%d",fheight,thresheight,fblur,thresblur),10):ToLogIf(self.debug):ToAllIf(self.verbose)
-if fblur>thresblur then DetectionAccepted=false end
-if AGL<=minheight and fheight0 and self.DetectionRun==self.DetectionCount then
-for DetectedObjectName,DetectedObject in pairs(self.DetectedObjects)do
-if self.DetectedObjects[DetectedObjectName].IsDetected==true and self.DetectedObjects[DetectedObjectName].DetectionTimeStamp+300<=DetectionTimeStamp then
-self.DetectedObjects[DetectedObjectName].IsDetected=false
-end
-end
-self:CreateDetectionItems()
-for DetectedItemID,DetectedItem in pairs(self.DetectedItems)do
-self:UpdateDetectedItemDetection(DetectedItem)
-self:CleanDetectionItem(DetectedItem,DetectedItemID)
-if DetectedItem then
-self:__DetectedItem(0.1,DetectedItem)
-end
-end
-end
-end
-end
-do
-function DETECTION_BASE:CleanDetectionItem(DetectedItem,DetectedItemID)
-local DetectedSet=DetectedItem.Set
-if DetectedSet:Count()==0 then
-self:RemoveDetectedItem(DetectedItemID)
-end
-return self
-end
-function DETECTION_BASE:ForgetDetectedUnit(UnitName)
-local DetectedItems=self:GetDetectedItems()
-for DetectedItemIndex,DetectedItem in pairs(DetectedItems)do
-local DetectedSet=self:GetDetectedItemSet(DetectedItem)
-if DetectedSet then
-DetectedSet:RemoveUnitsByName(UnitName)
-end
-end
-return self
-end
-function DETECTION_BASE:CreateDetectionItems()
-self:F("Error, in DETECTION_BASE class...")
-return self
-end
-end
-do
-function DETECTION_BASE:InitDetectVisual(DetectVisual)
-self.DetectVisual=DetectVisual
-return self
-end
-function DETECTION_BASE:InitDetectOptical(DetectOptical)
-self:F2()
-self.DetectOptical=DetectOptical
-return self
-end
-function DETECTION_BASE:InitDetectRadar(DetectRadar)
-self:F2()
-self.DetectRadar=DetectRadar
-return self
-end
-function DETECTION_BASE:InitDetectIRST(DetectIRST)
-self:F2()
-self.DetectIRST=DetectIRST
-return self
-end
-function DETECTION_BASE:InitDetectRWR(DetectRWR)
-self:F2()
-self.DetectRWR=DetectRWR
-return self
-end
-function DETECTION_BASE:InitDetectDLINK(DetectDLINK)
-self:F2()
-self.DetectDLINK=DetectDLINK
-return self
-end
-end
-do
-function DETECTION_BASE:FilterCategories(FilterCategories)
-self:F2()
-self._.FilterCategories={}
-if type(FilterCategories)=="table"then
-for CategoryID,Category in pairs(FilterCategories)do
-self._.FilterCategories[Category]=Category
-end
-else
-self._.FilterCategories[FilterCategories]=FilterCategories
-end
-return self
-end
-function DETECTION_BASE:SetRadarBlur(minheight,thresheight,thresblur,closing)
-self.RadarBlur=true
-self.RadarBlurMinHeight=minheight or 250
-self.RadarBlurThresHeight=thresheight or 90
-self.RadarBlurThresBlur=thresblur or 85
-self.RadarBlurClosing=closing or 20
-self.RadarBlurClosingSquare=self.RadarBlurClosing*self.RadarBlurClosing
-return self
-end
-end
-do
-function DETECTION_BASE:SetRefreshTimeInterval(RefreshTimeInterval)
-self:F2()
-self.RefreshTimeInterval=RefreshTimeInterval
-return self
-end
-end
-do
-function DETECTION_BASE:SetFriendliesRange(FriendliesRange)
-self:F2()
-self.FriendliesRange=FriendliesRange
-return self
-end
-end
-do
-function DETECTION_BASE:SetIntercept(Intercept,InterceptDelay)
-self:F2()
-self.Intercept=Intercept
-self.InterceptDelay=InterceptDelay
-return self
-end
-end
-do
-function DETECTION_BASE:SetAcceptRange(AcceptRange)
-self:F2()
-self.AcceptRange=AcceptRange
-return self
-end
-function DETECTION_BASE:SetAcceptZones(AcceptZones)
-self:F2()
-if type(AcceptZones)=="table"then
-if AcceptZones.ClassName and AcceptZones:IsInstanceOf(ZONE_BASE)then
-self.AcceptZones={AcceptZones}
-else
-self.AcceptZones=AcceptZones
-end
-else
-self:F({"AcceptZones must be a list of ZONE_BASE derived objects or one ZONE_BASE derived object",AcceptZones})
-error()
-end
-return self
-end
-function DETECTION_BASE:SetRejectZones(RejectZones)
-self:F2()
-if type(RejectZones)=="table"then
-if RejectZones.ClassName and RejectZones:IsInstanceOf(ZONE_BASE)then
-self.RejectZones={RejectZones}
-else
-self.RejectZones=RejectZones
-end
-else
-self:F({"RejectZones must be a list of ZONE_BASE derived objects or one ZONE_BASE derived object",RejectZones})
-error()
-end
-return self
-end
-end
-do
-function DETECTION_BASE:SetDistanceProbability(DistanceProbability)
-self:F2()
-self.DistanceProbability=DistanceProbability
-return self
-end
-function DETECTION_BASE:SetAlphaAngleProbability(AlphaAngleProbability)
-self:F2()
-self.AlphaAngleProbability=AlphaAngleProbability
-return self
-end
-function DETECTION_BASE:SetZoneProbability(ZoneArray)
-self:F2()
-self.ZoneProbability=ZoneArray
-return self
-end
-end
-do
-function DETECTION_BASE:AcceptChanges(DetectedItem)
-DetectedItem.Changed=false
-DetectedItem.Changes={}
-return self
-end
-function DETECTION_BASE:AddChangeItem(DetectedItem,ChangeCode,ItemUnitType)
-DetectedItem.Changed=true
-local ID=DetectedItem.ID
-DetectedItem.Changes=DetectedItem.Changes or{}
-DetectedItem.Changes[ChangeCode]=DetectedItem.Changes[ChangeCode]or{}
-DetectedItem.Changes[ChangeCode].ID=ID
-DetectedItem.Changes[ChangeCode].ItemUnitType=ItemUnitType
-self:F({"Change on Detected Item:",DetectedItemID=DetectedItem.ID,ChangeCode=ChangeCode,ItemUnitType=ItemUnitType})
-return self
-end
-function DETECTION_BASE:AddChangeUnit(DetectedItem,ChangeCode,ChangeUnitType)
-DetectedItem.Changed=true
-local ID=DetectedItem.ID
-DetectedItem.Changes=DetectedItem.Changes or{}
-DetectedItem.Changes[ChangeCode]=DetectedItem.Changes[ChangeCode]or{}
-DetectedItem.Changes[ChangeCode][ChangeUnitType]=DetectedItem.Changes[ChangeCode][ChangeUnitType]or 0
-DetectedItem.Changes[ChangeCode][ChangeUnitType]=DetectedItem.Changes[ChangeCode][ChangeUnitType]+1
-DetectedItem.Changes[ChangeCode].ID=ID
-self:F({"Change on Detected Unit:",DetectedItemID=DetectedItem.ID,ChangeCode=ChangeCode,ChangeUnitType=ChangeUnitType})
-return self
-end
-end
-do
-function DETECTION_BASE:SetFriendlyPrefixes(FriendlyPrefixes)
-self.FriendlyPrefixes=self.FriendlyPrefixes or{}
-if type(FriendlyPrefixes)~="table"then
-FriendlyPrefixes={FriendlyPrefixes}
-end
-for PrefixID,Prefix in pairs(FriendlyPrefixes)do
-self:F({FriendlyPrefix=Prefix})
-self.FriendlyPrefixes[Prefix]=Prefix
-end
-return self
-end
-function DETECTION_BASE:IsFriendliesNearBy(DetectedItem,Category)
-return(DetectedItem.FriendliesNearBy and DetectedItem.FriendliesNearBy[Category]~=nil)or false
-end
-function DETECTION_BASE:GetFriendliesNearBy(DetectedItem,Category)
-return DetectedItem.FriendliesNearBy and DetectedItem.FriendliesNearBy[Category]
-end
-function DETECTION_BASE:IsFriendliesNearIntercept(DetectedItem)
-return DetectedItem.FriendliesNearIntercept~=nil or false
-end
-function DETECTION_BASE:GetFriendliesNearIntercept(DetectedItem)
-return DetectedItem.FriendliesNearIntercept
-end
-function DETECTION_BASE:GetFriendliesDistance(DetectedItem)
-return DetectedItem.FriendliesDistance
-end
-function DETECTION_BASE:IsPlayersNearBy(DetectedItem)
-return DetectedItem.PlayersNearBy~=nil
-end
-function DETECTION_BASE:GetPlayersNearBy(DetectedItem)
-return DetectedItem.PlayersNearBy
-end
-function DETECTION_BASE:ReportFriendliesNearBy(TargetData)
-local DetectedItem=TargetData.DetectedItem
-local DetectedSet=TargetData.DetectedItem.Set
-local DetectedUnit=DetectedSet:GetFirst()
-DetectedItem.FriendliesNearBy=nil
-if DetectedUnit and DetectedUnit:IsAlive()then
-local DetectedUnitCoord=DetectedUnit:GetCoordinate()
-local InterceptCoord=TargetData.InterceptCoord or DetectedUnitCoord
-local SphereSearch={
-id=world.VolumeType.SPHERE,
-params={
-point=InterceptCoord:GetVec3(),
-radius=self.FriendliesRange,
-}
-}
-local FindNearByFriendlies=function(FoundDCSUnit,ReportGroupData)
-local DetectedItem=ReportGroupData.DetectedItem
-local DetectedSet=ReportGroupData.DetectedItem.Set
-local DetectedUnit=DetectedSet:GetFirst()
-local DetectedUnitCoord=DetectedUnit:GetCoordinate()
-local InterceptCoord=ReportGroupData.InterceptCoord or DetectedUnitCoord
-local ReportSetGroup=ReportGroupData.ReportSetGroup
-local EnemyCoalition=DetectedUnit:GetCoalition()
-local FoundUnitCoalition=FoundDCSUnit:getCoalition()
-local FoundUnitCategory=FoundDCSUnit:getDesc().category
-local FoundUnitName=FoundDCSUnit:getName()
-local FoundUnitGroupName=FoundDCSUnit:getGroup():getName()
-local EnemyUnitName=DetectedUnit:GetName()
-local FoundUnitInReportSetGroup=ReportSetGroup:FindGroup(FoundUnitGroupName)~=nil
-if FoundUnitInReportSetGroup==true then
-for PrefixID,Prefix in pairs(self.FriendlyPrefixes or{})do
-if string.find(FoundUnitName,Prefix:gsub("-","%%-"),1)then
-FoundUnitInReportSetGroup=false
-break
-end
-end
-end
-if FoundUnitCoalition~=EnemyCoalition and FoundUnitInReportSetGroup==false then
-local FriendlyUnit=UNIT:Find(FoundDCSUnit)
-local FriendlyUnitName=FriendlyUnit:GetName()
-local FriendlyUnitCategory=FriendlyUnit:GetDesc().category
-DetectedItem.FriendliesNearBy=DetectedItem.FriendliesNearBy or{}
-DetectedItem.FriendliesNearBy[FoundUnitCategory]=DetectedItem.FriendliesNearBy[FoundUnitCategory]or{}
-DetectedItem.FriendliesNearBy[FoundUnitCategory][FriendlyUnitName]=FriendlyUnit
-local Distance=DetectedUnitCoord:Get2DDistance(FriendlyUnit:GetCoordinate())
-DetectedItem.FriendliesDistance=DetectedItem.FriendliesDistance or{}
-DetectedItem.FriendliesDistance[Distance]=FriendlyUnit
-return true
-end
-return true
-end
-world.searchObjects(Object.Category.UNIT,SphereSearch,FindNearByFriendlies,TargetData)
-DetectedItem.PlayersNearBy=nil
-_DATABASE:ForEachPlayer(
-function(PlayerUnitName)
-local PlayerUnit=UNIT:FindByName(PlayerUnitName)
-if PlayerUnit and PlayerUnit:IsAlive()then
-local coord=PlayerUnit:GetCoordinate()
-if coord and coord:IsInRadius(DetectedUnitCoord,self.FriendliesRange)then
-local PlayerUnitCategory=PlayerUnit:GetDesc().category
-if(not self.FriendliesCategory)or(self.FriendliesCategory and(self.FriendliesCategory==PlayerUnitCategory))then
-local PlayerUnitName=PlayerUnit:GetName()
-DetectedItem.PlayersNearBy=DetectedItem.PlayersNearBy or{}
-DetectedItem.PlayersNearBy[PlayerUnitName]=PlayerUnit
-DetectedItem.FriendliesNearBy=DetectedItem.FriendliesNearBy or{}
-DetectedItem.FriendliesNearBy[PlayerUnitCategory]=DetectedItem.FriendliesNearBy[PlayerUnitCategory]or{}
-DetectedItem.FriendliesNearBy[PlayerUnitCategory][PlayerUnitName]=PlayerUnit
-local Distance=DetectedUnitCoord:Get2DDistance(PlayerUnit:GetCoordinate())
-DetectedItem.FriendliesDistance=DetectedItem.FriendliesDistance or{}
-DetectedItem.FriendliesDistance[Distance]=PlayerUnit
-end
-end
-end
-end)
-end
-self:F({Friendlies=DetectedItem.FriendliesNearBy,Players=DetectedItem.PlayersNearBy})
-end
-end
-function DETECTION_BASE:IsDetectedObjectIdentified(DetectedObject)
-local DetectedObjectName=DetectedObject.Name
-if DetectedObjectName then
-local DetectedObjectIdentified=self.DetectedObjectsIdentified[DetectedObjectName]==true
-return DetectedObjectIdentified
-else
-return nil
-end
-end
-function DETECTION_BASE:IdentifyDetectedObject(DetectedObject)
-local DetectedObjectName=DetectedObject.Name
-self.DetectedObjectsIdentified[DetectedObjectName]=true
-end
-function DETECTION_BASE:UnIdentifyDetectedObject(DetectedObject)
-local DetectedObjectName=DetectedObject.Name
-self.DetectedObjectsIdentified[DetectedObjectName]=false
-end
-function DETECTION_BASE:UnIdentifyAllDetectedObjects()
-self.DetectedObjectsIdentified={}
-end
-function DETECTION_BASE:GetDetectedObject(ObjectName)
-self:F2({ObjectName=ObjectName})
-if ObjectName then
-local DetectedObject=self.DetectedObjects[ObjectName]
-if DetectedObject then
-local DetectedUnit=UNIT:FindByName(ObjectName)
-if DetectedUnit and DetectedUnit:IsAlive()then
-if self:IsDetectedObjectIdentified(DetectedObject)==false then
-return DetectedObject
-end
-end
-end
-end
-return nil
-end
-function DETECTION_BASE:GetDetectedUnitTypeName(DetectedUnit)
-if DetectedUnit and DetectedUnit:IsAlive()then
-local DetectedUnitName=DetectedUnit:GetName()
-local DetectedObject=self.DetectedObjects[DetectedUnitName]
-if DetectedObject then
-if DetectedObject.KnowType then
-return DetectedUnit:GetTypeName()
-else
-return"Unknown"
-end
-else
-return"Unknown"
-end
-else
-return"Dead:"..DetectedUnit:GetName()
-end
-return"Undetected:"..DetectedUnit:GetName()
-end
-function DETECTION_BASE:AddDetectedItem(ItemPrefix,DetectedItemKey,Set)
-local DetectedItem={}
-self.DetectedItemCount=self.DetectedItemCount+1
-self.DetectedItemMax=self.DetectedItemMax+1
-DetectedItemKey=DetectedItemKey or self.DetectedItemMax
-self.DetectedItems[DetectedItemKey]=DetectedItem
-self.DetectedItemsByIndex[DetectedItemKey]=DetectedItem
-DetectedItem.Index=DetectedItemKey
-DetectedItem.Set=Set or SET_UNIT:New():FilterDeads():FilterCrashes()
-DetectedItem.ItemID=ItemPrefix.."."..self.DetectedItemMax
-DetectedItem.ID=self.DetectedItemMax
-DetectedItem.Removed=false
-if self.Locking then
-self:LockDetectedItem(DetectedItem)
-end
-return DetectedItem
-end
-function DETECTION_BASE:AddDetectedItemZone(ItemPrefix,DetectedItemKey,Set,Zone)
-self:F({ItemPrefix,DetectedItemKey,Set,Zone})
-local DetectedItem=self:AddDetectedItem(ItemPrefix,DetectedItemKey,Set)
-DetectedItem.Zone=Zone
-return DetectedItem
-end
-function DETECTION_BASE:RemoveDetectedItem(DetectedItemKey)
-local DetectedItem=self.DetectedItems[DetectedItemKey]
-if DetectedItem then
-self.DetectedItemCount=self.DetectedItemCount-1
-local DetectedItemIndex=DetectedItem.Index
-self.DetectedItemsByIndex[DetectedItemIndex]=nil
-self.DetectedItems[DetectedItemKey]=nil
-end
-end
-function DETECTION_BASE:GetDetectedItems()
-return self.DetectedItems
-end
-function DETECTION_BASE:GetDetectedItemsByIndex()
-return self.DetectedItemsByIndex
-end
-function DETECTION_BASE:GetDetectedItemsCount()
-local DetectedCount=self.DetectedItemCount
-return DetectedCount
-end
-function DETECTION_BASE:GetDetectedItemByKey(Key)
-self:F({DetectedItems=self.DetectedItems})
-local DetectedItem=self.DetectedItems[Key]
-if DetectedItem then
-return DetectedItem
-end
-return nil
-end
-function DETECTION_BASE:GetDetectedItemByIndex(Index)
-self:F({self.DetectedItemsByIndex})
-local DetectedItem=self.DetectedItemsByIndex[Index]
-if DetectedItem then
-return DetectedItem
-end
-return nil
-end
-function DETECTION_BASE:GetDetectedItemID(DetectedItem)
-return DetectedItem and DetectedItem.ItemID or""
-end
-function DETECTION_BASE:GetDetectedID(Index)
-local DetectedItem=self.DetectedItemsByIndex[Index]
-if DetectedItem then
-return DetectedItem.ID
-end
-return""
-end
-function DETECTION_BASE:GetDetectedItemSet(DetectedItem)
-local DetectedSetUnit=DetectedItem and DetectedItem.Set
-if DetectedSetUnit then
-return DetectedSetUnit
-end
-return nil
-end
-function DETECTION_BASE:UpdateDetectedItemDetection(DetectedItem)
-local IsDetected=false
-for UnitName,UnitData in pairs(DetectedItem.Set:GetSet())do
-local DetectedObject=self.DetectedObjects[UnitName]
-self:F({UnitName=UnitName,IsDetected=DetectedObject.IsDetected})
-if DetectedObject.IsDetected then
-IsDetected=true
-break
-end
-end
-self:F({IsDetected=DetectedItem.IsDetected})
-DetectedItem.IsDetected=IsDetected
-return IsDetected
-end
-function DETECTION_BASE:IsDetectedItemDetected(DetectedItem)
-return DetectedItem.IsDetected
-end
-do
-function DETECTION_BASE:GetDetectedItemZone(DetectedItem)
-local DetectedZone=DetectedItem and DetectedItem.Zone
-if DetectedZone then
-return DetectedZone
-end
-local Detected
-return nil
-end
-end
-function DETECTION_BASE:LockDetectedItems()
-for DetectedItemID,DetectedItem in pairs(self.DetectedItems)do
-self:LockDetectedItem(DetectedItem)
-end
-self.Locking=true
-return self
-end
-function DETECTION_BASE:UnlockDetectedItems()
-for DetectedItemID,DetectedItem in pairs(self.DetectedItems)do
-self:UnlockDetectedItem(DetectedItem)
-end
-self.Locking=nil
-return self
-end
-function DETECTION_BASE:IsDetectedItemLocked(DetectedItem)
-return self.Locking and DetectedItem.Locked==true
-end
-function DETECTION_BASE:LockDetectedItem(DetectedItem)
-DetectedItem.Locked=true
-return self
-end
-function DETECTION_BASE:UnlockDetectedItem(DetectedItem)
-DetectedItem.Locked=nil
-return self
-end
-function DETECTION_BASE:SetDetectedItemCoordinate(DetectedItem,Coordinate,DetectedItemUnit)
-self:F({Coordinate=Coordinate})
-if DetectedItem then
-if DetectedItemUnit then
-DetectedItem.Coordinate=Coordinate
-DetectedItem.Coordinate:SetHeading(DetectedItemUnit:GetHeading())
-DetectedItem.Coordinate.y=DetectedItemUnit:GetAltitude()
-DetectedItem.Coordinate:SetVelocity(DetectedItemUnit:GetVelocityMPS())
-end
-end
-end
-function DETECTION_BASE:GetDetectedItemCoordinate(DetectedItem)
-self:F({DetectedItem=DetectedItem})
-if DetectedItem then
-return DetectedItem.Coordinate
-end
-return nil
-end
-function DETECTION_BASE:GetDetectedItemCoordinates()
-local Coordinates={}
-for DetectedItemID,DetectedItem in pairs(self:GetDetectedItems())do
-Coordinates[DetectedItem]=self:GetDetectedItemCoordinate(DetectedItem)
-end
-return Coordinates
-end
-function DETECTION_BASE:SetDetectedItemThreatLevel(DetectedItem)
-local DetectedSet=DetectedItem.Set
-if DetectedItem then
-DetectedItem.ThreatLevel,DetectedItem.ThreatText=DetectedSet:CalculateThreatLevelA2G()
-end
-end
-function DETECTION_BASE:GetDetectedItemThreatLevel(DetectedItem)
-self:F({DetectedItem=DetectedItem})
-if DetectedItem then
-self:F({ThreatLevel=DetectedItem.ThreatLevel,ThreatText=DetectedItem.ThreatText})
-return DetectedItem.ThreatLevel or 0,DetectedItem.ThreatText or""
-end
-return nil,""
-end
-function DETECTION_BASE:DetectedItemReportSummary(DetectedItem,AttackGroup,Settings)
-self:F()
-return nil
-end
-function DETECTION_BASE:DetectedReportDetailed(AttackGroup)
-self:F()
-return nil
-end
-function DETECTION_BASE:GetDetectionSet()
-local DetectionSet=self.DetectionSet
-return DetectionSet
-end
-function DETECTION_BASE:NearestRecce(DetectedItem)
-local NearestRecce=nil
-local DistanceRecce=1000000000
-for RecceGroupName,RecceGroup in pairs(self.DetectionSet:GetSet())do
-if RecceGroup and RecceGroup:IsAlive()then
-for RecceUnit,RecceUnit in pairs(RecceGroup:GetUnits())do
-if RecceUnit:IsActive()then
-local RecceUnitCoord=RecceUnit:GetCoordinate()
-local Distance=RecceUnitCoord:Get2DDistance(self:GetDetectedItemCoordinate(DetectedItem))
-if Distance0 then
-DetectedItemCoordText=DetectedItemCoordinate:ToStringA2A(AttackGroup,Settings)
-else
-DetectedItemCoordText=DetectedItemCoordinate:ToStringA2G(AttackGroup,Settings)
-end
-local ThreatLevelA2G=self:GetDetectedItemThreatLevel(DetectedItem)
-local DetectedItemsCount=DetectedSet:Count()
-local DetectedItemsTypes=DetectedSet:GetTypeNames()
-local Report=REPORT:New()
-Report:Add(DetectedItemID..", "..DetectedItemCoordText)
-Report:Add(string.format("Threat: [%s%s]",string.rep("■",ThreatLevelA2G),string.rep("□",10-ThreatLevelA2G)))
-Report:Add(string.format("Type: %2d of %s",DetectedItemsCount,DetectedItemsTypes))
-return Report
-end
-return nil
-end
-function DETECTION_AREAS:DetectedReportDetailed(AttackGroup)
-self:F()
-local Report=REPORT:New()
-for DetectedItemIndex,DetectedItem in pairs(self.DetectedItems)do
-local DetectedItem=DetectedItem
-local ReportSummary=self:DetectedItemReportSummary(DetectedItem,AttackGroup)
-Report:SetTitle("Detected areas:")
-Report:Add(ReportSummary:Text())
-end
-local ReportText=Report:Text()
-return ReportText
-end
-function DETECTION_AREAS:CalculateIntercept(DetectedItem)
-local DetectedCoord=DetectedItem.Coordinate
-local DetectedSpeed=DetectedCoord:GetVelocity()
-local DetectedHeading=DetectedCoord:GetHeading()
-if self.Intercept then
-local DetectedSet=DetectedItem.Set
-local TranslateDistance=DetectedSpeed*self.InterceptDelay
-local InterceptCoord=DetectedCoord:Translate(TranslateDistance,DetectedHeading)
-DetectedItem.InterceptCoord=InterceptCoord
-else
-DetectedItem.InterceptCoord=DetectedCoord
-end
-end
-function DETECTION_AREAS:SmokeDetectedUnits()
-self:F2()
-self._SmokeDetectedUnits=true
-return self
-end
-function DETECTION_AREAS:FlareDetectedUnits()
-self:F2()
-self._FlareDetectedUnits=true
-return self
-end
-function DETECTION_AREAS:SmokeDetectedZones()
-self:F2()
-self._SmokeDetectedZones=true
-return self
-end
-function DETECTION_AREAS:FlareDetectedZones()
-self:F2()
-self._FlareDetectedZones=true
-return self
-end
-function DETECTION_AREAS:BoundDetectedZones()
-self:F2()
-self._BoundDetectedZones=true
-return self
-end
-function DETECTION_AREAS:GetChangeText(DetectedItem)
-self:F(DetectedItem)
-local MT={}
-for ChangeCode,ChangeData in pairs(DetectedItem.Changes)do
-if ChangeCode=="AA"then
-MT[#MT+1]="Detected new area "..ChangeData.ID..". The center target is a "..ChangeData.ItemUnitType.."."
-end
-if ChangeCode=="RAU"then
-MT[#MT+1]="Changed area "..ChangeData.ID..". Removed the center target."
-end
-if ChangeCode=="AAU"then
-MT[#MT+1]="Changed area "..ChangeData.ID..". The new center target is a "..ChangeData.ItemUnitType.."."
-end
-if ChangeCode=="RA"then
-MT[#MT+1]="Removed old area "..ChangeData.ID..". No more targets in this area."
-end
-if ChangeCode=="AU"then
-local MTUT={}
-for ChangeUnitType,ChangeUnitCount in pairs(ChangeData)do
-if ChangeUnitType~="ID"then
-MTUT[#MTUT+1]=ChangeUnitCount.." of "..ChangeUnitType
-end
-end
-MT[#MT+1]="Detected for area "..ChangeData.ID.." new target(s) "..table.concat(MTUT,", ").."."
-end
-if ChangeCode=="RU"then
-local MTUT={}
-for ChangeUnitType,ChangeUnitCount in pairs(ChangeData)do
-if ChangeUnitType~="ID"then
-MTUT[#MTUT+1]=ChangeUnitCount.." of "..ChangeUnitType
-end
-end
-MT[#MT+1]="Removed for area "..ChangeData.ID.." invisible or destroyed target(s) "..table.concat(MTUT,", ").."."
-end
-end
-return table.concat(MT,"\n")
-end
-function DETECTION_AREAS:CreateDetectionItems()
-self:F("Checking Detected Items for new Detected Units ...")
-for DetectedItemID,DetectedItemData in pairs(self.DetectedItems)do
-local DetectedItem=DetectedItemData
-if DetectedItem then
-self:T2({"Detected Item ID: ",DetectedItemID})
-local DetectedSet=DetectedItem.Set
-local AreaExists=false
-self:T3({"Zone Center Unit:",DetectedItem.Zone.ZoneUNIT.UnitName})
-local DetectedZoneObject=self:GetDetectedObject(DetectedItem.Zone.ZoneUNIT.UnitName)
-self:T3({"Detected Zone Object:",DetectedItem.Zone:GetName(),DetectedZoneObject})
-if DetectedZoneObject then
-AreaExists=true
-else
-DetectedSet:RemoveUnitsByName(DetectedItem.Zone.ZoneUNIT.UnitName)
-self:AddChangeItem(DetectedItem,'RAU',self:GetDetectedUnitTypeName(DetectedItem.Zone.ZoneUNIT))
-for DetectedUnitName,DetectedUnitData in pairs(DetectedSet:GetSet())do
-local DetectedUnit=DetectedUnitData
-local DetectedObject=self:GetDetectedObject(DetectedUnit.UnitName)
-local DetectedUnitTypeName=self:GetDetectedUnitTypeName(DetectedUnit)
-if DetectedObject then
-self:IdentifyDetectedObject(DetectedObject)
-AreaExists=true
-DetectedItem.Zone=ZONE_UNIT:New(DetectedUnit:GetName(),DetectedUnit,self.DetectionZoneRange)
-self:AddChangeItem(DetectedItem,"AAU",DetectedUnitTypeName)
-break
-else
-DetectedSet:Remove(DetectedUnitName)
-self:AddChangeUnit(DetectedItem,"RU",DetectedUnitTypeName)
-end
-end
-end
-if AreaExists then
-for DetectedUnitName,DetectedUnitData in pairs(DetectedSet:GetSet())do
-local DetectedUnit=DetectedUnitData
-local DetectedUnitTypeName=self:GetDetectedUnitTypeName(DetectedUnit)
-local DetectedObject=nil
-if DetectedUnit:IsAlive()then
-DetectedObject=self:GetDetectedObject(DetectedUnit:GetName())
-end
-if DetectedObject then
-if DetectedUnit:IsInZone(DetectedItem.Zone)then
-self:IdentifyDetectedObject(DetectedObject)
-DetectedSet:AddUnit(DetectedUnit)
-else
-DetectedSet:Remove(DetectedUnitName)
-self:AddChangeUnit(DetectedItem,"RU",DetectedUnitTypeName)
-end
-else
-self:AddChangeUnit(DetectedItem,"RU","destroyed target")
-DetectedSet:Remove(DetectedUnitName)
-end
-end
-else
-self:RemoveDetectedItem(DetectedItemID)
-self:AddChangeItem(DetectedItem,"RA")
-end
-end
-end
-for DetectedUnitName,DetectedObjectData in pairs(self.DetectedObjects)do
-local DetectedObject=self:GetDetectedObject(DetectedUnitName)
-if DetectedObject then
-local DetectedUnit=UNIT:FindByName(DetectedUnitName)
-local DetectedUnitTypeName=self:GetDetectedUnitTypeName(DetectedUnit)
-local AddedToDetectionArea=false
-for DetectedItemID,DetectedItemData in pairs(self.DetectedItems)do
-local DetectedItem=DetectedItemData
-if DetectedItem then
-local DetectedSet=DetectedItem.Set
-if not self:IsDetectedObjectIdentified(DetectedObject)and DetectedUnit:IsInZone(DetectedItem.Zone)then
-self:IdentifyDetectedObject(DetectedObject)
-DetectedSet:AddUnit(DetectedUnit)
-AddedToDetectionArea=true
-self:AddChangeUnit(DetectedItem,"AU",DetectedUnitTypeName)
-end
-end
-end
-if AddedToDetectionArea==false then
-local DetectedItem=self:AddDetectedItemZone("AREA",nil,
-SET_UNIT:New():FilterDeads():FilterCrashes(),
-ZONE_UNIT:New(DetectedUnitName,DetectedUnit,self.DetectionZoneRange)
-)
-DetectedItem.Set:AddUnit(DetectedUnit)
-self:AddChangeItem(DetectedItem,"AA",DetectedUnitTypeName)
-end
-end
-end
-for DetectedItemID,DetectedItemData in pairs(self.DetectedItems)do
-local DetectedItem=DetectedItemData
-local DetectedSet=DetectedItem.Set
-local DetectedFirstUnit=DetectedSet:GetFirst()
-local DetectedZone=DetectedItem.Zone
-local DetectedZoneCoord=DetectedZone:GetCoordinate()
-self:SetDetectedItemCoordinate(DetectedItem,DetectedZoneCoord,DetectedFirstUnit)
-self:CalculateIntercept(DetectedItem)
-local OldFriendliesNearbyGround=self:IsFriendliesNearBy(DetectedItem,Unit.Category.GROUND_UNIT)
-self:ReportFriendliesNearBy({DetectedItem=DetectedItem,ReportSetGroup=self.DetectionSet})
-local NewFriendliesNearbyGround=self:IsFriendliesNearBy(DetectedItem,Unit.Category.GROUND_UNIT)
-if OldFriendliesNearbyGround~=NewFriendliesNearbyGround then
-DetectedItem.Changed=true
-end
-self:SetDetectedItemThreatLevel(DetectedItem)
-self:NearestRecce(DetectedItem)
-if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then
-DetectedZone.ZoneUNIT:SmokeRed()
-end
-DetectedSet:ForEachUnit(
-function(DetectedUnit)
-if DetectedUnit:IsAlive()then
-if DETECTION_AREAS._FlareDetectedUnits or self._FlareDetectedUnits then
-DetectedUnit:FlareGreen()
-end
-if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then
-DetectedUnit:SmokeGreen()
-end
-end
-end)
-if DETECTION_AREAS._FlareDetectedZones or self._FlareDetectedZones then
-DetectedZone:FlareZone(SMOKECOLOR.White,30,math.random(0,90))
-end
-if DETECTION_AREAS._SmokeDetectedZones or self._SmokeDetectedZones then
-DetectedZone:SmokeZone(SMOKECOLOR.White,30)
-end
-if DETECTION_AREAS._BoundDetectedZones or self._BoundDetectedZones then
-self.CountryID=DetectedSet:GetFirst():GetCountry()
-DetectedZone:BoundZone(12,self.CountryID)
-end
-end
-end
-end
-do
-DETECTION_ZONES={
-ClassName="DETECTION_ZONES",
-DetectionZoneRange=nil,
-}
-function DETECTION_ZONES:New(DetectionSetZone,DetectionCoalition)
-local self=BASE:Inherit(self,DETECTION_BASE:New(DetectionSetZone))
-self.DetectionSetZone=DetectionSetZone
-self.DetectionCoalition=DetectionCoalition
-self._SmokeDetectedUnits=false
-self._FlareDetectedUnits=false
-self._SmokeDetectedZones=false
-self._FlareDetectedZones=false
-self._BoundDetectedZones=false
-return self
-end
-function DETECTION_ZONES:CountAliveRecce()
-return self.DetectionSetZone:Count()
-end
-function DETECTION_ZONES:ForEachAliveRecce(IteratorFunction,...)
-self:F2(arg)
-self.DetectionSetZone:ForEachZone(IteratorFunction,arg)
-return self
-end
-function DETECTION_ZONES:DetectedItemReportSummary(DetectedItem,AttackGroup,Settings)
-self:F({DetectedItem=DetectedItem})
-local DetectedItemID=self:GetDetectedItemID(DetectedItem)
-if DetectedItem then
-local DetectedSet=self:GetDetectedItemSet(DetectedItem)
-local ReportSummaryItem
-local DetectedZone=self:GetDetectedItemZone(DetectedItem)
-local DetectedItemCoordinate=DetectedZone:GetCoordinate()
-local DetectedItemCoordText=DetectedItemCoordinate:ToString(AttackGroup,Settings)
-local ThreatLevelA2G=self:GetDetectedItemThreatLevel(DetectedItem)
-local DetectedItemsCount=DetectedSet:Count()
-local DetectedItemsTypes=DetectedSet:GetTypeNames()
-local Report=REPORT:New()
-Report:Add(DetectedItemID..", "..DetectedItemCoordText)
-Report:Add(string.format("Threat: [%s]",string.rep("■",ThreatLevelA2G),string.rep("□",10-ThreatLevelA2G)))
-Report:Add(string.format("Type: %2d of %s",DetectedItemsCount,DetectedItemsTypes))
-Report:Add(string.format("Detected: %s",DetectedItem.IsDetected and"yes"or"no"))
-return Report
-end
-return nil
-end
-function DETECTION_ZONES:DetectedReportDetailed(AttackGroup)
-self:F()
-local Report=REPORT:New()
-for DetectedItemIndex,DetectedItem in pairs(self.DetectedItems)do
-local DetectedItem=DetectedItem
-local ReportSummary=self:DetectedItemReportSummary(DetectedItem,AttackGroup)
-Report:SetTitle("Detected areas:")
-Report:Add(ReportSummary:Text())
-end
-local ReportText=Report:Text()
-return ReportText
-end
-function DETECTION_ZONES:CalculateIntercept(DetectedItem)
-local DetectedCoord=DetectedItem.Coordinate
-DetectedItem.InterceptCoord=DetectedCoord
-end
-function DETECTION_ZONES:SmokeDetectedUnits()
-self:F2()
-self._SmokeDetectedUnits=true
-return self
-end
-function DETECTION_ZONES:FlareDetectedUnits()
-self:F2()
-self._FlareDetectedUnits=true
-return self
-end
-function DETECTION_ZONES:SmokeDetectedZones()
-self:F2()
-self._SmokeDetectedZones=true
-return self
-end
-function DETECTION_ZONES:FlareDetectedZones()
-self:F2()
-self._FlareDetectedZones=true
-return self
-end
-function DETECTION_ZONES:BoundDetectedZones()
-self:F2()
-self._BoundDetectedZones=true
-return self
-end
-function DETECTION_ZONES:GetChangeText(DetectedItem)
-self:F(DetectedItem)
-local MT={}
-for ChangeCode,ChangeData in pairs(DetectedItem.Changes)do
-if ChangeCode=="AA"then
-MT[#MT+1]="Detected new area "..ChangeData.ID..". The center target is a "..ChangeData.ItemUnitType.."."
-end
-if ChangeCode=="RAU"then
-MT[#MT+1]="Changed area "..ChangeData.ID..". Removed the center target."
-end
-if ChangeCode=="AAU"then
-MT[#MT+1]="Changed area "..ChangeData.ID..". The new center target is a "..ChangeData.ItemUnitType.."."
-end
-if ChangeCode=="RA"then
-MT[#MT+1]="Removed old area "..ChangeData.ID..". No more targets in this area."
-end
-if ChangeCode=="AU"then
-local MTUT={}
-for ChangeUnitType,ChangeUnitCount in pairs(ChangeData)do
-if ChangeUnitType~="ID"then
-MTUT[#MTUT+1]=ChangeUnitCount.." of "..ChangeUnitType
-end
-end
-MT[#MT+1]="Detected for area "..ChangeData.ID.." new target(s) "..table.concat(MTUT,", ").."."
-end
-if ChangeCode=="RU"then
-local MTUT={}
-for ChangeUnitType,ChangeUnitCount in pairs(ChangeData)do
-if ChangeUnitType~="ID"then
-MTUT[#MTUT+1]=ChangeUnitCount.." of "..ChangeUnitType
-end
-end
-MT[#MT+1]="Removed for area "..ChangeData.ID.." invisible or destroyed target(s) "..table.concat(MTUT,", ").."."
-end
-end
-return table.concat(MT,"\n")
-end
-function DETECTION_ZONES:CreateDetectionItems()
-self:F("Checking Detected Items for new Detected Units ...")
-local DetectedUnits=SET_UNIT:New()
-for ZoneName,DetectionZone in pairs(self.DetectionSetZone:GetSet())do
-local DetectedItem=self:GetDetectedItemByKey(ZoneName)
-if DetectedItem==nil then
-DetectedItem=self:AddDetectedItemZone("ZONE",ZoneName,nil,DetectionZone)
-end
-local DetectedItemSetUnit=self:GetDetectedItemSet(DetectedItem)
-DetectionZone:Scan({Object.Category.UNIT},{Unit.Category.GROUND_UNIT})
-local ZoneUnits=DetectionZone:GetScannedUnits()
-for DCSUnitID,DCSUnit in pairs(ZoneUnits)do
-local UnitName=DCSUnit:getName()
-local ZoneUnit=UNIT:FindByName(UnitName)
-local ZoneUnitCoalition=ZoneUnit:GetCoalition()
-if ZoneUnitCoalition==self.DetectionCoalition then
-if DetectedItemSetUnit:FindUnit(UnitName)==nil and DetectedUnits:FindUnit(UnitName)==nil then
-self:F("Adding "..UnitName)
-DetectedItemSetUnit:AddUnit(ZoneUnit)
-DetectedUnits:AddUnit(ZoneUnit)
-end
-end
-end
-end
-for DetectedItemID,DetectedItemData in pairs(self.DetectedItems)do
-local DetectedItem=DetectedItemData
-local DetectedSet=self:GetDetectedItemSet(DetectedItem)
-local DetectedFirstUnit=DetectedSet:GetFirst()
-local DetectedZone=self:GetDetectedItemZone(DetectedItem)
-local DetectedZoneCoord=DetectedZone:GetCoordinate()
-self:SetDetectedItemCoordinate(DetectedItem,DetectedZoneCoord,DetectedFirstUnit)
-self:CalculateIntercept(DetectedItem)
-local OldFriendliesNearbyGround=self:IsFriendliesNearBy(DetectedItem,Unit.Category.GROUND_UNIT)
-self:ReportFriendliesNearBy({DetectedItem=DetectedItem,ReportSetGroup=self.DetectionSetGroup})
-local NewFriendliesNearbyGround=self:IsFriendliesNearBy(DetectedItem,Unit.Category.GROUND_UNIT)
-if OldFriendliesNearbyGround~=NewFriendliesNearbyGround then
-DetectedItem.Changed=true
-end
-self:SetDetectedItemThreatLevel(DetectedItem)
-if DETECTION_ZONES._SmokeDetectedUnits or self._SmokeDetectedUnits then
-DetectedZone:SmokeZone(SMOKECOLOR.Red,30)
-end
-DetectedSet:ForEachUnit(
-function(DetectedUnit)
-if DetectedUnit:IsAlive()then
-if DETECTION_ZONES._FlareDetectedUnits or self._FlareDetectedUnits then
-DetectedUnit:FlareGreen()
-end
-if DETECTION_ZONES._SmokeDetectedUnits or self._SmokeDetectedUnits then
-DetectedUnit:SmokeGreen()
-end
-end
-end
-)
-if DETECTION_ZONES._FlareDetectedZones or self._FlareDetectedZones then
-DetectedZone:FlareZone(SMOKECOLOR.White,30,math.random(0,90))
-end
-if DETECTION_ZONES._SmokeDetectedZones or self._SmokeDetectedZones then
-DetectedZone:SmokeZone(SMOKECOLOR.White,30)
-end
-if DETECTION_ZONES._BoundDetectedZones or self._BoundDetectedZones then
-self.CountryID=DetectedSet:GetFirst():GetCountry()
-DetectedZone:BoundZone(12,self.CountryID)
-end
-end
-end
-function DETECTION_ZONES:onafterDetection(From,Event,To,Detection,DetectionTimeStamp)
-self.DetectionRun=self.DetectionRun+1
-if self.DetectionCount>0 and self.DetectionRun==self.DetectionCount then
-self:CreateDetectionItems()
-for DetectedItemID,DetectedItem in pairs(self.DetectedItems)do
-self:UpdateDetectedItemDetection(DetectedItem)
-self:CleanDetectionItem(DetectedItem,DetectedItemID)
-if DetectedItem then
-self:__DetectedItem(0.1,DetectedItem)
-end
-end
-self:__Detect(-self.RefreshTimeInterval)
-end
-end
-function DETECTION_ZONES:UpdateDetectedItemDetection(DetectedItem)
-local IsDetected=true
-DetectedItem.IsDetected=true
-return IsDetected
-end
-end
-ESCORT={
-ClassName="ESCORT",
-EscortName=nil,
-EscortClient=nil,
-EscortGroup=nil,
-EscortMode=1,
-MODE={
-FOLLOW=1,
-MISSION=2,
-},
-Targets={},
-FollowScheduler=nil,
-ReportTargets=true,
-OptionROE=AI.Option.Air.val.ROE.OPEN_FIRE,
-OptionReactionOnThreat=AI.Option.Air.val.REACTION_ON_THREAT.ALLOW_ABORT_MISSION,
-SmokeDirectionVector=false,
-TaskPoints={}
-}
-function ESCORT:New(EscortClient,EscortGroup,EscortName,EscortBriefing)
-local self=BASE:Inherit(self,BASE:New())
-self:F({EscortClient,EscortGroup,EscortName})
-self.EscortClient=EscortClient
-self.EscortGroup=EscortGroup
-self.EscortName=EscortName
-self.EscortBriefing=EscortBriefing
-self.EscortSetGroup=SET_GROUP:New()
-self.EscortSetGroup:AddObject(self.EscortGroup)
-self.EscortSetGroup:Flush()
-self.Detection=DETECTION_UNITS:New(self.EscortSetGroup,15000)
-self.EscortGroup.Detection=self.Detection
-if not self.EscortClient._EscortGroups then
-self.EscortClient._EscortGroups={}
-end
-if not self.EscortClient._EscortGroups[EscortGroup:GetName()]then
-self.EscortClient._EscortGroups[EscortGroup:GetName()]={}
-self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortGroup=self.EscortGroup
-self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortName=self.EscortName
-self.EscortClient._EscortGroups[EscortGroup:GetName()].Detection=self.EscortGroup.Detection
-end
-self.EscortMenu=MENU_GROUP:New(self.EscortClient:GetGroup(),self.EscortName)
-self.EscortGroup:WayPointInitialize(1)
-self.EscortGroup:OptionROTVertical()
-self.EscortGroup:OptionROEOpenFire()
-if not EscortBriefing then
-EscortGroup:MessageToClient(EscortGroup:GetCategoryName().." '"..EscortName.."' ("..EscortGroup:GetCallsign()..") reporting! "..
-"We're escorting your flight. "..
-"Use the Radio Menu and F10 and use the options under + "..EscortName.."\n",
-60,EscortClient
-)
-else
-EscortGroup:MessageToClient(EscortGroup:GetCategoryName().." '"..EscortName.."' ("..EscortGroup:GetCallsign()..") "..EscortBriefing,
-60,EscortClient
-)
-end
-self.FollowDistance=100
-self.CT1=0
-self.GT1=0
-self.FollowScheduler,self.FollowSchedule=SCHEDULER:New(self,self._FollowScheduler,{},1,.5,.01)
-self.FollowScheduler:Stop(self.FollowSchedule)
-self.EscortMode=ESCORT.MODE.MISSION
-return self
-end
-function ESCORT:SetDetection(Detection)
-self.Detection=Detection
-self.EscortGroup.Detection=self.Detection
-self.EscortClient._EscortGroups[self.EscortGroup:GetName()].Detection=self.EscortGroup.Detection
-Detection:__Start(1)
-end
-function ESCORT:TestSmokeDirectionVector(SmokeDirection)
-self.SmokeDirectionVector=(SmokeDirection==true)and true or false
-end
-function ESCORT:Menus()
-self:F()
-self:MenuFollowAt(100)
-self:MenuFollowAt(200)
-self:MenuFollowAt(300)
-self:MenuFollowAt(400)
-self:MenuScanForTargets(100,60)
-self:MenuHoldAtEscortPosition(30)
-self:MenuHoldAtLeaderPosition(30)
-self:MenuFlare()
-self:MenuSmoke()
-self:MenuReportTargets(60)
-self:MenuAssistedAttack()
-self:MenuROE()
-self:MenuEvasion()
-self:MenuResumeMission()
-return self
-end
-function ESCORT:MenuFollowAt(Distance)
-self:F(Distance)
-if self.EscortGroup:IsAir()then
-if not self.EscortMenuReportNavigation then
-self.EscortMenuReportNavigation=MENU_GROUP:New(self.EscortClient:GetGroup(),"Navigation",self.EscortMenu)
-end
-if not self.EscortMenuJoinUpAndFollow then
-self.EscortMenuJoinUpAndFollow={}
-end
-self.EscortMenuJoinUpAndFollow[#self.EscortMenuJoinUpAndFollow+1]=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Join-Up and Follow at "..Distance,self.EscortMenuReportNavigation,ESCORT._JoinUpAndFollow,self,Distance)
-self.EscortMode=ESCORT.MODE.FOLLOW
-end
-return self
-end
-function ESCORT:MenuHoldAtEscortPosition(Height,Seconds,MenuTextFormat)
-self:F({Height,Seconds,MenuTextFormat})
-if self.EscortGroup:IsAir()then
-if not self.EscortMenuHold then
-self.EscortMenuHold=MENU_GROUP:New(self.EscortClient:GetGroup(),"Hold position",self.EscortMenu)
-end
-if not Height then
-Height=30
-end
-if not Seconds then
-Seconds=0
-end
-local MenuText=""
-if not MenuTextFormat then
-if Seconds==0 then
-MenuText=string.format("Hold at %d meter",Height)
-else
-MenuText=string.format("Hold at %d meter for %d seconds",Height,Seconds)
-end
-else
-if Seconds==0 then
-MenuText=string.format(MenuTextFormat,Height)
-else
-MenuText=string.format(MenuTextFormat,Height,Seconds)
-end
-end
-if not self.EscortMenuHoldPosition then
-self.EscortMenuHoldPosition={}
-end
-self.EscortMenuHoldPosition[#self.EscortMenuHoldPosition+1]=MENU_GROUP_COMMAND
-:New(
-self.EscortClient:GetGroup(),
-MenuText,
-self.EscortMenuHold,
-ESCORT._HoldPosition,
-self,
-self.EscortGroup,
-Height,
-Seconds
-)
-end
-return self
-end
-function ESCORT:MenuHoldAtLeaderPosition(Height,Seconds,MenuTextFormat)
-self:F({Height,Seconds,MenuTextFormat})
-if self.EscortGroup:IsAir()then
-if not self.EscortMenuHold then
-self.EscortMenuHold=MENU_GROUP:New(self.EscortClient:GetGroup(),"Hold position",self.EscortMenu)
-end
-if not Height then
-Height=30
-end
-if not Seconds then
-Seconds=0
-end
-local MenuText=""
-if not MenuTextFormat then
-if Seconds==0 then
-MenuText=string.format("Rejoin and hold at %d meter",Height)
-else
-MenuText=string.format("Rejoin and hold at %d meter for %d seconds",Height,Seconds)
-end
-else
-if Seconds==0 then
-MenuText=string.format(MenuTextFormat,Height)
-else
-MenuText=string.format(MenuTextFormat,Height,Seconds)
-end
-end
-if not self.EscortMenuHoldAtLeaderPosition then
-self.EscortMenuHoldAtLeaderPosition={}
-end
-self.EscortMenuHoldAtLeaderPosition[#self.EscortMenuHoldAtLeaderPosition+1]=MENU_GROUP_COMMAND
-:New(
-self.EscortClient:GetGroup(),
-MenuText,
-self.EscortMenuHold,
-ESCORT._HoldPosition,
-{ParamSelf=self,
-ParamOrbitGroup=self.EscortClient,
-ParamHeight=Height,
-ParamSeconds=Seconds
-}
-)
-end
-return self
-end
-function ESCORT:MenuScanForTargets(Height,Seconds,MenuTextFormat)
-self:F({Height,Seconds,MenuTextFormat})
-if self.EscortGroup:IsAir()then
-if not self.EscortMenuScan then
-self.EscortMenuScan=MENU_GROUP:New(self.EscortClient:GetGroup(),"Scan for targets",self.EscortMenu)
-end
-if not Height then
-Height=100
-end
-if not Seconds then
-Seconds=30
-end
-local MenuText=""
-if not MenuTextFormat then
-if Seconds==0 then
-MenuText=string.format("At %d meter",Height)
-else
-MenuText=string.format("At %d meter for %d seconds",Height,Seconds)
-end
-else
-if Seconds==0 then
-MenuText=string.format(MenuTextFormat,Height)
-else
-MenuText=string.format(MenuTextFormat,Height,Seconds)
-end
-end
-if not self.EscortMenuScanForTargets then
-self.EscortMenuScanForTargets={}
-end
-self.EscortMenuScanForTargets[#self.EscortMenuScanForTargets+1]=MENU_GROUP_COMMAND
-:New(
-self.EscortClient:GetGroup(),
-MenuText,
-self.EscortMenuScan,
-ESCORT._ScanTargets,
-self,
-30
-)
-end
-return self
-end
-function ESCORT:MenuFlare(MenuTextFormat)
-self:F()
-if not self.EscortMenuReportNavigation then
-self.EscortMenuReportNavigation=MENU_GROUP:New(self.EscortClient:GetGroup(),"Navigation",self.EscortMenu)
-end
-local MenuText=""
-if not MenuTextFormat then
-MenuText="Flare"
-else
-MenuText=MenuTextFormat
-end
-if not self.EscortMenuFlare then
-self.EscortMenuFlare=MENU_GROUP:New(self.EscortClient:GetGroup(),MenuText,self.EscortMenuReportNavigation,ESCORT._Flare,self)
-self.EscortMenuFlareGreen=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Release green flare",self.EscortMenuFlare,ESCORT._Flare,self,FLARECOLOR.Green,"Released a green flare!")
-self.EscortMenuFlareRed=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Release red flare",self.EscortMenuFlare,ESCORT._Flare,self,FLARECOLOR.Red,"Released a red flare!")
-self.EscortMenuFlareWhite=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Release white flare",self.EscortMenuFlare,ESCORT._Flare,self,FLARECOLOR.White,"Released a white flare!")
-self.EscortMenuFlareYellow=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Release yellow flare",self.EscortMenuFlare,ESCORT._Flare,self,FLARECOLOR.Yellow,"Released a yellow flare!")
-end
-return self
-end
-function ESCORT:MenuSmoke(MenuTextFormat)
-self:F()
-if not self.EscortGroup:IsAir()then
-if not self.EscortMenuReportNavigation then
-self.EscortMenuReportNavigation=MENU_GROUP:New(self.EscortClient:GetGroup(),"Navigation",self.EscortMenu)
-end
-local MenuText=""
-if not MenuTextFormat then
-MenuText="Smoke"
-else
-MenuText=MenuTextFormat
-end
-if not self.EscortMenuSmoke then
-self.EscortMenuSmoke=MENU_GROUP:New(self.EscortClient:GetGroup(),"Smoke",self.EscortMenuReportNavigation,ESCORT._Smoke,self)
-self.EscortMenuSmokeGreen=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Release green smoke",self.EscortMenuSmoke,ESCORT._Smoke,self,SMOKECOLOR.Green,"Releasing green smoke!")
-self.EscortMenuSmokeRed=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Release red smoke",self.EscortMenuSmoke,ESCORT._Smoke,self,SMOKECOLOR.Red,"Releasing red smoke!")
-self.EscortMenuSmokeWhite=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Release white smoke",self.EscortMenuSmoke,ESCORT._Smoke,self,SMOKECOLOR.White,"Releasing white smoke!")
-self.EscortMenuSmokeOrange=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Release orange smoke",self.EscortMenuSmoke,ESCORT._Smoke,self,SMOKECOLOR.Orange,"Releasing orange smoke!")
-self.EscortMenuSmokeBlue=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Release blue smoke",self.EscortMenuSmoke,ESCORT._Smoke,self,SMOKECOLOR.Blue,"Releasing blue smoke!")
-end
-end
-return self
-end
-function ESCORT:MenuReportTargets(Seconds)
-self:F({Seconds})
-if not self.EscortMenuReportNearbyTargets then
-self.EscortMenuReportNearbyTargets=MENU_GROUP:New(self.EscortClient:GetGroup(),"Report targets",self.EscortMenu)
-end
-if not Seconds then
-Seconds=30
-end
-self.EscortMenuReportNearbyTargetsNow=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Report targets now!",self.EscortMenuReportNearbyTargets,ESCORT._ReportNearbyTargetsNow,self)
-self.EscortMenuReportNearbyTargetsOn=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Report targets on",self.EscortMenuReportNearbyTargets,ESCORT._SwitchReportNearbyTargets,self,true)
-self.EscortMenuReportNearbyTargetsOff=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Report targets off",self.EscortMenuReportNearbyTargets,ESCORT._SwitchReportNearbyTargets,self,false)
-self.EscortMenuAttackNearbyTargets=MENU_GROUP:New(self.EscortClient:GetGroup(),"Attack targets",self.EscortMenu)
-self.ReportTargetsScheduler,self.ReportTargetsSchedulerID=SCHEDULER:New(self,self._ReportTargetsScheduler,{},1,Seconds)
-return self
-end
-function ESCORT:MenuAssistedAttack()
-self:F()
-self.EscortMenuTargetAssistance=MENU_GROUP:New(self.EscortClient:GetGroup(),"Request assistance from",self.EscortMenu)
-return self
-end
-function ESCORT:MenuROE(MenuTextFormat)
-self:F(MenuTextFormat)
-if not self.EscortMenuROE then
-self.EscortMenuROE=MENU_GROUP:New(self.EscortClient:GetGroup(),"ROE",self.EscortMenu)
-if self.EscortGroup:OptionROEHoldFirePossible()then
-self.EscortMenuROEHoldFire=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Hold Fire",self.EscortMenuROE,ESCORT._ROE,self,self.EscortGroup:OptionROEHoldFire(),"Holding weapons!")
-end
-if self.EscortGroup:OptionROEReturnFirePossible()then
-self.EscortMenuROEReturnFire=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Return Fire",self.EscortMenuROE,ESCORT._ROE,self,self.EscortGroup:OptionROEReturnFire(),"Returning fire!")
-end
-if self.EscortGroup:OptionROEOpenFirePossible()then
-self.EscortMenuROEOpenFire=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Open Fire",self.EscortMenuROE,ESCORT._ROE,self,self.EscortGroup:OptionROEOpenFire(),"Opening fire on designated targets!!")
-end
-if self.EscortGroup:OptionROEWeaponFreePossible()then
-self.EscortMenuROEWeaponFree=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Weapon Free",self.EscortMenuROE,ESCORT._ROE,self,self.EscortGroup:OptionROEWeaponFree(),"Opening fire on targets of opportunity!")
-end
-end
-return self
-end
-function ESCORT:MenuEvasion(MenuTextFormat)
-self:F(MenuTextFormat)
-if self.EscortGroup:IsAir()then
-if not self.EscortMenuEvasion then
-self.EscortMenuEvasion=MENU_GROUP:New(self.EscortClient:GetGroup(),"Evasion",self.EscortMenu)
-if self.EscortGroup:OptionROTNoReactionPossible()then
-self.EscortMenuEvasionNoReaction=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Fight until death",self.EscortMenuEvasion,ESCORT._ROT,self,self.EscortGroup:OptionROTNoReaction(),"Fighting until death!")
-end
-if self.EscortGroup:OptionROTPassiveDefensePossible()then
-self.EscortMenuEvasionPassiveDefense=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Use flares, chaff and jammers",self.EscortMenuEvasion,ESCORT._ROT,self,self.EscortGroup:OptionROTPassiveDefense(),"Defending using jammers, chaff and flares!")
-end
-if self.EscortGroup:OptionROTEvadeFirePossible()then
-self.EscortMenuEvasionEvadeFire=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Evade enemy fire",self.EscortMenuEvasion,ESCORT._ROT,self,self.EscortGroup:OptionROTEvadeFire(),"Evading on enemy fire!")
-end
-if self.EscortGroup:OptionROTVerticalPossible()then
-self.EscortMenuOptionEvasionVertical=MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),"Go below radar and evade fire",self.EscortMenuEvasion,ESCORT._ROT,self,self.EscortGroup:OptionROTVertical(),"Evading on enemy fire with vertical manoeuvres!")
-end
-end
-end
-return self
-end
-function ESCORT:MenuResumeMission()
-self:F()
-if not self.EscortMenuResumeMission then
-self.EscortMenuResumeMission=MENU_GROUP:New(self.EscortClient:GetGroup(),"Resume mission from",self.EscortMenu)
-end
-return self
-end
-function ESCORT:_HoldPosition(OrbitGroup,OrbitHeight,OrbitSeconds)
-local EscortGroup=self.EscortGroup
-local EscortClient=self.EscortClient
-local OrbitUnit=OrbitGroup:GetUnit(1)
-self.FollowScheduler:Stop(self.FollowSchedule)
-local PointFrom={}
-local GroupVec3=EscortGroup:GetUnit(1):GetVec3()
-PointFrom={}
-PointFrom.x=GroupVec3.x
-PointFrom.y=GroupVec3.z
-PointFrom.speed=250
-PointFrom.type=AI.Task.WaypointType.TURNING_POINT
-PointFrom.alt=GroupVec3.y
-PointFrom.alt_type=AI.Task.AltitudeType.BARO
-local OrbitPoint=OrbitUnit:GetVec2()
-local PointTo={}
-PointTo.x=OrbitPoint.x
-PointTo.y=OrbitPoint.y
-PointTo.speed=250
-PointTo.type=AI.Task.WaypointType.TURNING_POINT
-PointTo.alt=OrbitHeight
-PointTo.alt_type=AI.Task.AltitudeType.BARO
-PointTo.task=EscortGroup:TaskOrbitCircleAtVec2(OrbitPoint,OrbitHeight,0)
-local Points={PointFrom,PointTo}
-EscortGroup:OptionROEHoldFire()
-EscortGroup:OptionROTPassiveDefense()
-EscortGroup:SetTask(EscortGroup:TaskRoute(Points))
-EscortGroup:MessageToClient("Orbiting at location.",10,EscortClient)
-end
-function ESCORT:_JoinUpAndFollow(Distance)
-local EscortGroup=self.EscortGroup
-local EscortClient=self.EscortClient
-self.Distance=Distance
-self:JoinUpAndFollow(EscortGroup,EscortClient,self.Distance)
-end
-function ESCORT:JoinUpAndFollow(EscortGroup,EscortClient,Distance)
-self:F({EscortGroup,EscortClient,Distance})
-self.FollowScheduler:Stop(self.FollowSchedule)
-EscortGroup:OptionROEHoldFire()
-EscortGroup:OptionROTPassiveDefense()
-self.EscortMode=ESCORT.MODE.FOLLOW
-self.CT1=0
-self.GT1=0
-self.FollowScheduler:Start(self.FollowSchedule)
-EscortGroup:MessageToClient("Rejoining and Following at "..Distance.."!",30,EscortClient)
-end
-function ESCORT:_Flare(Color,Message)
-local EscortGroup=self.EscortGroup
-local EscortClient=self.EscortClient
-EscortGroup:GetUnit(1):Flare(Color)
-EscortGroup:MessageToClient(Message,10,EscortClient)
-end
-function ESCORT:_Smoke(Color,Message)
-local EscortGroup=self.EscortGroup
-local EscortClient=self.EscortClient
-EscortGroup:GetUnit(1):Smoke(Color)
-EscortGroup:MessageToClient(Message,10,EscortClient)
-end
-function ESCORT:_ReportNearbyTargetsNow()
-local EscortGroup=self.EscortGroup
-local EscortClient=self.EscortClient
-self:_ReportTargetsScheduler()
-end
-function ESCORT:_SwitchReportNearbyTargets(ReportTargets)
-local EscortGroup=self.EscortGroup
-local EscortClient=self.EscortClient
-self.ReportTargets=ReportTargets
-if self.ReportTargets then
-if not self.ReportTargetsScheduler then
-self.ReportTargetsScheduler:Schedule(self,self._ReportTargetsScheduler,{},1,30)
-end
-else
-self.ReportTargetsScheduler:Remove(self.ReportTargetsSchedulerID)
-self.ReportTargetsScheduler=nil
-end
-end
-function ESCORT:_ScanTargets(ScanDuration)
-local EscortGroup=self.EscortGroup
-local EscortClient=self.EscortClient
-self.FollowScheduler:Stop(self.FollowSchedule)
-if EscortGroup:IsHelicopter()then
-EscortGroup:PushTask(
-EscortGroup:TaskControlled(
-EscortGroup:TaskOrbitCircle(200,20),
-EscortGroup:TaskCondition(nil,nil,nil,nil,ScanDuration,nil)
-),1)
-elseif EscortGroup:IsAirPlane()then
-EscortGroup:PushTask(
-EscortGroup:TaskControlled(
-EscortGroup:TaskOrbitCircle(1000,500),
-EscortGroup:TaskCondition(nil,nil,nil,nil,ScanDuration,nil)
-),1)
-end
-EscortGroup:MessageToClient("Scanning targets for "..ScanDuration.." seconds.",ScanDuration,EscortClient)
-if self.EscortMode==ESCORT.MODE.FOLLOW then
-self.FollowScheduler:Start(self.FollowSchedule)
-end
-end
-function _Resume(EscortGroup)
-env.info('_Resume')
-local Escort=EscortGroup:GetState(EscortGroup,"Escort")
-env.info("EscortMode = "..Escort.EscortMode)
-if Escort.EscortMode==ESCORT.MODE.FOLLOW then
-Escort:JoinUpAndFollow(EscortGroup,Escort.EscortClient,Escort.Distance)
-end
-end
-function ESCORT:_AttackTarget(DetectedItem)
-local EscortGroup=self.EscortGroup
-self:F(EscortGroup)
-local EscortClient=self.EscortClient
-self.FollowScheduler:Stop(self.FollowSchedule)
-if EscortGroup:IsAir()then
-EscortGroup:OptionROEOpenFire()
-EscortGroup:OptionROTPassiveDefense()
-EscortGroup:SetState(EscortGroup,"Escort",self)
-local DetectedSet=self.Detection:GetDetectedItemSet(DetectedItem)
-local Tasks={}
-DetectedSet:ForEachUnit(
-function(DetectedUnit,Tasks)
-if DetectedUnit:IsAlive()then
-Tasks[#Tasks+1]=EscortGroup:TaskAttackUnit(DetectedUnit)
-end
-end,Tasks
-)
-Tasks[#Tasks+1]=EscortGroup:TaskFunction("_Resume",{"''"})
-EscortGroup:SetTask(
-EscortGroup:TaskCombo(
-Tasks
-),1
-)
-else
-local DetectedSet=self.Detection:GetDetectedItemSet(DetectedItem)
-local Tasks={}
-DetectedSet:ForEachUnit(
-function(DetectedUnit,Tasks)
-if DetectedUnit:IsAlive()then
-Tasks[#Tasks+1]=EscortGroup:TaskFireAtPoint(DetectedUnit:GetVec2(),50)
-end
-end,Tasks
-)
-EscortGroup:SetTask(
-EscortGroup:TaskCombo(
-Tasks
-),1
-)
-end
-EscortGroup:MessageToClient("Engaging Designated Unit!",10,EscortClient)
-end
-function ESCORT:_AssistTarget(EscortGroupAttack,DetectedItem)
-local EscortGroup=self.EscortGroup
-local EscortClient=self.EscortClient
-self.FollowScheduler:Stop(self.FollowSchedule)
-if EscortGroupAttack:IsAir()then
-EscortGroupAttack:OptionROEOpenFire()
-EscortGroupAttack:OptionROTVertical()
-local DetectedSet=self.Detection:GetDetectedItemSet(DetectedItem)
-local Tasks={}
-DetectedSet:ForEachUnit(
-function(DetectedUnit,Tasks)
-if DetectedUnit:IsAlive()then
-Tasks[#Tasks+1]=EscortGroupAttack:TaskAttackUnit(DetectedUnit)
-end
-end,Tasks
-)
-Tasks[#Tasks+1]=EscortGroupAttack:TaskOrbitCircle(500,350)
-EscortGroupAttack:SetTask(
-EscortGroupAttack:TaskCombo(
-Tasks
-),1
-)
-else
-local DetectedSet=self.Detection:GetDetectedItemSet(DetectedItem)
-local Tasks={}
-DetectedSet:ForEachUnit(
-function(DetectedUnit,Tasks)
-if DetectedUnit:IsAlive()then
-Tasks[#Tasks+1]=EscortGroupAttack:TaskFireAtPoint(DetectedUnit:GetVec2(),50)
-end
-end,Tasks
-)
-EscortGroupAttack:SetTask(
-EscortGroupAttack:TaskCombo(
-Tasks
-),1
-)
-end
-EscortGroupAttack:MessageToClient("Assisting with the destroying the enemy unit!",10,EscortClient)
-end
-function ESCORT:_ROE(EscortROEFunction,EscortROEMessage)
-local EscortGroup=self.EscortGroup
-local EscortClient=self.EscortClient
-pcall(function()EscortROEFunction()end)
-EscortGroup:MessageToClient(EscortROEMessage,10,EscortClient)
-end
-function ESCORT:_ROT(EscortROTFunction,EscortROTMessage)
-local EscortGroup=self.EscortGroup
-local EscortClient=self.EscortClient
-pcall(function()EscortROTFunction()end)
-EscortGroup:MessageToClient(EscortROTMessage,10,EscortClient)
-end
-function ESCORT:_ResumeMission(WayPoint)
-local EscortGroup=self.EscortGroup
-local EscortClient=self.EscortClient
-self.FollowScheduler:Stop(self.FollowSchedule)
-local WayPoints=EscortGroup:GetTaskRoute()
-self:T(WayPoint,WayPoints)
-for WayPointIgnore=1,WayPoint do
-table.remove(WayPoints,1)
-end
-SCHEDULER:New(EscortGroup,EscortGroup.SetTask,{EscortGroup:TaskRoute(WayPoints)},1)
-EscortGroup:MessageToClient("Resuming mission from waypoint "..WayPoint..".",10,EscortClient)
-end
-function ESCORT:RegisterRoute()
-self:F()
-local EscortGroup=self.EscortGroup
-local TaskPoints=EscortGroup:GetTaskRoute()
-self:T(TaskPoints)
-return TaskPoints
-end
-function ESCORT:_FollowScheduler()
-self:F({self.FollowDistance})
-self:T({self.EscortClient.UnitName,self.EscortGroup.GroupName})
-if self.EscortGroup:IsAlive()and self.EscortClient:IsAlive()then
-local ClientUnit=self.EscortClient:GetClientGroupUnit()
-local GroupUnit=self.EscortGroup:GetUnit(1)
-local FollowDistance=self.FollowDistance
-self:T({ClientUnit.UnitName,GroupUnit.UnitName})
-if self.CT1==0 and self.GT1==0 then
-self.CV1=ClientUnit:GetVec3()
-self:T({"self.CV1",self.CV1})
-self.CT1=timer.getTime()
-self.GV1=GroupUnit:GetVec3()
-self.GT1=timer.getTime()
-else
-local CT1=self.CT1
-local CT2=timer.getTime()
-local CV1=self.CV1
-local CV2=ClientUnit:GetVec3()
-self.CT1=CT2
-self.CV1=CV2
-local CD=((CV2.x-CV1.x)^2+(CV2.y-CV1.y)^2+(CV2.z-CV1.z)^2)^0.5
-local CT=CT2-CT1
-local CS=(3600/CT)*(CD/1000)
-self:T2({"Client:",CS,CD,CT,CV2,CV1,CT2,CT1})
-local GT1=self.GT1
-local GT2=timer.getTime()
-local GV1=self.GV1
-local GV2=GroupUnit:GetVec3()
-self.GT1=GT2
-self.GV1=GV2
-local GD=((GV2.x-GV1.x)^2+(GV2.y-GV1.y)^2+(GV2.z-GV1.z)^2)^0.5
-local GT=GT2-GT1
-local GS=(3600/GT)*(GD/1000)
-self:T2({"Group:",GS,GD,GT,GV2,GV1,GT2,GT1})
-local GV={x=GV2.x-CV2.x,y=GV2.y-CV2.y,z=GV2.z-CV2.z}
-local GH2={x=GV2.x,y=CV2.y,z=GV2.z}
-local alpha=math.atan2(GV.z,GV.x)
-local CVI={x=CV2.x+FollowDistance*math.cos(alpha),
-y=GH2.y,
-z=CV2.z+FollowDistance*math.sin(alpha),
-}
-local DV={x=CV2.x-CVI.x,y=CV2.y-CVI.y,z=CV2.z-CVI.z}
-local DVu={x=DV.x/FollowDistance,y=DV.y/FollowDistance,z=DV.z/FollowDistance}
-local GDV={x=DVu.x*CS*8+CVI.x,y=CVI.y,z=DVu.z*CS*8+CVI.z}
-if self.SmokeDirectionVector==true then
-trigger.action.smoke(GDV,trigger.smokeColor.Red)
-end
-self:T2({"CV2:",CV2})
-self:T2({"CVI:",CVI})
-self:T2({"GDV:",GDV})
-local CatchUpDistance=((GDV.x-GV2.x)^2+(GDV.y-GV2.y)^2+(GDV.z-GV2.z)^2)^0.5
-local Time=10
-local CatchUpSpeed=(CatchUpDistance-(CS*8.4))/Time
-local Speed=CS+CatchUpSpeed
-if Speed<0 then
-Speed=0
-end
-self:T({"Client Speed, Escort Speed, Speed, FollowDistance, Time:",CS,GS,Speed,FollowDistance,Time})
-self.EscortGroup:RouteToVec3(GDV,Speed/3.6)
-end
-return true
-end
-return false
-end
-function ESCORT:_ReportTargetsScheduler()
-self:F(self.EscortGroup:GetName())
-if self.EscortGroup:IsAlive()and self.EscortClient:IsAlive()then
-if true then
-local EscortGroupName=self.EscortGroup:GetName()
-self.EscortMenuAttackNearbyTargets:RemoveSubMenus()
-if self.EscortMenuTargetAssistance then
-self.EscortMenuTargetAssistance:RemoveSubMenus()
-end
-local DetectedItems=self.Detection:GetDetectedItems()
-self:F(DetectedItems)
-local DetectedTargets=false
-local DetectedMsgs={}
-for ClientEscortGroupName,EscortGroupData in pairs(self.EscortClient._EscortGroups)do
-local ClientEscortTargets=EscortGroupData.Detection
-for DetectedItemIndex,DetectedItem in pairs(DetectedItems)do
-self:F({DetectedItemIndex,DetectedItem})
-local DetectedItemReportSummary=self.Detection:DetectedItemReportSummary(DetectedItem,EscortGroupData.EscortGroup,_DATABASE:GetPlayerSettings(self.EscortClient:GetPlayerName()))
-if ClientEscortGroupName==EscortGroupName then
-local DetectedMsg=DetectedItemReportSummary:Text("\n")
-DetectedMsgs[#DetectedMsgs+1]=DetectedMsg
-self:T(DetectedMsg)
-MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),
-DetectedMsg,
-self.EscortMenuAttackNearbyTargets,
-ESCORT._AttackTarget,
-self,
-DetectedItem
-)
-else
-if self.EscortMenuTargetAssistance then
-local DetectedMsg=DetectedItemReportSummary:Text("\n")
-self:T(DetectedMsg)
-local MenuTargetAssistance=MENU_GROUP:New(self.EscortClient:GetGroup(),EscortGroupData.EscortName,self.EscortMenuTargetAssistance)
-MENU_GROUP_COMMAND:New(self.EscortClient:GetGroup(),
-DetectedMsg,
-MenuTargetAssistance,
-ESCORT._AssistTarget,
-self,
-EscortGroupData.EscortGroup,
-DetectedItem
-)
-end
-end
-DetectedTargets=true
-end
-end
-self:F(DetectedMsgs)
-if DetectedTargets then
-self.EscortGroup:MessageToClient("Reporting detected targets:\n"..table.concat(DetectedMsgs,"\n"),20,self.EscortClient)
-else
-self.EscortGroup:MessageToClient("No targets detected.",10,self.EscortClient)
-end
-return true
-else
-end
-end
-return false
-end
-FOX={
-ClassName="FOX",
-verbose=0,
-Debug=false,
-lid=nil,
-menuadded={},
-menudisabled=nil,
-destroy=nil,
-launchalert=nil,
-marklaunch=nil,
-missiles={},
-players={},
-safezones={},
-launchzones={},
-protectedset=nil,
-explosionpower=0.1,
-explosiondist=200,
-explosiondist2=500,
-bigmissilemass=50,
-destroy=nil,
-dt50=5,
-dt10=1,
-dt05=0.5,
-dt01=0.1,
-dt00=0.01,
-}
-FOX.MenuF10={}
-FOX.MenuF10Root=nil
-FOX.version="0.8.0"
-function FOX:New()
-self.lid="FOX | "
-local self=BASE:Inherit(self,FSM:New())
-self:SetDefaultMissileDestruction(true)
-self:SetDefaultLaunchAlerts(true)
-self:SetDefaultLaunchMarks(true)
-self:SetExplosionDistance()
-self:SetExplosionDistanceBigMissiles()
-self:SetExplosionPower()
-self:SetStartState("Stopped")
-self:AddTransition("Stopped","Start","Running")
-self:AddTransition("*","Status","*")
-self:AddTransition("*","MissileLaunch","*")
-self:AddTransition("*","MissileDestroyed","*")
-self:AddTransition("*","EnterSafeZone","*")
-self:AddTransition("*","ExitSafeZone","*")
-self:AddTransition("Running","Stop","Stopped")
-return self
-end
-function FOX:onafterStart(From,Event,To)
-local text=string.format("Starting FOX Missile Trainer %s",FOX.version)
-env.info(text)
-self:HandleEvent(EVENTS.Birth)
-self:HandleEvent(EVENTS.Shot)
-if self.Debug then
-self:HandleEvent(EVENTS.Hit)
-end
-if self.Debug then
-self:TraceClass(self.ClassName)
-self:TraceLevel(2)
-end
-self:__Status(-20)
-end
-function FOX:onafterStop(From,Event,To)
-local text=string.format("Stopping FOX Missile Trainer %s",FOX.version)
-env.info(text)
-self:UnHandleEvent(EVENTS.Birth)
-self:UnHandleEvent(EVENTS.Shot)
-if self.Debug then
-self:UnhandleEvent(EVENTS.Hit)
-end
-end
-function FOX:AddSafeZone(zone)
-table.insert(self.safezones,zone)
-return self
-end
-function FOX:AddLaunchZone(zone)
-table.insert(self.launchzones,zone)
-return self
-end
-function FOX:SetProtectedGroupSet(groupset)
-self.protectedset=groupset
-return self
-end
-function FOX:AddProtectedGroup(group)
-if not self.protectedset then
-self.protectedset=SET_GROUP:New()
-end
-self.protectedset:AddGroup(group)
-return self
-end
-function FOX:SetExplosionPower(power)
-self.explosionpower=power or 0.1
-return self
-end
-function FOX:SetExplosionDistance(distance)
-self.explosiondist=distance or 200
-return self
-end
-function FOX:SetExplosionDistanceBigMissiles(distance,explosivemass)
-self.explosiondist2=distance or 500
-self.bigmissilemass=explosivemass or 50
-return self
-end
-function FOX:SetDisableF10Menu()
-self.menudisabled=true
-return self
-end
-function FOX:SetEnableF10Menu()
-self.menudisabled=false
-return self
-end
-function FOX:SetVerbosity(VerbosityLevel)
-self.verbose=VerbosityLevel or 0
-return self
-end
-function FOX:SetDefaultMissileDestruction(switch)
-if switch==nil then
-self.destroy=false
-else
-self.destroy=switch
-end
-return self
-end
-function FOX:SetDefaultLaunchAlerts(switch)
-if switch==nil then
-self.launchalert=false
-else
-self.launchalert=switch
-end
-return self
-end
-function FOX:SetDefaultLaunchMarks(switch)
-if switch==nil then
-self.marklaunch=false
-else
-self.marklaunch=switch
-end
-return self
-end
-function FOX:SetDebugOnOff(switch)
-if switch==nil then
-self.Debug=false
-else
-self.Debug=switch
-end
-return self
-end
-function FOX:SetDebugOn()
-self:SetDebugOnOff(true)
-return self
-end
-function FOX:SetDebugOff()
-self:SetDebugOff(false)
-return self
-end
-function FOX:onafterStatus(From,Event,To)
-local fsmstate=self:GetState()
-local time=timer.getAbsTime()
-local clock=UTILS.SecondsToClock(time)
-if self.verbose>=1 then
-self:I(self.lid..string.format("Missile trainer status %s: %s",clock,fsmstate))
-end
-self:_CheckMissileStatus()
-self:_CheckPlayers()
-if fsmstate=="Running"then
-self:__Status(-10)
-end
-end
-function FOX:_CheckPlayers()
-for playername,_playersettings in pairs(self.players)do
-local playersettings=_playersettings
-local unitname=playersettings.unitname
-local unit=UNIT:FindByName(unitname)
-if unit and unit:IsAlive()then
-local coord=unit:GetCoordinate()
-local issafe=self:_CheckCoordSafe(coord)
-if issafe then
-if not playersettings.inzone then
-self:EnterSafeZone(playersettings)
-playersettings.inzone=true
-end
-else
-if playersettings.inzone==true then
-self:ExitSafeZone(playersettings)
-playersettings.inzone=false
-end
-end
-end
-end
-end
-function FOX:_RemoveMissile(missile)
-if missile then
-for i,_missile in pairs(self.missiles)do
-local m=_missile
-if missile.missileName==m.missileName then
-table.remove(self.missiles,i)
-return
-end
-end
-end
-end
-function FOX:_CheckMissileStatus()
-local text="Missiles:"
-local inactive={}
-for i,_missile in pairs(self.missiles)do
-local missile=_missile
-local targetname="unkown"
-if missile.targetUnit then
-targetname=missile.targetUnit:GetName()
-end
-local playername="none"
-if missile.targetPlayer then
-playername=missile.targetPlayer.name
-end
-local active=tostring(missile.active)
-local mtype=missile.missileType
-local dtype=missile.missileType
-local range=UTILS.MetersToNM(missile.missileRange)
-if not active then
-table.insert(inactive,i)
-end
-local heading=self:_GetWeapongHeading(missile.weapon)
-text=text..string.format("\n[%d] %s: active=%s, range=%.1f NM, heading=%03d, target=%s, player=%s, missilename=%s",i,mtype,active,range,heading,targetname,playername,missile.missileName)
-end
-if#self.missiles==0 then
-text=text.." none"
-end
-if self.verbose>=2 then
-self:I(self.lid..text)
-end
-for i=#self.missiles,1,-1 do
-local missile=self.missiles[i]
-if missile and not missile.active then
-table.remove(self.missiles,i)
-end
-end
-end
-function FOX:_IsProtected(targetunit)
-if not self.protectedset then
-return false
-end
-if targetunit and targetunit:IsAlive()then
-local targetgroup=targetunit:GetGroup()
-if targetgroup then
-local targetname=targetgroup:GetName()
-for _,_group in pairs(self.protectedset:GetSet())do
-local group=_group
-if group then
-local groupname=group:GetName()
-if targetname==groupname then
-return true
-end
-end
-end
-end
-end
-return false
-end
-function FOX._FuncTrack(weapon,self,missile)
-local missileCoord=missile.missileCoord:UpdateFromVec3(weapon.vec3)
-local missileVelocity=weapon:GetSpeed()
-self:GetMissileTarget(missile)
-local target=nil
-if missile.targetUnit then
-if missile.targetPlayer then
-if missile.targetPlayer.destroy==true then
-target=missile.targetUnit
-end
-else
-if self:_IsProtected(missile.targetUnit)then
-target=missile.targetUnit
-end
-end
-else
-local function _GetTarget(_unit)
-local unit=_unit
-local playerCoord=unit:GetCoordinate()
-local dist=missileCoord:Get3DDistance(playerCoord)
-if dist<=self.explosiondist then
-return unit
-end
-end
-local mindist=nil
-for _,_player in pairs(self.players)do
-local player=_player
-if player.unitname~=missile.shooterName then
-local playerCoord=player.unit:GetCoordinate()
-local dist=missileCoord:Get3DDistance(playerCoord)
-local Dshooter2player=playerCoord:Get3DDistance(missile.shotCoord)
-if(mindist==nil or dist=self.bigmissilemass
-end
-if destroymissile and self:_CheckCoordSafe(targetVec3)then
-self:I(self.lid..string.format("Destroying missile %s(%s) fired by %s aimed at %s [player=%s] at distance %.1f m",
-missile.missileType,missile.missileName,missile.shooterName,target:GetName(),tostring(missile.targetPlayer~=nil),distance))
-weapon:Destroy()
-missile.active=false
-if self.Debug then
-missileCoord:SmokeRed()
-end
-self:MissileDestroyed(missile)
-if self.explosionpower>0 and distance>50 and(distShooter==nil or(distShooter and distShooter>50))then
-missileCoord:Explosion(self.explosionpower)
-end
-if missile.targetPlayer then
-local text=string.format("Destroying missile. %s",self:_DeadText())
-MESSAGE:New(text,10):ToGroup(target:GetGroup())
-missile.targetPlayer.dead=missile.targetPlayer.dead+1
-end
-else
-local dt=1.0
-if distance>50000 then
-dt=self.dt50
-elseif distance>10000 then
-dt=self.dt10
-elseif distance>5000 then
-dt=self.dt05
-elseif distance>1000 then
-dt=self.dt01
-else
-dt=self.dt00
-end
-weapon:SetTimeStepTrack(dt)
-end
-else
-self:T(self.lid..string.format("Missile %s(%s) fired by %s has no current target. Checking back in 0.1 sec.",missile.missileType,missile.missileName,missile.shooterName))
-weapon:SetTimeStepTrack(0.1)
-end
-end
-function FOX._FuncImpact(weapon,self,missile)
-if missile.targetPlayer then
-local player=missile.targetPlayer
-if player and player.unit:IsAlive()then
-local text=string.format("Missile defeated. Well done, %s!",player.name)
-MESSAGE:New(text,10):ToClient(player.client)
-player.defeated=player.defeated+1
-end
-end
-missile.active=false
-self:T(FOX.lid..string.format("Terminating missile track timer."))
-weapon.tracking=false
-end
-function FOX:onafterMissileLaunch(From,Event,To,missile)
-local text=string.format("FOX: Tracking missile %s(%s) - target %s - shooter %s",missile.missileType,missile.missileName,tostring(missile.targetName),missile.shooterName)
-self:I(FOX.lid..text)
-MESSAGE:New(text,10):ToAllIf(self.Debug)
-for _,_player in pairs(self.players)do
-local player=_player
-local playerUnit=player.unit
-if playerUnit and playerUnit:IsAlive()and player.coalition~=missile.shooterCoalition then
-local distance=playerUnit:GetCoordinate():Get3DDistance(missile.shotCoord)
-local bearing=playerUnit:GetCoordinate():HeadingTo(missile.shotCoord)
-if player.launchalert then
-if(missile.targetPlayer and player.unitname==missile.targetPlayer.unitname)or(distance Target=%s, fuse dist=%s, explosive=%s",
-tostring(missile.shooterName),tostring(missile.missileType),tostring(missile.missileName),tostring(missile.targetName),tostring(missile.fuseDist),tostring(missile.explosive)))
-if missile.targetPlayer or self:_IsProtected(missile.targetUnit)or missile.targetName=="unknown"then
-table.insert(self.missiles,missile)
-self:__MissileLaunch(0.1,missile)
-end
-end
-end
-function FOX:OnEventHit(EventData)
-self:T({eventhit=EventData})
-if EventData.Weapon==nil then
-return
-end
-if EventData.IniUnit==nil then
-return
-end
-if EventData.TgtUnit==nil then
-return
-end
-local weapon=EventData.Weapon
-local weaponname=weapon:getName()
-for i,_missile in pairs(self.missiles)do
-local missile=_missile
-if missile.missileName==weaponname then
-self:I(self.lid..string.format("WARNING: Missile %s (%s) hit target %s. Missile trainer target was %s.",missile.missileType,missile.missileName,EventData.TgtUnitName,missile.targetName))
-self:I({missile=missile})
-return
-end
-end
-end
-function FOX:_AddF10Commands(_unitName)
-self:F(_unitName)
-local _unit,playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit and playername then
-local group=_unit:GetGroup()
-local gid=group:GetID()
-if group and gid then
-if not self.menuadded[gid]then
-self.menuadded[gid]=true
-local _rootPath=nil
-if FOX.MenuF10Root then
-_rootPath=FOX.MenuF10Root
-else
-if FOX.MenuF10[gid]==nil then
-FOX.MenuF10[gid]=missionCommands.addSubMenuForGroup(gid,"FOX")
-end
-_rootPath=FOX.MenuF10[gid]
-end
-missionCommands.addCommandForGroup(gid,"Destroy Missiles On/Off",_rootPath,self._ToggleDestroyMissiles,self,_unitName)
-missionCommands.addCommandForGroup(gid,"Launch Alerts On/Off",_rootPath,self._ToggleLaunchAlert,self,_unitName)
-missionCommands.addCommandForGroup(gid,"Mark Launch On/Off",_rootPath,self._ToggleLaunchMark,self,_unitName)
-missionCommands.addCommandForGroup(gid,"My Status",_rootPath,self._MyStatus,self,_unitName)
-end
-else
-self:E(self.lid..string.format("ERROR: Could not find group or group ID in AddF10Menu() function. Unit name: %s.",_unitName or"unknown"))
-end
-else
-self:E(self.lid..string.format("ERROR: Player unit does not exist in AddF10Menu() function. Unit name: %s.",_unitName or"unknown"))
-end
-end
-function FOX:_MyStatus(_unitname)
-self:F2(_unitname)
-local unit,playername=self:_GetPlayerUnitAndName(_unitname)
-if unit and playername then
-local playerData=self.players[playername]
-if playerData then
-local m,mtext=self:_GetTargetMissiles(playerData.name)
-local text=string.format("Status of player %s:\n",playerData.name)
-local safe=self:_CheckCoordSafe(playerData.unit:GetCoordinate())
-text=text..string.format("Destroy missiles? %s\n",tostring(playerData.destroy))
-text=text..string.format("Launch alert? %s\n",tostring(playerData.launchalert))
-text=text..string.format("Launch marks? %s\n",tostring(playerData.marklaunch))
-text=text..string.format("Am I safe? %s\n",tostring(safe))
-text=text..string.format("Missiles defeated: %d\n",playerData.defeated)
-text=text..string.format("Missiles destroyed: %d\n",playerData.dead)
-text=text..string.format("Me target: %d\n%s",m,mtext)
-MESSAGE:New(text,10,nil,true):ToClient(playerData.client)
-end
-end
-end
-function FOX:_GetTargetMissiles(playername)
-local text=""
-local n=0
-for _,_missile in pairs(self.missiles)do
-local missile=_missile
-if missile.targetPlayer and missile.targetPlayer.name==playername then
-n=n+1
-text=text..string.format("Type %s: active %s\n",missile.missileType,tostring(missile.active))
-end
-end
-return n,text
-end
-function FOX:_ToggleLaunchAlert(_unitname)
-self:F2(_unitname)
-local unit,playername=self:_GetPlayerUnitAndName(_unitname)
-if unit and playername then
-local playerData=self.players[playername]
-if playerData then
-playerData.launchalert=not playerData.launchalert
-local text=""
-if playerData.launchalert==true then
-text=string.format("%s, missile launch alerts are now ENABLED.",playerData.name)
-else
-text=string.format("%s, missile launch alerts are now DISABLED.",playerData.name)
-end
-MESSAGE:New(text,5):ToClient(playerData.client)
-end
-end
-end
-function FOX:_ToggleLaunchMark(_unitname)
-self:F2(_unitname)
-local unit,playername=self:_GetPlayerUnitAndName(_unitname)
-if unit and playername then
-local playerData=self.players[playername]
-if playerData then
-playerData.marklaunch=not playerData.marklaunch
-local text=""
-if playerData.marklaunch==true then
-text=string.format("%s, missile launch marks are now ENABLED.",playerData.name)
-else
-text=string.format("%s, missile launch marks are now DISABLED.",playerData.name)
-end
-MESSAGE:New(text,5):ToClient(playerData.client)
-end
-end
-end
-function FOX:_ToggleDestroyMissiles(_unitname)
-self:F2(_unitname)
-local unit,playername=self:_GetPlayerUnitAndName(_unitname)
-if unit and playername then
-local playerData=self.players[playername]
-if playerData then
-playerData.destroy=not playerData.destroy
-local text=""
-if playerData.destroy==true then
-text=string.format("%s, incoming missiles will be DESTROYED.",playerData.name)
-else
-text=string.format("%s, incoming missiles will NOT be DESTROYED.",playerData.name)
-end
-MESSAGE:New(text,5):ToClient(playerData.client)
-end
-end
-end
-function FOX:_DeadText()
-local texts={}
-texts[1]="You're dead!"
-texts[2]="Meet your maker!"
-texts[3]="Time to meet your maker!"
-texts[4]="Well, I guess that was it!"
-texts[5]="Bye, bye!"
-texts[6]="Cheers buddy, was nice knowing you!"
-local r=math.random(#texts)
-return texts[r]
-end
-function FOX:_CheckCoordSafe(coord)
-if#self.safezones==0 then
-return true
-end
-for _,_zone in pairs(self.safezones)do
-local zone=_zone
-local Vec2={x=coord.x,y=coord.z}
-local inzone=zone:IsVec2InZone(Vec2)
-if inzone then
-return true
-end
-end
-return false
-end
-function FOX:_CheckCoordLaunch(coord)
-if#self.launchzones==0 then
-return true
-end
-for _,_zone in pairs(self.launchzones)do
-local zone=_zone
-local Vec2={x=coord.x,y=coord.z}
-local inzone=zone:IsVec2InZone(Vec2)
-if inzone then
-return true
-end
-end
-return false
-end
-function FOX:_GetWeapongHeading(weapon)
-if weapon and weapon:isExist()then
-local wp=weapon:getPosition()
-local wph=math.atan2(wp.x.z,wp.x.x)
-if wph<0 then
-wph=wph+2*math.pi
-end
-wph=math.deg(wph)
-return wph
-end
-return-1
-end
-function FOX:_SayNotchingHeadings(playerData,weapon)
-if playerData and playerData.unit and playerData.unit:IsAlive()then
-local nr,nl=self:_GetNotchingHeadings(weapon)
-if nr and nl then
-local text=string.format("Notching heading %03d° or %03d°",nr,nl)
-MESSAGE:New(text,5,"FOX"):ToClient(playerData.client)
-end
-end
-end
-function FOX:_GetNotchingHeadings(weapon)
-if weapon then
-local hdg=self:_GetWeapongHeading(weapon)
-local hdg1=hdg+90
-if hdg1>360 then
-hdg1=hdg1-360
-end
-local hdg2=hdg-90
-if hdg2<0 then
-hdg2=hdg2+360
-end
-return hdg1,hdg2
-end
-return nil,nil
-end
-function FOX:_GetPlayerFromUnitname(unitName)
-for _,_player in pairs(self.players)do
-local player=_player
-if player.unitname==unitName then
-return player
-end
-end
-return nil
-end
-function FOX:_GetPlayerFromUnit(unit)
-if unit and unit:IsAlive()then
-local unitname=unit:GetName()
-for _,_player in pairs(self.players)do
-local player=_player
-if player.unitname==unitname then
-return player
-end
-end
-end
-return nil
-end
-function FOX:_GetPlayerUnitAndName(_unitName)
-self:F2(_unitName)
-if _unitName~=nil then
-local DCSunit=Unit.getByName(_unitName)
-if DCSunit then
-local playername=DCSunit:getPlayerName()
-local unit=UNIT:Find(DCSunit)
-self:T2({DCSunit=DCSunit,unit=unit,playername=playername})
-if DCSunit and unit and playername then
-self:T(self.lid..string.format("Found DCS unit %s with player %s.",tostring(_unitName),tostring(playername)))
-return unit,playername
-end
-end
-end
-return nil,nil
-end
-MANTIS={
-ClassName="MANTIS",
-name="mymantis",
-SAM_Templates_Prefix="",
-SAM_Group=nil,
-EWR_Templates_Prefix="",
-EWR_Group=nil,
-Adv_EWR_Group=nil,
-HQ_Template_CC="",
-HQ_CC=nil,
-SAM_Table={},
-SAM_Table_Long={},
-SAM_Table_Medium={},
-SAM_Table_Short={},
-lid="",
-Detection=nil,
-AWACS_Detection=nil,
-debug=false,
-checkradius=25000,
-grouping=5000,
-acceptrange=80000,
-detectinterval=30,
-engagerange=95,
-autorelocate=false,
-advanced=false,
-adv_ratio=100,
-adv_state=0,
-AWACS_Prefix="",
-advAwacs=false,
-verbose=false,
-awacsrange=250000,
-Shorad=nil,
-ShoradLink=false,
-ShoradTime=600,
-ShoradActDistance=25000,
-UseEmOnOff=false,
-TimeStamp=0,
-state2flag=false,
-SamStateTracker={},
-DLink=false,
-DLTimeStamp=0,
-Padding=10,
-SuppressedGroups={},
-automode=true,
-autoshorad=true,
-ShoradGroupSet=nil,
-}
-MANTIS.AdvancedState={
-GREEN=0,
-AMBER=1,
-RED=2,
-}
-MANTIS.SamType={
-SHORT="Short",
-MEDIUM="Medium",
-LONG="Long",
-}
-MANTIS.SamData={
-["Hawk"]={Range=44,Blindspot=0,Height=9,Type="Medium",Radar="Hawk"},
-["NASAMS"]={Range=14,Blindspot=0,Height=3,Type="Short",Radar="NSAMS"},
-["Patriot"]={Range=99,Blindspot=0,Height=9,Type="Long",Radar="Patriot"},
-["Rapier"]={Range=6,Blindspot=0,Height=3,Type="Short",Radar="rapier"},
-["SA-2"]={Range=40,Blindspot=7,Height=25,Type="Medium",Radar="S_75M_Volhov"},
-["SA-3"]={Range=18,Blindspot=6,Height=18,Type="Short",Radar="5p73 s-125 ln"},
-["SA-5"]={Range=250,Blindspot=7,Height=40,Type="Long",Radar="5N62V"},
-["SA-6"]={Range=25,Blindspot=0,Height=8,Type="Medium",Radar="1S91"},
-["SA-10"]={Range=119,Blindspot=0,Height=18,Type="Long",Radar="S-300PS 4"},
-["SA-11"]={Range=35,Blindspot=0,Height=20,Type="Medium",Radar="SA-11"},
-["Roland"]={Range=8,Blindspot=0,Height=3,Type="Short",Radar="Roland"},
-["HQ-7"]={Range=12,Blindspot=0,Height=3,Type="Short",Radar="HQ-7"},
-["SA-9"]={Range=4,Blindspot=0,Height=3,Type="Short",Radar="Strela"},
-["SA-8"]={Range=10,Blindspot=0,Height=5,Type="Short",Radar="Osa 9A33"},
-["SA-19"]={Range=8,Blindspot=0,Height=3,Type="Short",Radar="Tunguska"},
-["SA-15"]={Range=11,Blindspot=0,Height=6,Type="Short",Radar="Tor 9A331"},
-["SA-13"]={Range=5,Blindspot=0,Height=3,Type="Short",Radar="Strela"},
-["Avenger"]={Range=4,Blindspot=0,Height=3,Type="Short",Radar="Avenger"},
-["Chaparrel"]={Range=8,Blindspot=0,Height=3,Type="Short",Radar="Chaparral"},
-["Linebacker"]={Range=4,Blindspot=0,Height=3,Type="Short",Radar="Linebacker"},
-["Silkworm"]={Range=90,Blindspot=1,Height=0.2,Type="Long",Radar="Silkworm"},
-["SA-10B"]={Range=75,Blindspot=0,Height=18,Type="Medium",Radar="SA-10B"},
-["SA-17"]={Range=50,Blindspot=3,Height=30,Type="Medium",Radar="SA-17"},
-["SA-20A"]={Range=150,Blindspot=5,Height=27,Type="Long",Radar="S-300PMU1"},
-["SA-20B"]={Range=200,Blindspot=4,Height=27,Type="Long",Radar="S-300PMU2"},
-["HQ-2"]={Range=50,Blindspot=6,Height=35,Type="Medium",Radar="HQ_2_Guideline_LN"},
-["SHORAD"]={Range=3,Blindspot=0,Height=3,Type="Short",Radar="Igla"},
-["TAMIR IDFA"]={Range=20,Blindspot=0.6,Height=12.3,Type="Short",Radar="IRON_DOME_LN"},
-["STUNNER IDFA"]={Range=250,Blindspot=1,Height=45,Type="Long",Radar="DAVID_SLING_LN"},
-}
-MANTIS.SamDataHDS={
-["SA-2 HDS"]={Range=56,Blindspot=7,Height=30,Type="Medium",Radar="V759"},
-["SA-3 HDS"]={Range=20,Blindspot=6,Height=30,Type="Short",Radar="V-601P"},
-["SA-10C HDS 2"]={Range=90,Blindspot=5,Height=25,Type="Long",Radar="5P85DE ln"},
-["SA-10C HDS 1"]={Range=90,Blindspot=5,Height=25,Type="Long",Radar="5P85CE ln"},
-["SA-12 HDS 2"]={Range=100,Blindspot=10,Height=25,Type="Long",Radar="S-300V 9A82 l"},
-["SA-12 HDS 1"]={Range=75,Blindspot=1,Height=25,Type="Long",Radar="S-300V 9A83 l"},
-["SA-23 HDS 2"]={Range=200,Blindspot=5,Height=37,Type="Long",Radar="S-300VM 9A82ME"},
-["SA-23 HDS 1"]={Range=100,Blindspot=1,Height=50,Type="Long",Radar="S-300VM 9A83ME"},
-["HQ-2 HDS"]={Range=50,Blindspot=6,Height=35,Type="Medium",Radar="HQ_2_Guideline_LN"},
-}
-MANTIS.SamDataSMA={
-["RBS98M SMA"]={Range=20,Blindspot=0,Height=8,Type="Short",Radar="RBS-98"},
-["RBS70 SMA"]={Range=8,Blindspot=0,Height=5.5,Type="Short",Radar="RBS-70"},
-["RBS70M SMA"]={Range=8,Blindspot=0,Height=5.5,Type="Short",Radar="BV410_RBS70"},
-["RBS90 SMA"]={Range=8,Blindspot=0,Height=5.5,Type="Short",Radar="RBS-90"},
-["RBS90M SMA"]={Range=8,Blindspot=0,Height=5.5,Type="Short",Radar="BV410_RBS90"},
-["RBS103A SMA"]={Range=150,Blindspot=3,Height=24.5,Type="Long",Radar="LvS-103_Lavett103_Rb103A"},
-["RBS103B SMA"]={Range=35,Blindspot=0,Height=36,Type="Medium",Radar="LvS-103_Lavett103_Rb103B"},
-["RBS103AM SMA"]={Range=150,Blindspot=3,Height=24.5,Type="Long",Radar="LvS-103_Lavett103_HX_Rb103A"},
-["RBS103BM SMA"]={Range=35,Blindspot=0,Height=36,Type="Medium",Radar="LvS-103_Lavett103_HX_Rb103B"},
-["Lvkv9040M SMA"]={Range=4,Blindspot=0,Height=2.5,Type="Short",Radar="LvKv9040"},
-}
-MANTIS.SamDataCH={
-["2S38 CH"]={Range=8,Blindspot=0.5,Height=6,Type="Short",Radar="2S38"},
-["PantsirS1 CH"]={Range=20,Blindspot=1.2,Height=15,Type="Short",Radar="PantsirS1"},
-["PantsirS2 CH"]={Range=30,Blindspot=1.2,Height=18,Type="Medium",Radar="PantsirS2"},
-["PGL-625 CH"]={Range=10,Blindspot=0.5,Height=5,Type="Short",Radar="PGL_625"},
-["HQ-17A CH"]={Range=20,Blindspot=1.5,Height=10,Type="Short",Radar="HQ17A"},
-["M903PAC2 CH"]={Range=160,Blindspot=3,Height=24.5,Type="Long",Radar="MIM104_M903_PAC2"},
-["M903PAC3 CH"]={Range=120,Blindspot=1,Height=40,Type="Long",Radar="MIM104_M903_PAC3"},
-["TorM2 CH"]={Range=12,Blindspot=1,Height=10,Type="Short",Radar="TorM2"},
-["TorM2K CH"]={Range=12,Blindspot=1,Height=10,Type="Short",Radar="TorM2K"},
-["TorM2M CH"]={Range=16,Blindspot=1,Height=10,Type="Short",Radar="TorM2M"},
-["NASAMS3-AMRAAMER CH"]={Range=50,Blindspot=2,Height=35.7,Type="Medium",Radar="CH_NASAMS3_LN_AMRAAM_ER"},
-["NASAMS3-AIM9X2 CH"]={Range=20,Blindspot=0.2,Height=18,Type="Short",Radar="CH_NASAMS3_LN_AIM9X2"},
-["C-RAM CH"]={Range=2,Blindspot=0,Height=2,Type="Short",Radar="CH_Centurion_C_RAM"},
-["PGZ-09 CH"]={Range=4,Blindspot=0,Height=3,Type="Short",Radar="CH_PGZ09"},
-["S350-9M100 CH"]={Range=15,Blindspot=1.5,Height=8,Type="Short",Radar="CH_S350_50P6_9M100"},
-["S350-9M96D CH"]={Range=150,Blindspot=2.5,Height=30,Type="Long",Radar="CH_S350_50P6_9M96D"},
-["LAV-AD CH"]={Range=8,Blindspot=0.2,Height=4.8,Type="Short",Radar="CH_LAVAD"},
-["HQ-22 CH"]={Range=170,Blindspot=5,Height=27,Type="Long",Radar="CH_HQ22_LN"},
-}
-do
-function MANTIS:New(name,samprefix,ewrprefix,hq,coalition,dynamic,awacs,EmOnOff,Padding,Zones)
-local self=BASE:Inherit(self,FSM:New())
-self.SAM_Templates_Prefix=samprefix or"Red SAM"
-self.EWR_Templates_Prefix=ewrprefix or"Red EWR"
-self.HQ_Template_CC=hq or nil
-self.Coalition=coalition or"red"
-self.SAM_Table={}
-self.SAM_Table_Long={}
-self.SAM_Table_Medium={}
-self.SAM_Table_Short={}
-self.dynamic=dynamic or false
-self.checkradius=25000
-self.grouping=5000
-self.acceptrange=80000
-self.detectinterval=30
-self.engagerange=95
-self.autorelocate=false
-self.autorelocateunits={HQ=false,EWR=false}
-self.advanced=false
-self.adv_ratio=100
-self.adv_state=0
-self.verbose=false
-self.Adv_EWR_Group=nil
-self.AWACS_Prefix=awacs or nil
-self.awacsrange=250000
-self.Shorad=nil
-self.ShoradLink=false
-self.ShoradTime=600
-self.ShoradActDistance=25000
-self.TimeStamp=timer.getAbsTime()
-self.relointerval=math.random(1800,3600)
-self.state2flag=false
-self.SamStateTracker={}
-self.DLink=false
-self.Padding=Padding or 10
-self.SuppressedGroups={}
-self.automode=true
-self.radiusscale={}
-self.radiusscale[MANTIS.SamType.LONG]=1.1
-self.radiusscale[MANTIS.SamType.MEDIUM]=1.2
-self.radiusscale[MANTIS.SamType.SHORT]=1.3
-self.usezones=false
-self.AcceptZones={}
-self.RejectZones={}
-self.ConflictZones={}
-self.maxlongrange=1
-self.maxmidrange=2
-self.maxshortrange=2
-self.maxclassic=6
-self.autoshorad=true
-self.ShoradGroupSet=SET_GROUP:New()
-self.FilterZones=Zones
-self.SkateZones=nil
-self.SkateNumber=3
-self.shootandscoot=false
-self.UseEmOnOff=true
-if EmOnOff==false then
-self.UseEmOnOff=false
-end
-if type(awacs)=="string"then
-self.advAwacs=true
-else
-self.advAwacs=false
-end
-self.lid=string.format("MANTIS %s | ",self.name)
-if self.debug then
-BASE:TraceOnOff(true)
-BASE:TraceClass(self.ClassName)
-BASE:TraceLevel(1)
-end
-self.ewr_templates={}
-if type(samprefix)~="table"then
-self.SAM_Templates_Prefix={samprefix}
-end
-if type(ewrprefix)~="table"then
-self.EWR_Templates_Prefix={ewrprefix}
-end
-for _,_group in pairs(self.SAM_Templates_Prefix)do
-table.insert(self.ewr_templates,_group)
-end
-for _,_group in pairs(self.EWR_Templates_Prefix)do
-table.insert(self.ewr_templates,_group)
-end
-if self.advAwacs then
-table.insert(self.ewr_templates,awacs)
-end
-self:T({self.ewr_templates})
-self.SAM_Group=SET_GROUP:New():FilterPrefixes(self.SAM_Templates_Prefix):FilterCoalitions(self.Coalition)
-self.EWR_Group=SET_GROUP:New():FilterPrefixes(self.ewr_templates):FilterCoalitions(self.Coalition)
-if self.FilterZones then
-self.SAM_Group:FilterZones(self.FilterZones)
-end
-if self.dynamic then
-self.SAM_Group:FilterStart()
-self.EWR_Group:FilterStart()
-else
-self.SAM_Group:FilterOnce()
-self.EWR_Group:FilterOnce()
-end
-if self.HQ_Template_CC then
-self.HQ_CC=GROUP:FindByName(self.HQ_Template_CC)
-end
-self.version="0.8.15"
-self:I(string.format("***** Starting MANTIS Version %s *****",self.version))
-self:SetStartState("Stopped")
-self:AddTransition("Stopped","Start","Running")
-self:AddTransition("*","Status","*")
-self:AddTransition("*","Relocating","*")
-self:AddTransition("*","GreenState","*")
-self:AddTransition("*","RedState","*")
-self:AddTransition("*","AdvStateChange","*")
-self:AddTransition("*","ShoradActivated","*")
-self:AddTransition("*","SeadSuppressionStart","*")
-self:AddTransition("*","SeadSuppressionEnd","*")
-self:AddTransition("*","SeadSuppressionPlanned","*")
-self:AddTransition("*","Stop","Stopped")
-return self
-end
-function MANTIS:_GetSAMTable()
-self:T(self.lid.."GetSAMTable")
-return self.SAM_Table
-end
-function MANTIS:_SetSAMTable(table)
-self:T(self.lid.."SetSAMTable")
-self.SAM_Table=table
-return self
-end
-function MANTIS:SetEWRGrouping(radius)
-self:T(self.lid.."SetEWRGrouping")
-local radius=radius or 5000
-self.grouping=radius
-return self
-end
-function MANTIS:AddScootZones(ZoneSet,Number,Random,Formation)
-self:T(self.lid.." AddScootZones")
-self.SkateZones=ZoneSet
-self.SkateNumber=Number or 3
-self.shootandscoot=true
-self.ScootRandom=Random
-self.ScootFormation=Formation or"Cone"
-return self
-end
-function MANTIS:AddZones(AcceptZones,RejectZones,ConflictZones)
-self:T(self.lid.."AddZones")
-self.AcceptZones=AcceptZones or{}
-self.RejectZones=RejectZones or{}
-self.ConflictZones=ConflictZones or{}
-if#AcceptZones>0 or#RejectZones>0 or#ConflictZones>0 then
-self.usezones=true
-end
-return self
-end
-function MANTIS:SetEWRRange(radius)
-self:T(self.lid.."SetEWRRange")
-return self
-end
-function MANTIS:SetSAMRadius(radius)
-self:T(self.lid.."SetSAMRadius")
-local radius=radius or 25000
-self.checkradius=radius
-return self
-end
-function MANTIS:SetSAMRange(range)
-self:T(self.lid.."SetSAMRange")
-local range=range or 95
-if range<0 or range>100 then
-range=95
-end
-self.engagerange=range
-return self
-end
-function MANTIS:SetMaxActiveSAMs(Short,Mid,Long,Classic)
-self:T(self.lid.."SetMaxActiveSAMs")
-self.maxclassic=Classic or 6
-self.maxlongrange=Long or 1
-self.maxmidrange=Mid or 2
-self.maxshortrange=Short or 2
-return self
-end
-function MANTIS:SetNewSAMRangeWhileRunning(range)
-self:T(self.lid.."SetNewSAMRangeWhileRunning")
-local range=range or 95
-if range<0 or range>100 then
-range=95
-end
-self.engagerange=range
-self:_RefreshSAMTable()
-self.mysead.EngagementRange=range
-return self
-end
-function MANTIS:Debug(onoff)
-self:T(self.lid.."SetDebug")
-local onoff=onoff or false
-self.debug=onoff
-if onoff then
-BASE:TraceOn()
-BASE:TraceClass("MANTIS")
-BASE:TraceLevel(1)
-else
-BASE:TraceOff()
-end
-return self
-end
-function MANTIS:GetCommandCenter()
-self:T(self.lid.."GetCommandCenter")
-if self.HQ_CC then
-return self.HQ_CC
-else
-return nil
-end
-end
-function MANTIS:SetAwacs(prefix)
-self:T(self.lid.."SetAwacs")
-if prefix~=nil then
-if type(prefix)=="string"then
-self.AWACS_Prefix=prefix
-self.advAwacs=true
-end
-end
-return self
-end
-function MANTIS:SetAwacsRange(range)
-self:T(self.lid.."SetAwacsRange")
-local range=range or 250000
-self.awacsrange=range
-return self
-end
-function MANTIS:SetCommandCenter(group)
-self:T(self.lid.."SetCommandCenter")
-local group=group or nil
-if group~=nil then
-if type(group)=="string"then
-self.HQ_CC=GROUP:FindByName(group)
-self.HQ_Template_CC=group
-else
-self.HQ_CC=group
-self.HQ_Template_CC=group:GetName()
-end
-end
-return self
-end
-function MANTIS:SetDetectInterval(interval)
-self:T(self.lid.."SetDetectInterval")
-local interval=interval or 30
-self.detectinterval=interval
-return self
-end
-function MANTIS:SetAdvancedMode(onoff,ratio)
-self:T(self.lid.."SetAdvancedMode")
-local onoff=onoff or false
-local ratio=ratio or 100
-if(type(self.HQ_Template_CC)=="string")and onoff and self.dynamic then
-self.adv_ratio=ratio
-self.advanced=true
-self.adv_state=0
-self.Adv_EWR_Group=SET_GROUP:New():FilterPrefixes(self.EWR_Templates_Prefix):FilterCoalitions(self.Coalition):FilterStart()
-self:I(string.format("***** Starting Advanced Mode MANTIS Version %s *****",self.version))
-else
-local text=self.lid.." Advanced Mode requires a HQ and dynamic to be set. Revisit your MANTIS:New() statement to add both."
-local m=MESSAGE:New(text,10,"MANTIS",true):ToAll()
-self:E(text)
-end
-return self
-end
-function MANTIS:SetUsingEmOnOff(switch)
-self:T(self.lid.."SetUsingEmOnOff")
-self.UseEmOnOff=switch or false
-return self
-end
-function MANTIS:SetUsingDLink(DLink)
-self:T(self.lid.."SetUsingDLink")
-self.DLink=true
-self.Detection=DLink
-self.DLTimeStamp=timer.getAbsTime()
-return self
-end
-function MANTIS:_CheckHQState()
-self:T(self.lid.."CheckHQState")
-local text=self.lid.." Checking HQ State"
-local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
-if self.verbose then self:I(text)end
-if self.advanced then
-local hq=self.HQ_Template_CC
-local hqgrp=GROUP:FindByName(hq)
-if hqgrp then
-if hqgrp:IsAlive()then
-return true
-else
-return false
-end
-end
-end
-return self
-end
-function MANTIS:_CheckEWRState()
-self:T(self.lid.."CheckEWRState")
-local text=self.lid.." Checking EWR State"
-local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
-if self.verbose then self:I(text)end
-if self.advanced then
-local EWR_Group=self.Adv_EWR_Group
-local nalive=EWR_Group:CountAlive()
-if self.advAwacs then
-local awacs=GROUP:FindByName(self.AWACS_Prefix)
-if awacs~=nil then
-if awacs:IsAlive()then
-nalive=nalive+1
-end
-end
-end
-if nalive>0 then
-return true
-else
-return false
-end
-end
-return self
-end
-function MANTIS:_CalcAdvState()
-self:T(self.lid.."CalcAdvState")
-local m=MESSAGE:New(self.lid.." Calculating Advanced State",10,"MANTIS"):ToAllIf(self.debug)
-if self.verbose then self:I(self.lid.." Calculating Advanced State")end
-local currstate=self.adv_state
-local EWR_State=self:_CheckEWRState()
-local HQ_State=self:_CheckHQState()
-if EWR_State and HQ_State then
-self.adv_state=0
-elseif EWR_State or HQ_State then
-self.adv_state=1
-else
-self.adv_state=2
-end
-local interval=self.detectinterval
-local ratio=self.adv_ratio/100
-ratio=ratio*self.adv_state
-local newinterval=interval+(interval*ratio)
-if self.debug or self.verbose then
-local text=self.lid..string.format(" Calculated OldState/NewState/Interval: %d / %d / %d",currstate,self.adv_state,newinterval)
-local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
-if self.verbose then self:I(text)end
-end
-return newinterval,currstate
-end
-function MANTIS:SetAutoRelocate(hq,ewr)
-self:T(self.lid.."SetAutoRelocate")
-local hqrel=hq or false
-local ewrel=ewr or false
-if hqrel or ewrel then
-self.autorelocate=true
-self.autorelocateunits={HQ=hqrel,EWR=ewrel}
-end
-return self
-end
-function MANTIS:_RelocateGroups()
-self:T(self.lid.."RelocateGroups")
-local text=self.lid.." Relocating Groups"
-local m=MESSAGE:New(text,10,"MANTIS",true):ToAllIf(self.debug)
-if self.verbose then self:I(text)end
-if self.autorelocate then
-local HQGroup=self.HQ_CC
-if self.autorelocateunits.HQ and self.HQ_CC and HQGroup:IsAlive()then
-local _hqgrp=self.HQ_CC
-local text=self.lid.." Relocating HQ"
-_hqgrp:RelocateGroundRandomInRadius(20,500,true,true)
-end
-if self.autorelocateunits.EWR then
-local EWR_GRP=SET_GROUP:New():FilterPrefixes(self.EWR_Templates_Prefix):FilterCoalitions(self.Coalition):FilterOnce()
-local EWR_Grps=EWR_GRP.Set
-for _,_grp in pairs(EWR_Grps)do
-if _grp:IsAlive()and _grp:IsGround()then
-local text=self.lid.." Relocating EWR ".._grp:GetName()
-local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
-if self.verbose then self:I(text)end
-_grp:RelocateGroundRandomInRadius(20,500,true,true)
-end
-end
-end
-end
-return self
-end
-function MANTIS:_CheckCoordinateInZones(coord)
-self:T(self.lid.."_CheckCoordinateInZones")
-local inzone=false
-if#self.AcceptZones>0 then
-for _,_zone in pairs(self.AcceptZones)do
-local zone=_zone
-if zone:IsCoordinateInZone(coord)then
-inzone=true
-self:T(self.lid.."Target coord in Accept Zone!")
-break
-end
-end
-end
-if#self.RejectZones>0 and inzone then
-for _,_zone in pairs(self.RejectZones)do
-local zone=_zone
-if zone:IsCoordinateInZone(coord)then
-inzone=false
-self:T(self.lid.."Target coord in Reject Zone!")
-break
-end
-end
-end
-if#self.ConflictZones>0 and not inzone then
-for _,_zone in pairs(self.ConflictZones)do
-local zone=_zone
-if zone:IsCoordinateInZone(coord)then
-inzone=true
-self:T(self.lid.."Target coord in Conflict Zone!")
-break
-end
-end
-end
-return inzone
-end
-function MANTIS:_PreFilterHeight(height)
-self:T(self.lid.."_PreFilterHeight")
-local set={}
-local dlink=self.Detection
-local detectedgroups=dlink:GetContactTable()
-for _,_contact in pairs(detectedgroups)do
-local contact=_contact
-local grp=contact.group
-if grp:IsAlive()then
-if grp:GetHeight(true)65 then
-self:_RefreshSAMTable()
-end
-if self.automode then
-local samset=self.SAM_Table_Long
-self:_CheckLoop(samset,detset,dlink,self.maxlongrange)
-local samset=self.SAM_Table_Medium
-self:_CheckLoop(samset,detset,dlink,self.maxmidrange)
-local samset=self.SAM_Table_Short
-self:_CheckLoop(samset,detset,dlink,self.maxshortrange)
-else
-local samset=self:_GetSAMTable()
-self:_CheckLoop(samset,detset,dlink,self.maxclassic)
-end
-return self
-end
-function MANTIS:_Relocate()
-self:T(self.lid.."Relocate")
-self:_RelocateGroups()
-return self
-end
-function MANTIS:_CheckAdvState()
-self:T(self.lid.."CheckAdvSate")
-local interval,oldstate=self:_CalcAdvState()
-local newstate=self.adv_state
-if newstate~=oldstate then
-self:__AdvStateChange(1,oldstate,newstate,interval)
-if newstate==2 then
-self.state2flag=true
-local samset=self:_GetSAMTable()
-for _,_data in pairs(samset)do
-local name=_data[1]
-local samgroup=GROUP:FindByName(name)
-if samgroup:IsAlive()then
-if self.UseEmOnOff then
-samgroup:EnableEmission(true)
-else
-samgroup:OptionAlarmStateRed()
-end
-end
-end
-elseif newstate<=1 then
-self.detectinterval=interval
-self.state2flag=false
-end
-end
-return self
-end
-function MANTIS:_CheckDLinkState()
-self:T(self.lid.."_CheckDLinkState")
-local dlink=self.Detection
-local TS=timer.getAbsTime()
-if not dlink:Is("Running")and(TS-self.DLTimeStamp>29)then
-self.DLink=false
-self.Detection=self:StartDetection()
-self:I(self.lid.."Intel DLink not running - switching back to single detection!")
-end
-end
-function MANTIS:onafterStart(From,Event,To)
-self:T({From,Event,To})
-self:T(self.lid.."Starting MANTIS")
-self:SetSAMStartState()
-if not INTEL then
-self.Detection=self:StartDetection()
-else
-self.Detection=self:StartIntelDetection()
-end
-if self.autoshorad then
-self.Shorad=SHORAD:New(self.name.."-SHORAD",self.name.."-SHORAD",self.SAM_Group,self.ShoradActDistance,self.ShoradTime,self.coalition,self.UseEmOnOff)
-self.Shorad:SetDefenseLimits(80,95)
-self.ShoradLink=true
-self.Shorad.Groupset=self.ShoradGroupSet
-self.Shorad.debug=self.debug
-end
-if self.shootandscoot and self.SkateZones and self.Shorad then
-self.Shorad:AddScootZones(self.SkateZones,self.SkateNumber or 3,self.ScootRandom,self.ScootFormation)
-end
-self:__Status(-math.random(1,10))
-return self
-end
-function MANTIS:onbeforeStatus(From,Event,To)
-self:T({From,Event,To})
-if not self.state2flag then
-self:_Check(self.Detection,self.DLink)
-end
-if self.autorelocate then
-local relointerval=self.relointerval
-local thistime=timer.getAbsTime()
-local timepassed=thistime-self.TimeStamp
-local halfintv=math.floor(timepassed/relointerval)
-if halfintv>=1 then
-self.TimeStamp=timer.getAbsTime()
-self:_Relocate()
-self:__Relocating(1)
-end
-end
-if self.advanced then
-self:_CheckAdvState()
-end
-if self.DLink then
-self:_CheckDLinkState()
-end
-return self
-end
-function MANTIS:onafterStatus(From,Event,To)
-self:T({From,Event,To})
-if self.debug and self.verbose then
-self:I(self.lid.."Status Report")
-for _name,_state in pairs(self.SamStateTracker)do
-self:I(string.format("Site %s\tStatus %s",_name,_state))
-end
-end
-local interval=self.detectinterval*-1
-self:__Status(interval)
-return self
-end
-function MANTIS:onafterStop(From,Event,To)
-self:T({From,Event,To})
-return self
-end
-function MANTIS:onafterRelocating(From,Event,To)
-self:T({From,Event,To})
-return self
-end
-function MANTIS:onafterGreenState(From,Event,To,Group)
-self:T({From,Event,To,Group:GetName()})
-return self
-end
-function MANTIS:onafterRedState(From,Event,To,Group)
-self:T({From,Event,To,Group:GetName()})
-return self
-end
-function MANTIS:onafterAdvStateChange(From,Event,To,Oldstate,Newstate,Interval)
-self:T({From,Event,To,Oldstate,Newstate,Interval})
-return self
-end
-function MANTIS:onafterShoradActivated(From,Event,To,Name,Radius,Ontime)
-self:T({From,Event,To,Name,Radius,Ontime})
-return self
-end
-function MANTIS:onafterSeadSuppressionStart(From,Event,To,Group,Name,Attacker)
-self:T({From,Event,To,Name})
-self.SuppressedGroups[Name]=true
-if self.ShoradLink then
-local Shorad=self.Shorad
-local radius=self.checkradius
-local ontime=self.ShoradTime
-Shorad:WakeUpShorad(Name,radius,ontime)
-self:__ShoradActivated(1,Name,radius,ontime)
-end
-return self
-end
-function MANTIS:onafterSeadSuppressionEnd(From,Event,To,Group,Name)
-self:T({From,Event,To,Name})
-self.SuppressedGroups[Name]=false
-return self
-end
-function MANTIS:onafterSeadSuppressionPlanned(From,Event,To,Group,Name,SuppressionStartTime,SuppressionEndTime,Attacker)
-self:T({From,Event,To,Name})
-return self
-end
-end
-MISSILETRAINER={
-ClassName="MISSILETRAINER",
-TrackingMissiles={},
-}
-function MISSILETRAINER._Alive(Client,self)
-if self.Briefing then
-Client:Message(self.Briefing,15,"Trainer")
-end
-if self.MenusOnOff==true then
-Client:Message("Use the 'Radio Menu' -> 'Other (F10)' -> 'Missile Trainer' menu options to change the Missile Trainer settings (for all players).",15,"Trainer")
-Client.MainMenu=MENU_GROUP:New(Client:GetGroup(),"Missile Trainer",nil)
-Client.MenuMessages=MENU_GROUP:New(Client:GetGroup(),"Messages",Client.MainMenu)
-Client.MenuOn=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Messages On",Client.MenuMessages,self._MenuMessages,{MenuSelf=self,MessagesOnOff=true})
-Client.MenuOff=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Messages Off",Client.MenuMessages,self._MenuMessages,{MenuSelf=self,MessagesOnOff=false})
-Client.MenuTracking=MENU_GROUP:New(Client:GetGroup(),"Tracking",Client.MainMenu)
-Client.MenuTrackingToAll=MENU_GROUP_COMMAND:New(Client:GetGroup(),"To All",Client.MenuTracking,self._MenuMessages,{MenuSelf=self,TrackingToAll=true})
-Client.MenuTrackingToTarget=MENU_GROUP_COMMAND:New(Client:GetGroup(),"To Target",Client.MenuTracking,self._MenuMessages,{MenuSelf=self,TrackingToAll=false})
-Client.MenuTrackOn=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Tracking On",Client.MenuTracking,self._MenuMessages,{MenuSelf=self,TrackingOnOff=true})
-Client.MenuTrackOff=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Tracking Off",Client.MenuTracking,self._MenuMessages,{MenuSelf=self,TrackingOnOff=false})
-Client.MenuTrackIncrease=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Frequency Increase",Client.MenuTracking,self._MenuMessages,{MenuSelf=self,TrackingFrequency=-1})
-Client.MenuTrackDecrease=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Frequency Decrease",Client.MenuTracking,self._MenuMessages,{MenuSelf=self,TrackingFrequency=1})
-Client.MenuAlerts=MENU_GROUP:New(Client:GetGroup(),"Alerts",Client.MainMenu)
-Client.MenuAlertsToAll=MENU_GROUP_COMMAND:New(Client:GetGroup(),"To All",Client.MenuAlerts,self._MenuMessages,{MenuSelf=self,AlertsToAll=true})
-Client.MenuAlertsToTarget=MENU_GROUP_COMMAND:New(Client:GetGroup(),"To Target",Client.MenuAlerts,self._MenuMessages,{MenuSelf=self,AlertsToAll=false})
-Client.MenuHitsOn=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Hits On",Client.MenuAlerts,self._MenuMessages,{MenuSelf=self,AlertsHitsOnOff=true})
-Client.MenuHitsOff=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Hits Off",Client.MenuAlerts,self._MenuMessages,{MenuSelf=self,AlertsHitsOnOff=false})
-Client.MenuLaunchesOn=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Launches On",Client.MenuAlerts,self._MenuMessages,{MenuSelf=self,AlertsLaunchesOnOff=true})
-Client.MenuLaunchesOff=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Launches Off",Client.MenuAlerts,self._MenuMessages,{MenuSelf=self,AlertsLaunchesOnOff=false})
-Client.MenuDetails=MENU_GROUP:New(Client:GetGroup(),"Details",Client.MainMenu)
-Client.MenuDetailsDistanceOn=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Range On",Client.MenuDetails,self._MenuMessages,{MenuSelf=self,DetailsRangeOnOff=true})
-Client.MenuDetailsDistanceOff=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Range Off",Client.MenuDetails,self._MenuMessages,{MenuSelf=self,DetailsRangeOnOff=false})
-Client.MenuDetailsBearingOn=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Bearing On",Client.MenuDetails,self._MenuMessages,{MenuSelf=self,DetailsBearingOnOff=true})
-Client.MenuDetailsBearingOff=MENU_GROUP_COMMAND:New(Client:GetGroup(),"Bearing Off",Client.MenuDetails,self._MenuMessages,{MenuSelf=self,DetailsBearingOnOff=false})
-Client.MenuDistance=MENU_GROUP:New(Client:GetGroup(),"Set distance to plane",Client.MainMenu)
-Client.MenuDistance50=MENU_GROUP_COMMAND:New(Client:GetGroup(),"50 meter",Client.MenuDistance,self._MenuMessages,{MenuSelf=self,Distance=50/1000})
-Client.MenuDistance100=MENU_GROUP_COMMAND:New(Client:GetGroup(),"100 meter",Client.MenuDistance,self._MenuMessages,{MenuSelf=self,Distance=100/1000})
-Client.MenuDistance150=MENU_GROUP_COMMAND:New(Client:GetGroup(),"150 meter",Client.MenuDistance,self._MenuMessages,{MenuSelf=self,Distance=150/1000})
-Client.MenuDistance200=MENU_GROUP_COMMAND:New(Client:GetGroup(),"200 meter",Client.MenuDistance,self._MenuMessages,{MenuSelf=self,Distance=200/1000})
-else
-if Client.MainMenu then
-Client.MainMenu:Remove()
-end
-end
-local ClientID=Client:GetID()
-self:T(ClientID)
-if not self.TrackingMissiles[ClientID]then
-self.TrackingMissiles[ClientID]={}
-end
-self.TrackingMissiles[ClientID].Client=Client
-if not self.TrackingMissiles[ClientID].MissileData then
-self.TrackingMissiles[ClientID].MissileData={}
-end
-end
-function MISSILETRAINER:New(Distance,Briefing)
-local self=BASE:Inherit(self,BASE:New())
-self:F(Distance)
-if Briefing then
-self.Briefing=Briefing
-end
-self.Schedulers={}
-self.SchedulerID=0
-self.MessageInterval=2
-self.MessageLastTime=timer.getTime()
-self.Distance=Distance/1000
-self:HandleEvent(EVENTS.Shot)
-self.DBClients=SET_CLIENT:New():FilterStart()
-self.DBClients:ForEachClient(
-function(Client)
-self:F("ForEach:"..Client.UnitName)
-Client:Alive(self._Alive,self)
-end
-)
-self.MessagesOnOff=true
-self.TrackingToAll=false
-self.TrackingOnOff=true
-self.TrackingFrequency=3
-self.AlertsToAll=true
-self.AlertsHitsOnOff=true
-self.AlertsLaunchesOnOff=true
-self.DetailsRangeOnOff=true
-self.DetailsBearingOnOff=true
-self.MenusOnOff=true
-self.TrackingMissiles={}
-self.TrackingScheduler=SCHEDULER:New(self,self._TrackMissiles,{},0.5,0.05,0)
-return self
-end
-function MISSILETRAINER:InitMessagesOnOff(MessagesOnOff)
-self:F(MessagesOnOff)
-self.MessagesOnOff=MessagesOnOff
-if self.MessagesOnOff==true then
-MESSAGE:New("Messages ON",15,"Menu"):ToAll()
-else
-MESSAGE:New("Messages OFF",15,"Menu"):ToAll()
-end
-return self
-end
-function MISSILETRAINER:InitTrackingToAll(TrackingToAll)
-self:F(TrackingToAll)
-self.TrackingToAll=TrackingToAll
-if self.TrackingToAll==true then
-MESSAGE:New("Missile tracking to all players ON",15,"Menu"):ToAll()
-else
-MESSAGE:New("Missile tracking to all players OFF",15,"Menu"):ToAll()
-end
-return self
-end
-function MISSILETRAINER:InitTrackingOnOff(TrackingOnOff)
-self:F(TrackingOnOff)
-self.TrackingOnOff=TrackingOnOff
-if self.TrackingOnOff==true then
-MESSAGE:New("Missile tracking ON",15,"Menu"):ToAll()
-else
-MESSAGE:New("Missile tracking OFF",15,"Menu"):ToAll()
-end
-return self
-end
-function MISSILETRAINER:InitTrackingFrequency(TrackingFrequency)
-self:F(TrackingFrequency)
-self.TrackingFrequency=self.TrackingFrequency+TrackingFrequency
-if self.TrackingFrequency<0.5 then
-self.TrackingFrequency=0.5
-end
-if self.TrackingFrequency then
-MESSAGE:New("Missile tracking frequency is "..self.TrackingFrequency.." seconds.",15,"Menu"):ToAll()
-end
-return self
-end
-function MISSILETRAINER:InitAlertsToAll(AlertsToAll)
-self:F(AlertsToAll)
-self.AlertsToAll=AlertsToAll
-if self.AlertsToAll==true then
-MESSAGE:New("Alerts to all players ON",15,"Menu"):ToAll()
-else
-MESSAGE:New("Alerts to all players OFF",15,"Menu"):ToAll()
-end
-return self
-end
-function MISSILETRAINER:InitAlertsHitsOnOff(AlertsHitsOnOff)
-self:F(AlertsHitsOnOff)
-self.AlertsHitsOnOff=AlertsHitsOnOff
-if self.AlertsHitsOnOff==true then
-MESSAGE:New("Alerts Hits ON",15,"Menu"):ToAll()
-else
-MESSAGE:New("Alerts Hits OFF",15,"Menu"):ToAll()
-end
-return self
-end
-function MISSILETRAINER:InitAlertsLaunchesOnOff(AlertsLaunchesOnOff)
-self:F(AlertsLaunchesOnOff)
-self.AlertsLaunchesOnOff=AlertsLaunchesOnOff
-if self.AlertsLaunchesOnOff==true then
-MESSAGE:New("Alerts Launches ON",15,"Menu"):ToAll()
-else
-MESSAGE:New("Alerts Launches OFF",15,"Menu"):ToAll()
-end
-return self
-end
-function MISSILETRAINER:InitRangeOnOff(DetailsRangeOnOff)
-self:F(DetailsRangeOnOff)
-self.DetailsRangeOnOff=DetailsRangeOnOff
-if self.DetailsRangeOnOff==true then
-MESSAGE:New("Range display ON",15,"Menu"):ToAll()
-else
-MESSAGE:New("Range display OFF",15,"Menu"):ToAll()
-end
-return self
-end
-function MISSILETRAINER:InitBearingOnOff(DetailsBearingOnOff)
-self:F(DetailsBearingOnOff)
-self.DetailsBearingOnOff=DetailsBearingOnOff
-if self.DetailsBearingOnOff==true then
-MESSAGE:New("Bearing display OFF",15,"Menu"):ToAll()
-else
-MESSAGE:New("Bearing display OFF",15,"Menu"):ToAll()
-end
-return self
-end
-function MISSILETRAINER:InitMenusOnOff(MenusOnOff)
-self:F(MenusOnOff)
-self.MenusOnOff=MenusOnOff
-if self.MenusOnOff==true then
-MESSAGE:New("Menus are ENABLED (only when a player rejoins a slot)",15,"Menu"):ToAll()
-else
-MESSAGE:New("Menus are DISABLED",15,"Menu"):ToAll()
-end
-return self
-end
-function MISSILETRAINER._MenuMessages(MenuParameters)
-local self=MenuParameters.MenuSelf
-if MenuParameters.MessagesOnOff~=nil then
-self:InitMessagesOnOff(MenuParameters.MessagesOnOff)
-end
-if MenuParameters.TrackingToAll~=nil then
-self:InitTrackingToAll(MenuParameters.TrackingToAll)
-end
-if MenuParameters.TrackingOnOff~=nil then
-self:InitTrackingOnOff(MenuParameters.TrackingOnOff)
-end
-if MenuParameters.TrackingFrequency~=nil then
-self:InitTrackingFrequency(MenuParameters.TrackingFrequency)
-end
-if MenuParameters.AlertsToAll~=nil then
-self:InitAlertsToAll(MenuParameters.AlertsToAll)
-end
-if MenuParameters.AlertsHitsOnOff~=nil then
-self:InitAlertsHitsOnOff(MenuParameters.AlertsHitsOnOff)
-end
-if MenuParameters.AlertsLaunchesOnOff~=nil then
-self:InitAlertsLaunchesOnOff(MenuParameters.AlertsLaunchesOnOff)
-end
-if MenuParameters.DetailsRangeOnOff~=nil then
-self:InitRangeOnOff(MenuParameters.DetailsRangeOnOff)
-end
-if MenuParameters.DetailsBearingOnOff~=nil then
-self:InitBearingOnOff(MenuParameters.DetailsBearingOnOff)
-end
-if MenuParameters.Distance~=nil then
-self.Distance=MenuParameters.Distance
-MESSAGE:New("Hit detection distance set to "..(self.Distance*1000).." meters",15,"Menu"):ToAll()
-end
-end
-function MISSILETRAINER:OnEventShot(EVentData)
-self:F({EVentData})
-local TrainerSourceDCSUnit=EVentData.IniDCSUnit
-local TrainerSourceDCSUnitName=EVentData.IniDCSUnitName
-local TrainerWeapon=EVentData.Weapon
-local TrainerWeaponName=EVentData.WeaponName
-self:T("Missile Launched = "..TrainerWeaponName)
-local TrainerTargetDCSUnit=TrainerWeapon:getTarget()
-if TrainerTargetDCSUnit then
-local TrainerTargetDCSUnitName=Unit.getName(TrainerTargetDCSUnit)
-local TrainerTargetSkill=_DATABASE.Templates.Units[TrainerTargetDCSUnitName].Template.skill
-self:T(TrainerTargetDCSUnitName)
-local Client=self.DBClients:FindClient(TrainerTargetDCSUnitName)
-if Client then
-local TrainerSourceUnit=UNIT:Find(TrainerSourceDCSUnit)
-local TrainerTargetUnit=UNIT:Find(TrainerTargetDCSUnit)
-if self.MessagesOnOff==true and self.AlertsLaunchesOnOff==true then
-local Message=MESSAGE:New(
-string.format("%s launched a %s",
-TrainerSourceUnit:GetTypeName(),
-TrainerWeaponName
-)..self:_AddRange(Client,TrainerWeapon)..self:_AddBearing(Client,TrainerWeapon),5,"Launch Alert")
-if self.AlertsToAll then
-Message:ToAll()
-else
-Message:ToClient(Client)
-end
-end
-local ClientID=Client:GetID()
-self:T(ClientID)
-local MissileData={}
-MissileData.TrainerSourceUnit=TrainerSourceUnit
-MissileData.TrainerWeapon=TrainerWeapon
-MissileData.TrainerTargetUnit=TrainerTargetUnit
-MissileData.TrainerWeaponTypeName=TrainerWeapon:getTypeName()
-MissileData.TrainerWeaponLaunched=true
-table.insert(self.TrackingMissiles[ClientID].MissileData,MissileData)
-end
-else
-if(TrainerWeapon:getTypeName()=="9M311")then
-SCHEDULER:New(TrainerWeapon,TrainerWeapon.destroy,{},1)
-else
-end
-end
-end
-function MISSILETRAINER:_AddRange(Client,TrainerWeapon)
-local RangeText=""
-if self.DetailsRangeOnOff then
-local PositionMissile=TrainerWeapon:getPoint()
-local TargetVec3=Client:GetVec3()
-local Range=((PositionMissile.x-TargetVec3.x)^2+
-(PositionMissile.y-TargetVec3.y)^2+
-(PositionMissile.z-TargetVec3.z)^2
-)^0.5/1000
-RangeText=string.format(", at %4.2fkm",Range)
-end
-return RangeText
-end
-function MISSILETRAINER:_AddBearing(Client,TrainerWeapon)
-local BearingText=""
-if self.DetailsBearingOnOff then
-local PositionMissile=TrainerWeapon:getPoint()
-local TargetVec3=Client:GetVec3()
-self:T2({TargetVec3,PositionMissile})
-local DirectionVector={x=PositionMissile.x-TargetVec3.x,y=PositionMissile.y-TargetVec3.y,z=PositionMissile.z-TargetVec3.z}
-local DirectionRadians=math.atan2(DirectionVector.z,DirectionVector.x)
-if DirectionRadians<0 then
-DirectionRadians=DirectionRadians+2*math.pi
-end
-local DirectionDegrees=DirectionRadians*180/math.pi
-BearingText=string.format(", %d degrees",DirectionDegrees)
-end
-return BearingText
-end
-function MISSILETRAINER:_TrackMissiles()
-self:F2()
-local ShowMessages=false
-if self.MessagesOnOff and self.MessageLastTime+self.TrackingFrequency<=timer.getTime()then
-self.MessageLastTime=timer.getTime()
-ShowMessages=true
-end
-for ClientDataID,ClientData in pairs(self.TrackingMissiles)do
-local Client=ClientData.Client
-if Client and Client:IsAlive()then
-for MissileDataID,MissileData in pairs(ClientData.MissileData)do
-self:T3(MissileDataID)
-local TrainerSourceUnit=MissileData.TrainerSourceUnit
-local TrainerWeapon=MissileData.TrainerWeapon
-local TrainerTargetUnit=MissileData.TrainerTargetUnit
-local TrainerWeaponTypeName=MissileData.TrainerWeaponTypeName
-local TrainerWeaponLaunched=MissileData.TrainerWeaponLaunched
-if Client and Client:IsAlive()and TrainerSourceUnit and TrainerSourceUnit:IsAlive()and TrainerWeapon and TrainerWeapon:isExist()and TrainerTargetUnit and TrainerTargetUnit:IsAlive()then
-local PositionMissile=TrainerWeapon:getPosition().p
-local TargetVec3=Client:GetVec3()
-local Distance=((PositionMissile.x-TargetVec3.x)^2+
-(PositionMissile.y-TargetVec3.y)^2+
-(PositionMissile.z-TargetVec3.z)^2
-)^0.5/1000
-if Distance<=self.Distance then
-TrainerWeapon:destroy()
-if self.MessagesOnOff==true and self.AlertsHitsOnOff==true then
-self:T("killed")
-local Message=MESSAGE:New(
-string.format("%s launched by %s killed %s",
-TrainerWeapon:getTypeName(),
-TrainerSourceUnit:GetTypeName(),
-TrainerTargetUnit:GetPlayerName()
-),15,"Hit Alert")
-if self.AlertsToAll==true then
-Message:ToAll()
-else
-Message:ToClient(Client)
-end
-MissileData=nil
-table.remove(ClientData.MissileData,MissileDataID)
-self:T(ClientData.MissileData)
-end
-end
-else
-if not(TrainerWeapon and TrainerWeapon:isExist())then
-if self.MessagesOnOff==true and self.AlertsLaunchesOnOff==true then
-local Message=MESSAGE:New(
-string.format("%s launched by %s self destructed!",
-TrainerWeaponTypeName,
-TrainerSourceUnit:GetTypeName()
-),5,"Tracking")
-if self.AlertsToAll==true then
-Message:ToAll()
-else
-Message:ToClient(Client)
-end
-end
-MissileData=nil
-table.remove(ClientData.MissileData,MissileDataID)
-self:T(ClientData.MissileData)
-end
-end
-end
-else
-self.TrackingMissiles[ClientDataID]=nil
-end
-end
-if ShowMessages==true and self.MessagesOnOff==true and self.TrackingOnOff==true then
-for ClientDataID,ClientData in pairs(self.TrackingMissiles)do
-local Client=ClientData.Client
-ClientData.MessageToClient=""
-ClientData.MessageToAll=""
-for TrackingDataID,TrackingData in pairs(self.TrackingMissiles)do
-for MissileDataID,MissileData in pairs(TrackingData.MissileData)do
-local TrainerSourceUnit=MissileData.TrainerSourceUnit
-local TrainerWeapon=MissileData.TrainerWeapon
-local TrainerTargetUnit=MissileData.TrainerTargetUnit
-local TrainerWeaponTypeName=MissileData.TrainerWeaponTypeName
-local TrainerWeaponLaunched=MissileData.TrainerWeaponLaunched
-if Client and Client:IsAlive()and TrainerSourceUnit and TrainerSourceUnit:IsAlive()and TrainerWeapon and TrainerWeapon:isExist()and TrainerTargetUnit and TrainerTargetUnit:IsAlive()then
-if ShowMessages==true then
-local TrackingTo
-TrackingTo=string.format(" -> %s",
-TrainerWeaponTypeName
-)
-if ClientDataID==TrackingDataID then
-if ClientData.MessageToClient==""then
-ClientData.MessageToClient="Missiles to You:\n"
-end
-ClientData.MessageToClient=ClientData.MessageToClient..TrackingTo..self:_AddRange(ClientData.Client,TrainerWeapon)..self:_AddBearing(ClientData.Client,TrainerWeapon).."\n"
-else
-if self.TrackingToAll==true then
-if ClientData.MessageToAll==""then
-ClientData.MessageToAll="Missiles to other Players:\n"
-end
-ClientData.MessageToAll=ClientData.MessageToAll..TrackingTo..self:_AddRange(ClientData.Client,TrainerWeapon)..self:_AddBearing(ClientData.Client,TrainerWeapon).." ( "..TrainerTargetUnit:GetPlayerName().." )\n"
-end
-end
-end
-end
-end
-end
-if ClientData.MessageToClient~=""or ClientData.MessageToAll~=""then
-local Message=MESSAGE:New(ClientData.MessageToClient..ClientData.MessageToAll,1,"Tracking"):ToClient(Client)
-end
-end
-end
-return true
-end
-MOVEMENT={
-ClassName="MOVEMENT",
-}
-function MOVEMENT:New(MovePrefixes,MoveMaximum)
-local self=BASE:Inherit(self,BASE:New())
-self:F({MovePrefixes,MoveMaximum})
-if type(MovePrefixes)=='table'then
-self.MovePrefixes=MovePrefixes
-else
-self.MovePrefixes={MovePrefixes}
-end
-self.MoveCount=0
-self.MoveMaximum=MoveMaximum
-self.AliveUnits=0
-self.MoveUnits={}
-self:HandleEvent(EVENTS.Birth)
-self:ScheduleStart()
-return self
-end
-function MOVEMENT:ScheduleStart()
-self:F()
-self.MoveFunction=SCHEDULER:New(self,self._Scheduler,{},1,120)
-end
-function MOVEMENT:ScheduleStop()
-self:F()
-end
-function MOVEMENT:OnEventBirth(EventData)
-self:F({EventData})
-if timer.getTime0()0 then
-local MoveProbability=(self.MoveMaximum*100)/self.AliveUnits
-self:T('Move Probability = '..MoveProbability)
-for MovementUnitName,MovementGroupName in pairs(self.MoveUnits)do
-local MovementGroup=Group.getByName(MovementGroupName)
-if MovementGroup and MovementGroup:isExist()then
-local MoveOrStop=math.random(1,100)
-self:T('MoveOrStop = '..MoveOrStop)
-if MoveOrStop<=MoveProbability then
-self:T('Group continues moving = '..MovementGroupName)
-trigger.action.groupContinueMoving(MovementGroup)
-else
-self:T('Group stops moving = '..MovementGroupName)
-trigger.action.groupStopMoving(MovementGroup)
-end
-else
-self.MoveUnits[MovementUnitName]=nil
-end
-end
-end
-return true
-end
-PSEUDOATC={
-ClassName="PSEUDOATC",
-group={},
-Debug=false,
-mdur=30,
-mrefresh=120,
-talt=3,
-chatty=true,
-eventsmoose=true,
-reportplayername=false,
-}
-PSEUDOATC.id="PseudoATC | "
-PSEUDOATC.version="0.10.5"
-function PSEUDOATC:New()
-local self=BASE:Inherit(self,BASE:New())
-self:E(PSEUDOATC.id..string.format("PseudoATC version %s",PSEUDOATC.version))
-return self
-end
-function PSEUDOATC:Start()
-self:F()
-self:I(PSEUDOATC.id.."Starting PseudoATC")
-self:HandleEvent(EVENTS.Birth,self._OnBirth)
-self:HandleEvent(EVENTS.Land,self._PlayerLanded)
-self:HandleEvent(EVENTS.Takeoff,self._PlayerTakeOff)
-self:HandleEvent(EVENTS.PlayerLeaveUnit,self._PlayerLeft)
-self:HandleEvent(EVENTS.Crash,self._PlayerLeft)
-end
-function PSEUDOATC:DebugOn()
-self.Debug=true
-end
-function PSEUDOATC:DebugOff()
-self.Debug=false
-end
-function PSEUDOATC:ChattyOn()
-self.chatty=true
-end
-function PSEUDOATC:ChattyOff()
-self.chatty=false
-end
-function PSEUDOATC:SetMessageDuration(duration)
-self.mdur=duration or 30
-end
-function PSEUDOATC:SetReportPlayername()
-self.reportplayername=true
-return self
-end
-function PSEUDOATC:SetMenuRefresh(interval)
-self.mrefresh=interval or 120
-end
-function PSEUDOATC:SetEventsMoose(switch)
-self.eventsmoose=switch
-end
-function PSEUDOATC:SetReportAltInterval(interval)
-self.talt=interval or 3
-end
-function PSEUDOATC:_OnBirth(EventData)
-self:F({EventData=EventData})
-local _unitName=EventData.IniUnitName
-local _unit=EventData.IniUnit
-local _playername=EventData.IniPlayerName
-if _unit and _playername then
-self:PlayerEntered(_unit)
-end
-end
-function PSEUDOATC:_PlayerLeft(EventData)
-self:F({EventData=EventData})
-local _unitName=EventData.IniUnitName
-local _unit=EventData.IniUnit
-local _playername=EventData.IniPlayerName
-if _unit and _playername then
-self:PlayerLeft(_unit)
-end
-end
-function PSEUDOATC:_PlayerLanded(EventData)
-self:F({EventData=EventData})
-local _unitName=EventData.IniUnitName
-local _unit=EventData.IniUnit
-local _playername=EventData.IniPlayerName
-local _base=nil
-local _baseName=nil
-if EventData.place then
-_base=EventData.place
-_baseName=EventData.place:getName()
-end
-if _unit and _playername and _base then
-self:PlayerLanded(_unit,_baseName)
-end
-end
-function PSEUDOATC:_PlayerTakeOff(EventData)
-self:F({EventData=EventData})
-local _unitName=EventData.IniUnitName
-local _unit=EventData.IniUnit
-local _playername=EventData.IniPlayerName
-local _base=nil
-local _baseName=nil
-if EventData.place then
-_base=EventData.place
-_baseName=EventData.place:getName()
-end
-if _unit and _playername and _base then
-self:PlayerTakeOff(_unit,_baseName)
-end
-end
-function PSEUDOATC:PlayerEntered(unit)
-self:F2({unit=unit})
-local group=unit:GetGroup()
-local GID=group:GetID()
-local GroupName=group:GetName()
-local PlayerName=unit:GetPlayerName()
-local UnitName=unit:GetName()
-local CallSign=unit:GetCallsign()
-local UID=unit:GetDCSObject():getID()
-if not self.group[GID]then
-self.group[GID]={}
-self.group[GID].player={}
-end
-self.group[GID].player[UID]={}
-self.group[GID].player[UID].group=group
-self.group[GID].player[UID].unit=unit
-self.group[GID].player[UID].groupname=GroupName
-self.group[GID].player[UID].unitname=UnitName
-self.group[GID].player[UID].playername=PlayerName
-self.group[GID].player[UID].callsign=CallSign
-self.group[GID].player[UID].waypoints=group:GetTaskRoute()
-local text=string.format("Player %s entered unit %s of group %s (id=%d).",PlayerName,UnitName,GroupName,GID)
-self:T(PSEUDOATC.id..text)
-MESSAGE:New(text,30):ToAllIf(self.Debug)
-local countPlayerInGroup=0
-for _ in pairs(self.group[GID].player)do countPlayerInGroup=countPlayerInGroup+1 end
-if countPlayerInGroup<=1 then
-self.group[GID].menu_main=missionCommands.addSubMenuForGroup(GID,"Pseudo ATC")
-end
-self:MenuCreatePlayer(GID,UID)
-self:LocalAirports(GID,UID)
-self:MenuAirports(GID,UID)
-self:MenuWaypoints(GID,UID)
-self.group[GID].player[UID].scheduler,self.group[GID].player[UID].schedulerid=SCHEDULER:New(nil,self.MenuRefresh,{self,GID,UID},self.mrefresh,self.mrefresh)
-end
-function PSEUDOATC:PlayerLanded(unit,place)
-self:F2({unit=unit,place=place})
-local group=unit:GetGroup()
-local GID=group:GetID()
-local UID=unit:GetDCSObject():getID()
-local PlayerName=unit:GetPlayerName()or"Ghost"
-local UnitName=unit:GetName()or"Ghostplane"
-local GroupName=group:GetName()or"Ghostgroup"
-if self.Debug then
-local text=string.format("Player %s in unit %s of group %s landed at %s.",PlayerName,UnitName,GroupName,place)
-self:T(PSEUDOATC.id..text)
-MESSAGE:New(text,30):ToAllIf(self.Debug)
-end
-self:AltitudeTimerStop(GID,UID)
-if place and self.chatty then
-local text=string.format("Touchdown! Welcome to %s pilot %s. Have a nice day!",place,PlayerName)
-MESSAGE:New(text,self.mdur):ToGroup(group)
-end
-end
-function PSEUDOATC:PlayerTakeOff(unit,place)
-self:F2({unit=unit,place=place})
-local group=unit:GetGroup()
-local PlayerName=unit:GetPlayerName()or"Ghost"
-local UnitName=unit:GetName()or"Ghostplane"
-local GroupName=group:GetName()or"Ghostgroup"
-local CallSign=unit:GetCallsign()or"Ghost11"
-if self.Debug then
-local text=string.format("Player %s in unit %s of group %s took off at %s.",PlayerName,UnitName,GroupName,place)
-self:T(PSEUDOATC.id..text)
-MESSAGE:New(text,30):ToAllIf(self.Debug)
-end
-if place and self.chatty then
-local text=string.format("%s, %s, you are airborne. Have a safe trip!",place,CallSign)
-if self.reportplayername then
-text=string.format("%s, %s, you are airborne. Have a safe trip!",place,PlayerName)
-end
-MESSAGE:New(text,self.mdur):ToGroup(group)
-end
-end
-function PSEUDOATC:PlayerLeft(unit)
-self:F({unit=unit})
-local group=unit:GetGroup()
-local GID=group:GetID()
-local UID=unit:GetDCSObject():getID()
-if self.group[GID]and self.group[GID].player and self.group[GID].player[UID]then
-local PlayerName=self.group[GID].player[UID].playername
-local CallSign=self.group[GID].player[UID].callsign
-local UnitName=self.group[GID].player[UID].unitname
-local GroupName=self.group[GID].player[UID].groupname
-local text=string.format("Player %s (callsign %s) of group %s just left unit %s.",PlayerName,CallSign,GroupName,UnitName)
-self:T(PSEUDOATC.id..text)
-MESSAGE:New(text,30):ToAllIf(self.Debug)
-if self.group[GID].player[UID].schedulerid then
-self.group[GID].player[UID].scheduler:Stop(self.group[GID].player[UID].schedulerid)
-end
-self:AltitudeTimerStop(GID,UID)
-if self.group[GID].player[UID].menu_own then
-missionCommands.removeItemForGroup(GID,self.group[GID].player[UID].menu_own)
-end
-local countPlayerInGroup=0
-for _ in pairs(self.group[GID].player)do countPlayerInGroup=countPlayerInGroup+1 end
-if self.group[GID].menu_main and countPlayerInGroup==1 then
-missionCommands.removeItemForGroup(GID,self.group[GID].menu_main)
-end
-self.group[GID].player[UID]=nil
-end
-end
-function PSEUDOATC:MenuRefresh(GID,UID)
-self:F({GID=GID,UID=UID})
-local text=string.format("Refreshing menues for player %s in group %s.",self.group[GID].player[UID].playername,self.group[GID].player[UID].groupname)
-self:T(PSEUDOATC.id..text)
-MESSAGE:New(text,30):ToAllIf(self.Debug)
-self:MenuClear(GID,UID)
-self:LocalAirports(GID,UID)
-self:MenuAirports(GID,UID)
-self:MenuWaypoints(GID,UID)
-end
-function PSEUDOATC:MenuCreatePlayer(GID,UID)
-self:F({GID=GID,UID=UID})
-local PlayerName=self.group[GID].player[UID].playername
-self.group[GID].player[UID].menu_own=missionCommands.addSubMenuForGroup(GID,PlayerName,self.group[GID].menu_main)
-end
-function PSEUDOATC:MenuClear(GID,UID)
-self:F({GID=GID,UID=UID})
-local text=string.format("Clearing menus for player %s in group %s.",self.group[GID].player[UID].playername,self.group[GID].player[UID].groupname)
-self:T(PSEUDOATC.id..text)
-MESSAGE:New(text,30):ToAllIf(self.Debug)
-if self.group[GID].player[UID].menu_airports then
-missionCommands.removeItemForGroup(GID,self.group[GID].player[UID].menu_airports)
-self.group[GID].player[UID].menu_airports=nil
-else
-self:T2(PSEUDOATC.id.."No airports to clear menus.")
-end
-if self.group[GID].player[UID].menu_waypoints then
-missionCommands.removeItemForGroup(GID,self.group[GID].player[UID].menu_waypoints)
-self.group[GID].player[UID].menu_waypoints=nil
-end
-if self.group[GID].player[UID].menu_reportalt then
-missionCommands.removeItemForGroup(GID,self.group[GID].player[UID].menu_reportalt)
-self.group[GID].player[UID].menu_reportalt=nil
-end
-if self.group[GID].player[UID].menu_requestalt then
-missionCommands.removeItemForGroup(GID,self.group[GID].player[UID].menu_requestalt)
-self.group[GID].player[UID].menu_requestalt=nil
-end
-end
-function PSEUDOATC:MenuAirports(GID,UID)
-self:F({GID=GID,UID=UID})
-self.group[GID].player[UID].menu_airports=missionCommands.addSubMenuForGroup(GID,"Local Airports",self.group[GID].player[UID].menu_own)
-local i=0
-for _,airport in pairs(self.group[GID].player[UID].airports)do
-i=i+1
-if i>10 then
-break
-end
-local name=airport.name
-local d=airport.distance
-local pos=AIRBASE:FindByName(name):GetCoordinate()
-local submenu=missionCommands.addSubMenuForGroup(GID,name,self.group[GID].player[UID].menu_airports)
-missionCommands.addCommandForGroup(GID,"Weather Report",submenu,self.ReportWeather,self,GID,UID,pos,name)
-missionCommands.addCommandForGroup(GID,"Request BR",submenu,self.ReportBR,self,GID,UID,pos,name)
-self:T(string.format(PSEUDOATC.id.."Creating airport menu item %s for ID %d",name,GID))
-end
-end
-function PSEUDOATC:MenuWaypoints(GID,UID)
-self:F({GID=GID,UID=UID})
-local callsign=self.group[GID].player[UID].callsign
-self:T(PSEUDOATC.id..string.format("Creating waypoint menu for %s (ID %d).",callsign,GID))
-if#self.group[GID].player[UID].waypoints>0 then
-self.group[GID].player[UID].menu_waypoints=missionCommands.addSubMenuForGroup(GID,"Waypoints",self.group[GID].player[UID].menu_own)
-local j=0
-for i,wp in pairs(self.group[GID].player[UID].waypoints)do
-j=j+1
-if j>10 then
-break
-end
-local pos=COORDINATE:New(wp.x,wp.alt,wp.y)
-local name=string.format("Waypoint %d",i-1)
-if wp.name and wp.name~=""then
-name=string.format("Waypoint %s",wp.name)
-end
-local submenu=missionCommands.addSubMenuForGroup(GID,name,self.group[GID].player[UID].menu_waypoints)
-missionCommands.addCommandForGroup(GID,"Weather Report",submenu,self.ReportWeather,self,GID,UID,pos,name)
-missionCommands.addCommandForGroup(GID,"Request BR",submenu,self.ReportBR,self,GID,UID,pos,name)
-end
-end
-self.group[GID].player[UID].menu_reportalt=missionCommands.addCommandForGroup(GID,"Talk me down",self.group[GID].player[UID].menu_own,self.AltidudeTimerToggle,self,GID,UID)
-self.group[GID].player[UID].menu_requestalt=missionCommands.addCommandForGroup(GID,"Request altitude",self.group[GID].player[UID].menu_own,self.ReportHeight,self,GID,UID)
-end
-function PSEUDOATC:ReportWeather(GID,UID,position,location)
-self:F({GID=GID,UID=UID,position=position,location=location})
-local settings=_DATABASE:GetPlayerSettings(self.group[GID].player[UID].playername)or _SETTINGS
-local text=string.format("Local weather at %s:\n",location)
-local Pqnh=position:GetPressure(0)
-local Pqfe=position:GetPressure()
-local hPa2inHg=0.0295299830714
-local hPa2mmHg=0.7500615613030
-local _Pqnh=string.format("%.2f inHg",Pqnh*hPa2inHg)
-local _Pqfe=string.format("%.2f inHg",Pqfe*hPa2inHg)
-if settings:IsMetric()then
-_Pqnh=string.format("%.1f mmHg",Pqnh*hPa2mmHg)
-_Pqfe=string.format("%.1f mmHg",Pqfe*hPa2mmHg)
-end
-text=text..string.format("QFE %.1f hPa = %s.\n",Pqfe,_Pqfe)
-text=text..string.format("QNH %.1f hPa = %s.\n",Pqnh,_Pqnh)
-local T=position:GetTemperature()
-local _T=string.format('%d°F',UTILS.CelsiusToFahrenheit(T))
-if settings:IsMetric()then
-_T=string.format('%d°C',T)
-end
-local text=text..string.format("Temperature %s\n",_T)
-local Dir,Vel=position:GetWind()
-local Bn,Bd=UTILS.BeaufortScale(Vel)
-local Ds=string.format('%03d°',Dir)
-local Vs=string.format("%.1f knots",UTILS.MpsToKnots(Vel))
-if settings:IsMetric()then
-Vs=string.format('%.1f m/s',Vel)
-end
-local text=text..string.format("%s, Wind from %s at %s (%s).",self.group[GID].player[UID].playername,Ds,Vs,Bd)
-self:_DisplayMessageToGroup(self.group[GID].player[UID].unit,text,self.mdur,true)
-end
-function PSEUDOATC:ReportBR(GID,UID,position,location)
-self:F({GID=GID,UID=UID,position=position,location=location})
-local unit=self.group[GID].player[UID].unit
-local coord=unit:GetCoordinate()
-local angle=coord:HeadingTo(position)
-local range=coord:Get2DDistance(position)
-local Bs=string.format('%03d°',angle)
-local settings=_DATABASE:GetPlayerSettings(self.group[GID].player[UID].playername)or _SETTINGS
-local Rs=string.format("%.1f NM",UTILS.MetersToNM(range))
-if settings:IsMetric()then
-Rs=string.format("%.1f km",range/1000)
-end
-local text=string.format("%s: Bearing %s, Range %s.",location,Bs,Rs)
-self:_DisplayMessageToGroup(self.group[GID].player[UID].unit,text,self.mdur,true)
-end
-function PSEUDOATC:ReportHeight(GID,UID,dt,_clear)
-self:F({GID=GID,UID=UID,dt=dt})
-local dt=dt or self.mdur
-if _clear==nil then
-_clear=false
-end
-local function get_AGL(p)
-local agl=0
-local vec2={x=p.x,y=p.z}
-local ground=land.getHeight(vec2)
-local agl=p.y-ground
-return agl
-end
-local unit=self.group[GID].player[UID].unit
-if unit and unit:IsAlive()then
-local position=unit:GetCoordinate()
-local height=get_AGL(position)
-local callsign=unit:GetCallsign()
-local PlayerName=self.group[GID].player[UID].playername
-local settings=_DATABASE:GetPlayerSettings(self.group[GID].player[UID].playername)or _SETTINGS
-local Hs=string.format("%d ft",UTILS.MetersToFeet(height))
-if settings:IsMetric()then
-Hs=string.format("%d m",height)
-end
-local _text=string.format("%s, your altitude is %s AGL.",callsign,Hs)
-if self.reportplayername then
-_text=string.format("%s, your altitude is %s AGL.",PlayerName,Hs)
-end
-if _clear==false then
-_text=_text..string.format(" FL%03d.",position.y/30.48)
-end
-self:_DisplayMessageToGroup(self.group[GID].player[UID].unit,_text,dt,_clear)
-return height
-end
-return 0
-end
-function PSEUDOATC:AltidudeTimerToggle(GID,UID)
-self:F({GID=GID,UID=UID})
-if self.group[GID].player[UID].altimerid then
-self:AltitudeTimerStop(GID,UID)
-else
-self:AltitudeTimeStart(GID,UID)
-end
-end
-function PSEUDOATC:AltitudeTimeStart(GID,UID)
-self:F({GID=GID,UID=UID})
-self:T(PSEUDOATC.id..string.format("Starting altitude report timer for player ID %d.",UID))
-self.group[GID].player[UID].altimer,self.group[GID].player[UID].altimerid=SCHEDULER:New(nil,self.ReportHeight,{self,GID,UID,1,true},1,3)
-end
-function PSEUDOATC:AltitudeTimerStop(GID,UID)
-self:F({GID=GID,UID=UID})
-self:T(PSEUDOATC.id..string.format("Stopping altitude report timer for player ID %d.",UID))
-if self.group[GID].player[UID].altimerid then
-self.group[GID].player[UID].altimer:Stop(self.group[GID].player[UID].altimerid)
-end
-self.group[GID].player[UID].altimer=nil
-self.group[GID].player[UID].altimerid=nil
-end
-function PSEUDOATC:LocalAirports(GID,UID)
-self:F({GID=GID,UID=UID})
-self.group[GID].player[UID].airports=nil
-self.group[GID].player[UID].airports={}
-local pos=self.group[GID].player[UID].unit:GetCoordinate()
-for i=0,2 do
-local airports=coalition.getAirbases(i)
-for _,airbase in pairs(airports)do
-local name=airbase:getName()
-local a=AIRBASE:FindByName(name)
-if a then
-local q=a:GetCoordinate()
-local d=q:Get2DDistance(pos)
-table.insert(self.group[GID].player[UID].airports,{distance=d,name=name})
-end
-end
-end
-local function compare(a,b)
-return a.distance1 then
-target.target:PatrolZones({self.rangezone},target.speed*0.75,ENUMS.Formation.Vehicle.OffRoad)
-end
-end
-if self.rangecontrolfreq and not self.useSRS then
-self.rangecontrol=RADIOQUEUE:New(self.rangecontrolfreq,nil,self.rangename)
-self.rangecontrol.schedonce=true
-self.rangecontrol:SetDigit(0,RANGE.Sound.RC0.filename,RANGE.Sound.RC0.duration,self.soundpath)
-self.rangecontrol:SetDigit(1,RANGE.Sound.RC1.filename,RANGE.Sound.RC1.duration,self.soundpath)
-self.rangecontrol:SetDigit(2,RANGE.Sound.RC2.filename,RANGE.Sound.RC2.duration,self.soundpath)
-self.rangecontrol:SetDigit(3,RANGE.Sound.RC3.filename,RANGE.Sound.RC3.duration,self.soundpath)
-self.rangecontrol:SetDigit(4,RANGE.Sound.RC4.filename,RANGE.Sound.RC4.duration,self.soundpath)
-self.rangecontrol:SetDigit(5,RANGE.Sound.RC5.filename,RANGE.Sound.RC5.duration,self.soundpath)
-self.rangecontrol:SetDigit(6,RANGE.Sound.RC6.filename,RANGE.Sound.RC6.duration,self.soundpath)
-self.rangecontrol:SetDigit(7,RANGE.Sound.RC7.filename,RANGE.Sound.RC7.duration,self.soundpath)
-self.rangecontrol:SetDigit(8,RANGE.Sound.RC8.filename,RANGE.Sound.RC8.duration,self.soundpath)
-self.rangecontrol:SetDigit(9,RANGE.Sound.RC9.filename,RANGE.Sound.RC9.duration,self.soundpath)
-self.rangecontrol:SetSenderCoordinate(self.location)
-self.rangecontrol:SetSenderUnitName(self.rangecontrolrelayname)
-self.rangecontrol:Start(1,0.1)
-if self.instructorfreq and not self.useSRS then
-self.instructor=RADIOQUEUE:New(self.instructorfreq,nil,self.rangename)
-self.instructor.schedonce=true
-self.instructor:SetDigit(0,RANGE.Sound.IR0.filename,RANGE.Sound.IR0.duration,self.soundpath)
-self.instructor:SetDigit(1,RANGE.Sound.IR1.filename,RANGE.Sound.IR1.duration,self.soundpath)
-self.instructor:SetDigit(2,RANGE.Sound.IR2.filename,RANGE.Sound.IR2.duration,self.soundpath)
-self.instructor:SetDigit(3,RANGE.Sound.IR3.filename,RANGE.Sound.IR3.duration,self.soundpath)
-self.instructor:SetDigit(4,RANGE.Sound.IR4.filename,RANGE.Sound.IR4.duration,self.soundpath)
-self.instructor:SetDigit(5,RANGE.Sound.IR5.filename,RANGE.Sound.IR5.duration,self.soundpath)
-self.instructor:SetDigit(6,RANGE.Sound.IR6.filename,RANGE.Sound.IR6.duration,self.soundpath)
-self.instructor:SetDigit(7,RANGE.Sound.IR7.filename,RANGE.Sound.IR7.duration,self.soundpath)
-self.instructor:SetDigit(8,RANGE.Sound.IR8.filename,RANGE.Sound.IR8.duration,self.soundpath)
-self.instructor:SetDigit(9,RANGE.Sound.IR9.filename,RANGE.Sound.IR9.duration,self.soundpath)
-self.instructor:SetSenderCoordinate(self.location)
-self.instructor:SetSenderUnitName(self.instructorrelayname)
-self.instructor:Start(1,0.1)
-end
-end
-if self.autosave then
-self:Load()
-end
-if self.Debug then
-self:_MarkTargetsOnMap()
-self:_SmokeBombTargets()
-self:_SmokeStrafeTargets()
-self:_SmokeStrafeTargetBoxes()
-self.rangezone:SmokeZone(SMOKECOLOR.White)
-end
-self:__Status(-60)
-end
-function RANGE:SetMaxStrafeAlt(maxalt)
-self.strafemaxalt=maxalt or RANGE.Defaults.strafemaxalt
-return self
-end
-function RANGE:SetBombtrackTimestep(dt)
-self.dtBombtrack=dt or RANGE.Defaults.dtBombtrack
-return self
-end
-function RANGE:SetMessageTimeDuration(time)
-self.Tmsg=time or RANGE.Defaults.Tmsg
-return self
-end
-function RANGE:SetAutosaveOn()
-self.autosave=true
-return self
-end
-function RANGE:SetAutosaveOff()
-self.autosave=false
-return self
-end
-function RANGE:SetTargetSheet(path,prefix)
-if io then
-self.targetsheet=true
-self.targetpath=path
-self.targetprefix=prefix
-else
-self:E(self.lid.."ERROR: io is not desanitized. Cannot save target sheet.")
-end
-return self
-end
-function RANGE:SetFunkManOn(Port,Host)
-self.funkmanSocket=SOCKET:New(Port,Host)
-return self
-end
-function RANGE:SetMessageToExaminer(examinergroupname,exclusively)
-self.examinergroupname=examinergroupname
-self.examinerexclusive=exclusively
-return self
-end
-function RANGE:SetDisplayedMaxPlayerResults(nmax)
-self.ndisplayresult=nmax or RANGE.Defaults.ndisplayresult
-return self
-end
-function RANGE:SetRangeRadius(radius)
-self.rangeradius=radius*1000 or RANGE.Defaults.rangeradius
-return self
-end
-function RANGE:SetDefaultPlayerSmokeBomb(switch)
-if switch==true or switch==nil then
-self.defaultsmokebomb=true
-else
-self.defaultsmokebomb=false
-end
-return self
-end
-function RANGE:SetBombtrackThreshold(distance)
-self.BombtrackThreshold=(distance or 25)*1000
-return self
-end
-function RANGE:SetRangeLocation(coordinate)
-self.location=coordinate
-return self
-end
-function RANGE:SetRangeZone(zone)
-self.rangezone=zone
-return self
-end
-function RANGE:SetBombTargetSmokeColor(colorid)
-self.BombSmokeColor=colorid or SMOKECOLOR.Red
-return self
-end
-function RANGE:SetScoreBombDistance(distance)
-self.scorebombdistance=distance or 1000
-return self
-end
-function RANGE:SetStrafeTargetSmokeColor(colorid)
-self.StrafeSmokeColor=colorid or SMOKECOLOR.Green
-return self
-end
-function RANGE:SetStrafePitSmokeColor(colorid)
-self.StrafePitSmokeColor=colorid or SMOKECOLOR.White
-return self
-end
-function RANGE:SetSmokeTimeDelay(delay)
-self.TdelaySmoke=delay or RANGE.Defaults.TdelaySmoke
-return self
-end
-function RANGE:DebugON()
-self.Debug=true
-return self
-end
-function RANGE:DebugOFF()
-self.Debug=false
-return self
-end
-function RANGE:SetMessagesOFF()
-self.messages=false
-return self
-end
-function RANGE:SetMessagesON()
-self.messages=true
-return self
-end
-function RANGE:TrackBombsON()
-self.trackbombs=true
-return self
-end
-function RANGE:TrackBombsOFF()
-self.trackbombs=false
-return self
-end
-function RANGE:TrackRocketsON()
-self.trackrockets=true
-return self
-end
-function RANGE:TrackRocketsOFF()
-self.trackrockets=false
-return self
-end
-function RANGE:TrackMissilesON()
-self.trackmissiles=true
-return self
-end
-function RANGE:TrackMissilesOFF()
-self.trackmissiles=false
-return self
-end
-function RANGE:SetSRS(PathToSRS,Port,Coalition,Frequency,Modulation,Volume,PathToGoogleKey)
-if PathToSRS or MSRS.path then
-self.useSRS=true
-self.controlmsrs=MSRS:New(PathToSRS or MSRS.path,Frequency or 256,Modulation or radio.modulation.AM,Volume or 1.0)
-self.controlmsrs:SetPort(Port or MSRS.port)
-self.controlmsrs:SetCoalition(Coalition or coalition.side.BLUE)
-self.controlmsrs:SetLabel("RANGEC")
-self.controlsrsQ=MSRSQUEUE:New("CONTROL")
-self.instructmsrs=MSRS:New(PathToSRS or MSRS.path,Frequency or 305,Modulation or radio.modulation.AM,Volume or 1.0)
-self.instructmsrs:SetPort(Port or MSRS.port)
-self.instructmsrs:SetCoalition(Coalition or coalition.side.BLUE)
-self.instructmsrs:SetLabel("RANGEI")
-self.instructsrsQ=MSRSQUEUE:New("INSTRUCT")
-if PathToGoogleKey then
-self.controlmsrs:SetGoogle(PathToGoogleKey)
-self.instructmsrs:SetGoogle(PathToGoogleKey)
-end
-else
-self:E(self.lid..string.format("ERROR: No SRS path specified!"))
-end
-return self
-end
-function RANGE:SetSRSRangeControl(frequency,modulation,voice,culture,gender,relayunitname)
-if not self.instructmsrs then
-self:E(self.lid.."Use myrange:SetSRS() once first before using myrange:SetSRSRangeControl!")
-return self
-end
-self.rangecontrolfreq=frequency or 256
-self.controlmsrs:SetFrequencies(self.rangecontrolfreq)
-self.controlmsrs:SetModulations(modulation or radio.modulation.AM)
-self.controlmsrs:SetVoice(voice)
-self.controlmsrs:SetCulture(culture or"en-US")
-self.controlmsrs:SetGender(gender or"female")
-self.rangecontrol=true
-if relayunitname then
-local unit=UNIT:FindByName(relayunitname)
-local Coordinate=unit:GetCoordinate()
-self.rangecontrolrelayname=relayunitname
-end
-return self
-end
-function RANGE:SetSRSRangeInstructor(frequency,modulation,voice,culture,gender,relayunitname)
-if not self.instructmsrs then
-self:E(self.lid.."Use myrange:SetSRS() once first before using myrange:SetSRSRangeInstructor!")
-return self
-end
-self.instructorfreq=frequency or 305
-self.instructmsrs:SetFrequencies(self.instructorfreq)
-self.instructmsrs:SetModulations(modulation or radio.modulation.AM)
-self.instructmsrs:SetVoice(voice)
-self.instructmsrs:SetCulture(culture or"en-US")
-self.instructmsrs:SetGender(gender or"male")
-self.instructor=true
-if relayunitname then
-local unit=UNIT:FindByName(relayunitname)
-local Coordinate=unit:GetCoordinate()
-self.instructmsrs:SetCoordinate(Coordinate)
-self.instructorrelayname=relayunitname
-end
-return self
-end
-function RANGE:SetRangeControl(frequency,relayunitname)
-self.rangecontrolfreq=frequency or 256
-self.rangecontrolrelayname=relayunitname
-return self
-end
-function RANGE:SetInstructorRadio(frequency,relayunitname)
-self.instructorfreq=frequency or 305
-self.instructorrelayname=relayunitname
-return self
-end
-function RANGE:SetSoundfilesPath(path)
-self.soundpath=tostring(path or"Range Soundfiles/")
-self:I(self.lid..string.format("Setting sound files path to %s",self.soundpath))
-return self
-end
-function RANGE:AddStrafePit(targetnames,boxlength,boxwidth,heading,inverseheading,goodpass,foulline)
-self:F({targetnames=targetnames,boxlength=boxlength,boxwidth=boxwidth,heading=heading,inverseheading=inverseheading,goodpass=goodpass,foulline=foulline})
-if type(targetnames)~="table"then
-targetnames={targetnames}
-end
-local _targets={}
-local center=nil
-local ntargets=0
-for _i,_name in ipairs(targetnames)do
-local _isstatic=self:_CheckStatic(_name)
-local unit=nil
-if _isstatic==true then
-self:T(self.lid..string.format("Adding STATIC object %s as strafe target #%d.",_name,_i))
-unit=STATIC:FindByName(_name,false)
-elseif _isstatic==false then
-self:T(self.lid..string.format("Adding UNIT object %s as strafe target #%d.",_name,_i))
-unit=UNIT:FindByName(_name)
-else
-local text=string.format("ERROR! Could not find ANY strafe target object with name %s.",_name)
-self:E(self.lid..text)
-end
-if unit then
-table.insert(_targets,unit)
-if center==nil then
-center=unit
-end
-ntargets=ntargets+1
-end
-end
-if ntargets==0 then
-local text=string.format("ERROR! No strafe target could be found when calling RANGE:AddStrafePit() for range %s",self.rangename)
-self:E(self.lid..text)
-return
-end
-local l=boxlength or RANGE.Defaults.boxlength
-local w=(boxwidth or RANGE.Defaults.boxwidth)/2
-local heading=heading or center:GetHeading()
-if inverseheading~=nil then
-if inverseheading then
-heading=heading-180
-end
-end
-if heading<0 then
-heading=heading+360
-end
-if heading>360 then
-heading=heading-360
-end
-goodpass=goodpass or RANGE.Defaults.goodpass
-foulline=foulline or RANGE.Defaults.foulline
-local Ccenter=center:GetCoordinate()
-local _name=center:GetName()
-local p={}
-p[#p+1]=Ccenter:Translate(w,heading+90)
-p[#p+1]=p[#p]:Translate(l,heading)
-p[#p+1]=p[#p]:Translate(2*w,heading-90)
-p[#p+1]=p[#p]:Translate(-l,heading)
-local pv2={}
-for i,p in ipairs(p)do
-pv2[i]={x=p.x,y=p.z}
-end
-local _polygon=ZONE_POLYGON_BASE:New(_name,pv2)
-local st={}
-st.name=_name
-st.polygon=_polygon
-st.coordinate=Ccenter
-st.goodPass=goodpass
-st.targets=_targets
-st.foulline=foulline
-st.smokepoints=p
-st.heading=heading
-table.insert(self.strafeTargets,st)
-local text=string.format("Adding new strafe target %s with %d targets: heading = %03d, box_L = %.1f, box_W = %.1f, goodpass = %d, foul line = %.1f",_name,ntargets,heading,l,w,goodpass,foulline)
-self:T(self.lid..text)
-return self
-end
-function RANGE:AddStrafePitGroup(group,boxlength,boxwidth,heading,inverseheading,goodpass,foulline)
-self:F({group=group,boxlength=boxlength,boxwidth=boxwidth,heading=heading,inverseheading=inverseheading,goodpass=goodpass,foulline=foulline})
-if group and group:IsAlive()then
-local _units=group:GetUnits()
-local _names={}
-for _,_unit in ipairs(_units)do
-local _unit=_unit
-if _unit and _unit:IsAlive()then
-local _name=_unit:GetName()
-table.insert(_names,_name)
-end
-end
-self:AddStrafePit(_names,boxlength,boxwidth,heading,inverseheading,goodpass,foulline)
-end
-return self
-end
-function RANGE:AddBombingTargets(targetnames,goodhitrange,randommove)
-self:F({targetnames=targetnames,goodhitrange=goodhitrange,randommove=randommove})
-if type(targetnames)~="table"then
-targetnames={targetnames}
-end
-goodhitrange=goodhitrange or RANGE.Defaults.goodhitrange
-for _,name in pairs(targetnames)do
-local _isstatic=self:_CheckStatic(name)
-if _isstatic==true then
-local _static=STATIC:FindByName(name)
-self:T2(self.lid..string.format("Adding static bombing target %s with hit range %d.",name,goodhitrange,false))
-self:AddBombingTargetUnit(_static,goodhitrange)
-elseif _isstatic==false then
-local _unit=UNIT:FindByName(name)
-self:T2(self.lid..string.format("Adding unit bombing target %s with hit range %d.",name,goodhitrange,randommove))
-self:AddBombingTargetUnit(_unit,goodhitrange,randommove)
-else
-self:E(self.lid..string.format("ERROR! Could not find bombing target %s.",name))
-end
-end
-return self
-end
-function RANGE:AddBombingTargetUnit(unit,goodhitrange,randommove)
-self:F({unit=unit,goodhitrange=goodhitrange,randommove=randommove})
-local name=unit:GetName()
-local _isstatic=self:_CheckStatic(name)
-goodhitrange=goodhitrange or RANGE.Defaults.goodhitrange
-if randommove==nil or _isstatic==true then
-randommove=false
-end
-if _isstatic==true then
-self:I(self.lid..string.format("Adding STATIC bombing target %s with good hit range %d. Random move = %s.",name,goodhitrange,tostring(randommove)))
-elseif _isstatic==false then
-self:I(self.lid..string.format("Adding UNIT bombing target %s with good hit range %d. Random move = %s.",name,goodhitrange,tostring(randommove)))
-else
-self:E(self.lid..string.format("ERROR! No bombing target with name %s could be found. Carefully check all UNIT and STATIC names defined in the mission editor!",name))
-end
-local speed=0
-if _isstatic==false then
-speed=self:_GetSpeed(unit)
-end
-local target={}
-target.name=name
-target.target=unit
-target.goodhitrange=goodhitrange
-target.move=randommove
-target.speed=speed
-target.coordinate=unit:GetCoordinate()
-if _isstatic then
-target.type=RANGE.TargetType.STATIC
-else
-target.type=RANGE.TargetType.UNIT
-end
-table.insert(self.bombingTargets,target)
-return self
-end
-function RANGE:AddBombingTargetCoordinate(coord,name,goodhitrange)
-local target={}
-target.name=name or"Bomb Target"
-target.target=nil
-target.goodhitrange=goodhitrange or RANGE.Defaults.goodhitrange
-target.move=false
-target.speed=0
-target.coordinate=coord
-target.type=RANGE.TargetType.COORD
-table.insert(self.bombingTargets,target)
-return self
-end
-function RANGE:AddBombingTargetScenery(scenery,goodhitrange)
-local name=scenery:GetName()
-goodhitrange=goodhitrange or RANGE.Defaults.goodhitrange
-if name then
-self:I(self.lid..string.format("Adding SCENERY bombing target %s with good hit range %d",name,goodhitrange))
-else
-self:E(self.lid..string.format("ERROR! No bombing target with name %s could be found!",name))
-end
-local target={}
-target.name=name
-target.target=scenery
-target.goodhitrange=goodhitrange
-target.move=false
-target.speed=0
-target.coordinate=scenery:GetCoordinate()
-target.type=RANGE.TargetType.SCENERY
-table.insert(self.bombingTargets,target)
-return self
-end
-function RANGE:AddBombingTargetGroup(group,goodhitrange,randommove)
-self:F({group=group,goodhitrange=goodhitrange,randommove=randommove})
-if group then
-local _units=group:GetUnits()
-for _,_unit in pairs(_units)do
-if _unit and _unit:IsAlive()then
-self:AddBombingTargetUnit(_unit,goodhitrange,randommove)
-end
-end
-end
-return self
-end
-function RANGE:GetFoullineDistance(namepit,namefoulline)
-self:F({namepit=namepit,namefoulline=namefoulline})
-local _staticpit=self:_CheckStatic(namepit)
-local _staticfoul=self:_CheckStatic(namefoulline)
-local pit=nil
-if _staticpit==true then
-pit=STATIC:FindByName(namepit,false)
-elseif _staticpit==false then
-pit=UNIT:FindByName(namepit)
-else
-self:E(self.lid..string.format("ERROR! Pit object %s could not be found in GetFoullineDistance function. Check the name in the ME.",namepit))
-end
-local foul=nil
-if _staticfoul==true then
-foul=STATIC:FindByName(namefoulline,false)
-elseif _staticfoul==false then
-foul=UNIT:FindByName(namefoulline)
-else
-self:E(self.lid..string.format("ERROR! Foul line object %s could not be found in GetFoullineDistance function. Check the name in the ME.",namefoulline))
-end
-local fouldist=0
-if pit~=nil and foul~=nil then
-fouldist=pit:GetCoordinate():Get2DDistance(foul:GetCoordinate())
-else
-self:E(self.lid..string.format("ERROR! Foul line distance could not be determined. Check pit object name %s and foul line object name %s in the ME.",namepit,namefoulline))
-end
-self:T(self.lid..string.format("Foul line distance = %.1f m.",fouldist))
-return fouldist
-end
-function RANGE:OnEventBirth(EventData)
-self:F({eventbirth=EventData})
-if not EventData.IniPlayerName then return end
-local _unitName=EventData.IniUnitName
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-self:T3(self.lid.."BIRTH: unit = "..tostring(EventData.IniUnitName))
-self:T3(self.lid.."BIRTH: group = "..tostring(EventData.IniGroupName))
-self:T3(self.lid.."BIRTH: player = "..tostring(_playername))
-if _unit and _playername then
-local _uid=_unit:GetID()
-local _group=_unit:GetGroup()
-local _gid=_group:GetID()
-local _callsign=_unit:GetCallsign()
-local text=string.format("Player %s, callsign %s entered unit %s (UID %d) of group %s (GID %d)",_playername,_callsign,_unitName,_uid,_group:GetName(),_gid)
-self:T(self.lid..text)
-self.strafeStatus[_uid]=nil
-if self.Coalition then
-if EventData.IniCoalition==self.Coalition then
-self:ScheduleOnce(0.1,self._AddF10Commands,self,_unitName)
-end
-else
-self:ScheduleOnce(0.1,self._AddF10Commands,self,_unitName)
-end
-self.PlayerSettings[_playername]={}
-self.PlayerSettings[_playername].smokebombimpact=self.defaultsmokebomb
-self.PlayerSettings[_playername].flaredirecthits=false
-self.PlayerSettings[_playername].smokecolor=SMOKECOLOR.Blue
-self.PlayerSettings[_playername].flarecolor=FLARECOLOR.Red
-self.PlayerSettings[_playername].delaysmoke=true
-self.PlayerSettings[_playername].messages=true
-self.PlayerSettings[_playername].client=CLIENT:FindByName(_unitName,nil,true)
-self.PlayerSettings[_playername].unitname=_unitName
-self.PlayerSettings[_playername].unit=_unit
-self.PlayerSettings[_playername].playername=_playername
-self.PlayerSettings[_playername].airframe=EventData.IniUnit:GetTypeName()
-self.PlayerSettings[_playername].inzone=false
-if self.planes[_uid]~=true then
-self.timerCheckZone=TIMER:New(self._CheckInZone,self,EventData.IniUnitName):Start(1,1)
-self.planes[_uid]=true
-end
-end
-end
-function RANGE:OnEventHit(EventData)
-self:F({eventhit=EventData})
-self:T3(self.lid.."HIT: Ini unit = "..tostring(EventData.IniUnitName))
-self:T3(self.lid.."HIT: Ini group = "..tostring(EventData.IniGroupName))
-self:T3(self.lid.."HIT: Tgt target = "..tostring(EventData.TgtUnitName))
-local _unitName=EventData.IniUnitName
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit==nil or _playername==nil then
-return
-end
-local _unitID=_unit:GetID()
-local target=EventData.TgtUnit
-local targetname=EventData.TgtUnitName
-local _currentTarget=self.strafeStatus[_unitID]
-if _currentTarget and target:IsAlive()then
-local playerPos=_unit:GetCoordinate()
-local targetPos=target:GetCoordinate()
-for _,_target in pairs(_currentTarget.zone.targets)do
-if _target and _target:IsAlive()and _target:GetName()==targetname then
-local dist=playerPos:Get2DDistance(targetPos)
-if dist>_currentTarget.zone.foulline then
-_currentTarget.hits=_currentTarget.hits+1
-if _unit and _playername and self.PlayerSettings[_playername].flaredirecthits then
-targetPos:Flare(self.PlayerSettings[_playername].flarecolor)
-end
-else
-if _currentTarget.pastfoulline==false and _unit and _playername then
-local _d=_currentTarget.zone.foulline
-local text=string.format("%s, Invalid hit!\nYou already passed foul line distance of %d m for target %s.",self:_myname(_unitName),_d,targetname)
-if self.useSRS then
-local ttstext=string.format("%s, Invalid hit! You already passed foul line distance of %d meters for target %s.",self:_myname(_unitName),_d,targetname)
-self.controlsrsQ:NewTransmission(ttstext,nil,self.controlmsrs,nil,2)
-end
-self:_DisplayMessageToGroup(_unit,text)
-self:T2(self.lid..text)
-_currentTarget.pastfoulline=true
-end
-end
-end
-end
-end
-for _,_bombtarget in pairs(self.bombingTargets)do
-local _target=_bombtarget.target
-if _target and _target:IsAlive()and _bombtarget.name==targetname then
-if _unit and _playername then
-if self.PlayerSettings[_playername].flaredirecthits then
-local targetPos=_target:GetCoordinate()
-targetPos:Flare(self.PlayerSettings[_playername].flarecolor)
-end
-end
-end
-end
-end
-function RANGE._OnImpact(weapon,self,playerData,attackHdg,attackAlt,attackVel)
-local _closetTarget=nil
-local _distance=nil
-local _closeCoord=nil
-local _hitquality="POOR"
-local _callsign=self:_myname(playerData.unitname)
-local _playername=playerData.playername
-local _unit=playerData.unit
-local impactcoord=weapon:GetImpactCoordinate()
-local insidezone=self.rangezone:IsCoordinateInZone(impactcoord)
-if playerData.smokebombimpact and insidezone then
-if playerData.delaysmoke then
-timer.scheduleFunction(self._DelayedSmoke,{coord=impactcoord,color=playerData.smokecolor},timer.getTime()+self.TdelaySmoke)
-else
-impactcoord:Smoke(playerData.smokecolor)
-end
-end
-for _,_bombtarget in pairs(self.bombingTargets)do
-local bombtarget=_bombtarget
-local targetcoord=self:_GetBombTargetCoordinate(_bombtarget)
-if targetcoord then
-local _temp=impactcoord:Get2DDistance(targetcoord)
-if _distance==nil or _temp<_distance then
-_distance=_temp
-_closetTarget=bombtarget
-_closeCoord=targetcoord
-if _distance<=1.53 then
-_hitquality="SHACK"
-elseif _distance<=0.5*bombtarget.goodhitrange then
-_hitquality="EXCELLENT"
-elseif _distance<=bombtarget.goodhitrange then
-_hitquality="GOOD"
-elseif _distance<=2*bombtarget.goodhitrange then
-_hitquality="INEFFECTIVE"
-else
-_hitquality="POOR"
-end
-end
-end
-end
-if _distance and _distance<=self.scorebombdistance then
-if not self.bombPlayerResults[_playername]then
-self.bombPlayerResults[_playername]={}
-end
-local _results=self.bombPlayerResults[_playername]
-local result={}
-result.command=SOCKET.DataType.BOMBRESULT
-result.name=_closetTarget.name or"unknown"
-result.distance=_distance
-result.radial=_closeCoord:HeadingTo(impactcoord)
-result.weapon=weapon:GetTypeName()or"unknown"
-result.quality=_hitquality
-result.player=playerData.playername
-result.time=timer.getAbsTime()
-result.clock=UTILS.SecondsToClock(result.time,true)
-result.midate=UTILS.GetDCSMissionDate()
-result.theatre=env.mission.theatre
-result.airframe=playerData.airframe
-result.roundsFired=0
-result.roundsHit=0
-result.roundsQuality="N/A"
-result.rangename=self.rangename
-result.attackHdg=attackHdg
-result.attackVel=attackVel
-result.attackAlt=attackAlt
-result.date=os and os.date()or"n/a"
-table.insert(_results,result)
-self:Impact(result,playerData)
-elseif insidezone then
-local _message=string.format("%s, weapon impacted too far from nearest range target (>%.1f km). No score!",_callsign,self.scorebombdistance/1000)
-if self.useSRS then
-local ttstext=string.format("%s, weapon impacted too far from nearest range target, mor than %.1f kilometer. No score!",_callsign,self.scorebombdistance/1000)
-self.controlsrsQ:NewTransmission(ttstext,nil,self.controlmsrs,nil,2)
-end
-self:_DisplayMessageToGroup(_unit,_message,nil,false)
-if self.rangecontrol then
-if self.useSRS then
-self.controlsrsQ:NewTransmission(_message,nil,self.controlmsrs,nil,1)
-else
-self.rangecontrol:NewTransmission(RANGE.Sound.RCWeaponImpactedTooFar.filename,RANGE.Sound.RCWeaponImpactedTooFar.duration,self.soundpath,nil,nil,_message,self.subduration)
-end
-end
-else
-self:T(self.lid.."Weapon impacted outside range zone.")
-end
-end
-function RANGE:OnEventShot(EventData)
-self:F({eventshot=EventData})
-if EventData.Weapon==nil or EventData.IniDCSUnit==nil or EventData.IniPlayerName==nil then
-return
-end
-local weapon=WEAPON:New(EventData.weapon)
-local _track=(weapon:IsBomb()and self.trackbombs)or(weapon:IsRocket()and self.trackrockets)or(weapon:IsMissile()and self.trackmissiles)
-local _unitName=EventData.IniUnitName
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-local dPR=self.BombtrackThreshold*2
-if _unit and _playername then
-dPR=_unit:GetCoordinate():Get2DDistance(self.location)
-self:T(self.lid..string.format("Range %s, player %s, player-range distance = %d km.",self.rangename,_playername,dPR/1000))
-end
-if _track and dPR<=self.BombtrackThreshold and _unit and _playername then
-local playerData=self.PlayerSettings[_playername]
-local attackHdg=_unit:GetHeading()
-local attackAlt=_unit:GetHeight()
-attackAlt=UTILS.MetersToFeet(attackAlt)
-local attackVel=_unit:GetVelocityKNOTS()
-self:T(self.lid..string.format("RANGE %s: Tracking %s - %s.",self.rangename,weapon:GetTypeName(),weapon:GetName()))
-weapon:SetFuncImpact(RANGE._OnImpact,self,playerData,attackHdg,attackAlt,attackVel)
-self:T(self.lid..string.format("Range %s, player %s: Tracking of weapon starts in 0.1 seconds.",self.rangename,_playername))
-weapon:StartTrack(0.1)
-end
-end
-function RANGE:onafterStatus(From,Event,To)
-if self.verbose>0 then
-local fsmstate=self:GetState()
-local text=string.format("Range status: %s",fsmstate)
-if self.instructor then
-local alive="N/A"
-if self.instructorrelayname then
-local relay=UNIT:FindByName(self.instructorrelayname)
-if relay then
-alive=tostring(relay:IsAlive())
-end
-end
-text=text..string.format(", Instructor %.3f MHz (Relay=%s alive=%s)",self.instructorfreq,tostring(self.instructorrelayname),alive)
-end
-if self.rangecontrol then
-local alive="N/A"
-if self.rangecontrolrelayname then
-local relay=UNIT:FindByName(self.rangecontrolrelayname)
-if relay then
-alive=tostring(relay:IsAlive())
-end
-end
-text=text..string.format(", Control %.3f MHz (Relay=%s alive=%s)",self.rangecontrolfreq,tostring(self.rangecontrolrelayname),alive)
-end
-self:I(self.lid..text)
-end
-self:_CheckPlayers()
-self:__Status(-10)
-end
-function RANGE:onafterEnterRange(From,Event,To,player)
-if self.instructor and self.rangecontrol then
-if self.useSRS then
-local text=string.format("You entered the bombing range. For hit assessment, contact the range controller at %.3f MHz",self.rangecontrolfreq)
-local ttstext=string.format("You entered the bombing range. For hit assessment, contact the range controller at %.3f mega hertz.",self.rangecontrolfreq)
-local group=player.client:GetGroup()
-self.instructsrsQ:NewTransmission(ttstext,nil,self.instructmsrs,nil,1,{group},text,10)
-else
-local RF=UTILS.Split(string.format("%.3f",self.rangecontrolfreq),".")
-self.instructor:NewTransmission(RANGE.Sound.IREnterRange.filename,RANGE.Sound.IREnterRange.duration,self.soundpath)
-self.instructor:Number2Transmission(RF[1])
-if tonumber(RF[2])>0 then
-self.instructor:NewTransmission(RANGE.Sound.IRDecimal.filename,RANGE.Sound.IRDecimal.duration,self.soundpath)
-self.instructor:Number2Transmission(RF[2])
-end
-self.instructor:NewTransmission(RANGE.Sound.IRMegaHertz.filename,RANGE.Sound.IRMegaHertz.duration,self.soundpath)
-end
-end
-end
-function RANGE:onafterExitRange(From,Event,To,player)
-if self.instructor then
-if self.useSRS then
-local text="You left the bombing range zone. "
-local r=math.random(5)
-if r==1 then
-text=text.."Have a nice day!"
-elseif r==2 then
-text=text.."Take care and bye bye!"
-elseif r==3 then
-text=text.."Talk to you soon!"
-elseif r==4 then
-text=text.."See you in two weeks!"
-elseif r==5 then
-text=text.."!"
-end
-self.instructsrsQ:NewTransmission(text,nil,self.instructmsrs,nil,1,{player.client:GetGroup()},text,10)
-else
-self.instructor:NewTransmission(RANGE.Sound.IRExitRange.filename,RANGE.Sound.IRExitRange.duration,self.soundpath)
-end
-end
-end
-function RANGE:onafterImpact(From,Event,To,result,player)
-local targetname=nil
-if#self.bombingTargets>1 then
-targetname=result.name
-end
-local text=string.format("%s, impact %03d° for %d ft (%d m)",player.playername,result.radial,UTILS.MetersToFeet(result.distance),result.distance)
-if targetname then
-text=text..string.format(" from bulls of target %s.",targetname)
-else
-text=text.."."
-end
-text=text..string.format(" %s hit.",result.quality)
-if self.rangecontrol then
-if self.useSRS then
-local group=player.client:GetGroup()
-self.controlsrsQ:NewTransmission(text,nil,self.controlmsrs,nil,1,{group},text,10)
-else
-self.rangecontrol:NewTransmission(RANGE.Sound.RCImpact.filename,RANGE.Sound.RCImpact.duration,self.soundpath,nil,nil,text,self.subduration)
-self.rangecontrol:Number2Transmission(string.format("%03d",result.radial),nil,0.1)
-self.rangecontrol:NewTransmission(RANGE.Sound.RCDegrees.filename,RANGE.Sound.RCDegrees.duration,self.soundpath)
-self.rangecontrol:NewTransmission(RANGE.Sound.RCFor.filename,RANGE.Sound.RCFor.duration,self.soundpath)
-self.rangecontrol:Number2Transmission(string.format("%d",UTILS.MetersToFeet(result.distance)))
-self.rangecontrol:NewTransmission(RANGE.Sound.RCFeet.filename,RANGE.Sound.RCFeet.duration,self.soundpath)
-if result.quality=="POOR"then
-self.rangecontrol:NewTransmission(RANGE.Sound.RCPoorHit.filename,RANGE.Sound.RCPoorHit.duration,self.soundpath,nil,0.5)
-elseif result.quality=="INEFFECTIVE"then
-self.rangecontrol:NewTransmission(RANGE.Sound.RCIneffectiveHit.filename,RANGE.Sound.RCIneffectiveHit.duration,self.soundpath,nil,0.5)
-elseif result.quality=="GOOD"then
-self.rangecontrol:NewTransmission(RANGE.Sound.RCGoodHit.filename,RANGE.Sound.RCGoodHit.duration,self.soundpath,nil,0.5)
-elseif result.quality=="EXCELLENT"then
-self.rangecontrol:NewTransmission(RANGE.Sound.RCExcellentHit.filename,RANGE.Sound.RCExcellentHit.duration,self.soundpath,nil,0.5)
-end
-end
-end
-if player.unitname and not self.useSRS then
-local unit=UNIT:FindByName(player.unitname)
-self:_DisplayMessageToGroup(unit,text,nil,true)
-self:T(self.lid..text)
-end
-if self.autosave then
-self:Save()
-end
-if self.funkmanSocket then
-self.funkmanSocket:SendTable(result)
-end
-end
-function RANGE:onafterStrafeResult(From,Event,To,player,result)
-if self.funkmanSocket then
-self.funkmanSocket:SendTable(result)
-end
-end
-function RANGE:onbeforeSave(From,Event,To)
-if io and lfs then
-return true
-else
-self:E(self.lid..string.format("WARNING: io and/or lfs not desanitized. Cannot save player results."))
-return false
-end
-end
-function RANGE:onafterSave(From,Event,To)
-local function _savefile(filename,data)
-local f=io.open(filename,"wb")
-if f then
-f:write(data)
-f:close()
-self:I(self.lid..string.format("Saving player results to file %s",tostring(filename)))
-else
-self:E(self.lid..string.format("ERROR: Could not save results to file %s",tostring(filename)))
-end
-end
-local path=lfs.writedir()..[[Logs\]]
-local filename=path..string.format("RANGE-%s_BombingResults.csv",self.rangename)
-local scores="Name,Pass,Target,Distance,Radial,Quality,Weapon,Airframe,Mission Time"
-for playername,results in pairs(self.bombPlayerResults)do
-for i,_result in pairs(results)do
-local result=_result
-local distance=result.distance
-local weapon=result.weapon
-local target=result.name
-local radial=result.radial
-local quality=result.quality
-local time=UTILS.SecondsToClock(result.time,true)
-local airframe=result.airframe
-local date=result.date or"n/a"
-scores=scores..string.format("\n%s,%d,%s,%.2f,%03d,%s,%s,%s,%s,%s",playername,i,target,distance,radial,quality,weapon,airframe,time,date)
-end
-end
-_savefile(filename,scores)
-end
-function RANGE:onbeforeLoad(From,Event,To)
-if io and lfs then
-return true
-else
-self:E(self.lid..string.format("WARNING: io and/or lfs not desanitized. Cannot load player results."))
-return false
-end
-end
-function RANGE:onafterLoad(From,Event,To)
-local function _loadfile(filename)
-local f=io.open(filename,"rb")
-if f then
-local data=f:read("*all")
-f:close()
-return data
-else
-self:E(self.lid..string.format("WARNING: Could not load player results from file %s. File might not exist just yet.",tostring(filename)))
-return nil
-end
-end
-local path=lfs.writedir()..[[Logs\]]
-local filename=path..string.format("RANGE-%s_BombingResults.csv",self.rangename)
-local text=string.format("Loading player bomb results from file %s",filename)
-self:I(self.lid..text)
-local data=_loadfile(filename)
-if data then
-local results=UTILS.Split(data,"\n")
-table.remove(results,1)
-self.bombPlayerResults={}
-for _,_result in pairs(results)do
-local resultdata=UTILS.Split(_result,",")
-local result={}
-local playername=resultdata[1]
-result.player=playername
-result.name=tostring(resultdata[3])
-result.distance=tonumber(resultdata[4])
-result.radial=tonumber(resultdata[5])
-result.quality=tostring(resultdata[6])
-result.weapon=tostring(resultdata[7])
-result.airframe=tostring(resultdata[8])
-result.time=UTILS.ClockToSeconds(resultdata[9]or"00:00:00")
-result.date=resultdata[10]or"n/a"
-self.bombPlayerResults[playername]=self.bombPlayerResults[playername]or{}
-table.insert(self.bombPlayerResults[playername],result)
-end
-end
-end
-function RANGE:_SaveTargetSheet(_playername,result)
-local function _savefile(filename,data)
-local f=io.open(filename,"wb")
-if f then
-f:write(data)
-f:close()
-else
-env.info("RANGEBOSS EDIT - could not save target sheet to file")
-end
-end
-local path=self.targetpath
-if lfs then
-path=path or lfs.writedir()..[[Logs\]]
-end
-local filename=nil
-for i=1,9999 do
-if self.targetprefix then
-filename=string.format("%s_%s-%04d.csv",self.targetprefix,result.airframe,i)
-else
-local name=UTILS.ReplaceIllegalCharacters(_playername,"_")
-filename=string.format("RANGERESULTS-%s_Targetsheet-%s-%04d.csv",self.rangename,name,i)
-end
-if path~=nil then
-filename=path.."\\"..filename
-end
-local _exists=UTILS.FileExists(filename)
-if not _exists then
-break
-end
-end
-local data="Name,Target,Rounds Fired,Rounds Hit,Rounds Quality,Airframe,Mission Time,OS Time\n"
-local target=result.name
-local airframe=result.airframe
-local roundsFired=result.roundsFired
-local roundsHit=result.roundsHit
-local strafeResult=result.roundsQuality
-local time=UTILS.SecondsToClock(result.time)
-local date="n/a"
-if os then
-date=os.date()
-end
-data=data..string.format("%s,%s,%d,%d,%s,%s,%s,%s",_playername,target,roundsFired,roundsHit,strafeResult,airframe,time,date)
-_savefile(filename,data)
-end
-function RANGE._DelayedSmoke(_args)
-_args.coord:Smoke(_args.color)
-end
-function RANGE:_DisplayMyStrafePitResults(_unitName)
-self:F(_unitName)
-local _unit,_playername,_multiplayer=self:_GetPlayerUnitAndName(_unitName)
-if _unit and _playername then
-local _message=string.format("My Top %d Strafe Pit Results:\n",self.ndisplayresult)
-local _results=self.strafePlayerResults[_playername]
-if _results==nil then
-_message=string.format("%s: No Score yet.",_playername)
-else
-local _sort=function(a,b)
-return a.roundsHit>b.roundsHit
-end
-table.sort(_results,_sort)
-local _bestMsg=""
-local _count=1
-for _,_result in pairs(_results)do
-local result=_result
-_message=_message..string.format("\n[%d] Hits %d - %s - %s",_count,result.roundsHit,result.name,result.roundsQuality)
-if _bestMsg==""then
-_bestMsg=string.format("Hits %d - %s - %s",result.roundsHit,result.name,result.roundsQuality)
-end
-if _count==self.ndisplayresult then
-break
-end
-_count=_count+1
-end
-_message=_message.."\n\nBEST: ".._bestMsg
-end
-self:_DisplayMessageToGroup(_unit,_message,nil,true,true,_multiplayer)
-end
-end
-function RANGE:_DisplayStrafePitResults(_unitName)
-self:F(_unitName)
-local _unit,_playername,_multiplayer=self:_GetPlayerUnitAndName(_unitName)
-if _unit and _playername then
-local _playerResults={}
-local _message=string.format("Strafe Pit Results - Top %d Players:\n",self.ndisplayresult)
-for _playerName,_results in pairs(self.strafePlayerResults)do
-local _best=nil
-for _,_result in pairs(_results)do
-if _best==nil or _result.roundsHit>_best.roundsHit then
-_best=_result
-end
-end
-if _best~=nil then
-local text=string.format("%s: Hits %i - %s - %s",_playerName,_best.roundsHit,_best.name,_best.roundsQuality)
-table.insert(_playerResults,{msg=text,hits=_best.roundsHit})
-end
-end
-local _sort=function(a,b)
-return a.hits>b.hits
-end
-table.sort(_playerResults,_sort)
-for _i=1,math.min(#_playerResults,self.ndisplayresult)do
-_message=_message..string.format("\n[%d] %s",_i,_playerResults[_i].msg)
-end
-if#_playerResults<1 then
-_message=_message.."No player scored yet."
-end
-self:_DisplayMessageToGroup(_unit,_message,nil,true,true,_multiplayer)
-end
-end
-function RANGE:_DisplayMyBombingResults(_unitName)
-self:F(_unitName)
-local _unit,_playername,_multiplayer=self:_GetPlayerUnitAndName(_unitName)
-if _unit and _playername then
-local _message=string.format("My Top %d Bombing Results:\n",self.ndisplayresult)
-local _results=self.bombPlayerResults[_playername]
-if _results==nil then
-_message=_playername..": No Score yet."
-else
-local _sort=function(a,b)
-return a.distance180 then
-heading=heading-180
-else
-heading=heading+180
-end
-local mycoord=coord:ToStringA2G(_unit,_settings)
-_text=_text..string.format("\n- %s: heading %03d°\n%s",_strafepit.name,heading,mycoord)
-end
-self:_DisplayMessageToGroup(_unit,_text,nil,true,true,_multiplayer)
-end
-end
-function RANGE:_DisplayRangeWeather(_unitname)
-self:F(_unitname)
-local unit,playername,_multiplayer=self:_GetPlayerUnitAndName(_unitname)
-if unit and playername then
-local text=""
-local coord=unit:GetCoordinate()
-if self.location then
-local position=self.location
-local T=position:GetTemperature()
-local P=position:GetPressure()
-local Wd,Ws=position:GetWind()
-local Bn,Bd=UTILS.BeaufortScale(Ws)
-local WD=string.format('%03d°',Wd)
-local Ts=string.format("%d°C",T)
-local hPa2inHg=0.0295299830714
-local hPa2mmHg=0.7500615613030
-local settings=_DATABASE:GetPlayerSettings(playername)or _SETTINGS
-local tT=string.format("%d°C",T)
-local tW=string.format("%.1f m/s",Ws)
-local tP=string.format("%.1f mmHg",P*hPa2mmHg)
-if settings:IsImperial()then
-tW=string.format("%.1f knots",UTILS.MpsToKnots(Ws))
-tP=string.format("%.2f inHg",P*hPa2inHg)
-end
-text=text..string.format("Weather Report at %s:\n",self.rangename)
-text=text..string.format("--------------------------------------------------\n")
-text=text..string.format("Temperature %s\n",tT)
-text=text..string.format("Wind from %s at %s (%s)\n",WD,tW,Bd)
-text=text..string.format("QFE %.1f hPa = %s",P,tP)
-else
-text=string.format("No range location defined for range %s.",self.rangename)
-end
-self:_DisplayMessageToGroup(unit,text,nil,true,true,_multiplayer)
-self:T2(self.lid..text)
-else
-self:T(self.lid..string.format("ERROR! Could not find player unit in RangeInfo! Name = %s",_unitname))
-end
-end
-function RANGE:_CheckPlayers()
-for playername,_playersettings in pairs(self.PlayerSettings)do
-local playersettings=_playersettings
-local unitname=playersettings.unitname
-local unit=UNIT:FindByName(unitname)
-if unit and unit:IsAlive()then
-if unit:IsInZone(self.rangezone)then
-if not playersettings.inzone then
-playersettings.inzone=true
-self:EnterRange(playersettings)
-end
-else
-if playersettings.inzone==true then
-playersettings.inzone=false
-self:ExitRange(playersettings)
-end
-end
-end
-end
-end
-function RANGE:_CheckInZone(_unitName)
-self:F2(_unitName)
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-local unitheading=0
-if _unit and _playername then
-local playerData=self.PlayerSettings[_playername]
-local function checkme(targetheading,_zone)
-local zone=_zone
-local unitheading=_unit:GetHeading()
-local pitheading=targetheading-180
-local deltaheading=unitheading-pitheading
-local towardspit=math.abs(deltaheading)<=90 or math.abs(deltaheading-360)<=90
-if towardspit then
-local vec3=_unit:GetVec3()
-local vec2={x=vec3.x,y=vec3.z}
-local landheight=land.getHeight(vec2)
-local unitalt=vec3.y-landheight
-if unitalt<=self.strafemaxalt then
-local unitinzone=zone:IsVec2InZone(vec2)
-return unitinzone
-end
-end
-return false
-end
-local _unitID=_unit:GetID()
-local _currentStrafeRun=self.strafeStatus[_unitID]
-if _currentStrafeRun then
-local zone=_currentStrafeRun.zone.polygon
-local unitinzone=checkme(_currentStrafeRun.zone.heading,zone)
-if unitinzone then
-_currentStrafeRun.time=_currentStrafeRun.time+1
-else
-_currentStrafeRun.time=_currentStrafeRun.time+1
-if _currentStrafeRun.time<=3 then
-self.strafeStatus[_unitID]=nil
-local _msg=string.format("%s left strafing zone %s too quickly. No Score.",_playername,_currentStrafeRun.zone.name)
-self:_DisplayMessageToGroup(_unit,_msg,nil,true)
-if self.rangecontrol then
-if self.useSRS then
-local group=_unit:GetGroup()
-local text="You left the strafing zone too quickly! No score!"
-self.controlsrsQ:NewTransmission(text,nil,self.controlmsrs,nil,1)
-else
-self.rangecontrol:NewTransmission(RANGE.Sound.RCLeftStrafePitTooQuickly.filename,RANGE.Sound.RCLeftStrafePitTooQuickly.duration,self.soundpath)
-end
-end
-else
-local _ammo=self:_GetAmmo(_unitName)
-local _result=self.strafeStatus[_unitID]
-local _sound=nil
-local shots=_result.ammo-_ammo
-local accur=0
-if shots>0 then
-accur=_result.hits/shots*100
-if accur>100 then
-accur=100
-end
-end
-local resulttext=""
-if _result.pastfoulline==true then
-resulttext="* INVALID - PASSED FOUL LINE *"
-_sound=RANGE.Sound.RCPoorPass
-else
-if accur>=90 then
-resulttext="DEADEYE PASS"
-_sound=RANGE.Sound.RCExcellentPass
-elseif accur>=75 then
-resulttext="EXCELLENT PASS"
-_sound=RANGE.Sound.RCExcellentPass
-elseif accur>=50 then
-resulttext="GOOD PASS"
-_sound=RANGE.Sound.RCGoodPass
-elseif accur>=25 then
-resulttext="INEFFECTIVE PASS"
-_sound=RANGE.Sound.RCIneffectivePass
-else
-resulttext="POOR PASS"
-_sound=RANGE.Sound.RCPoorPass
-end
-end
-local _text=string.format("%s, hits on target %s: %d",self:_myname(_unitName),_result.zone.name,_result.hits)
-local ttstext=string.format("%s, hits on target %s: %d.",self:_myname(_unitName),_result.zone.name,_result.hits)
-if shots and accur then
-_text=_text..string.format("\nTotal rounds fired %d. Accuracy %.1f %%.",shots,accur)
-ttstext=ttstext..string.format(". Total rounds fired %d. Accuracy %.1f percent.",shots,accur)
-end
-_text=_text..string.format("\n%s",resulttext)
-ttstext=ttstext..string.format(" %s",resulttext)
-self:_DisplayMessageToGroup(_unit,_text)
-local result={}
-result.command=SOCKET.DataType.STRAFERESULT
-result.player=_playername
-result.name=_result.zone.name or"unknown"
-result.time=timer.getAbsTime()
-result.clock=UTILS.SecondsToClock(result.time)
-result.midate=UTILS.GetDCSMissionDate()
-result.theatre=env.mission.theatre
-result.roundsFired=shots
-result.roundsHit=_result.hits
-result.roundsQuality=resulttext
-result.strafeAccuracy=accur
-result.rangename=self.rangename
-result.airframe=playerData.airframe
-result.invalid=_result.pastfoulline
-self:StrafeResult(playerData,result)
-if playerData and playerData.targeton and self.targetsheet then
-self:_SaveTargetSheet(_playername,result)
-end
-if self.rangecontrol then
-if self.useSRS then
-self.controlsrsQ:NewTransmission(ttstext,nil,self.controlmsrs,nil,1)
-else
-self.rangecontrol:NewTransmission(RANGE.Sound.RCHitsOnTarget.filename,RANGE.Sound.RCHitsOnTarget.duration,self.soundpath)
-self.rangecontrol:Number2Transmission(string.format("%d",_result.hits))
-if shots and accur then
-self.rangecontrol:NewTransmission(RANGE.Sound.RCTotalRoundsFired.filename,RANGE.Sound.RCTotalRoundsFired.duration,self.soundpath,nil,0.2)
-self.rangecontrol:Number2Transmission(string.format("%d",shots),nil,0.2)
-self.rangecontrol:NewTransmission(RANGE.Sound.RCAccuracy.filename,RANGE.Sound.RCAccuracy.duration,self.soundpath,nil,0.2)
-self.rangecontrol:Number2Transmission(string.format("%d",UTILS.Round(accur,0)))
-self.rangecontrol:NewTransmission(RANGE.Sound.RCPercent.filename,RANGE.Sound.RCPercent.duration,self.soundpath)
-end
-self.rangecontrol:NewTransmission(_sound.filename,_sound.duration,self.soundpath,nil,0.5)
-end
-end
-self.strafeStatus[_unitID]=nil
-local _stats=self.strafePlayerResults[_playername]or{}
-table.insert(_stats,result)
-self.strafePlayerResults[_playername]=_stats
-end
-end
-else
-for _,_targetZone in pairs(self.strafeTargets)do
-local target=_targetZone
-local zone=target.polygon
-local unitinzone=checkme(target.heading,zone)
-if unitinzone then
-local _ammo=self:_GetAmmo(_unitName)
-self.strafeStatus[_unitID]={hits=0,zone=target,time=1,ammo=_ammo,pastfoulline=false}
-local _msg=string.format("%s, rolling in on strafe pit %s.",self:_myname(_unitName),target.name)
-if self.rangecontrol then
-if self.useSRS then
-self.controlsrsQ:NewTransmission(_msg,nil,self.controlmsrs,nil,1)
-else
-self.rangecontrol:NewTransmission(RANGE.Sound.RCRollingInOnStrafeTarget.filename,RANGE.Sound.RCRollingInOnStrafeTarget.duration,self.soundpath)
-end
-end
-self:_DisplayMessageToGroup(_unit,_msg,10,true)
-self:RollingIn(playerData,target)
-break
-end
-end
-end
-end
-end
-function RANGE:_AddF10Commands(_unitName)
-self:F(_unitName)
-local _unit,playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit and playername then
-local group=_unit:GetGroup()
-local _gid=group:GetID()
-if group and _gid then
-if not self.MenuAddedTo[_gid]then
-self.MenuAddedTo[_gid]=true
-local _rangePath=nil
-if RANGE.MenuF10Root then
-_rangePath=MENU_GROUP:New(group,"On the Range")
-else
-if RANGE.MenuF10[_gid]==nil then
-RANGE.MenuF10[_gid]=MENU_GROUP:New(group,"On the Range")
-end
-_rangePath=MENU_GROUP:New(group,self.rangename,RANGE.MenuF10[_gid])
-end
-local _statsPath=MENU_GROUP:New(group,"Statistics",_rangePath)
-local _markPath=MENU_GROUP:New(group,"Mark Targets",_rangePath)
-local _settingsPath=MENU_GROUP:New(group,"My Settings",_rangePath)
-local _infoPath=MENU_GROUP:New(group,"Range Info",_rangePath)
-local _mysmokePath=MENU_GROUP:New(group,"Smoke Color",_settingsPath)
-local _myflarePath=MENU_GROUP:New(group,"Flare Color",_settingsPath)
-local _MoMap=MENU_GROUP_COMMAND:New(group,"Mark On Map",_markPath,self._MarkTargetsOnMap,self,_unitName)
-local _IllRng=MENU_GROUP_COMMAND:New(group,"Illuminate Range",_markPath,self._IlluminateBombTargets,self,_unitName)
-local _SSpit=MENU_GROUP_COMMAND:New(group,"Smoke Strafe Pits",_markPath,self._SmokeStrafeTargetBoxes,self,_unitName)
-local _SStgts=MENU_GROUP_COMMAND:New(group,"Smoke Strafe Tgts",_markPath,self._SmokeStrafeTargets,self,_unitName)
-local _SBtgts=MENU_GROUP_COMMAND:New(group,"Smoke Bomb Tgts",_markPath,self._SmokeBombTargets,self,_unitName)
-local _AllSR=MENU_GROUP_COMMAND:New(group,"All Strafe Results",_statsPath,self._DisplayStrafePitResults,self,_unitName)
-local _AllBR=MENU_GROUP_COMMAND:New(group,"All Bombing Results",_statsPath,self._DisplayBombingResults,self,_unitName)
-local _MySR=MENU_GROUP_COMMAND:New(group,"My Strafe Results",_statsPath,self._DisplayMyStrafePitResults,self,_unitName)
-local _MyBR=MENU_GROUP_COMMAND:New(group,"My Bomb Results",_statsPath,self._DisplayMyBombingResults,self,_unitName)
-local _ResetST=MENU_GROUP_COMMAND:New(group,"Reset All Stats",_statsPath,self._ResetRangeStats,self,_unitName)
-local _BlueSM=MENU_GROUP_COMMAND:New(group,"Blue Smoke",_mysmokePath,self._playersmokecolor,self,_unitName,SMOKECOLOR.Blue)
-local _GrSM=MENU_GROUP_COMMAND:New(group,"Green Smoke",_mysmokePath,self._playersmokecolor,self,_unitName,SMOKECOLOR.Green)
-local _OrSM=MENU_GROUP_COMMAND:New(group,"Orange Smoke",_mysmokePath,self._playersmokecolor,self,_unitName,SMOKECOLOR.Orange)
-local _ReSM=MENU_GROUP_COMMAND:New(group,"Red Smoke",_mysmokePath,self._playersmokecolor,self,_unitName,SMOKECOLOR.Red)
-local _WhSm=MENU_GROUP_COMMAND:New(group,"White Smoke",_mysmokePath,self._playersmokecolor,self,_unitName,SMOKECOLOR.White)
-local _GrFl=MENU_GROUP_COMMAND:New(group,"Green Flares",_myflarePath,self._playerflarecolor,self,_unitName,FLARECOLOR.Green)
-local _ReFl=MENU_GROUP_COMMAND:New(group,"Red Flares",_myflarePath,self._playerflarecolor,self,_unitName,FLARECOLOR.Red)
-local _WhFl=MENU_GROUP_COMMAND:New(group,"White Flares",_myflarePath,self._playerflarecolor,self,_unitName,FLARECOLOR.White)
-local _YeFl=MENU_GROUP_COMMAND:New(group,"Yellow Flares",_myflarePath,self._playerflarecolor,self,_unitName,FLARECOLOR.Yellow)
-local _SmDe=MENU_GROUP_COMMAND:New(group,"Smoke Delay On/Off",_settingsPath,self._SmokeBombDelayOnOff,self,_unitName)
-local _SmIm=MENU_GROUP_COMMAND:New(group,"Smoke Impact On/Off",_settingsPath,self._SmokeBombImpactOnOff,self,_unitName)
-local _FlHi=MENU_GROUP_COMMAND:New(group,"Flare Hits On/Off",_settingsPath,self._FlareDirectHitsOnOff,self,_unitName)
-local _AlMeA=MENU_GROUP_COMMAND:New(group,"All Messages On/Off",_settingsPath,self._MessagesToPlayerOnOff,self,_unitName)
-local _TrpSh=MENU_GROUP_COMMAND:New(group,"Targetsheet On/Off",_settingsPath,self._TargetsheetOnOff,self,_unitName)
-local _WeIn=MENU_GROUP_COMMAND:New(group,"General Info",_infoPath,self._DisplayRangeInfo,self,_unitName)
-local _WeRe=MENU_GROUP_COMMAND:New(group,"Weather Report",_infoPath,self._DisplayRangeWeather,self,_unitName)
-local _BoTgtgs=MENU_GROUP_COMMAND:New(group,"Bombing Targets",_infoPath,self._DisplayBombTargets,self,_unitName)
-local _StrPits=MENU_GROUP_COMMAND:New(group,"Strafe Pits",_infoPath,self._DisplayStrafePits,self,_unitName):Refresh()
-end
-else
-self:E(self.lid.."Could not find group or group ID in AddF10Menu() function. Unit name: ".._unitName or"N/A")
-end
-else
-self:E(self.lid.."Player unit does not exist in AddF10Menu() function. Unit name: ".._unitName or"N/A")
-end
-end
-function RANGE:_GetBombTargetCoordinate(target)
-local coord=nil
-if target.type==RANGE.TargetType.UNIT then
-if target.target and target.target:IsAlive()then
-coord=target.target:GetCoordinate()
-target.coordinate=coord
-else
-coord=target.coordinate
-end
-elseif target.type==RANGE.TargetType.STATIC then
-coord=target.coordinate
-elseif target.type==RANGE.TargetType.COORD then
-coord=target.coordinate
-elseif target.type==RANGE.TargetType.SCENERY then
-coord=target.coordinate
-else
-self:E(self.lid.."ERROR: Unknown target type.")
-end
-return coord
-end
-function RANGE:_GetAmmo(unitname)
-self:F2(unitname)
-local ammo=0
-local unit,playername=self:_GetPlayerUnitAndName(unitname)
-if unit and playername then
-local has_ammo=false
-local ammotable=unit:GetAmmo()
-self:T2({ammotable=ammotable})
-if ammotable~=nil then
-local weapons=#ammotable
-self:T2(self.lid..string.format("Number of weapons %d.",weapons))
-for w=1,weapons do
-local Nammo=ammotable[w]["count"]
-local Tammo=ammotable[w]["desc"]["typeName"]
-if string.match(Tammo,"shell")then
-ammo=ammo+Nammo
-local text=string.format("Player %s has %d rounds ammo of type %s",playername,Nammo,Tammo)
-self:T(self.lid..text)
-else
-local text=string.format("Player %s has %d ammo of type %s",playername,Nammo,Tammo)
-self:T(self.lid..text)
-end
-end
-end
-end
-return ammo
-end
-function RANGE:_MarkTargetsOnMap(_unitName)
-self:F(_unitName)
-local group=nil
-if _unitName then
-group=UNIT:FindByName(_unitName):GetGroup()
-end
-for _,_bombtarget in pairs(self.bombingTargets)do
-local bombtarget=_bombtarget
-local coord=self:_GetBombTargetCoordinate(_bombtarget)
-if group then
-coord:MarkToGroup(string.format("Bomb target %s:\n%s\n%s",bombtarget.name,coord:ToStringLLDMS(),coord:ToStringBULLS(group:GetCoalition())),group)
-else
-coord:MarkToAll(string.format("Bomb target %s",bombtarget.name))
-end
-end
-for _,_strafepit in pairs(self.strafeTargets)do
-for _,_target in pairs(_strafepit.targets)do
-local _target=_target
-if _target and _target:IsAlive()then
-local coord=_target:GetCoordinate()
-if group then
-coord:MarkToGroup(string.format("Strafe target %s:\n%s\n%s",_target:GetName(),coord:ToStringLLDMS(),coord:ToStringBULLS(group:GetCoalition())),group)
-else
-coord:MarkToAll("Strafe target ".._target:GetName())
-end
-end
-end
-end
-if _unitName then
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-local text=string.format("%s, %s, range targets are now marked on F10 map.",self.rangename,_playername)
-self:_DisplayMessageToGroup(_unit,text,5)
-end
-end
-function RANGE:_IlluminateBombTargets(_unitName)
-self:F(_unitName)
-local bomb={}
-for _,_bombtarget in pairs(self.bombingTargets)do
-local _target=_bombtarget.target
-local coord=self:_GetBombTargetCoordinate(_bombtarget)
-if coord then
-table.insert(bomb,coord)
-end
-end
-if#bomb>0 then
-local coord=bomb[math.random(#bomb)]
-local c=COORDINATE:New(coord.x,coord.y+math.random(self.illuminationminalt,self.illuminationmaxalt),coord.z)
-c:IlluminationBomb()
-end
-local strafe={}
-for _,_strafepit in pairs(self.strafeTargets)do
-for _,_target in pairs(_strafepit.targets)do
-local _target=_target
-if _target and _target:IsAlive()then
-local coord=_target:GetCoordinate()
-table.insert(strafe,coord)
-end
-end
-end
-if#strafe>0 then
-local coord=strafe[math.random(#strafe)]
-local c=COORDINATE:New(coord.x,coord.y+math.random(self.illuminationminalt,self.illuminationmaxalt),coord.z)
-c:IlluminationBomb()
-end
-if _unitName then
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-local text=string.format("%s, %s, range targets are illuminated.",self.rangename,_playername)
-self:_DisplayMessageToGroup(_unit,text,5)
-end
-end
-function RANGE:_ResetRangeStats(_unitName)
-self:F(_unitName)
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit and _playername then
-self.strafePlayerResults[_playername]=nil
-self.bombPlayerResults[_playername]=nil
-local text=string.format("%s, %s, your range stats were cleared.",self.rangename,_playername)
-self:DisplayMessageToGroup(_unit,text,5,false,true)
-end
-end
-function RANGE:_DisplayMessageToGroup(_unit,_text,_time,_clear,display,_togroup)
-self:F({unit=_unit,text=_text,time=_time,clear=_clear})
-_time=_time or self.Tmsg
-if _clear==nil or _clear==false then
-_clear=false
-else
-_clear=true
-end
-if self.messages==false then
-return
-end
-if _unit and _unit:IsAlive()then
-local _gid=_unit:GetGroup():GetID()
-local _grp=_unit:GetGroup()
-local _,playername=self:_GetPlayerUnitAndName(_unit:GetName())
-local playermessage=self.PlayerSettings[playername].messages
-if _gid and(playermessage==true or display)and(not self.examinerexclusive)then
-if _togroup and _grp then
-local m=MESSAGE:New(_text,_time,nil,_clear):ToGroup(_grp)
-else
-local m=MESSAGE:New(_text,_time,nil,_clear):ToUnit(_unit)
-end
-end
-if self.examinergroupname~=nil then
-local _examinerid=GROUP:FindByName(self.examinergroupname)
-if _examinerid then
-local m=MESSAGE:New(_text,_time,nil,_clear):ToGroup(_examinerid)
-end
-end
-end
-end
-function RANGE:_SmokeBombImpactOnOff(unitname)
-self:F(unitname)
-local unit,playername,_multiplayer=self:_GetPlayerUnitAndName(unitname)
-if unit and playername then
-local text
-if self.PlayerSettings[playername].smokebombimpact==true then
-self.PlayerSettings[playername].smokebombimpact=false
-text=string.format("%s, %s, smoking impact points of bombs is now OFF.",self.rangename,playername)
-else
-self.PlayerSettings[playername].smokebombimpact=true
-text=string.format("%s, %s, smoking impact points of bombs is now ON.",self.rangename,playername)
-end
-self:_DisplayMessageToGroup(unit,text,5,false,true)
-end
-end
-function RANGE:_SmokeBombDelayOnOff(unitname)
-self:F(unitname)
-local unit,playername,_multiplayer=self:_GetPlayerUnitAndName(unitname)
-if unit and playername then
-local text
-if self.PlayerSettings[playername].delaysmoke==true then
-self.PlayerSettings[playername].delaysmoke=false
-text=string.format("%s, %s, delayed smoke of bombs is now OFF.",self.rangename,playername)
-else
-self.PlayerSettings[playername].delaysmoke=true
-text=string.format("%s, %s, delayed smoke of bombs is now ON.",self.rangename,playername)
-end
-self:_DisplayMessageToGroup(unit,text,5,false,true)
-end
-end
-function RANGE:_MessagesToPlayerOnOff(unitname)
-self:F(unitname)
-local unit,playername,_multiplayer=self:_GetPlayerUnitAndName(unitname)
-if unit and playername then
-local text
-if self.PlayerSettings[playername].messages==true then
-text=string.format("%s, %s, display of ALL messages is now OFF.",self.rangename,playername)
-else
-text=string.format("%s, %s, display of ALL messages is now ON.",self.rangename,playername)
-end
-self:_DisplayMessageToGroup(unit,text,5,false,true)
-self.PlayerSettings[playername].messages=not self.PlayerSettings[playername].messages
-end
-end
-function RANGE:_TargetsheetOnOff(_unitname)
-self:F2(_unitname)
-local unit,playername,_multiplayer=self:_GetPlayerUnitAndName(_unitname)
-if unit and playername then
-local playerData=self.PlayerSettings[playername]
-if playerData then
-local text=""
-if self.targetsheet then
-playerData.targeton=not playerData.targeton
-if playerData and playerData.targeton==true then
-text=string.format("roger, your targetsheets are now SAVED.")
-else
-text=string.format("affirm, your targetsheets are NOT SAVED.")
-end
-else
-text="negative, target sheet data recorder is broken on this range."
-end
-self:_DisplayMessageToGroup(unit,text,5,false,false)
-end
-end
-end
-function RANGE:_FlareDirectHitsOnOff(unitname)
-self:F(unitname)
-local unit,playername,_multiplayer=self:_GetPlayerUnitAndName(unitname)
-if unit and playername then
-local text
-if self.PlayerSettings[playername].flaredirecthits==true then
-self.PlayerSettings[playername].flaredirecthits=false
-text=string.format("%s, %s, flaring direct hits is now OFF.",self.rangename,playername)
-else
-self.PlayerSettings[playername].flaredirecthits=true
-text=string.format("%s, %s, flaring direct hits is now ON.",self.rangename,playername)
-end
-self:_DisplayMessageToGroup(unit,text,5,false,true)
-end
-end
-function RANGE:_SmokeBombTargets(unitname)
-self:F(unitname)
-for _,_bombtarget in pairs(self.bombingTargets)do
-local _target=_bombtarget.target
-local coord=self:_GetBombTargetCoordinate(_bombtarget)
-if coord then
-coord:Smoke(self.BombSmokeColor)
-end
-end
-if unitname then
-local unit,playername=self:_GetPlayerUnitAndName(unitname)
-local text=string.format("%s, %s, bombing targets are now marked with %s smoke.",self.rangename,playername,self:_smokecolor2text(self.BombSmokeColor))
-self:_DisplayMessageToGroup(unit,text,5)
-end
-end
-function RANGE:_SmokeStrafeTargets(unitname)
-self:F(unitname)
-for _,_target in pairs(self.strafeTargets)do
-_target.coordinate:Smoke(self.StrafeSmokeColor)
-end
-if unitname then
-local unit,playername=self:_GetPlayerUnitAndName(unitname)
-local text=string.format("%s, %s, strafing tragets are now marked with %s smoke.",self.rangename,playername,self:_smokecolor2text(self.StrafeSmokeColor))
-self:_DisplayMessageToGroup(unit,text,5)
-end
-end
-function RANGE:_SmokeStrafeTargetBoxes(unitname)
-self:F(unitname)
-for _,_target in pairs(self.strafeTargets)do
-local zone=_target.polygon
-zone:SmokeZone(self.StrafePitSmokeColor,4)
-for _,_point in pairs(_target.smokepoints)do
-_point:SmokeOrange()
-end
-end
-if unitname then
-local unit,playername=self:_GetPlayerUnitAndName(unitname)
-local text=string.format("%s, %s, strafing pit approach boxes are now marked with %s smoke.",self.rangename,playername,self:_smokecolor2text(self.StrafePitSmokeColor))
-self:_DisplayMessageToGroup(unit,text,5)
-end
-end
-function RANGE:_playersmokecolor(_unitName,color)
-self:F({unitname=_unitName,color=color})
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit and _playername then
-self.PlayerSettings[_playername].smokecolor=color
-local text=string.format("%s, %s, your bomb impacts are now smoked in %s.",self.rangename,_playername,self:_smokecolor2text(color))
-self:_DisplayMessageToGroup(_unit,text,5)
-end
-end
-function RANGE:_playerflarecolor(_unitName,color)
-self:F({unitname=_unitName,color=color})
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit and _playername then
-self.PlayerSettings[_playername].flarecolor=color
-local text=string.format("%s, %s, your direct hits are now flared in %s.",self.rangename,_playername,self:_flarecolor2text(color))
-self:_DisplayMessageToGroup(_unit,text,5)
-end
-end
-function RANGE:_smokecolor2text(color)
-self:F(color)
-local txt=""
-if color==SMOKECOLOR.Blue then
-txt="blue"
-elseif color==SMOKECOLOR.Green then
-txt="green"
-elseif color==SMOKECOLOR.Orange then
-txt="orange"
-elseif color==SMOKECOLOR.Red then
-txt="red"
-elseif color==SMOKECOLOR.White then
-txt="white"
-else
-txt=string.format("unknown color (%s)",tostring(color))
-end
-return txt
-end
-function RANGE:_flarecolor2text(color)
-self:F(color)
-local txt=""
-if color==FLARECOLOR.Green then
-txt="green"
-elseif color==FLARECOLOR.Red then
-txt="red"
-elseif color==FLARECOLOR.White then
-txt="white"
-elseif color==FLARECOLOR.Yellow then
-txt="yellow"
-else
-txt=string.format("unknown color (%s)",tostring(color))
-end
-return txt
-end
-function RANGE:_CheckStatic(name)
-self:F2(name)
-local _DCSstatic=StaticObject.getByName(name)
-if _DCSstatic and _DCSstatic:isExist()then
-local _MOOSEstatic=STATIC:FindByName(name,false)
-if not _MOOSEstatic then
-self:T(self.lid..string.format("Adding DCS static to MOOSE database. Name = %s.",name))
-_DATABASE:AddStatic(name)
-end
-return true
-else
-self:T3(self.lid..string.format("No static object with name %s exists.",name))
-end
-if UNIT:FindByName(name)then
-return false
-else
-self:T3(self.lid..string.format("No unit object with name %s exists.",name))
-end
-return nil
-end
-function RANGE:_GetSpeed(controllable)
-self:F2(controllable)
-local desc=controllable:GetDesc()
-local speed=0
-if desc then
-speed=desc.speedMax*3.6
-self:T({speed=speed})
-end
-return speed
-end
-function RANGE:_GetPlayerUnitAndName(_unitName)
-self:F2(_unitName)
-if _unitName~=nil then
-local multiplayer=false
-local DCSunit=Unit.getByName(_unitName)
-if DCSunit then
-local playername=DCSunit:getPlayerName()
-local unit=UNIT:Find(DCSunit)
-self:T2({DCSunit=DCSunit,unit=unit,playername=playername})
-if DCSunit and unit and playername then
-self:F2(playername)
-local grp=unit:GetGroup()
-if grp and grp:CountAliveUnits()>1 then
-multiplayer=true
-end
-return unit,playername,multiplayer
-end
-end
-end
-return nil,nil,nil
-end
-function RANGE:_myname(unitname)
-self:F2(unitname)
-local pname="Ghost 1 1"
-local unit=UNIT:FindByName(unitname)
-if unit and unit:IsAlive()then
-local grp=unit:GetGroup()
-if grp and grp:IsAlive()then
-pname=grp:GetCustomCallSign(true,true)
-end
-end
-return pname
-end
-RAT={
-ClassName="RAT",
-Debug=false,
-templategroup=nil,
-alias=nil,
-spawninitialized=false,
-spawndelay=5,
-spawninterval=5,
-coalition=nil,
-country=nil,
-category=nil,
-groupsize=nil,
-friendly="same",
-ctable={},
-aircraft={},
-Vcruisemax=nil,
-Vclimb=1500,
-AlphaDescent=3.6,
-roe="hold",
-rot="noreaction",
-takeoff=0,
-landing=9,
-mindist=5000,
-maxdist=5000000,
-airports_map={},
-airports={},
-random_departure=true,
-random_destination=true,
-departure_ports={},
-destination_ports={},
-Ndestination_Airports=0,
-Ndestination_Zones=0,
-Ndeparture_Airports=0,
-Ndeparture_Zones=0,
-destinationzone=false,
-return_zones={},
-returnzone=false,
-excluded_ports={},
-departure_Azone=nil,
-destination_Azone=nil,
-addfriendlydepartures=false,
-addfriendlydestinations=false,
-ratcraft={},
-Tinactive=600,
-reportstatus=false,
-statusinterval=30,
-placemarkers=false,
-FLcruise=nil,
-FLminuser=nil,
-FLmaxuser=nil,
-FLuser=nil,
-commute=false,
-starshape=false,
-homebase=nil,
-continuejourney=false,
-alive=0,
-ngroups=nil,
-f10menu=false,
-Menu={},
-SubMenuName=nil,
-respawn_at_landing=false,
-norespawn=false,
-respawn_after_takeoff=false,
-respawn_after_crash=true,
-respawn_inair=true,
-respawn_delay=0,
-markerids={},
-waypointdescriptions={},
-waypointstatus={},
-livery=nil,
-skill="High",
-ATCswitch=true,
-radio=nil,
-frequency=nil,
-modulation=nil,
-actype=nil,
-uncontrolled=false,
-invisible=false,
-immortal=false,
-activate_uncontrolled=false,
-activate_delay=5,
-activate_delta=5,
-activate_frand=0,
-activate_max=1,
-onboardnum=nil,
-onboardnum0=1,
-checkonrunway=true,
-onrunwayradius=75,
-onrunwaymaxretry=3,
-checkontop=false,
-ontopradius=2,
-termtype=nil,
-parkingscanradius=40,
-parkingscanscenery=false,
-parkingverysafe=false,
-despawnair=true,
-eplrs=false,
-}
-RAT.cat={
-plane="plane",
-heli="heli",
-}
-RAT.wp={
-coldorhot=0,
-air=1,
-runway=2,
-hot=3,
-cold=4,
-climb=5,
-cruise=6,
-descent=7,
-holding=8,
-landing=9,
-finalwp=10,
-}
-RAT.status={
-Departure="At departure point",
-Climb="Climbing",
-Cruise="Cruising",
-Uturn="Flying back home",
-Descent="Descending",
-DescentHolding="Descend to holding point",
-Holding="Holding",
-Destination="Arrived at destination",
-Uncontrolled="Uncontrolled",
-Spawned="Spawned",
-EventBirthAir="Born in air",
-EventBirth="Ready and starting engines",
-EventEngineStartAir="On journey",
-EventEngineStart="Started engines and taxiing",
-EventTakeoff="Airborne after take-off",
-EventLand="Landed and taxiing",
-EventEngineShutdown="Engines off",
-EventDead="Dead",
-EventCrash="Crashed",
-}
-RAT.coal={
-same="same",
-sameonly="sameonly",
-neutral="neutral",
-}
-RAT.unit={
-ft2meter=0.305,
-kmh2ms=0.278,
-FL2m=30.48,
-nm2km=1.852,
-nm2m=1852,
-}
-RAT.ROE={
-weaponhold="hold",
-weaponfree="free",
-returnfire="return",
-}
-RAT.ROT={
-evade="evade",
-passive="passive",
-noreaction="noreaction",
-}
-RAT.ATC={
-init=false,
-flight={},
-airport={},
-unregistered=-1,
-onfinal=-100,
-Nclearance=2,
-delay=240,
-messages=true,
-}
-RAT.markerid=0
-RAT.MenuF10=nil
-RAT.id="RAT | "
-RAT.version={
-version="2.3.9",
-print=true,
-}
-function RAT:New(groupname,alias)
-BASE:F({groupname=groupname,alias=alias})
-self=BASE:Inherit(self,SPAWN:NewWithAlias(groupname,alias))
-if RAT.version.print then
-env.info(RAT.id.."Version "..RAT.version.version)
-RAT.version.print=false
-end
-self:F(RAT.id..string.format("Creating new RAT object from template: %s.",groupname))
-alias=alias or groupname
-self.alias=alias
-local DCSgroup=Group.getByName(groupname)
-if DCSgroup==nil then
-self:E(RAT.id..string.format("ERROR: Group with name %s does not exist in the mission editor!",groupname))
-return nil
-end
-self.templategroup=GROUP:FindByName(groupname)
-self.groupsize=self.templategroup:GetSize()
-self.coalition=DCSgroup:getCoalition()
-self:_InitAircraft(DCSgroup)
-self:_GetAirportsOfMap()
-return self
-end
-function RAT:Spawn(naircraft)
-if self.spawninitialized==true then
-self:E("ERROR: Spawn function should only be called once per RAT object! Exiting and returning nil.")
-return nil
-else
-self.spawninitialized=true
-end
-self.ngroups=naircraft or 1
-if self.ATCswitch and not RAT.ATC.init then
-self:_ATCInit(self.airports_map)
-end
-if self.f10menu and not RAT.MenuF10 then
-RAT.MenuF10=MENU_MISSION:New("RAT")
-end
-self:_SetCoalitionTable()
-self:_GetAirportsOfCoalition()
-if not self.SubMenuName then
-self.SubMenuName=self.alias
-end
-if self.departure_Azone~=nil then
-self.departure_ports=self:_GetAirportsInZone(self.departure_Azone)
-end
-if self.destination_Azone~=nil then
-self.destination_ports=self:_GetAirportsInZone(self.destination_Azone)
-end
-if self.addfriendlydepartures then
-self:_AddFriendlyAirports(self.departure_ports)
-end
-if self.addfriendlydestinations then
-self:_AddFriendlyAirports(self.destination_ports)
-end
-if self.FLcruise==nil then
-if self.category==RAT.cat.plane then
-self.FLcruise=200*RAT.unit.FL2m
-else
-self.FLcruise=005*RAT.unit.FL2m
-end
-end
-if self.category==RAT.cat.heli then
-self.mindist=50
-end
-self:_CheckConsistency()
-local text=string.format("\n******************************************************\n")
-text=text..string.format("Spawning %i aircraft from template %s of type %s.\n",self.ngroups,self.SpawnTemplatePrefix,self.aircraft.type)
-text=text..string.format("Alias: %s\n",self.alias)
-text=text..string.format("Category: %s\n",self.category)
-text=text..string.format("Friendly coalitions: %s\n",self.friendly)
-text=text..string.format("Number of airports on map : %i\n",#self.airports_map)
-text=text..string.format("Number of friendly airports: %i\n",#self.airports)
-text=text..string.format("Totally random departure: %s\n",tostring(self.random_departure))
-if not self.random_departure then
-text=text..string.format("Number of departure airports: %d\n",self.Ndeparture_Airports)
-text=text..string.format("Number of departure zones : %d\n",self.Ndeparture_Zones)
-end
-text=text..string.format("Totally random destination: %s\n",tostring(self.random_destination))
-if not self.random_destination then
-text=text..string.format("Number of destination airports: %d\n",self.Ndestination_Airports)
-text=text..string.format("Number of destination zones : %d\n",self.Ndestination_Zones)
-end
-text=text..string.format("Min dist to destination: %4.1f\n",self.mindist)
-text=text..string.format("Max dist to destination: %4.1f\n",self.maxdist)
-text=text..string.format("Terminal type: %s\n",tostring(self.termtype))
-text=text..string.format("Takeoff type: %i\n",self.takeoff)
-text=text..string.format("Landing type: %i\n",self.landing)
-text=text..string.format("Commute: %s\n",tostring(self.commute))
-text=text..string.format("Journey: %s\n",tostring(self.continuejourney))
-text=text..string.format("Destination Zone: %s\n",tostring(self.destinationzone))
-text=text..string.format("Return Zone: %s\n",tostring(self.returnzone))
-text=text..string.format("Spawn delay: %4.1f\n",self.spawndelay)
-text=text..string.format("Spawn interval: %4.1f\n",self.spawninterval)
-text=text..string.format("Respawn delay: %s\n",tostring(self.respawn_delay))
-text=text..string.format("Respawn off: %s\n",tostring(self.norespawn))
-text=text..string.format("Respawn after landing: %s\n",tostring(self.respawn_at_landing))
-text=text..string.format("Respawn after take-off: %s\n",tostring(self.respawn_after_takeoff))
-text=text..string.format("Respawn after crash: %s\n",tostring(self.respawn_after_crash))
-text=text..string.format("Respawn in air: %s\n",tostring(self.respawn_inair))
-text=text..string.format("ROE: %s\n",tostring(self.roe))
-text=text..string.format("ROT: %s\n",tostring(self.rot))
-text=text..string.format("Immortal: %s\n",tostring(self.immortal))
-text=text..string.format("Invisible: %s\n",tostring(self.invisible))
-text=text..string.format("Vclimb: %4.1f\n",self.Vclimb)
-text=text..string.format("AlphaDescent: %4.2f\n",self.AlphaDescent)
-text=text..string.format("Vcruisemax: %s\n",tostring(self.Vcruisemax))
-text=text..string.format("FLcruise = %6.1f km = FL%3.0f\n",self.FLcruise/1000,self.FLcruise/RAT.unit.FL2m)
-text=text..string.format("FLuser: %s\n",tostring(self.Fluser))
-text=text..string.format("FLminuser: %s\n",tostring(self.FLminuser))
-text=text..string.format("FLmaxuser: %s\n",tostring(self.FLmaxuser))
-text=text..string.format("Place markers: %s\n",tostring(self.placemarkers))
-text=text..string.format("Report status: %s\n",tostring(self.reportstatus))
-text=text..string.format("Status interval: %4.1f\n",self.statusinterval)
-text=text..string.format("Time inactive: %4.1f\n",self.Tinactive)
-text=text..string.format("Create F10 menu : %s\n",tostring(self.f10menu))
-text=text..string.format("F10 submenu name: %s\n",self.SubMenuName)
-text=text..string.format("ATC enabled : %s\n",tostring(self.ATCswitch))
-text=text..string.format("Radio comms : %s\n",tostring(self.radio))
-text=text..string.format("Radio frequency : %s\n",tostring(self.frequency))
-text=text..string.format("Radio modulation : %s\n",tostring(self.frequency))
-text=text..string.format("Tail # prefix : %s\n",tostring(self.onboardnum))
-text=text..string.format("Check on runway: %s\n",tostring(self.checkonrunway))
-text=text..string.format("Max respawn attempts: %s\n",tostring(self.onrunwaymaxretry))
-text=text..string.format("Check on top: %s\n",tostring(self.checkontop))
-text=text..string.format("Uncontrolled: %s\n",tostring(self.uncontrolled))
-if self.uncontrolled and self.activate_uncontrolled then
-text=text..string.format("Uncontrolled max : %4.1f\n",self.activate_max)
-text=text..string.format("Uncontrolled delay: %4.1f\n",self.activate_delay)
-text=text..string.format("Uncontrolled delta: %4.1f\n",self.activate_delta)
-text=text..string.format("Uncontrolled frand: %4.1f\n",self.activate_frand)
-end
-if self.livery then
-text=text..string.format("Available liveries:\n")
-for _,livery in pairs(self.livery)do
-text=text..string.format("- %s\n",livery)
-end
-end
-text=text..string.format("******************************************************\n")
-self:T(RAT.id..text)
-if self.f10menu then
-self.Menu[self.SubMenuName]=MENU_MISSION:New(self.SubMenuName,RAT.MenuF10)
-self.Menu[self.SubMenuName]["groups"]=MENU_MISSION:New("Groups",self.Menu[self.SubMenuName])
-MENU_MISSION_COMMAND:New("Spawn new group",self.Menu[self.SubMenuName],self._SpawnWithRoute,self)
-MENU_MISSION_COMMAND:New("Delete markers",self.Menu[self.SubMenuName],self._DeleteMarkers,self)
-MENU_MISSION_COMMAND:New("Status report",self.Menu[self.SubMenuName],self.Status,self,true)
-end
-local Tstart=self.spawndelay
-local dt=self.spawninterval
-if self.takeoff==RAT.wp.runway and not self.random_departure then
-dt=math.max(dt,180)
-end
-local Tstop=Tstart+dt*(self.ngroups-1)
-SCHEDULER:New(nil,self.Status,{self},Tstart+1,self.statusinterval)
-self:HandleEvent(EVENTS.Birth,self._OnBirth)
-self:HandleEvent(EVENTS.EngineStartup,self._OnEngineStartup)
-self:HandleEvent(EVENTS.Takeoff,self._OnTakeoff)
-self:HandleEvent(EVENTS.Land,self._OnLand)
-self:HandleEvent(EVENTS.EngineShutdown,self._OnEngineShutdown)
-self:HandleEvent(EVENTS.Dead,self._OnDeadOrCrash)
-self:HandleEvent(EVENTS.Crash,self._OnDeadOrCrash)
-self:HandleEvent(EVENTS.Hit,self._OnHit)
-if self.ngroups==0 then
-return nil
-end
-SCHEDULER:New(nil,self._SpawnWithRoute,{self},Tstart,dt,0.0,Tstop)
-if self.uncontrolled and self.activate_uncontrolled then
-SCHEDULER:New(nil,self._ActivateUncontrolled,{self},self.activate_delay,self.activate_delta,self.activate_frand)
-end
-return true
-end
-function RAT:_CheckConsistency()
-self:F2()
-if not self.random_departure then
-for _,name in pairs(self.departure_ports)do
-if self:_AirportExists(name)then
-self.Ndeparture_Airports=self.Ndeparture_Airports+1
-elseif self:_ZoneExists(name)then
-self.Ndeparture_Zones=self.Ndeparture_Zones+1
-end
-end
-if self.Ndeparture_Zones>0 and self.takeoff~=RAT.wp.air then
-self.takeoff=RAT.wp.air
-self:E(RAT.id..string.format("ERROR: At least one zone defined as departure and takeoff is NOT set to air. Enabling air start for RAT group %s!",self.alias))
-end
-if self.Ndeparture_Airports==0 and self.Ndeparture_Zone==0 then
-self.random_departure=true
-local text=string.format("No airports or zones found given in SetDeparture(). Enabling random departure airports for RAT group %s!",self.alias)
-self:E(RAT.id.."ERROR: "..text)
-MESSAGE:New(text,30):ToAll()
-end
-end
-if not self.random_destination then
-for _,name in pairs(self.destination_ports)do
-if self:_AirportExists(name)then
-self.Ndestination_Airports=self.Ndestination_Airports+1
-elseif self:_ZoneExists(name)then
-self.Ndestination_Zones=self.Ndestination_Zones+1
-end
-end
-if self.Ndestination_Zones>0 and self.landing~=RAT.wp.air and not self.returnzone then
-self.landing=RAT.wp.air
-self.destinationzone=true
-self:E(RAT.id.."ERROR: At least one zone defined as destination and landing is NOT set to air. Enabling destination zone!")
-end
-if self.Ndestination_Airports==0 and self.Ndestination_Zones==0 then
-self.random_destination=true
-local text="No airports or zones found given in SetDestination(). Enabling random destination airports!"
-self:E(RAT.id.."ERROR: "..text)
-MESSAGE:New(text,30):ToAll()
-end
-end
-if self.destinationzone and self.returnzone then
-self:E(RAT.id.."ERROR: Destination zone _and_ return to zone not possible! Disabling return to zone.")
-self.returnzone=false
-end
-if self.returnzone and self.takeoff==RAT.wp.air then
-self.landing=RAT.wp.air
-end
-if self.FLminuser then
-self.FLminuser=math.min(self.FLminuser,self.aircraft.ceiling)
-end
-if self.FLmaxuser then
-self.FLmaxuser=math.min(self.FLmaxuser,self.aircraft.ceiling)
-end
-if self.FLcruise then
-self.FLcruise=math.min(self.FLcruise,self.aircraft.ceiling)
-end
-if self.FLminuser and self.FLmaxuser then
-if self.FLminuser>self.FLmaxuser then
-local min=self.FLminuser
-local max=self.FLmaxuser
-self.FLminuser=max
-self.FLmaxuser=min
-end
-end
-if self.FLminuser and self.FLcruiseself.FLmaxuser then
-self.FLcruise=self.FLmaxuser
-end
-if self.uncontrolled then
-self.takeoff=RAT.wp.cold
-end
-end
-function RAT:SetCoalition(friendly)
-self:F2(friendly)
-if friendly:lower()=="sameonly"then
-self.friendly=RAT.coal.sameonly
-elseif friendly:lower()=="neutral"then
-self.friendly=RAT.coal.neutral
-else
-self.friendly=RAT.coal.same
-end
-return self
-end
-function RAT:SetCoalitionAircraft(color)
-self:F2(color)
-if color:lower()=="blue"then
-self.coalition=coalition.side.BLUE
-if not self.country then
-self.country=country.id.USA
-end
-elseif color:lower()=="red"then
-self.coalition=coalition.side.RED
-if not self.country then
-self.country=country.id.RUSSIA
-end
-elseif color:lower()=="neutral"then
-self.coalition=coalition.side.NEUTRAL
-if not self.country then
-self.country=country.id.SWITZERLAND
-end
-end
-return self
-end
-function RAT:SetCountry(id)
-self:F2(id)
-self.country=id
-return self
-end
-function RAT:SetTerminalType(termtype)
-self:F2(termtype)
-self.termtype=termtype
-return self
-end
-function RAT:SetParkingScanRadius(radius)
-self:F2(radius)
-self.parkingscanradius=radius or 50
-return self
-end
-function RAT:SetParkingScanSceneryON()
-self:F2()
-self.parkingscanscenery=true
-return self
-end
-function RAT:SetParkingScanSceneryOFF()
-self:F2()
-self.parkingscanscenery=false
-return self
-end
-function RAT:SetParkingSpotSafeON()
-self:F2()
-self.parkingverysafe=true
-return self
-end
-function RAT:SetParkingSpotSafeOFF()
-self:F2()
-self.parkingverysafe=false
-return self
-end
-function RAT:SetDespawnAirOFF()
-self.despawnair=false
-return self
-end
-function RAT:SetTakeoff(type)
-self:F2(type)
-local _Type
-if type:lower()=="takeoff-cold"or type:lower()=="cold"then
-_Type=RAT.wp.cold
-elseif type:lower()=="takeoff-hot"or type:lower()=="hot"then
-_Type=RAT.wp.hot
-elseif type:lower()=="takeoff-runway"or type:lower()=="runway"then
-_Type=RAT.wp.runway
-elseif type:lower()=="air"then
-_Type=RAT.wp.air
-else
-_Type=RAT.wp.coldorhot
-end
-self.takeoff=_Type
-return self
-end
-function RAT:SetTakeoffCold()
-self.takeoff=RAT.wp.cold
-return self
-end
-function RAT:SetTakeoffHot()
-self.takeoff=RAT.wp.hot
-return self
-end
-function RAT:SetTakeoffRunway()
-self.takeoff=RAT.wp.runway
-return self
-end
-function RAT:SetTakeoffColdOrHot()
-self.takeoff=RAT.wp.coldorhot
-return self
-end
-function RAT:SetTakeoffAir()
-self.takeoff=RAT.wp.air
-return self
-end
-function RAT:SetDeparture(departurenames)
-self:F2(departurenames)
-self.random_departure=false
-local names
-if type(departurenames)=="table"then
-names=departurenames
-elseif type(departurenames)=="string"then
-names={departurenames}
-else
-self:E(RAT.id.."ERROR: Input parameter must be a string or a table in SetDeparture()!")
-end
-for _,name in pairs(names)do
-if self:_AirportExists(name)then
-table.insert(self.departure_ports,name)
-elseif self:_ZoneExists(name)then
-table.insert(self.departure_ports,name)
-else
-self:E(RAT.id.."ERROR: No departure airport or zone found with name "..name)
-end
-end
-return self
-end
-function RAT:SetDestination(destinationnames)
-self:F2(destinationnames)
-self.random_destination=false
-local names
-if type(destinationnames)=="table"then
-names=destinationnames
-elseif type(destinationnames)=="string"then
-names={destinationnames}
-else
-self:E(RAT.id.."ERROR: Input parameter must be a string or a table in SetDestination()!")
-end
-for _,name in pairs(names)do
-if self:_AirportExists(name)then
-table.insert(self.destination_ports,name)
-elseif self:_ZoneExists(name)then
-table.insert(self.destination_ports,name)
-else
-self:E(RAT.id.."ERROR: No destination airport or zone found with name "..name)
-end
-end
-return self
-end
-function RAT:DestinationZone()
-self:F2()
-self.destinationzone=true
-self.landing=RAT.wp.air
-return self
-end
-function RAT:ReturnZone()
-self:F2()
-self.returnzone=true
-return self
-end
-function RAT:SetDestinationsFromZone(zone)
-self:F2(zone)
-self.random_destination=false
-self.destination_Azone=zone
-return self
-end
-function RAT:SetDeparturesFromZone(zone)
-self:F2(zone)
-self.random_departure=false
-self.departure_Azone=zone
-return self
-end
-function RAT:AddFriendlyAirportsToDepartures()
-self:F2()
-self.addfriendlydepartures=true
-return self
-end
-function RAT:AddFriendlyAirportsToDestinations()
-self:F2()
-self.addfriendlydestinations=true
-return self
-end
-function RAT:ExcludedAirports(ports)
-self:F2(ports)
-if type(ports)=="string"then
-self.excluded_ports={ports}
-else
-self.excluded_ports=ports
-end
-return self
-end
-function RAT:SetAISkill(skill)
-self:F2(skill)
-if skill:lower()=="average"then
-self.skill="Average"
-elseif skill:lower()=="good"then
-self.skill="Good"
-elseif skill:lower()=="excellent"then
-self.skill="Excellent"
-elseif skill:lower()=="random"then
-self.skill="Random"
-else
-self.skill="High"
-end
-return self
-end
-function RAT:Livery(skins)
-self:F2(skins)
-if type(skins)=="string"then
-self.livery={skins}
-else
-self.livery=skins
-end
-return self
-end
-function RAT:ChangeAircraft(actype)
-self:F2(actype)
-self.actype=actype
-return self
-end
-function RAT:ContinueJourney()
-self:F2()
-self.continuejourney=true
-self.commute=false
-return self
-end
-function RAT:Commute(starshape)
-self:F2()
-self.commute=true
-self.continuejourney=false
-if starshape then
-self.starshape=starshape
-else
-self.starshape=false
-end
-return self
-end
-function RAT:SetSpawnDelay(delay)
-self:F2(delay)
-delay=delay or 5
-self.spawndelay=math.max(0.5,delay)
-return self
-end
-function RAT:SetSpawnInterval(interval)
-self:F2(interval)
-interval=interval or 5
-self.spawninterval=math.max(0.5,interval)
-return self
-end
-function RAT:RespawnAfterLanding(delay)
-self:F2(delay)
-delay=delay or 180
-self.respawn_at_landing=true
-delay=math.max(1.0,delay)
-self.respawn_delay=delay
-return self
-end
-function RAT:SetRespawnDelay(delay)
-self:F2(delay)
-delay=delay or 1.0
-delay=math.max(1.0,delay)
-self.respawn_delay=delay
-return self
-end
-function RAT:NoRespawn()
-self:F2()
-self.norespawn=true
-return self
-end
-function RAT:SetMaxRespawnTriedWhenSpawnedOnRunway(n)
-self:F2(n)
-n=n or 3
-self.onrunwaymaxretry=n
-return self
-end
-function RAT:RespawnAfterTakeoff()
-self:F2()
-self.respawn_after_takeoff=true
-return self
-end
-function RAT:RespawnAfterCrashON()
-self:F2()
-self.respawn_after_crash=true
-return self
-end
-function RAT:RespawnAfterCrashOFF()
-self:F2()
-self.respawn_after_crash=false
-return self
-end
-function RAT:RespawnInAirAllowed()
-self:F2()
-self.respawn_inair=true
-return self
-end
-function RAT:RespawnInAirNotAllowed()
-self:F2()
-self.respawn_inair=false
-return self
-end
-function RAT:CheckOnRunway(switch,distance)
-self:F2(switch)
-if switch==nil then
-switch=true
-end
-self.checkonrunway=switch
-self.onrunwayradius=distance or 75
-return self
-end
-function RAT:CheckOnTop(switch,radius)
-self:F2(switch)
-if switch==nil then
-switch=true
-end
-self.checkontop=switch
-self.ontopradius=radius or 2
-return self
-end
-function RAT:ParkingSpotDB(switch)
-self:E("RAT ParkingSpotDB function is obsolete and will be removed soon!")
-return self
-end
-function RAT:RadioON()
-self:F2()
-self.radio=true
-return self
-end
-function RAT:RadioOFF()
-self:F2()
-self.radio=false
-return self
-end
-function RAT:RadioFrequency(frequency)
-self:F2(frequency)
-self.frequency=frequency
-return self
-end
-function RAT:RadioModulation(modulation)
-self:F2(modulation)
-if modulation=="AM"then
-self.modulation=radio.modulation.AM
-elseif modulation=="FM"then
-self.modulation=radio.modulation.FM
-else
-self.modulation=radio.modulation.AM
-end
-return self
-end
-function RAT:RadioMenuON()
-self:F2()
-self.f10menu=true
-return self
-end
-function RAT:RadioMenuOFF()
-self:F2()
-self.f10menu=false
-return self
-end
-function RAT:Invisible()
-self:F2()
-self.invisible=true
-return self
-end
-function RAT:SetEPLRS(switch)
-if switch==nil or switch==true then
-self.eplrs=true
-else
-self.eplrs=false
-end
-return self
-end
-function RAT:Immortal()
-self:F2()
-self.immortal=true
-return self
-end
-function RAT:Uncontrolled()
-self:F2()
-self.uncontrolled=true
-return self
-end
-function RAT:ActivateUncontrolled(maxactivated,delay,delta,frand)
-self:F2({max=maxactivated,delay=delay,delta=delta,rand=frand})
-self.activate_uncontrolled=true
-self.activate_max=maxactivated or 1
-self.activate_delay=delay or 1
-self.activate_delta=delta or 1
-self.activate_frand=frand or 0
-self.activate_delay=math.max(self.activate_delay,1)
-self.activate_delta=math.max(self.activate_delta,0)
-self.activate_frand=math.max(self.activate_frand,0)
-self.activate_frand=math.min(self.activate_frand,1)
-return self
-end
-function RAT:TimeDestroyInactive(time)
-self:F2(time)
-time=time or self.Tinactive
-time=math.max(time,60)
-self.Tinactive=time
-return self
-end
-function RAT:SetMaxCruiseSpeed(speed)
-self:F2(speed)
-self.Vcruisemax=speed/3.6
-return self
-end
-function RAT:SetClimbRate(rate)
-self:F2(rate)
-rate=rate or self.Vclimb
-rate=math.max(rate,100)
-rate=math.min(rate,15000)
-self.Vclimb=rate
-return self
-end
-function RAT:SetDescentAngle(angle)
-self:F2(angle)
-angle=angle or self.AlphaDescent
-angle=math.max(angle,0.5)
-angle=math.min(angle,50)
-self.AlphaDescent=angle
-return self
-end
-function RAT:SetROE(roe)
-self:F2(roe)
-if roe=="return"then
-self.roe=RAT.ROE.returnfire
-elseif roe=="free"then
-self.roe=RAT.ROE.weaponfree
-else
-self.roe=RAT.ROE.weaponhold
-end
-return self
-end
-function RAT:SetROT(rot)
-self:F2(rot)
-if rot=="passive"then
-self.rot=RAT.ROT.passive
-elseif rot=="evade"then
-self.rot=RAT.ROT.evade
-else
-self.rot=RAT.ROT.noreaction
-end
-return self
-end
-function RAT:MenuName(name)
-self:F2(name)
-self.SubMenuName=tostring(name)
-return self
-end
-function RAT:EnableATC(switch)
-self:F2(switch)
-if switch==nil then
-switch=true
-end
-self.ATCswitch=switch
-return self
-end
-function RAT:ATC_Messages(switch)
-self:F2(switch)
-if switch==nil then
-switch=true
-end
-RAT.ATC.messages=switch
-return self
-end
-function RAT:ATC_Clearance(n)
-self:F2(n)
-RAT.ATC.Nclearance=n or 2
-return self
-end
-function RAT:ATC_Delay(time)
-self:F2(time)
-RAT.ATC.delay=time or 240
-return self
-end
-function RAT:SetMinDistance(dist)
-self:F2(dist)
-self.mindist=math.max(100,dist*1000)
-return self
-end
-function RAT:SetMaxDistance(dist)
-self:F2(dist)
-self.maxdist=dist*1000
-return self
-end
-function RAT:_Debug(switch)
-self:F2(switch)
-if switch==nil then
-switch=true
-end
-self.Debug=switch
-return self
-end
-function RAT:Debugmode()
-self:F2()
-self.Debug=true
-return self
-end
-function RAT:StatusReports(switch)
-self:F2(switch)
-if switch==nil then
-switch=true
-end
-self.reportstatus=switch
-return self
-end
-function RAT:PlaceMarkers(switch)
-self:F2(switch)
-if switch==nil then
-switch=true
-end
-self.placemarkers=switch
-return self
-end
-function RAT:SetFL(FL)
-self:F2(FL)
-FL=FL or self.FLcruise
-FL=math.max(FL,0)
-self.FLuser=FL*RAT.unit.FL2m
-return self
-end
-function RAT:SetFLmax(FL)
-self:F2(FL)
-self.FLmaxuser=FL*RAT.unit.FL2m
-return self
-end
-function RAT:SetMaxCruiseAltitude(alt)
-self:F2(alt)
-self.FLmaxuser=alt
-return self
-end
-function RAT:SetFLmin(FL)
-self:F2(FL)
-self.FLminuser=FL*RAT.unit.FL2m
-return self
-end
-function RAT:SetMinCruiseAltitude(alt)
-self:F2(alt)
-self.FLminuser=alt
-return self
-end
-function RAT:SetFLcruise(FL)
-self:F2(FL)
-self.FLcruise=FL*RAT.unit.FL2m
-return self
-end
-function RAT:SetCruiseAltitude(alt)
-self:F2(alt)
-self.FLcruise=alt
-return self
-end
-function RAT:SetOnboardNum(tailnumprefix,zero)
-self:F2({tailnumprefix=tailnumprefix,zero=zero})
-self.onboardnum=tailnumprefix
-if zero~=nil then
-self.onboardnum0=zero
-end
-return self
-end
-function RAT:_InitAircraft(DCSgroup)
-self:F2(DCSgroup)
-local DCSunit=DCSgroup:getUnit(1)
-local DCSdesc=DCSunit:getDesc()
-local DCScategory=DCSgroup:getCategory()
-local DCStype=DCSunit:getTypeName()
-if DCScategory==Group.Category.AIRPLANE then
-self.category=RAT.cat.plane
-elseif DCScategory==Group.Category.HELICOPTER then
-self.category=RAT.cat.heli
-else
-self.category="other"
-self:E(RAT.id.."ERROR: Group of RAT is neither airplane nor helicopter!")
-end
-self.aircraft.type=DCStype
-self.aircraft.fuel=DCSunit:getFuel()
-self.aircraft.Rmax=DCSdesc.range*RAT.unit.nm2m
-self.aircraft.Reff=self.aircraft.Rmax*self.aircraft.fuel*0.95
-self.aircraft.Vmax=DCSdesc.speedMax
-self.aircraft.Vymax=DCSdesc.VyMax
-self.aircraft.ceiling=DCSdesc.Hmax
-if DCSdesc.box then
-self.aircraft.length=DCSdesc.box.max.x
-self.aircraft.height=DCSdesc.box.max.y
-self.aircraft.width=DCSdesc.box.max.z
-elseif DCStype=="Mirage-F1CE"then
-self.aircraft.length=16
-self.aircraft.height=5
-self.aircraft.width=9
-end
-self.aircraft.box=math.max(self.aircraft.length,self.aircraft.width)
-local text=string.format("\n******************************************************\n")
-text=text..string.format("Aircraft parameters:\n")
-text=text..string.format("Template group = %s\n",self.SpawnTemplatePrefix)
-text=text..string.format("Alias = %s\n",self.alias)
-text=text..string.format("Category = %s\n",self.category)
-text=text..string.format("Type = %s\n",self.aircraft.type)
-text=text..string.format("Length (x) = %6.1f m\n",self.aircraft.length)
-text=text..string.format("Width (z) = %6.1f m\n",self.aircraft.width)
-text=text..string.format("Height (y) = %6.1f m\n",self.aircraft.height)
-text=text..string.format("Max air speed = %6.1f m/s\n",self.aircraft.Vmax)
-text=text..string.format("Max climb speed = %6.1f m/s\n",self.aircraft.Vymax)
-text=text..string.format("Initial Fuel = %6.1f\n",self.aircraft.fuel*100)
-text=text..string.format("Max range = %6.1f km\n",self.aircraft.Rmax/1000)
-text=text..string.format("Eff range = %6.1f km (with 95 percent initial fuel amount)\n",self.aircraft.Reff/1000)
-text=text..string.format("Ceiling = %6.1f km = FL%3.0f\n",self.aircraft.ceiling/1000,self.aircraft.ceiling/RAT.unit.FL2m)
-text=text..string.format("******************************************************\n")
-self:T(RAT.id..text)
-end
-function RAT:_SpawnWithRoute(_departure,_destination,_takeoff,_landing,_livery,_waypoint,_lastpos,_nrespawn,parkingdata)
-self:F({rat=RAT.id,departure=_departure,destination=_destination,takeoff=_takeoff,landing=_landing,livery=_livery,waypoint=_waypoint,lastpos=_lastpos,nrespawn=_nrespawn})
-local takeoff=self.takeoff
-local landing=self.landing
-if _takeoff then
-takeoff=_takeoff
-end
-if _landing then
-landing=_landing
-end
-if takeoff==RAT.wp.coldorhot then
-local temp={RAT.wp.cold,RAT.wp.hot}
-takeoff=temp[math.random(2)]
-end
-local nrespawn=0
-if _nrespawn then
-nrespawn=_nrespawn
-end
-local departure,destination,waypoints,WPholding,WPfinal=self:_SetRoute(takeoff,landing,_departure,_destination,_waypoint)
-if not(departure and destination and waypoints)then
-return nil
-end
-local livery
-if _livery then
-livery=_livery
-elseif self.livery then
-livery=self.livery[math.random(#self.livery)]
-local text=string.format("Chosen livery for group %s: %s",self:_AnticipatedGroupName(),livery)
-self:T(RAT.id..text)
-else
-livery=nil
-end
-local successful=self:_ModifySpawnTemplate(waypoints,livery,_lastpos,departure,takeoff,parkingdata)
-if not successful then
-return nil
-end
-local group=self:SpawnWithIndex(self.SpawnIndex)
-self.alive=self.alive+1
-self:T(RAT.id..string.format("Alive groups counter now = %d.",self.alive))
-if self.ATCswitch and landing==RAT.wp.landing then
-if self.returnzone then
-self:_ATCAddFlight(group:GetName(),departure:GetName())
-else
-self:_ATCAddFlight(group:GetName(),destination:GetName())
-end
-end
-if self.placemarkers then
-self:_PlaceMarkers(waypoints,self.SpawnIndex)
-end
-if self.invisible then
-self:_CommandInvisible(group,true)
-end
-if self.immortal then
-self:_CommandImmortal(group,true)
-end
-if self.eplrs then
-group:CommandEPLRS(true,1)
-end
-self:_SetROE(group,self.roe)
-self:_SetROT(group,self.rot)
-self.ratcraft[self.SpawnIndex]={}
-self.ratcraft[self.SpawnIndex]["group"]=group
-self.ratcraft[self.SpawnIndex]["destination"]=destination
-self.ratcraft[self.SpawnIndex]["departure"]=departure
-self.ratcraft[self.SpawnIndex]["waypoints"]=waypoints
-self.ratcraft[self.SpawnIndex]["airborne"]=group:InAir()
-self.ratcraft[self.SpawnIndex]["nunits"]=group:GetInitialSize()
-if group:InAir()then
-self.ratcraft[self.SpawnIndex]["Tground"]=nil
-self.ratcraft[self.SpawnIndex]["Pground"]=nil
-self.ratcraft[self.SpawnIndex]["Uground"]=nil
-self.ratcraft[self.SpawnIndex]["Tlastcheck"]=nil
-else
-self.ratcraft[self.SpawnIndex]["Tground"]=timer.getTime()
-self.ratcraft[self.SpawnIndex]["Pground"]=group:GetCoordinate()
-self.ratcraft[self.SpawnIndex]["Uground"]={}
-for _,_unit in pairs(group:GetUnits())do
-local _unitname=_unit:GetName()
-self.ratcraft[self.SpawnIndex]["Uground"][_unitname]=_unit:GetCoordinate()
-end
-self.ratcraft[self.SpawnIndex]["Tlastcheck"]=timer.getTime()
-end
-self.ratcraft[self.SpawnIndex]["P0"]=group:GetCoordinate()
-self.ratcraft[self.SpawnIndex]["Pnow"]=group:GetCoordinate()
-self.ratcraft[self.SpawnIndex]["Distance"]=0
-self.ratcraft[self.SpawnIndex].takeoff=takeoff
-self.ratcraft[self.SpawnIndex].landing=landing
-self.ratcraft[self.SpawnIndex].wpholding=WPholding
-self.ratcraft[self.SpawnIndex].wpfinal=WPfinal
-self.ratcraft[self.SpawnIndex].active=not self.uncontrolled
-self.ratcraft[self.SpawnIndex]["status"]=RAT.status.Spawned
-self.ratcraft[self.SpawnIndex].livery=livery
-self.ratcraft[self.SpawnIndex].despawnme=false
-self.ratcraft[self.SpawnIndex].nrespawn=nrespawn
-if self.f10menu then
-local name=self.aircraft.type.." ID "..tostring(self.SpawnIndex)
-self.Menu[self.SubMenuName].groups[self.SpawnIndex]=MENU_MISSION:New(name,self.Menu[self.SubMenuName].groups)
-self.Menu[self.SubMenuName].groups[self.SpawnIndex]["roe"]=MENU_MISSION:New("Set ROE",self.Menu[self.SubMenuName].groups[self.SpawnIndex])
-MENU_MISSION_COMMAND:New("Weapons hold",self.Menu[self.SubMenuName].groups[self.SpawnIndex]["roe"],self._SetROE,self,group,RAT.ROE.weaponhold)
-MENU_MISSION_COMMAND:New("Weapons free",self.Menu[self.SubMenuName].groups[self.SpawnIndex]["roe"],self._SetROE,self,group,RAT.ROE.weaponfree)
-MENU_MISSION_COMMAND:New("Return fire",self.Menu[self.SubMenuName].groups[self.SpawnIndex]["roe"],self._SetROE,self,group,RAT.ROE.returnfire)
-self.Menu[self.SubMenuName].groups[self.SpawnIndex]["rot"]=MENU_MISSION:New("Set ROT",self.Menu[self.SubMenuName].groups[self.SpawnIndex])
-MENU_MISSION_COMMAND:New("No reaction",self.Menu[self.SubMenuName].groups[self.SpawnIndex]["rot"],self._SetROT,self,group,RAT.ROT.noreaction)
-MENU_MISSION_COMMAND:New("Passive defense",self.Menu[self.SubMenuName].groups[self.SpawnIndex]["rot"],self._SetROT,self,group,RAT.ROT.passive)
-MENU_MISSION_COMMAND:New("Evade on fire",self.Menu[self.SubMenuName].groups[self.SpawnIndex]["rot"],self._SetROT,self,group,RAT.ROT.evade)
-MENU_MISSION_COMMAND:New("Despawn group",self.Menu[self.SubMenuName].groups[self.SpawnIndex],self._Despawn,self,group)
-MENU_MISSION_COMMAND:New("Place markers",self.Menu[self.SubMenuName].groups[self.SpawnIndex],self._PlaceMarkers,self,waypoints,self.SpawnIndex)
-MENU_MISSION_COMMAND:New("Status report",self.Menu[self.SubMenuName].groups[self.SpawnIndex],self.Status,self,true,self.SpawnIndex)
-end
-return self.SpawnIndex
-end
-function RAT:ClearForLanding(name)
-trigger.action.setUserFlag(name,1)
-local flagvalue=trigger.misc.getUserFlag(name)
-self:T(RAT.id.."ATC: User flag value (landing) for "..name.." set to "..flagvalue)
-end
-function RAT:_Respawn(index,lastpos,delay)
-local departure=self.ratcraft[index].departure
-local destination=self.ratcraft[index].destination
-local takeoff=self.ratcraft[index].takeoff
-local landing=self.ratcraft[index].landing
-local livery=self.ratcraft[index].livery
-local lastwp=self.ratcraft[index].waypoints[#self.ratcraft[index].waypoints]
-local _departure=nil
-local _destination=nil
-local _takeoff=nil
-local _landing=nil
-local _livery=nil
-local _lastwp=nil
-local _lastpos=nil
-if self.continuejourney then
-_departure=destination:GetName()
-_livery=livery
-if landing==RAT.wp.landing and lastpos and not(self.respawn_at_landing or self.respawn_after_takeoff)then
-if destination:GetCategory()==4 then
-_lastpos=lastpos
-end
-end
-if self.destinationzone then
-_takeoff=RAT.wp.air
-_landing=RAT.wp.air
-elseif self.returnzone then
-_takeoff=self.takeoff
-if self.takeoff==RAT.wp.air then
-_landing=RAT.wp.air
-else
-_landing=RAT.wp.landing
-end
-_departure=departure:GetName()
-else
-_takeoff=self.takeoff
-_landing=self.landing
-end
-elseif self.commute then
-if self.starshape==true then
-if destination:GetName()==self.homebase then
-_departure=self.homebase
-_destination=nil
-else
-_departure=destination:GetName()
-_destination=self.homebase
-end
-else
-_departure=destination:GetName()
-_destination=departure:GetName()
-end
-_livery=livery
-if landing==RAT.wp.landing and lastpos and not(self.respawn_at_landing or self.respawn_after_takeoff)then
-if destination:GetCategory()==4 then
-_lastpos=lastpos
-end
-end
-if self.destinationzone then
-if self.takeoff==RAT.wp.air then
-_takeoff=RAT.wp.air
-_landing=RAT.wp.air
-else
-if takeoff==RAT.wp.air then
-_takeoff=self.takeoff
-_landing=RAT.wp.air
-else
-_takeoff=RAT.wp.air
-_landing=RAT.wp.landing
-end
-end
-elseif self.returnzone then
-_departure=departure:GetName()
-_destination=destination:GetName()
-_takeoff=self.takeoff
-_landing=self.landing
-end
-end
-if _takeoff==RAT.wp.air and(self.continuejourney or self.commute)then
-_lastwp=lastwp
-end
-self:T2({departure=_departure,destination=_destination,takeoff=_takeoff,landing=_landing,livery=_livery,lastwp=_lastwp})
-local respawndelay
-if delay then
-respawndelay=delay
-elseif self.respawn_delay then
-respawndelay=self.respawn_delay+3
-else
-respawndelay=3
-end
-local arg={}
-arg.self=self
-arg.departure=_departure
-arg.destination=_destination
-arg.takeoff=_takeoff
-arg.landing=_landing
-arg.livery=_livery
-arg.lastwp=_lastwp
-arg.lastpos=_lastpos
-self:T(RAT.id..string.format("%s delayed respawn in %.1f seconds.",self.alias,respawndelay))
-SCHEDULER:New(nil,self._SpawnWithRouteTimer,{arg},respawndelay)
-end
-function RAT._SpawnWithRouteTimer(arg)
-RAT._SpawnWithRoute(arg.self,arg.departure,arg.destination,arg.takeoff,arg.landing,arg.livery,arg.lastwp,arg.lastpos)
-end
-function RAT:_SetRoute(takeoff,landing,_departure,_destination,_waypoint)
-local VxCruiseMax
-if self.Vcruisemax then
-VxCruiseMax=math.min(self.Vcruisemax,self.aircraft.Vmax)
-else
-VxCruiseMax=math.min(self.aircraft.Vmax*0.90,250)
-end
-local VxCruiseMin=math.min(VxCruiseMax*0.70,166)
-local VxCruise=UTILS.RandomGaussian((VxCruiseMax-VxCruiseMin)/2+VxCruiseMin,(VxCruiseMax-VxCruiseMax)/4,VxCruiseMin,VxCruiseMax)
-local VxClimb=math.min(self.aircraft.Vmax*0.90,200)
-local VxDescent=math.min(self.aircraft.Vmax*0.60,140)
-local VxHolding=VxDescent*0.9
-local VxFinal=VxHolding*0.9
-local VyClimb=math.min(self.Vclimb*RAT.unit.ft2meter/60,self.aircraft.Vymax)
-local AlphaClimb=math.asin(VyClimb/VxClimb)
-local AlphaDescent=math.rad(self.AlphaDescent)
-local FLcruise_expect=self.FLcruise
-local departure=nil
-if _departure then
-if self:_AirportExists(_departure)then
-departure=AIRBASE:FindByName(_departure)
-if takeoff==RAT.wp.air then
-departure=departure:GetZone()
-end
-elseif self:_ZoneExists(_departure)then
-departure=ZONE:FindByName(_departure)
-else
-local text=string.format("ERROR! Specified departure airport %s does not exist for %s.",_departure,self.alias)
-self:E(RAT.id..text)
-end
-else
-departure=self:_PickDeparture(takeoff)
-if self.commute and self.starshape==true and self.homebase==nil then
-self.homebase=departure:GetName()
-end
-end
-if not departure then
-local text=string.format("ERROR! No valid departure airport could be found for %s.",self.alias)
-self:E(RAT.id..text)
-return nil
-end
-local Pdeparture
-if takeoff==RAT.wp.air then
-if _waypoint then
-Pdeparture=COORDINATE:New(_waypoint.x,_waypoint.alt,_waypoint.y)
-else
-local vec2=departure:GetRandomVec2()
-Pdeparture=COORDINATE:NewFromVec2(vec2)
-end
-else
-Pdeparture=departure:GetCoordinate()
-end
-local H_departure
-if takeoff==RAT.wp.air then
-local Hmin
-if self.category==RAT.cat.plane then
-Hmin=1000
-else
-Hmin=50
-end
-H_departure=self:_Randomize(FLcruise_expect*0.7,0.3,Pdeparture.y+Hmin,FLcruise_expect)
-if self.FLminuser then
-H_departure=math.max(H_departure,self.FLminuser)
-end
-if _waypoint then
-H_departure=_waypoint.alt
-end
-else
-H_departure=Pdeparture.y
-end
-local mindist=self.mindist
-if self.FLminuser then
-local hclimb=self.FLminuser-H_departure
-local hdescent=self.FLminuser-H_departure
-local Dclimb,Ddescent,Dtot=self:_MinDistance(AlphaClimb,AlphaDescent,hclimb,hdescent)
-if takeoff==RAT.wp.air and landing==RAT.wpair then
-mindist=0
-elseif takeoff==RAT.wp.air then
-mindist=Ddescent
-elseif landing==RAT.wp.air then
-mindist=Dclimb
-else
-mindist=Dtot
-end
-mindist=math.max(self.mindist,mindist)
-local text=string.format("Adjusting min distance to %d km (for given min FL%03d)",mindist/1000,self.FLminuser/RAT.unit.FL2m)
-self:T(RAT.id..text)
-end
-local destination=nil
-if _destination then
-if self:_AirportExists(_destination)then
-destination=AIRBASE:FindByName(_destination)
-if landing==RAT.wp.air or self.returnzone then
-destination=destination:GetZone()
-end
-elseif self:_ZoneExists(_destination)then
-destination=ZONE:FindByName(_destination)
-else
-local text=string.format("ERROR: Specified destination airport/zone %s does not exist for %s!",_destination,self.alias)
-self:E(RAT.id.."ERROR: "..text)
-end
-else
-local random=self.random_destination
-if self.continuejourney and _departure and#self.destination_ports<3 then
-random=true
-end
-local mylanding=landing
-local acrange=self.aircraft.Reff
-if self.returnzone then
-mylanding=RAT.wp.air
-acrange=self.aircraft.Reff/2
-end
-destination=self:_PickDestination(departure,Pdeparture,mindist,math.min(acrange,self.maxdist),random,mylanding)
-end
-if not destination then
-local text=string.format("No valid destination airport could be found for %s!",self.alias)
-MESSAGE:New(text,60):ToAll()
-self:E(RAT.id.."ERROR: "..text)
-return nil
-end
-if destination:GetName()==departure:GetName()then
-local text=string.format("%s: Destination and departure are identical. Airport/zone %s.",self.alias,destination:GetName())
-MESSAGE:New(text,30):ToAll()
-self:E(RAT.id.."ERROR: "..text)
-end
-local Preturn
-local destination_returnzone
-if self.returnzone then
-local vec2=destination:GetRandomVec2()
-Preturn=COORDINATE:NewFromVec2(vec2)
-destination_returnzone=destination
-destination=departure
-end
-local Pdestination
-if landing==RAT.wp.air then
-local vec2=destination:GetRandomVec2()
-Pdestination=COORDINATE:NewFromVec2(vec2)
-else
-Pdestination=destination:GetCoordinate()
-end
-local H_destination=Pdestination.y
-local Rhmin=8000
-local Rhmax=20000
-if self.category==RAT.cat.heli then
-Rhmin=500
-Rhmax=1000
-end
-local Vholding=Pdestination:GetRandomVec2InRadius(Rhmax,Rhmin)
-local Pholding=COORDINATE:NewFromVec2(Vholding)
-local H_holding=Pholding.y
-local h_holding
-if self.category==RAT.cat.plane then
-h_holding=1200
-else
-h_holding=150
-end
-h_holding=self:_Randomize(h_holding,0.2)
-local Hh_holding=H_holding+h_holding
-if landing==RAT.wp.air then
-Hh_holding=H_departure
-end
-local d_holding=Pholding:Get2DDistance(Pdestination)
-local heading
-local d_total
-if self.returnzone then
-heading=self:_Course(Pdeparture,Preturn)
-d_total=Pdeparture:Get2DDistance(Preturn)+Preturn:Get2DDistance(Pholding)
-else
-heading=self:_Course(Pdeparture,Pholding)
-d_total=Pdeparture:Get2DDistance(Pholding)
-end
-if takeoff==RAT.wp.air then
-local H_departure_max
-if landing==RAT.wp.air then
-H_departure_max=H_departure
-else
-H_departure_max=d_total*math.tan(AlphaDescent)+Hh_holding
-end
-H_departure=math.min(H_departure,H_departure_max)
-end
-local deltaH=math.abs(H_departure-Hh_holding)
-local phi=math.atan(deltaH/d_total)
-local phi_climb
-local phi_descent
-if(H_departure>Hh_holding)then
-phi_climb=AlphaClimb+phi
-phi_descent=AlphaDescent-phi
-else
-phi_climb=AlphaClimb-phi
-phi_descent=AlphaDescent+phi
-end
-local D_total
-if self.returnzone then
-D_total=math.sqrt(deltaH*deltaH+d_total/2*d_total/2)
-else
-D_total=math.sqrt(deltaH*deltaH+d_total*d_total)
-end
-local gamma=math.rad(180)-phi_climb-phi_descent
-local a=D_total*math.sin(phi_climb)/math.sin(gamma)
-local b=D_total*math.sin(phi_descent)/math.sin(gamma)
-local hphi_max=b*math.sin(phi_climb)
-local hphi_max2=a*math.sin(phi_descent)
-local h_max1=b*math.sin(AlphaClimb)
-local h_max2=a*math.sin(AlphaDescent)
-local h_max
-if(H_departure>Hh_holding)then
-h_max=math.min(h_max1,h_max2)
-else
-h_max=math.max(h_max1,h_max2)
-end
-local FLmax=h_max+H_departure
-local FLmin=math.max(H_departure,Hh_holding)
-if self.category==RAT.cat.heli then
-FLmin=math.max(H_departure,H_destination)+50
-FLmax=math.max(H_departure,H_destination)+1000
-end
-FLmax=math.min(FLmax,self.aircraft.ceiling)
-if self.FLminuser then
-FLmin=math.max(self.FLminuser,FLmin)
-end
-if self.FLmaxuser then
-FLmax=math.min(self.FLmaxuser,FLmax)
-end
-if FLmin>FLmax then
-FLmin=FLmax
-end
-if FLcruise_expectFLmax then
-FLcruise_expect=FLmax
-end
-local FLcruise=UTILS.RandomGaussian(FLcruise_expect,math.abs(FLmax-FLmin)/4,FLmin,FLmax)
-if self.FLuser then
-FLcruise=self.FLuser
-FLcruise=math.max(FLcruise,FLmin)
-FLcruise=math.min(FLcruise,FLmax)
-end
-local h_climb=FLcruise-H_departure
-local h_descent=FLcruise-Hh_holding
-local d_climb=h_climb/math.tan(AlphaClimb)
-local d_descent=h_descent/math.tan(AlphaDescent)
-local d_cruise=d_total-d_climb-d_descent
-local text=string.format("\n******************************************************\n")
-text=text..string.format("Template = %s\n",self.SpawnTemplatePrefix)
-text=text..string.format("Alias = %s\n",self.alias)
-text=text..string.format("Group name = %s\n\n",self:_AnticipatedGroupName())
-text=text..string.format("Speeds:\n")
-text=text..string.format("VxCruiseMin = %6.1f m/s = %5.1f km/h\n",VxCruiseMin,VxCruiseMin*3.6)
-text=text..string.format("VxCruiseMax = %6.1f m/s = %5.1f km/h\n",VxCruiseMax,VxCruiseMax*3.6)
-text=text..string.format("VxCruise = %6.1f m/s = %5.1f km/h\n",VxCruise,VxCruise*3.6)
-text=text..string.format("VxClimb = %6.1f m/s = %5.1f km/h\n",VxClimb,VxClimb*3.6)
-text=text..string.format("VxDescent = %6.1f m/s = %5.1f km/h\n",VxDescent,VxDescent*3.6)
-text=text..string.format("VxHolding = %6.1f m/s = %5.1f km/h\n",VxHolding,VxHolding*3.6)
-text=text..string.format("VxFinal = %6.1f m/s = %5.1f km/h\n",VxFinal,VxFinal*3.6)
-text=text..string.format("VyClimb = %6.1f m/s\n",VyClimb)
-text=text..string.format("\nDistances:\n")
-text=text..string.format("d_climb = %6.1f km\n",d_climb/1000)
-text=text..string.format("d_cruise = %6.1f km\n",d_cruise/1000)
-text=text..string.format("d_descent = %6.1f km\n",d_descent/1000)
-text=text..string.format("d_holding = %6.1f km\n",d_holding/1000)
-text=text..string.format("d_total = %6.1f km\n",d_total/1000)
-text=text..string.format("\nHeights:\n")
-text=text..string.format("H_departure = %6.1f m ASL\n",H_departure)
-text=text..string.format("H_destination = %6.1f m ASL\n",H_destination)
-text=text..string.format("H_holding = %6.1f m ASL\n",H_holding)
-text=text..string.format("h_climb = %6.1f m\n",h_climb)
-text=text..string.format("h_descent = %6.1f m\n",h_descent)
-text=text..string.format("h_holding = %6.1f m\n",h_holding)
-text=text..string.format("delta H = %6.1f m\n",deltaH)
-text=text..string.format("FLmin = %6.1f m ASL = FL%03d\n",FLmin,FLmin/RAT.unit.FL2m)
-text=text..string.format("FLcruise = %6.1f m ASL = FL%03d\n",FLcruise,FLcruise/RAT.unit.FL2m)
-text=text..string.format("FLmax = %6.1f m ASL = FL%03d\n",FLmax,FLmax/RAT.unit.FL2m)
-text=text..string.format("\nAngles:\n")
-text=text..string.format("Alpha climb = %6.2f Deg\n",math.deg(AlphaClimb))
-text=text..string.format("Alpha descent = %6.2f Deg\n",math.deg(AlphaDescent))
-text=text..string.format("Phi (slope) = %6.2f Deg\n",math.deg(phi))
-text=text..string.format("Phi climb = %6.2f Deg\n",math.deg(phi_climb))
-text=text..string.format("Phi descent = %6.2f Deg\n",math.deg(phi_descent))
-if self.Debug then
-local h_climb_max=FLmax-H_departure
-local h_descent_max=FLmax-Hh_holding
-local d_climb_max=h_climb_max/math.tan(AlphaClimb)
-local d_descent_max=h_descent_max/math.tan(AlphaDescent)
-local d_cruise_max=d_total-d_climb_max-d_descent_max
-text=text..string.format("Heading = %6.1f Deg\n",heading)
-text=text..string.format("\nSSA triangle:\n")
-text=text..string.format("D_total = %6.1f km\n",D_total/1000)
-text=text..string.format("gamma = %6.1f Deg\n",math.deg(gamma))
-text=text..string.format("a = %6.1f m\n",a)
-text=text..string.format("b = %6.1f m\n",b)
-text=text..string.format("hphi_max = %6.1f m\n",hphi_max)
-text=text..string.format("hphi_max2 = %6.1f m\n",hphi_max2)
-text=text..string.format("h_max1 = %6.1f m\n",h_max1)
-text=text..string.format("h_max2 = %6.1f m\n",h_max2)
-text=text..string.format("h_max = %6.1f m\n",h_max)
-text=text..string.format("\nMax heights and distances:\n")
-text=text..string.format("d_climb_max = %6.1f km\n",d_climb_max/1000)
-text=text..string.format("d_cruise_max = %6.1f km\n",d_cruise_max/1000)
-text=text..string.format("d_descent_max = %6.1f km\n",d_descent_max/1000)
-text=text..string.format("h_climb_max = %6.1f m\n",h_climb_max)
-text=text..string.format("h_descent_max = %6.1f m\n",h_descent_max)
-end
-text=text..string.format("******************************************************\n")
-self:T2(RAT.id..text)
-if d_cruise<0 then
-d_cruise=100
-end
-local wp={}
-local c={}
-local wpholding=nil
-local wpfinal=nil
-c[#c+1]=Pdeparture
-wp[#wp+1]=self:_Waypoint(#wp+1,"Departure",takeoff,c[#wp+1],VxClimb,H_departure,departure)
-self.waypointdescriptions[#wp]="Departure"
-self.waypointstatus[#wp]=RAT.status.Departure
-if takeoff==RAT.wp.air then
-if d_climb<5000 or d_cruise<5000 then
-d_cruise=d_cruise+d_climb
-else
-c[#c+1]=c[#c]:Translate(d_climb,heading)
-wp[#wp+1]=self:_Waypoint(#wp+1,"Begin of Cruise",RAT.wp.cruise,c[#wp+1],VxCruise,FLcruise)
-self.waypointdescriptions[#wp]="Begin of Cruise"
-self.waypointstatus[#wp]=RAT.status.Cruise
-end
-else
-c[#c+1]=c[#c]:Translate(d_climb/2,heading)
-c[#c+1]=c[#c]:Translate(d_climb/2,heading)
-wp[#wp+1]=self:_Waypoint(#wp+1,"Climb",RAT.wp.climb,c[#wp+1],VxClimb,H_departure+(FLcruise-H_departure)/2)
-self.waypointdescriptions[#wp]="Climb"
-self.waypointstatus[#wp]=RAT.status.Climb
-wp[#wp+1]=self:_Waypoint(#wp+1,"Begin of Cruise",RAT.wp.cruise,c[#wp+1],VxCruise,FLcruise)
-self.waypointdescriptions[#wp]="Begin of Cruise"
-self.waypointstatus[#wp]=RAT.status.Cruise
-end
-if self.returnzone then
-c[#c+1]=Preturn
-wp[#wp+1]=self:_Waypoint(#wp+1,"Return Zone",RAT.wp.cruise,c[#wp+1],VxCruise,FLcruise)
-self.waypointdescriptions[#wp]="Return Zone"
-self.waypointstatus[#wp]=RAT.status.Uturn
-end
-if landing==RAT.wp.air then
-c[#c+1]=Pdestination
-wp[#wp+1]=self:_Waypoint(#wp+1,"Final Destination",RAT.wp.finalwp,c[#wp+1],VxCruise,FLcruise)
-self.waypointdescriptions[#wp]="Final Destination"
-self.waypointstatus[#wp]=RAT.status.Destination
-elseif self.returnzone then
-c[#c+1]=c[#c]:Translate(d_cruise/2,heading-180)
-wp[#wp+1]=self:_Waypoint(#wp+1,"End of Cruise",RAT.wp.cruise,c[#wp+1],VxCruise,FLcruise)
-self.waypointdescriptions[#wp]="End of Cruise"
-self.waypointstatus[#wp]=RAT.status.Descent
-else
-c[#c+1]=c[#c]:Translate(d_cruise,heading)
-wp[#wp+1]=self:_Waypoint(#wp+1,"End of Cruise",RAT.wp.cruise,c[#wp+1],VxCruise,FLcruise)
-self.waypointdescriptions[#wp]="End of Cruise"
-self.waypointstatus[#wp]=RAT.status.Descent
-end
-if landing==RAT.wp.landing then
-if self.returnzone then
-c[#c+1]=c[#c]:Translate(d_descent/2,heading-180)
-wp[#wp+1]=self:_Waypoint(#wp+1,"Descent",RAT.wp.descent,c[#wp+1],VxDescent,FLcruise-(FLcruise-(h_holding+H_holding))/2)
-self.waypointdescriptions[#wp]="Descent"
-self.waypointstatus[#wp]=RAT.status.DescentHolding
-else
-c[#c+1]=c[#c]:Translate(d_descent/2,heading)
-wp[#wp+1]=self:_Waypoint(#wp+1,"Descent",RAT.wp.descent,c[#wp+1],VxDescent,FLcruise-(FLcruise-(h_holding+H_holding))/2)
-self.waypointdescriptions[#wp]="Descent"
-self.waypointstatus[#wp]=RAT.status.DescentHolding
-end
-end
-if landing==RAT.wp.landing then
-c[#c+1]=Pholding
-wp[#wp+1]=self:_Waypoint(#wp+1,"Holding Point",RAT.wp.holding,c[#wp+1],VxHolding,H_holding+h_holding)
-self.waypointdescriptions[#wp]="Holding Point"
-self.waypointstatus[#wp]=RAT.status.Holding
-wpholding=#wp
-c[#c+1]=Pdestination
-wp[#wp+1]=self:_Waypoint(#wp+1,"Final Destination",landing,c[#wp+1],VxFinal,H_destination,destination)
-self.waypointdescriptions[#wp]="Final Destination"
-self.waypointstatus[#wp]=RAT.status.Destination
-end
-wpfinal=#wp
-local waypoints={}
-for _,p in ipairs(wp)do
-table.insert(waypoints,p)
-end
-self:_Routeinfo(waypoints,"Waypoint info in set_route:")
-if self.returnzone then
-return departure,destination_returnzone,waypoints,wpholding,wpfinal
-else
-return departure,destination,waypoints,wpholding,wpfinal
-end
-end
-function RAT:_PickDeparture(takeoff)
-local departures={}
-if self.random_departure then
-for _,_airport in pairs(self.airports)do
-local airport=_airport
-local name=airport:GetName()
-if not self:_Excluded(name)then
-if takeoff==RAT.wp.air then
-table.insert(departures,airport:GetZone())
-else
-local nspots=1
-if self.termtype~=nil then
-nspots=airport:GetParkingSpotsNumber(self.termtype)
-end
-if nspots>0 then
-table.insert(departures,airport)
-end
-end
-end
-end
-else
-for _,name in pairs(self.departure_ports)do
-local dep=nil
-if self:_AirportExists(name)then
-if takeoff==RAT.wp.air then
-dep=AIRBASE:FindByName(name):GetZone()
-else
-dep=AIRBASE:FindByName(name)
-if self.termtype~=nil and dep~=nil then
-local _dep=dep
-local nspots=_dep:GetParkingSpotsNumber(self.termtype)
-if nspots==0 then
-dep=nil
-end
-end
-end
-elseif self:_ZoneExists(name)then
-if takeoff==RAT.wp.air then
-dep=ZONE:FindByName(name)
-else
-self:E(RAT.id..string.format("ERROR! Takeoff is not in air. Cannot use %s as departure.",name))
-end
-else
-self:E(RAT.id..string.format("ERROR: No airport or zone found with name %s.",name))
-end
-if dep then
-table.insert(departures,dep)
-end
-end
-end
-self:T(RAT.id..string.format("Number of possible departures for %s= %d",self.alias,#departures))
-local departure=departures[math.random(#departures)]
-local text
-if departure and departure:GetName()then
-if takeoff==RAT.wp.air then
-text=string.format("%s: Chosen departure zone: %s",self.alias,departure:GetName())
-else
-text=string.format("%s: Chosen departure airport: %s (ID %d)",self.alias,departure:GetName(),departure:GetID())
-end
-self:T(RAT.id..text)
-else
-self:E(RAT.id..string.format("ERROR! No departure airport or zone found for %s.",self.alias))
-departure=nil
-end
-return departure
-end
-function RAT:_PickDestination(departure,q,minrange,maxrange,random,landing)
-minrange=minrange or self.mindist
-maxrange=maxrange or self.maxdist
-local destinations={}
-if random then
-for _,_airport in pairs(self.airports)do
-local airport=_airport
-local name=airport:GetName()
-if self:_IsFriendly(name)and not self:_Excluded(name)and name~=departure:GetName()then
-local distance=q:Get2DDistance(airport:GetCoordinate())
-if distance>=minrange and distance<=maxrange then
-if landing==RAT.wp.air then
-table.insert(destinations,airport:GetZone())
-else
-local nspot=1
-if self.termtype then
-nspot=airport:GetParkingSpotsNumber(self.termtype)
-end
-if nspot>0 then
-table.insert(destinations,airport)
-end
-end
-end
-end
-end
-else
-for _,name in pairs(self.destination_ports)do
-if name~=departure:GetName()then
-local dest=nil
-if self:_AirportExists(name)then
-if landing==RAT.wp.air then
-dest=AIRBASE:FindByName(name):GetZone()
-else
-dest=AIRBASE:FindByName(name)
-local nspot=1
-if self.termtype then
-nspot=dest:GetParkingSpotsNumber(self.termtype)
-end
-if nspot==0 then
-dest=nil
-end
-end
-elseif self:_ZoneExists(name)then
-if landing==RAT.wp.air then
-dest=ZONE:FindByName(name)
-else
-self:E(RAT.id..string.format("ERROR! Landing is not in air. Cannot use zone %s as destination!",name))
-end
-else
-self:E(RAT.id..string.format("ERROR! No airport or zone found with name %s",name))
-end
-if dest then
-local distance=q:Get2DDistance(dest:GetCoordinate())
-if distance>=minrange and distance<=maxrange then
-table.insert(destinations,dest)
-else
-local text=string.format("Destination %s is ouside range. Distance = %5.1f km, min = %5.1f km, max = %5.1f km.",name,distance,minrange,maxrange)
-self:T(RAT.id..text)
-end
-end
-end
-end
-end
-self:T(RAT.id..string.format("Number of possible destinations = %s.",#destinations))
-if#destinations>0 then
-local function compare(a,b)
-local qa=q:Get2DDistance(a:GetCoordinate())
-local qb=q:Get2DDistance(b:GetCoordinate())
-return qa0 then
-destination=destinations[math.random(#destinations)]
-local text
-if landing==RAT.wp.air then
-text=string.format("%s: Chosen destination zone: %s.",self.alias,destination:GetName())
-else
-text=string.format("%s Chosen destination airport: %s (ID %d).",self.alias,destination:GetName(),destination:GetID())
-end
-self:T(RAT.id..text)
-else
-self:E(RAT.id.."ERROR! No destination airport or zone found.")
-destination=nil
-end
-return destination
-end
-function RAT:_GetAirportsInZone(zone)
-local airports={}
-for _,airport in pairs(self.airports)do
-local name=airport:GetName()
-local coord=airport:GetCoordinate()
-if zone:IsPointVec3InZone(coord)then
-table.insert(airports,name)
-end
-end
-return airports
-end
-function RAT:_Excluded(port)
-for _,name in pairs(self.excluded_ports)do
-if name==port then
-return true
-end
-end
-return false
-end
-function RAT:_IsFriendly(port)
-for _,airport in pairs(self.airports)do
-local name=airport:GetName()
-if name==port then
-return true
-end
-end
-return false
-end
-function RAT:_GetAirportsOfMap()
-local _coalition
-for i=0,2 do
-if i==0 then
-_coalition=coalition.side.NEUTRAL
-elseif i==1 then
-_coalition=coalition.side.RED
-elseif i==2 then
-_coalition=coalition.side.BLUE
-end
-local ab=coalition.getAirbases(i)
-for _,airbase in pairs(ab)do
-local _id=airbase:getID()
-local _p=airbase:getPosition().p
-local _name=airbase:getName()
-local _myab=AIRBASE:FindByName(_name)
-if _myab then
-table.insert(self.airports_map,_myab)
-local text="MOOSE: Airport ID = ".._myab:GetID().." and Name = ".._myab:GetName()..", Category = ".._myab:GetCategory()..", TypeName = ".._myab:GetTypeName()
-self:T(RAT.id..text)
-else
-self:E(RAT.id..string.format("WARNING: Airbase %s does not exsist as MOOSE object!",tostring(_name)))
-end
-end
-end
-end
-function RAT:_GetAirportsOfCoalition()
-for _,coalition in pairs(self.ctable)do
-for _,_airport in pairs(self.airports_map)do
-local airport=_airport
-local category=airport:GetAirbaseCategory()
-if airport:GetCoalition()==coalition then
-local condition1=self.category==RAT.cat.plane and category==Airbase.Category.HELIPAD
-local condition2=self.category==RAT.cat.plane and category==Airbase.Category.SHIP
-if not(condition1 or condition2)then
-table.insert(self.airports,airport)
-end
-end
-end
-end
-if#self.airports==0 then
-local text=string.format("No possible departure/destination airports found for RAT %s.",tostring(self.alias))
-MESSAGE:New(text,10):ToAll()
-self:E(RAT.id..text)
-end
-end
-function RAT:Status(message,forID)
-if message==nil then
-message=false
-end
-if forID==nil then
-forID=false
-end
-local Tnow=timer.getTime()
-local nalive=0
-for spawnindex,ratcraft in ipairs(self.ratcraft)do
-local group=ratcraft.group
-if group and group:IsAlive()and(group:GetCoordinate()or group:GetVec3())then
-nalive=nalive+1
-local prefix=self:_GetPrefixFromGroup(group)
-local life=self:_GetLife(group)
-local fuel=group:GetFuel()*100.0
-local airborne=group:InAir()
-local coords=group:GetCoordinate()or group:GetVec3()
-local alt=1000
-if coords then
-alt=coords.y or 1000
-end
-local departure=ratcraft.departure:GetName()
-local destination=ratcraft.destination:GetName()
-local type=self.aircraft.type
-local status=ratcraft.status
-local active=ratcraft.active
-local Nunits=ratcraft.nunits
-local N0units=group:GetInitialSize()
-local Tg=0
-local Dg=0
-local dTlast=0
-local stationary=false
-if airborne then
-ratcraft["Tground"]=nil
-ratcraft["Pground"]=nil
-ratcraft["Uground"]=nil
-ratcraft["Tlastcheck"]=nil
-else
-if ratcraft["Tground"]then
-Tg=Tnow-ratcraft["Tground"]
-Dg=coords:Get2DDistance(ratcraft["Pground"])
-dTlast=Tnow-ratcraft["Tlastcheck"]
-if dTlast>self.Tinactive then
-for _,_unit in pairs(group:GetUnits())do
-if _unit and _unit:IsAlive()then
-local unitname=_unit:GetName()
-local unitcoord=_unit:GetCoordinate()
-local Ug=unitcoord:Get2DDistance(ratcraft.Uground[unitname])
-self:T2(RAT.id..string.format("Unit %s travelled distance on ground %.1f m since %d seconds.",unitname,Ug,dTlast))
-if Ug<50 and active and status~=RAT.status.EventBirth then
-stationary=true
-end
-ratcraft["Uground"][unitname]=unitcoord
-end
-end
-ratcraft["Tlastcheck"]=Tnow
-ratcraft["Pground"]=coords
-end
-else
-ratcraft["Tground"]=Tnow
-ratcraft["Tlastcheck"]=Tnow
-ratcraft["Pground"]=coords
-ratcraft["Uground"]={}
-for _,_unit in pairs(group:GetUnits())do
-local unitname=_unit:GetName()
-ratcraft.Uground[unitname]=_unit:GetCoordinate()
-end
-end
-end
-local Pn=coords
-local Dtravel=Pn:Get2DDistance(ratcraft["Pnow"])
-ratcraft["Pnow"]=Pn
-ratcraft["Distance"]=ratcraft["Distance"]+Dtravel
-local Ddestination=Pn:Get2DDistance(ratcraft.destination:GetCoordinate())
-if(forID and spawnindex==forID)or(not forID)then
-local text=string.format("ID %i of flight %s",spawnindex,prefix)
-if N0units>1 then
-text=text..string.format(" (%d/%d)\n",Nunits,N0units)
-else
-text=text.."\n"
-end
-if self.commute then
-text=text..string.format("%s commuting between %s and %s\n",type,departure,destination)
-elseif self.continuejourney then
-text=text..string.format("%s travelling from %s to %s (and continueing form there)\n",type,departure,destination)
-else
-text=text..string.format("%s travelling from %s to %s\n",type,departure,destination)
-end
-text=text..string.format("Status: %s",status)
-if airborne then
-text=text.." [airborne]\n"
-else
-text=text.." [on ground]\n"
-end
-text=text..string.format("Fuel = %3.0f %%\n",fuel)
-text=text..string.format("Life = %3.0f %%\n",life)
-text=text..string.format("FL%03d = %i m ASL\n",alt/RAT.unit.FL2m,alt)
-text=text..string.format("Distance travelled = %6.1f km\n",ratcraft["Distance"]/1000)
-text=text..string.format("Distance to destination = %6.1f km",Ddestination/1000)
-if not airborne then
-text=text..string.format("\nTime on ground = %6.0f seconds\n",Tg)
-text=text..string.format("Position change = %8.1f m since %3.0f seconds.",Dg,dTlast)
-end
-self:T(RAT.id..text)
-if message then
-MESSAGE:New(text,20):ToAll()
-end
-end
-if not airborne then
-if stationary then
-local text=string.format("Group %s is despawned after being %d seconds inaktive on ground.",self.alias,dTlast)
-self:T(RAT.id..text)
-self:_Despawn(group)
-end
-if life<10 and Dtravel<100 then
-local text=string.format("Damaged group %s is despawned. Life = %3.0f",self.alias,life)
-self:T(RAT.id..text)
-self:_Despawn(group)
-end
-end
-if ratcraft.despawnme then
-local text=string.format("Flight %s will be despawned NOW!",self.alias)
-self:T(RAT.id..text)
-if(not self.norespawn)and(not self.respawn_after_takeoff)then
-local idx=self:GetSpawnIndexFromGroup(group)
-local coord=group:GetCoordinate()
-self:_Respawn(idx,coord,0)
-end
-if self.despawnair then
-self:_Despawn(group,0)
-end
-end
-else
-local text=string.format("Group does not exist in loop ratcraft status.")
-self:T2(RAT.id..text)
-end
-end
-local text=string.format("Alive groups of %s: %d, nalive=%d/%d",self.alias,self.alive,nalive,self.ngroups)
-self:T(RAT.id..text)
-MESSAGE:New(text,20):ToAllIf(message and not forID)
-end
-function RAT:_GetLife(group)
-local life=0.0
-if group and group:IsAlive()then
-local unit=group:GetUnit(1)
-if unit then
-life=unit:GetLife()/unit:GetLife0()*100
-else
-self:T2(RAT.id.."ERROR! Unit does not exist in RAT_Getlife(). Returning zero.")
-end
-else
-self:T2(RAT.id.."ERROR! Group does not exist in RAT_Getlife(). Returning zero.")
-end
-return life
-end
-function RAT:_SetStatus(group,status)
-if group and group:IsAlive()then
-local index=self:GetSpawnIndexFromGroup(group)
-if self.ratcraft[index]then
-self.ratcraft[index].status=status
-local no1=status==RAT.status.Departure
-local no2=status==RAT.status.EventBirthAir
-local no3=status==RAT.status.Holding
-local text=string.format("Flight %s: %s.",group:GetName(),status)
-self:T(RAT.id..text)
-if not(no1 or no2 or no3)then
-MESSAGE:New(text,10):ToAllIf(self.reportstatus)
-end
-end
-end
-end
-function RAT:GetStatus(group)
-if group and group:IsAlive()then
-local index=self:GetSpawnIndexFromGroup(group)
-if self.ratcraft[index]then
-return self.ratcraft[index].status
-end
-end
-return"nonexistant"
-end
-function RAT:_OnBirth(EventData)
-self:F3(EventData)
-self:T3(RAT.id.."Captured event birth!")
-local SpawnGroup=EventData.IniGroup
-if SpawnGroup then
-local EventPrefix=self:_GetPrefixFromGroup(SpawnGroup)
-if EventPrefix then
-if EventPrefix==self.alias then
-local text="Event: Group "..SpawnGroup:GetName().." was born."
-self:T(RAT.id..text)
-local status="unknown in birth"
-if SpawnGroup:InAir()then
-status=RAT.status.EventBirthAir
-elseif self.uncontrolled then
-status=RAT.status.Uncontrolled
-else
-status=RAT.status.EventBirth
-end
-self:_SetStatus(SpawnGroup,status)
-local i=self:GetSpawnIndexFromGroup(SpawnGroup)
-local _departure=self.ratcraft[i].departure:GetName()
-local _destination=self.ratcraft[i].destination:GetName()
-local _nrespawn=self.ratcraft[i].nrespawn
-local _takeoff=self.ratcraft[i].takeoff
-local _landing=self.ratcraft[i].landing
-local _livery=self.ratcraft[i].livery
-local _airbase=AIRBASE:FindByName(_departure)
-local onrunway=false
-if _airbase then
-if self.checkonrunway and _takeoff~=RAT.wp.runway and _takeoff~=RAT.wp.air then
-onrunway=_airbase:CheckOnRunWay(SpawnGroup,self.onrunwayradius,false)
-end
-end
-if onrunway then
-local text=string.format("ERROR: RAT group of %s was spawned on runway. Group #%d will be despawned immediately!",self.alias,i)
-MESSAGE:New(text,30):ToAllIf(self.Debug)
-self:E(RAT.id..text)
-if self.Debug then
-SpawnGroup:FlareRed()
-end
-self:_Despawn(SpawnGroup)
-if(self.Ndeparture_Airports>=2 or self.random_departure)and _nrespawn new state %s.",SpawnGroup:GetName(),currentstate,status)
-self:T(RAT.id..text)
-local idx=self:GetSpawnIndexFromGroup(SpawnGroup)
-local coord=SpawnGroup:GetCoordinate()
-self:_Respawn(idx,coord)
-end
-text="Event: Group "..SpawnGroup:GetName().." will be destroyed now."
-self:T(RAT.id..text)
-self:_Despawn(SpawnGroup)
-end
-end
-end
-else
-self:T2(RAT.id.."ERROR: Group does not exist in RAT:_OnEngineShutdown().")
-end
-end
-function RAT:_OnHit(EventData)
-self:F3(EventData)
-self:T(RAT.id..string.format("Captured event Hit by %s! Initiator %s. Target %s",self.alias,tostring(EventData.IniUnitName),tostring(EventData.TgtUnitName)))
-local SpawnGroup=EventData.TgtGroup
-if SpawnGroup then
-local EventPrefix=self:_GetPrefixFromGroup(SpawnGroup)
-if EventPrefix and EventPrefix==self.alias then
-self:T(RAT.id..string.format("Event: Group %s was hit. Unit %s.",SpawnGroup:GetName(),tostring(EventData.TgtUnitName)))
-local text=string.format("%s, unit %s was hit!",self.alias,EventData.TgtUnitName)
-MESSAGE:New(text,10):ToAllIf(self.reportstatus or self.Debug)
-end
-end
-end
-function RAT:_OnDeadOrCrash(EventData)
-self:F3(EventData)
-self:T3(RAT.id.."Captured event DeadOrCrash!")
-local SpawnGroup=EventData.IniGroup
-if SpawnGroup then
-local EventPrefix=self:_GetPrefixFromGroup(SpawnGroup)
-if EventPrefix then
-if EventPrefix==self.alias then
-self.alive=self.alive-1
-local text=string.format("Event: Group %s crashed or died. Alive counter = %d.",SpawnGroup:GetName(),self.alive)
-self:T(RAT.id..text)
-if EventData.id==world.event.S_EVENT_CRASH then
-self:_OnCrash(EventData)
-elseif EventData.id==world.event.S_EVENT_DEAD then
-self:_OnDead(EventData)
-end
-end
-end
-end
-end
-function RAT:_OnDead(EventData)
-self:F3(EventData)
-self:T3(RAT.id.."Captured event Dead!")
-local SpawnGroup=EventData.IniGroup
-if SpawnGroup then
-local EventPrefix=self:_GetPrefixFromGroup(SpawnGroup)
-if EventPrefix then
-if EventPrefix==self.alias then
-local text=string.format("Event: Group %s died. Unit %s.",SpawnGroup:GetName(),EventData.IniUnitName)
-self:T(RAT.id..text)
-local status=RAT.status.EventDead
-self:_SetStatus(SpawnGroup,status)
-end
-end
-else
-self:T2(RAT.id.."ERROR: Group does not exist in RAT:_OnDead().")
-end
-end
-function RAT:_OnCrash(EventData)
-self:F3(EventData)
-self:T3(RAT.id.."Captured event Crash!")
-local SpawnGroup=EventData.IniGroup
-if SpawnGroup then
-local EventPrefix=self:_GetPrefixFromGroup(SpawnGroup)
-if EventPrefix and EventPrefix==self.alias then
-local _i=self:GetSpawnIndexFromGroup(SpawnGroup)
-self.ratcraft[_i].nunits=self.ratcraft[_i].nunits-1
-local _n=self.ratcraft[_i].nunits
-local _n0=SpawnGroup:GetInitialSize()
-local text=string.format("Event: Group %s crashed. Unit %s. Units still alive %d of %d.",SpawnGroup:GetName(),EventData.IniUnitName,_n,_n0)
-self:T(RAT.id..text)
-local status=RAT.status.EventCrash
-self:_SetStatus(SpawnGroup,status)
-if _n==0 and self.respawn_after_crash and not self.norespawn then
-local text=string.format("No units left of group %s. Group will be respawned now.",SpawnGroup:GetName())
-self:T(RAT.id..text)
-local idx=self:GetSpawnIndexFromGroup(SpawnGroup)
-local coord=SpawnGroup:GetCoordinate()
-self:_Respawn(idx,coord)
-end
-end
-else
-if self.Debug then
-self:E(RAT.id.."ERROR: Group does not exist in RAT:_OnCrash().")
-end
-end
-end
-function RAT:_Despawn(group,delay)
-if group~=nil then
-local index=self:GetSpawnIndexFromGroup(group)
-if index~=nil then
-self.ratcraft[index].group=nil
-self.ratcraft[index]["status"]="Dead"
-local despawndelay=0
-if delay then
-despawndelay=delay
-elseif self.respawn_delay then
-despawndelay=self.respawn_delay
-end
-self:T(RAT.id..string.format("%s delayed despawn in %.1f seconds.",self.alias,despawndelay))
-SCHEDULER:New(nil,self._Destroy,{self,group},despawndelay)
-if self.f10menu and self.SubMenuName~=nil then
-self.Menu[self.SubMenuName]["groups"][index]:Remove()
-end
-end
-end
-end
-function RAT:_Destroy(group)
-self:F2(group)
-local DCSGroup=group:GetDCSObject()
-if DCSGroup and DCSGroup:isExist()then
-local triggerdead=true
-for _,DCSUnit in pairs(DCSGroup:getUnits())do
-if DCSUnit then
-if triggerdead then
-self:_CreateEventDead(timer.getTime(),DCSUnit)
-triggerdead=false
-end
-_DATABASE:DeleteUnit(DCSUnit:getName())
-end
-end
-DCSGroup:destroy()
-DCSGroup=nil
-end
-return nil
-end
-function RAT:_CreateEventDead(EventTime,Initiator)
-self:F({EventTime,Initiator})
-local Event={
-id=world.event.S_EVENT_DEAD,
-time=EventTime,
-initiator=Initiator,
-}
-world.onEvent(Event)
-end
-function RAT:_Waypoint(index,description,Type,Coord,Speed,Altitude,Airport)
-local _Altitude=Altitude or Coord.y
-local Hland=Coord:GetLandHeight()
-local _Type=nil
-local _Action=nil
-local _alttype="RADIO"
-if Type==RAT.wp.cold then
-_Type="TakeOffParking"
-_Action="From Parking Area"
-_Altitude=10
-_alttype="RADIO"
-elseif Type==RAT.wp.hot then
-_Type="TakeOffParkingHot"
-_Action="From Parking Area Hot"
-_Altitude=10
-_alttype="RADIO"
-elseif Type==RAT.wp.runway then
-_Type="TakeOff"
-_Action="From Parking Area"
-_Altitude=10
-_alttype="RADIO"
-elseif Type==RAT.wp.air then
-_Type="Turning Point"
-_Action="Turning Point"
-_alttype="BARO"
-elseif Type==RAT.wp.climb then
-_Type="Turning Point"
-_Action="Turning Point"
-_alttype="BARO"
-elseif Type==RAT.wp.cruise then
-_Type="Turning Point"
-_Action="Turning Point"
-_alttype="BARO"
-elseif Type==RAT.wp.descent then
-_Type="Turning Point"
-_Action="Turning Point"
-_alttype="BARO"
-elseif Type==RAT.wp.holding then
-_Type="Turning Point"
-_Action="Turning Point"
-_alttype="BARO"
-elseif Type==RAT.wp.landing then
-_Type="Land"
-_Action="Landing"
-_Altitude=10
-_alttype="RADIO"
-elseif Type==RAT.wp.finalwp then
-_Type="Turning Point"
-_Action="Turning Point"
-_alttype="BARO"
-else
-self:E(RAT.id.."ERROR: Unknown waypoint type in RAT:Waypoint() function!")
-_Type="Turning Point"
-_Action="Turning Point"
-_alttype="RADIO"
-end
-local text=string.format("\n******************************************************\n")
-text=text..string.format("Waypoint = %d\n",index)
-text=text..string.format("Template = %s\n",self.SpawnTemplatePrefix)
-text=text..string.format("Alias = %s\n",self.alias)
-text=text..string.format("Type: %i - %s\n",Type,_Type)
-text=text..string.format("Action: %s\n",_Action)
-text=text..string.format("Coord: x = %6.1f km, y = %6.1f km, alt = %6.1f m\n",Coord.x/1000,Coord.z/1000,Coord.y)
-text=text..string.format("Speed = %6.1f m/s = %6.1f km/h = %6.1f knots\n",Speed,Speed*3.6,Speed*1.94384)
-text=text..string.format("Land = %6.1f m ASL\n",Hland)
-text=text..string.format("Altitude = %6.1f m (%s)\n",_Altitude,_alttype)
-if Airport then
-if Type==RAT.wp.air then
-text=text..string.format("Zone = %s\n",Airport:GetName())
-else
-text=text..string.format("Airport = %s\n",Airport:GetName())
-end
-else
-text=text..string.format("No airport/zone specified\n")
-end
-text=text.."******************************************************\n"
-self:T2(RAT.id..text)
-local RoutePoint={}
-RoutePoint.x=Coord.x
-RoutePoint.y=Coord.z
-RoutePoint.alt=_Altitude
-RoutePoint.alt_type=_alttype
-RoutePoint.type=_Type
-RoutePoint.action=_Action
-RoutePoint.speed=Speed
-RoutePoint.speed_locked=true
-RoutePoint.ETA=nil
-RoutePoint.ETA_locked=false
-RoutePoint.name=description
-if(Airport~=nil)and(Type~=RAT.wp.air)then
-local AirbaseID=Airport:GetID()
-local AirbaseCategory=Airport:GetAirbaseCategory()
-if AirbaseCategory==Airbase.Category.SHIP then
-RoutePoint.linkUnit=AirbaseID
-RoutePoint.helipadId=AirbaseID
-elseif AirbaseCategory==Airbase.Category.HELIPAD then
-RoutePoint.linkUnit=AirbaseID
-RoutePoint.helipadId=AirbaseID
-elseif AirbaseCategory==Airbase.Category.AIRDROME then
-RoutePoint.airdromeId=AirbaseID
-else
-self:T(RAT.id.."Unknown Airport category in _Waypoint()!")
-end
-end
-RoutePoint.properties={
-["vnav"]=1,
-["scale"]=0,
-["angle"]=0,
-["vangle"]=0,
-["steer"]=2,
-}
-local TaskCombo={}
-local TaskHolding=self:_TaskHolding({x=Coord.x,y=Coord.z},Altitude,Speed,self:_Randomize(90,0.9))
-local TaskWaypoint=self:_TaskFunction("RAT._WaypointFunction",self,index)
-RoutePoint.task={}
-RoutePoint.task.id="ComboTask"
-RoutePoint.task.params={}
-TaskCombo[#TaskCombo+1]=TaskWaypoint
-if Type==RAT.wp.holding then
-TaskCombo[#TaskCombo+1]=TaskHolding
-end
-RoutePoint.task.params.tasks=TaskCombo
-return RoutePoint
-end
-function RAT:_Routeinfo(waypoints,comment)
-local text=string.format("\n******************************************************\n")
-text=text..string.format("Template = %s\n",self.SpawnTemplatePrefix)
-if comment then
-text=text..comment.."\n"
-end
-text=text..string.format("Number of waypoints = %i\n",#waypoints)
-for i=1,#waypoints do
-local p=waypoints[i]
-text=text..string.format("WP #%i: x = %6.1f km, y = %6.1f km, alt = %6.1f m %s\n",i-1,p.x/1000,p.y/1000,p.alt,self.waypointdescriptions[i])
-end
-local total=0.0
-for i=1,#waypoints-1 do
-local point1=waypoints[i]
-local point2=waypoints[i+1]
-local x1=point1.x
-local y1=point1.y
-local x2=point2.x
-local y2=point2.y
-local d=math.sqrt((x1-x2)^2+(y1-y2)^2)
-local heading=self:_Course(point1,point2)
-total=total+d
-text=text..string.format("Distance from WP %i-->%i = %6.1f km. Heading = %03d : %s - %s\n",i-1,i,d/1000,heading,self.waypointdescriptions[i],self.waypointdescriptions[i+1])
-end
-text=text..string.format("Total distance = %6.1f km\n",total/1000)
-text=text..string.format("******************************************************\n")
-self:T2(RAT.id..text)
-return total
-end
-function RAT:_TaskHolding(P1,Altitude,Speed,Duration)
-local dx=3000
-local dy=0
-if self.category==RAT.cat.heli then
-dx=200
-dy=0
-end
-local P2={}
-P2.x=P1.x+dx
-P2.y=P1.y+dy
-local Task={
-id='Orbit',
-params={
-pattern=AI.Task.OrbitPattern.RACE_TRACK,
-point=P1,
-point2=P2,
-speed=Speed,
-altitude=Altitude
-}
-}
-local DCSTask={}
-DCSTask.id="ControlledTask"
-DCSTask.params={}
-DCSTask.params.task=Task
-if self.ATCswitch then
-local userflagname=string.format("%s#%03d",self.alias,self.SpawnIndex+1)
-local maxholdingduration=60*120
-DCSTask.params.stopCondition={userFlag=userflagname,userFlagValue=1,duration=maxholdingduration}
-else
-DCSTask.params.stopCondition={duration=Duration}
-end
-return DCSTask
-end
-function RAT._WaypointFunction(group,rat,wp)
-local Tnow=timer.getTime()
-local sdx=rat:GetSpawnIndexFromGroup(group)
-local departure=rat.ratcraft[sdx].departure:GetName()
-local destination=rat.ratcraft[sdx].destination:GetName()
-local landing=rat.ratcraft[sdx].landing
-local WPholding=rat.ratcraft[sdx].wpholding
-local WPfinal=rat.ratcraft[sdx].wpfinal
-local text
-text=string.format("Flight %s passing waypoint #%d %s.",group:GetName(),wp,rat.waypointdescriptions[wp])
-BASE.T(rat,RAT.id..text)
-local status=rat.waypointstatus[wp]
-rat:_SetStatus(group,status)
-if wp==WPholding then
-text=string.format("Flight %s to %s ATC: Holding and awaiting landing clearance.",group:GetName(),destination)
-MESSAGE:New(text,10):ToAllIf(rat.reportstatus)
-if rat.ATCswitch then
-if rat.f10menu then
-MENU_MISSION_COMMAND:New("Clear for landing",rat.Menu[rat.SubMenuName].groups[sdx],rat.ClearForLanding,rat,group:GetName())
-end
-rat._ATCRegisterFlight(rat,group:GetName(),Tnow)
-end
-end
-if wp==WPfinal then
-text=string.format("Flight %s arrived at final destination %s.",group:GetName(),destination)
-MESSAGE:New(text,10):ToAllIf(rat.reportstatus)
-BASE.T(rat,RAT.id..text)
-if landing==RAT.wp.air then
-text=string.format("Activating despawn switch for flight %s! Group will be detroyed soon.",group:GetName())
-MESSAGE:New(text,10):ToAllIf(rat.Debug)
-BASE.T(rat,RAT.id..text)
-rat.ratcraft[sdx].despawnme=true
-end
-end
-end
-function RAT:_TaskFunction(FunctionString,...)
-self:F2({FunctionString,arg})
-local DCSTask
-local ArgumentKey
-local templatename=self.templategroup:GetName()
-local groupname=self:_AnticipatedGroupName()
-local DCSScript={}
-DCSScript[#DCSScript+1]="local MissionControllable = GROUP:FindByName(\""..groupname.."\") "
-DCSScript[#DCSScript+1]="local RATtemplateControllable = GROUP:FindByName(\""..templatename.."\") "
-if arg and arg.n>0 then
-ArgumentKey='_'..tostring(arg):match("table: (.*)")
-self.templategroup:SetState(self.templategroup,ArgumentKey,arg)
-DCSScript[#DCSScript+1]="local Arguments = RATtemplateControllable:GetState(RATtemplateControllable, '"..ArgumentKey.."' ) "
-DCSScript[#DCSScript+1]=FunctionString.."( MissionControllable, unpack( Arguments ) )"
-else
-DCSScript[#DCSScript+1]=FunctionString.."( MissionControllable )"
-end
-DCSTask=self.templategroup:TaskWrappedAction(self.templategroup:CommandDoScript(table.concat(DCSScript)))
-return DCSTask
-end
-function RAT:_AnticipatedGroupName(index)
-local index=index or self.SpawnIndex+1
-return string.format("%s#%03d",self.alias,index)
-end
-function RAT:_ActivateUncontrolled()
-self:F()
-local idx={}
-local rat={}
-local nactive=0
-for spawnindex,ratcraft in pairs(self.ratcraft)do
-local group=ratcraft.group
-if group and group:IsAlive()then
-local text=string.format("Uncontrolled: Group = %s (spawnindex = %d), active = %s.",ratcraft.group:GetName(),spawnindex,tostring(ratcraft.active))
-self:T2(RAT.id..text)
-if ratcraft.active then
-nactive=nactive+1
-else
-table.insert(idx,spawnindex)
-end
-end
-end
-local text=string.format("Uncontrolled: Ninactive = %d, Nactive = %d (of max %d).",#idx,nactive,self.activate_max)
-self:T(RAT.id..text)
-if#idx>0 and nactive=1 then
-for i=1,nunits do
-table.insert(parkingspots,spots[1].Coordinate)
-table.insert(parkingindex,spots[1].TerminalID)
-end
-PointVec3=spots[1].Coordinate
-else
-_notenough=true
-end
-elseif spawnonairport then
-if nfree>=nunits then
-for i=1,nunits do
-table.insert(parkingspots,spots[i].Coordinate)
-table.insert(parkingindex,spots[i].TerminalID)
-end
-else
-_notenough=true
-end
-end
-if _notenough then
-if self.respawn_inair and not self.SpawnUnControlled then
-self:E(RAT.id..string.format("WARNING: Group %s has no parking spots at %s ==> air start!",self.SpawnTemplatePrefix,departure:GetName()))
-spawnonground=false
-spawnonship=false
-spawnonfarp=false
-spawnonrunway=false
-waypoints[1].type=GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][1]
-waypoints[1].action=GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][2]
-PointVec3.x=PointVec3.x+math.random(-1500,1500)
-PointVec3.z=PointVec3.z+math.random(-1500,1500)
-if self.category==RAT.cat.heli then
-PointVec3.y=PointVec3:GetLandHeight()+math.random(100,1000)
-else
-PointVec3.y=PointVec3:GetLandHeight()+math.random(500,3000)
-end
-else
-self:E(RAT.id..string.format("WARNING: Group %s has no parking spots at %s ==> No emergency air start or uncontrolled spawning ==> No spawn!",self.SpawnTemplatePrefix,departure:GetName()))
-return nil
-end
-end
-else
-end
-for UnitID=1,nunits do
-local UnitTemplate=SpawnTemplate.units[UnitID]
-local SX=UnitTemplate.x
-local SY=UnitTemplate.y
-local BX=SpawnTemplate.route.points[1].x
-local BY=SpawnTemplate.route.points[1].y
-local TX=PointVec3.x+(SX-BX)
-local TY=PointVec3.z+(SY-BY)
-if spawnonground then
-if spawnonship or spawnonfarp or spawnonrunway or automatic then
-self:T(RAT.id..string.format("RAT group %s spawning at farp, ship or runway %s.",self.alias,departure:GetName()))
-SpawnTemplate.units[UnitID].x=PointVec3.x
-SpawnTemplate.units[UnitID].y=PointVec3.z
-SpawnTemplate.units[UnitID].alt=PointVec3.y
-else
-self:T(RAT.id..string.format("RAT group %s spawning at airbase %s on parking spot id %d",self.alias,departure:GetName(),parkingindex[UnitID]))
-SpawnTemplate.units[UnitID].x=parkingspots[UnitID].x
-SpawnTemplate.units[UnitID].y=parkingspots[UnitID].z
-SpawnTemplate.units[UnitID].alt=parkingspots[UnitID].y
-end
-else
-self:T(RAT.id..string.format("RAT group %s spawning in air at %s.",self.alias,departure:GetName()))
-SpawnTemplate.units[UnitID].x=TX
-SpawnTemplate.units[UnitID].y=TY
-SpawnTemplate.units[UnitID].alt=PointVec3.y
-end
-if self.Debug then
-local unitspawn=COORDINATE:New(SpawnTemplate.units[UnitID].x,SpawnTemplate.units[UnitID].alt,SpawnTemplate.units[UnitID].y)
-unitspawn:MarkToAll(string.format("RAT %s Spawnplace unit #%d",self.alias,UnitID))
-end
-UnitTemplate.parking=nil
-UnitTemplate.parking_id=nil
-if parkingindex[UnitID]and not automatic then
-UnitTemplate.parking=parkingindex[UnitID]
-end
-self:T2(RAT.id..string.format("RAT group %s unit number %d: Parking = %s",self.alias,UnitID,tostring(UnitTemplate.parking)))
-self:T2(RAT.id..string.format("RAT group %s unit number %d: Parking ID = %s",self.alias,UnitID,tostring(UnitTemplate.parking_id)))
-SpawnTemplate.units[UnitID].heading=heading
-SpawnTemplate.units[UnitID].psi=-heading
-if livery then
-SpawnTemplate.units[UnitID].livery_id=livery
-end
-if self.actype then
-SpawnTemplate.units[UnitID]["type"]=self.actype
-end
-SpawnTemplate.units[UnitID]["skill"]=self.skill
-if self.onboardnum then
-SpawnTemplate.units[UnitID]["onboard_num"]=string.format("%s%d%02d",self.onboardnum,(self.SpawnIndex-1)%10,(self.onboardnum0-1)+UnitID)
-end
-SpawnTemplate.CoalitionID=self.coalition
-if self.country then
-SpawnTemplate.CountryID=self.country
-end
-end
-for i,wp in ipairs(waypoints)do
-SpawnTemplate.route.points[i]=wp
-end
-SpawnTemplate.x=PointVec3.x
-SpawnTemplate.y=PointVec3.z
-if self.radio then
-SpawnTemplate.communication=self.radio
-end
-if self.frequency then
-SpawnTemplate.frequency=self.frequency
-end
-if self.modulation then
-SpawnTemplate.modulation=self.modulation
-end
-self:T(SpawnTemplate)
-end
-end
-return true
-end
-function RAT:_ATCInit(airports_map)
-if not RAT.ATC.init then
-local text
-text="Starting RAT ATC.\nSimultanious = "..RAT.ATC.Nclearance.."\n".."Delay = "..RAT.ATC.delay
-BASE:T(RAT.id..text)
-RAT.ATC.init=true
-for _,ap in pairs(airports_map)do
-local name=ap:GetName()
-RAT.ATC.airport[name]={}
-RAT.ATC.airport[name].queue={}
-RAT.ATC.airport[name].busy=false
-RAT.ATC.airport[name].onfinal={}
-RAT.ATC.airport[name].Nonfinal=0
-RAT.ATC.airport[name].traffic=0
-RAT.ATC.airport[name].Tlastclearance=nil
-end
-SCHEDULER:New(nil,RAT._ATCCheck,{self},5,15)
-SCHEDULER:New(nil,RAT._ATCStatus,{self},5,60)
-RAT.ATC.T0=timer.getTime()
-end
-end
-function RAT:_ATCAddFlight(name,dest)
-BASE:T(string.format("%sATC %s: Adding flight %s with destination %s.",RAT.id,dest,name,dest))
-RAT.ATC.flight[name]={}
-RAT.ATC.flight[name].destination=dest
-RAT.ATC.flight[name].Tarrive=-1
-RAT.ATC.flight[name].holding=-1
-RAT.ATC.flight[name].Tonfinal=-1
-end
-function RAT:_ATCDelFlight(t,entry)
-for k,_ in pairs(t)do
-if k==entry then
-t[entry]=nil
-end
-end
-end
-function RAT:_ATCRegisterFlight(name,time)
-BASE:T(RAT.id.."Flight "..name.." registered at ATC for landing clearance.")
-RAT.ATC.flight[name].Tarrive=time
-RAT.ATC.flight[name].holding=0
-end
-function RAT:_ATCStatus()
-local Tnow=timer.getTime()
-for name,_ in pairs(RAT.ATC.flight)do
-local hold=RAT.ATC.flight[name].holding
-local dest=RAT.ATC.flight[name].destination
-if hold>=0 then
-local busy="Runway state is unknown"
-if RAT.ATC.airport[dest].Nonfinal>0 then
-busy="Runway is occupied by "..RAT.ATC.airport[dest].Nonfinal
-else
-busy="Runway is currently clear"
-end
-local text=string.format("ATC %s: Flight %s is holding for %i:%02d. %s.",dest,name,hold/60,hold%60,busy)
-BASE:T(RAT.id..text)
-elseif hold==RAT.ATC.onfinal then
-local Tfinal=Tnow-RAT.ATC.flight[name].Tonfinal
-local text=string.format("ATC %s: Flight %s is on final. Waiting %i:%02d for landing event.",dest,name,Tfinal/60,Tfinal%60)
-BASE:T(RAT.id..text)
-elseif hold==RAT.ATC.unregistered then
-else
-BASE:E(RAT.id.."ERROR: Unknown holding time in RAT:_ATCStatus().")
-end
-end
-end
-function RAT:_ATCCheck()
-RAT:_ATCQueue()
-local Tnow=timer.getTime()
-for name,_ in pairs(RAT.ATC.airport)do
-for qID,flight in ipairs(RAT.ATC.airport[name].queue)do
-local nqueue=#RAT.ATC.airport[name].queue
-local landing1
-if RAT.ATC.airport[name].Tlastclearance then
-landing1=(Tnow-RAT.ATC.airport[name].Tlastclearance>RAT.ATC.delay)and RAT.ATC.airport[name].Nonfinal=0 then
-RAT.ATC.flight[name].holding=Tnow-RAT.ATC.flight[name].Tarrive
-end
-local hold=RAT.ATC.flight[name].holding
-local dest=RAT.ATC.flight[name].destination
-if hold>=0 and airport==dest then
-_queue[#_queue+1]={name,hold}
-end
-end
-local function compare(a,b)
-return a[2]>b[2]
-end
-table.sort(_queue,compare)
-RAT.ATC.airport[airport].queue={}
-for k,v in ipairs(_queue)do
-table.insert(RAT.ATC.airport[airport].queue,v[1])
-end
-end
-end
-RATMANAGER={
-ClassName="RATMANAGER",
-Debug=false,
-rat={},
-name={},
-alive={},
-planned={},
-min={},
-nrat=0,
-ntot=nil,
-Tcheck=60,
-dTspawn=1.0,
-manager=nil,
-managerid=nil,
-}
-RATMANAGER.id="RATMANAGER | "
-function RATMANAGER:New(ntot)
-local self=BASE:Inherit(self,BASE:New())
-self.ntot=ntot or 1
-self:E(RATMANAGER.id..string.format("Creating manager for %d groups.",ntot))
-return self
-end
-function RATMANAGER:Add(ratobject,min)
-ratobject.norespawn=true
-ratobject.f10menu=false
-self.nrat=self.nrat+1
-self.rat[self.nrat]=ratobject
-self.alive[self.nrat]=0
-self.planned[self.nrat]=0
-self.name[self.nrat]=ratobject.alias
-self.min[self.nrat]=min or 1
-self:T(RATMANAGER.id..string.format("Adding ratobject %s with min flights = %d",self.name[self.nrat],self.min[self.nrat]))
-ratobject:Spawn(0)
-return self
-end
-function RATMANAGER:Start(delay)
-local delay=delay or 5
-local text=string.format(RATMANAGER.id.."RAT manager will be started in %d seconds.\n",delay)
-text=text..string.format("Managed groups:\n")
-for i=1,self.nrat do
-text=text..string.format("- %s with min groups %d\n",self.name[i],self.min[i])
-end
-text=text..string.format("Number of constantly alive groups %d",self.ntot)
-self:E(text)
-SCHEDULER:New(nil,self._Start,{self},delay)
-return self
-end
-function RATMANAGER:_Start()
-local n=0
-for i=1,self.nrat do
-n=n+self.min[i]
-end
-self.ntot=math.max(self.ntot,n)
-local N=self:_RollDice(self.nrat,self.ntot,self.min,self.alive)
-local time=0.0
-for i=1,self.nrat do
-for j=1,N[i]do
-time=time+self.dTspawn
-SCHEDULER:New(nil,RAT._SpawnWithRoute,{self.rat[i]},time)
-end
-end
-for i=1,self.nrat do
-if self.rat[i].uncontrolled and self.rat[i].activate_uncontrolled then
-local Tactivate=math.max(time+1,self.rat[i].activate_delay)
-SCHEDULER:New(self.rat[i],self.rat[i]._ActivateUncontrolled,{self.rat[i]},Tactivate,self.rat[i].activate_delta,self.rat[i].activate_frand)
-end
-end
-local TstartManager=math.max(time+1,self.Tcheck)
-self.manager,self.managerid=SCHEDULER:New(self,self._Manage,{self},TstartManager,self.Tcheck)
-local text=string.format(RATMANAGER.id.."Starting RAT manager with scheduler ID %s in %d seconds. Repeat interval %d seconds.",self.managerid,TstartManager,self.Tcheck)
-self:E(text)
-return self
-end
-function RATMANAGER:Stop(delay)
-delay=delay or 1
-self:E(string.format(RATMANAGER.id.."Manager will be stopped in %d seconds.",delay))
-SCHEDULER:New(nil,self._Stop,{self},delay)
-return self
-end
-function RATMANAGER:_Stop()
-self:E(string.format(RATMANAGER.id.."Stopping manager with scheduler ID %s.",self.managerid))
-self.manager:Stop(self.managerid)
-return self
-end
-function RATMANAGER:SetTcheck(dt)
-self.Tcheck=dt or 60
-return self
-end
-function RATMANAGER:SetTspawn(dt)
-self.dTspawn=dt or 1.0
-return self
-end
-function RATMANAGER:_Manage()
-local ntot=self:_Count()
-local text=string.format("Number of alive groups %d. New groups to be spawned %d.",ntot,self.ntot-ntot)
-self:T(RATMANAGER.id..text)
-local N=self:_RollDice(self.nrat,self.ntot,self.min,self.alive)
-local time=0.0
-for i=1,self.nrat do
-for j=1,N[i]do
-time=time+self.dTspawn
-self.planned[i]=self.planned[i]+1
-SCHEDULER:New(nil,RATMANAGER._Spawn,{self,i},time)
-end
-end
-end
-function RATMANAGER:_Spawn(i)
-local rat=self.rat[i]
-rat:_SpawnWithRoute()
-self.planned[i]=self.planned[i]-1
-end
-function RATMANAGER:_Count()
-local ntotal=0
-for i=1,self.nrat do
-local n=0
-local ratobject=self.rat[i]
-for spawnindex,ratcraft in pairs(ratobject.ratcraft)do
-local group=ratcraft.group
-if group and group:IsAlive()then
-n=n+1
-end
-end
-self.alive[i]=n
-ntotal=ntotal+n
-local text=string.format("Number of alive groups of %s = %d, planned=%d",self.name[i],n,self.planned[i])
-self:T(RATMANAGER.id..text)
-end
-return ntotal
-end
-function RATMANAGER:_RollDice(nrat,ntot,min,alive)
-local function sum(A,index)
-local summe=0
-for _,i in ipairs(index)do
-summe=summe+A[i]
-end
-return summe
-end
-local N={}
-local M={}
-local P={}
-for i=1,nrat do
-local a=alive[i]+self.planned[i]
-N[#N+1]=0
-M[#M+1]=math.max(a,min[i])
-P[#P+1]=math.max(min[i]-a,0)
-end
-local mini={}
-local maxi={}
-local rattab={}
-for i=1,nrat do
-table.insert(rattab,i)
-end
-local done={}
-local nnew=ntot
-for i=1,nrat do
-nnew=nnew-alive[i]-self.planned[i]
-end
-for i=1,nrat-1 do
-local r=math.random(#rattab)
-local j=rattab[r]
-table.remove(rattab,r)
-table.insert(done,j)
-local sN=sum(N,done)
-local sP=sum(P,rattab)
-maxi[j]=nnew-sN-sP
-mini[j]=P[j]
-if maxi[j]>=mini[j]then
-N[j]=math.random(mini[j],maxi[j])
-else
-N[j]=0
-end
-self:T3(string.format("RATMANAGER: i=%d, alive=%d, planned=%d, min=%d, mini=%d, maxi=%d, add=%d, sumN=%d, sumP=%d",j,alive[j],self.planned[i],min[j],mini[j],maxi[j],N[j],sN,sP))
-end
-local j=rattab[1]
-N[j]=nnew-sum(N,done)
-mini[j]=nnew-sum(N,done)
-maxi[j]=nnew-sum(N,done)
-table.remove(rattab,1)
-table.insert(done,j)
-local text=RATMANAGER.id.."\n"
-for i=1,nrat do
-text=text..string.format("%s: i=%d, alive=%d, planned=%d, min=%d, mini=%d, maxi=%d, add=%d\n",self.name[i],i,alive[i],self.planned[i],min[i],mini[i],maxi[i],N[i])
-end
-text=text..string.format("Total # of groups to add = %d",sum(N,done))
-self:T(text)
-return N
-end
-SCORING={
-ClassName="SCORING",
-ClassID=0,
-Players={},
-AutoSave=true,
-version="1.17.1"
-}
-local _SCORINGCoalition={
-[1]="Red",
-[2]="Blue",
-}
-local _SCORINGCategory={
-[Unit.Category.AIRPLANE]="Plane",
-[Unit.Category.HELICOPTER]="Helicopter",
-[Unit.Category.GROUND_UNIT]="Vehicle",
-[Unit.Category.SHIP]="Ship",
-[Unit.Category.STRUCTURE]="Structure",
-}
-function SCORING:New(GameName)
-local self=BASE:Inherit(self,BASE:New())
-if GameName then
-self.GameName=GameName
-else
-error("A game name must be given to register the scoring results")
-end
-self.ScoringObjects={}
-self.ScoringZones={}
-self:SetMessagesToAll()
-self:SetMessagesHit(false)
-self:SetMessagesDestroy(true)
-self:SetMessagesScore(true)
-self:SetMessagesZone(true)
-self:SetScaleDestroyScore(10)
-self:SetScaleDestroyPenalty(30)
-self:SetFratricide(self.ScaleDestroyPenalty*3)
-self.penaltyonfratricide=true
-self:SetCoalitionChangePenalty(self.ScaleDestroyPenalty)
-self.penaltyoncoalitionchange=true
-self:SetDisplayMessagePrefix()
-self:HandleEvent(EVENTS.Dead,self._EventOnDeadOrCrash)
-self:HandleEvent(EVENTS.Crash,self._EventOnDeadOrCrash)
-self:HandleEvent(EVENTS.Hit,self._EventOnHit)
-self:HandleEvent(EVENTS.Birth)
-self:HandleEvent(EVENTS.PlayerLeaveUnit)
-self.ScoringPlayerScan=BASE:ScheduleOnce(1,function()
-for PlayerName,PlayerUnit in pairs(_DATABASE:GetPlayerUnits())do
-self:_AddPlayerFromUnit(PlayerUnit)
-self:SetScoringMenu(PlayerUnit:GetGroup())
-end
-end)
-self.AutoSave=true
-self:OpenCSV(GameName)
-return self
-end
-function SCORING:SetDisplayMessagePrefix(DisplayMessagePrefix)
-self.DisplayMessagePrefix=DisplayMessagePrefix or""
-return self
-end
-function SCORING:SetScaleDestroyScore(Scale)
-self.ScaleDestroyScore=Scale
-return self
-end
-function SCORING:SetScaleDestroyPenalty(Scale)
-self.ScaleDestroyPenalty=Scale
-return self
-end
-function SCORING:AddUnitScore(ScoreUnit,Score)
-local UnitName=ScoreUnit:GetName()
-self.ScoringObjects[UnitName]=Score
-return self
-end
-function SCORING:RemoveUnitScore(ScoreUnit)
-local UnitName=ScoreUnit:GetName()
-self.ScoringObjects[UnitName]=nil
-return self
-end
-function SCORING:AddStaticScore(ScoreStatic,Score)
-local StaticName=ScoreStatic:GetName()
-self.ScoringObjects[StaticName]=Score
-return self
-end
-function SCORING:RemoveStaticScore(ScoreStatic)
-local StaticName=ScoreStatic:GetName()
-self.ScoringObjects[StaticName]=nil
-return self
-end
-function SCORING:AddScoreGroup(ScoreGroup,Score)
-local ScoreUnits=ScoreGroup:GetUnits()
-for ScoreUnitID,ScoreUnit in pairs(ScoreUnits)do
-local UnitName=ScoreUnit:GetName()
-self.ScoringObjects[UnitName]=Score
-end
-return self
-end
-function SCORING:AddZoneScore(ScoreZone,Score)
-local ZoneName=ScoreZone:GetName()
-self.ScoringZones[ZoneName]={}
-self.ScoringZones[ZoneName].ScoreZone=ScoreZone
-self.ScoringZones[ZoneName].Score=Score
-return self
-end
-function SCORING:RemoveZoneScore(ScoreZone)
-local ZoneName=ScoreZone:GetName()
-self.ScoringZones[ZoneName]=nil
-return self
-end
-function SCORING:SetMessagesHit(OnOff)
-self.MessagesHit=OnOff
-return self
-end
-function SCORING:IfMessagesHit()
-return self.MessagesHit
-end
-function SCORING:SetMessagesDestroy(OnOff)
-self.MessagesDestroy=OnOff
-return self
-end
-function SCORING:IfMessagesDestroy()
-return self.MessagesDestroy
-end
-function SCORING:SetMessagesScore(OnOff)
-self.MessagesScore=OnOff
-return self
-end
-function SCORING:IfMessagesScore()
-return self.MessagesScore
-end
-function SCORING:SetMessagesZone(OnOff)
-self.MessagesZone=OnOff
-return self
-end
-function SCORING:IfMessagesZone()
-return self.MessagesZone
-end
-function SCORING:SetMessagesToAll()
-self.MessagesAudience=1
-return self
-end
-function SCORING:IfMessagesToAll()
-return self.MessagesAudience==1
-end
-function SCORING:SetMessagesToCoalition()
-self.MessagesAudience=2
-return self
-end
-function SCORING:IfMessagesToCoalition()
-return self.MessagesAudience==2
-end
-function SCORING:SetFratricide(Fratricide)
-self.Fratricide=Fratricide
-return self
-end
-function SCORING:SwitchFratricide(OnOff)
-self.penaltyonfratricide=OnOff
-return self
-end
-function SCORING:SwitchTreason(OnOff)
-self.penaltyoncoalitionchange=OnOff
-return self
-end
-function SCORING:SetCoalitionChangePenalty(CoalitionChangePenalty)
-self.CoalitionChangePenalty=CoalitionChangePenalty
-return self
-end
-function SCORING:SetScoringMenu(ScoringGroup)
-local Menu=MENU_GROUP:New(ScoringGroup,'Scoring and Statistics')
-local ReportGroupSummary=MENU_GROUP_COMMAND:New(ScoringGroup,'Summary report players in group',Menu,SCORING.ReportScoreGroupSummary,self,ScoringGroup)
-local ReportGroupDetailed=MENU_GROUP_COMMAND:New(ScoringGroup,'Detailed report players in group',Menu,SCORING.ReportScoreGroupDetailed,self,ScoringGroup)
-local ReportToAllSummary=MENU_GROUP_COMMAND:New(ScoringGroup,'Summary report all players',Menu,SCORING.ReportScoreAllSummary,self,ScoringGroup)
-self:SetState(ScoringGroup,"ScoringMenu",Menu)
-return self
-end
-function SCORING:_AddPlayerFromUnit(UnitData)
-self:F(UnitData)
-if UnitData:IsAlive()then
-local UnitName=UnitData:GetName()
-local PlayerName=UnitData:GetPlayerName()
-local UnitDesc=UnitData:GetDesc()
-local UnitCategory=UnitDesc.category
-local UnitCoalition=UnitData:GetCoalition()
-local UnitTypeName=UnitData:GetTypeName()
-local UnitThreatLevel,UnitThreatType=UnitData:GetThreatLevel()
-self:T({PlayerName,UnitName,UnitCategory,UnitCoalition,UnitTypeName})
-if self.Players[PlayerName]==nil then
-self.Players[PlayerName]={}
-self.Players[PlayerName].Hit={}
-self.Players[PlayerName].Destroy={}
-self.Players[PlayerName].Goals={}
-self.Players[PlayerName].Mission={}
-self.Players[PlayerName].HitPlayers={}
-self.Players[PlayerName].Score=0
-self.Players[PlayerName].Penalty=0
-self.Players[PlayerName].PenaltyCoalition=0
-self.Players[PlayerName].PenaltyWarning=0
-end
-if not self.Players[PlayerName].UnitCoalition then
-self.Players[PlayerName].UnitCoalition=UnitCoalition
-else
-if self.Players[PlayerName].UnitCoalition~=UnitCoalition and self.penaltyoncoalitionchange then
-self.Players[PlayerName].Penalty=self.Players[PlayerName].Penalty+self.CoalitionChangePenalty or 50
-self.Players[PlayerName].PenaltyCoalition=self.Players[PlayerName].PenaltyCoalition+1
-MESSAGE:NewType(self.DisplayMessagePrefix.."Player '"..PlayerName.."' changed coalition from ".._SCORINGCoalition[self.Players[PlayerName].UnitCoalition].." to ".._SCORINGCoalition[UnitCoalition]..
-"(changed "..self.Players[PlayerName].PenaltyCoalition.." times the coalition). "..self.CoalitionChangePenalty.." penalty points added.",
-MESSAGE.Type.Information
-):ToAll()
-self:ScoreCSV(PlayerName,"","COALITION_PENALTY",1,-1*self.CoalitionChangePenalty,self.Players[PlayerName].UnitName,_SCORINGCoalition[self.Players[PlayerName].UnitCoalition],_SCORINGCategory[self.Players[PlayerName].UnitCategory],self.Players[PlayerName].UnitType,
-UnitName,_SCORINGCoalition[UnitCoalition],_SCORINGCategory[UnitCategory],UnitData:GetTypeName())
-end
-end
-self.Players[PlayerName].UnitName=UnitName
-self.Players[PlayerName].UnitCoalition=UnitCoalition
-self.Players[PlayerName].UnitCategory=UnitCategory
-self.Players[PlayerName].UnitType=UnitTypeName
-self.Players[PlayerName].UNIT=UnitData
-self.Players[PlayerName].ThreatLevel=UnitThreatLevel
-self.Players[PlayerName].ThreatType=UnitThreatType
-if self.Players[PlayerName].Penalty>self.Fratricide*0.50 and self.penaltyonfratricide then
-if self.Players[PlayerName].PenaltyWarning<1 then
-MESSAGE:NewType(self.DisplayMessagePrefix.."Player '"..PlayerName.."': WARNING! If you continue to commit FRATRICIDE and have a PENALTY score higher than "..self.Fratricide..", you will be COURT MARTIALED and DISMISSED from this mission! \nYour total penalty is: "..self.Players[PlayerName].Penalty,
-MESSAGE.Type.Information)
-:ToAll()
-self.Players[PlayerName].PenaltyWarning=self.Players[PlayerName].PenaltyWarning+1
-end
-end
-if self.Players[PlayerName].Penalty>self.Fratricide and self.penaltyonfratricide then
-MESSAGE:NewType(self.DisplayMessagePrefix.."Player '"..PlayerName.."' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!",
-MESSAGE.Type.Information)
-:ToAll()
-UnitData:GetGroup():Destroy()
-end
-end
-end
-function SCORING:AddGoalScorePlayer(PlayerName,GoalTag,Text,Score)
-self:F({PlayerName,PlayerName,GoalTag,Text,Score})
-if PlayerName then
-local PlayerData=self.Players[PlayerName]
-PlayerData.Goals[GoalTag]=PlayerData.Goals[GoalTag]or{Score=0}
-PlayerData.Goals[GoalTag].Score=PlayerData.Goals[GoalTag].Score+Score
-PlayerData.Score=PlayerData.Score+Score
-if Text then
-MESSAGE:NewType(self.DisplayMessagePrefix..Text,
-MESSAGE.Type.Information)
-:ToAll()
-end
-self:ScoreCSV(PlayerName,"","GOAL_"..string.upper(GoalTag),1,Score,nil)
-end
-end
-function SCORING:AddGoalScore(PlayerUnit,GoalTag,Text,Score)
-local PlayerName=PlayerUnit:GetPlayerName()
-self:T2({PlayerUnit.UnitName,PlayerName,GoalTag,Text,Score})
-if PlayerName then
-local PlayerData=self.Players[PlayerName]
-PlayerData.Goals[GoalTag]=PlayerData.Goals[GoalTag]or{Score=0}
-PlayerData.Goals[GoalTag].Score=PlayerData.Goals[GoalTag].Score+Score
-PlayerData.Score=PlayerData.Score+Score
-if Text then
-MESSAGE:NewType(self.DisplayMessagePrefix..Text,
-MESSAGE.Type.Information)
-:ToAll()
-end
-self:ScoreCSV(PlayerName,"","GOAL_"..string.upper(GoalTag),1,Score,PlayerUnit:GetName())
-end
-end
-function SCORING:_AddMissionTaskScore(Mission,PlayerUnit,Text,Score)
-local PlayerName=PlayerUnit:GetPlayerName()
-local MissionName=Mission:GetName()
-self:F({Mission:GetName(),PlayerUnit.UnitName,PlayerName,Text,Score})
-if PlayerName then
-local PlayerData=self.Players[PlayerName]
-if not PlayerData.Mission[MissionName]then
-PlayerData.Mission[MissionName]={}
-PlayerData.Mission[MissionName].ScoreTask=0
-PlayerData.Mission[MissionName].ScoreMission=0
-end
-self:T(PlayerName)
-self:T(PlayerData.Mission[MissionName])
-PlayerData.Score=self.Players[PlayerName].Score+Score
-PlayerData.Mission[MissionName].ScoreTask=self.Players[PlayerName].Mission[MissionName].ScoreTask+Score
-if Text then
-MESSAGE:NewType(self.DisplayMessagePrefix..Mission:GetText().." : "..Text.." Score: "..Score,
-MESSAGE.Type.Information)
-:ToAll()
-end
-self:ScoreCSV(PlayerName,"","TASK_"..MissionName:gsub(' ','_'),1,Score,PlayerUnit:GetName())
-end
-end
-function SCORING:_AddMissionGoalScore(Mission,PlayerName,Text,Score)
-local MissionName=Mission:GetName()
-self:F({Mission:GetName(),PlayerName,Text,Score})
-if PlayerName then
-local PlayerData=self.Players[PlayerName]
-if not PlayerData.Mission[MissionName]then
-PlayerData.Mission[MissionName]={}
-PlayerData.Mission[MissionName].ScoreTask=0
-PlayerData.Mission[MissionName].ScoreMission=0
-end
-self:T(PlayerName)
-self:T(PlayerData.Mission[MissionName])
-PlayerData.Score=self.Players[PlayerName].Score+Score
-PlayerData.Mission[MissionName].ScoreTask=self.Players[PlayerName].Mission[MissionName].ScoreTask+Score
-if Text then
-MESSAGE:NewType(string.format("%s%s: %s! Player %s receives %d score!",self.DisplayMessagePrefix,Mission:GetText(),Text,PlayerName,Score),MESSAGE.Type.Information):ToAll()
-end
-self:ScoreCSV(PlayerName,"","TASK_"..MissionName:gsub(' ','_'),1,Score)
-end
-end
-function SCORING:_AddMissionScore(Mission,Text,Score)
-local MissionName=Mission:GetName()
-self:F({Mission,Text,Score})
-self:F(self.Players)
-for PlayerName,PlayerData in pairs(self.Players)do
-self:F(PlayerData)
-if PlayerData.Mission[MissionName]then
-PlayerData.Score=PlayerData.Score+Score
-PlayerData.Mission[MissionName].ScoreMission=PlayerData.Mission[MissionName].ScoreMission+Score
-if Text then
-MESSAGE:NewType(self.DisplayMessagePrefix.."Player '"..PlayerName.."' has "..Text.." in "..Mission:GetText()..". "..Score.." mission score!",
-MESSAGE.Type.Information)
-:ToAll()
-end
-self:ScoreCSV(PlayerName,"","MISSION_"..MissionName:gsub(' ','_'),1,Score)
-end
-end
-end
-function SCORING:OnEventBirth(Event)
-if Event.IniUnit then
-Event.IniUnit.ThreatLevel,Event.IniUnit.ThreatType=Event.IniUnit:GetThreatLevel()
-if Event.IniObjectCategory==1 then
-local PlayerName=Event.IniUnit:GetPlayerName()
-Event.IniUnit.BirthTime=timer.getTime()
-if PlayerName then
-self:_AddPlayerFromUnit(Event.IniUnit)
-self:SetScoringMenu(Event.IniGroup)
-end
-end
-end
-end
-function SCORING:OnEventPlayerLeaveUnit(Event)
-if Event.IniUnit then
-local Menu=self:GetState(Event.IniUnit:GetGroup(),"ScoringMenu")
-if Menu then
-end
-end
-end
-function SCORING:_EventOnHit(Event)
-self:F({Event})
-local InitUnit=nil
-local InitUNIT=nil
-local InitUnitName=""
-local InitGroup=nil
-local InitGroupName=""
-local InitPlayerName=nil
-local InitCoalition=nil
-local InitCategory=nil
-local InitType=nil
-local InitUnitCoalition=nil
-local InitUnitCategory=nil
-local InitUnitType=nil
-local TargetUnit=nil
-local TargetUNIT=nil
-local TargetUnitName=""
-local TargetGroup=nil
-local TargetGroupName=""
-local TargetPlayerName=nil
-local TargetCoalition=nil
-local TargetCategory=nil
-local TargetType=nil
-local TargetUnitCoalition=nil
-local TargetUnitCategory=nil
-local TargetUnitType=nil
-if Event.IniDCSUnit then
-InitUnit=Event.IniDCSUnit
-InitUNIT=Event.IniUnit
-InitUnitName=Event.IniDCSUnitName
-InitGroup=Event.IniDCSGroup
-InitGroupName=Event.IniDCSGroupName
-InitPlayerName=Event.IniPlayerName
-InitCoalition=Event.IniCoalition
-InitCategory=Event.IniCategory
-InitType=Event.IniTypeName
-InitUnitCoalition=_SCORINGCoalition[InitCoalition]
-InitUnitCategory=_SCORINGCategory[InitCategory]
-InitUnitType=InitType
-self:T({InitUnitName,InitGroupName,InitPlayerName,InitCoalition,InitCategory,InitType,InitUnitCoalition,InitUnitCategory,InitUnitType})
-end
-if Event.TgtDCSUnit then
-TargetUnit=Event.TgtDCSUnit
-TargetUNIT=Event.TgtUnit
-TargetUnitName=Event.TgtDCSUnitName
-TargetGroup=Event.TgtDCSGroup
-TargetGroupName=Event.TgtDCSGroupName
-TargetPlayerName=Event.TgtPlayerName
-TargetCoalition=Event.TgtCoalition
-TargetCategory=Event.TgtCategory
-TargetType=Event.TgtTypeName
-TargetUnitCoalition=_SCORINGCoalition[TargetCoalition]
-TargetUnitCategory=_SCORINGCategory[TargetCategory]
-TargetUnitType=TargetType
-self:T({TargetUnitName,TargetGroupName,TargetPlayerName,TargetCoalition,TargetCategory,TargetType,TargetUnitCoalition,TargetUnitCategory,TargetUnitType})
-end
-if InitPlayerName~=nil then
-self:_AddPlayerFromUnit(InitUNIT)
-if self.Players[InitPlayerName]then
-if TargetPlayerName~=nil then
-self:_AddPlayerFromUnit(TargetUNIT)
-end
-self:T("Hitting Something")
-if TargetCategory then
-local Player=self.Players[InitPlayerName]
-Player.Hit[TargetCategory]=Player.Hit[TargetCategory]or{}
-Player.Hit[TargetCategory][TargetUnitName]=Player.Hit[TargetCategory][TargetUnitName]or{}
-local PlayerHit=Player.Hit[TargetCategory][TargetUnitName]
-PlayerHit.Score=PlayerHit.Score or 0
-PlayerHit.Penalty=PlayerHit.Penalty or 0
-PlayerHit.ScoreHit=PlayerHit.ScoreHit or 0
-PlayerHit.PenaltyHit=PlayerHit.PenaltyHit or 0
-PlayerHit.TimeStamp=PlayerHit.TimeStamp or 0
-PlayerHit.UNIT=PlayerHit.UNIT or TargetUNIT
-if PlayerHit.UNIT.ThreatType==nil then
-PlayerHit.ThreatLevel,PlayerHit.ThreatType=PlayerHit.UNIT:GetThreatLevel()
-if PlayerHit.ThreatType==nil then
-PlayerHit.ThreatLevel=1
-PlayerHit.ThreatType="Unknown"
-end
-else
-PlayerHit.ThreatLevel=PlayerHit.UNIT.ThreatLevel
-PlayerHit.ThreatType=PlayerHit.UNIT.ThreatType
-end
-if timer.getTime()-PlayerHit.TimeStamp>1 then
-PlayerHit.TimeStamp=timer.getTime()
-if TargetPlayerName~=nil then
-Player.HitPlayers[TargetPlayerName]=true
-end
-local Score=0
-if InitCoalition then
-if InitCoalition==TargetCoalition then
-local Penalty=10
-Player.Penalty=Player.Penalty+Penalty
-PlayerHit.Penalty=PlayerHit.Penalty+Penalty
-PlayerHit.PenaltyHit=PlayerHit.PenaltyHit+1
-if TargetPlayerName~=nil then
-MESSAGE:NewType(self.DisplayMessagePrefix.."Player '"..InitPlayerName.."' hit friendly player '"..TargetPlayerName.."' "..TargetUnitCategory.." ( "..TargetType.." ) "..PlayerHit.PenaltyHit.." times. "..
-"Penalty: -"..Penalty..". Score Total:"..Player.Score-Player.Penalty,
-MESSAGE.Type.Update)
-:ToAllIf(self:IfMessagesHit()and self:IfMessagesToAll())
-:ToCoalitionIf(InitCoalition,self:IfMessagesHit()and self:IfMessagesToCoalition())
-else
-MESSAGE:NewType(self.DisplayMessagePrefix.."Player '"..InitPlayerName.."' hit friendly target "..TargetUnitCategory.." ( "..TargetType.." ) "..PlayerHit.PenaltyHit.." times. "..
-"Penalty: -"..Penalty..". Score Total:"..Player.Score-Player.Penalty,
-MESSAGE.Type.Update)
-:ToAllIf(self:IfMessagesHit()and self:IfMessagesToAll())
-:ToCoalitionIf(InitCoalition,self:IfMessagesHit()and self:IfMessagesToCoalition())
-end
-self:ScoreCSV(InitPlayerName,TargetPlayerName,"HIT_PENALTY",1,-10,InitUnitName,InitUnitCoalition,InitUnitCategory,InitUnitType,TargetUnitName,TargetUnitCoalition,TargetUnitCategory,TargetUnitType)
-else
-PlayerHit.ScoreHit=PlayerHit.ScoreHit+1
-if TargetPlayerName~=nil then
-MESSAGE:NewType(self.DisplayMessagePrefix.."Player '"..InitPlayerName.."' hit enemy player '"..TargetPlayerName.."' "..TargetUnitCategory.." ( "..TargetType.." ) "..PlayerHit.ScoreHit.." times. "..
-"Score: "..PlayerHit.Score..". Score Total:"..Player.Score-Player.Penalty,
-MESSAGE.Type.Update)
-:ToAllIf(self:IfMessagesHit()and self:IfMessagesToAll())
-:ToCoalitionIf(InitCoalition,self:IfMessagesHit()and self:IfMessagesToCoalition())
-else
-MESSAGE:NewType(self.DisplayMessagePrefix.."Player '"..InitPlayerName.."' hit enemy target "..TargetUnitCategory.." ( "..TargetType.." ) "..PlayerHit.ScoreHit.." times. "..
-"Score: "..PlayerHit.Score..". Score Total:"..Player.Score-Player.Penalty,
-MESSAGE.Type.Update)
-:ToAllIf(self:IfMessagesHit()and self:IfMessagesToAll())
-:ToCoalitionIf(InitCoalition,self:IfMessagesHit()and self:IfMessagesToCoalition())
-end
-self:ScoreCSV(InitPlayerName,TargetPlayerName,"HIT_SCORE",1,1,InitUnitName,InitUnitCoalition,InitUnitCategory,InitUnitType,TargetUnitName,TargetUnitCoalition,TargetUnitCategory,TargetUnitType)
-end
-else
-MESSAGE:NewType(self.DisplayMessagePrefix.."Player '"..InitPlayerName.."' hit scenery object.",
-MESSAGE.Type.Update)
-:ToAllIf(self:IfMessagesHit()and self:IfMessagesToAll())
-:ToCoalitionIf(InitCoalition,self:IfMessagesHit()and self:IfMessagesToCoalition())
-self:ScoreCSV(InitPlayerName,"","HIT_SCORE",1,0,InitUnitName,InitUnitCoalition,InitUnitCategory,InitUnitType,TargetUnitName,"","Scenery",TargetUnitType)
-end
-end
-end
-end
-elseif InitPlayerName==nil then
-end
-if Event.WeaponPlayerName~=nil then
-self:_AddPlayerFromUnit(Event.WeaponUNIT)
-if self.Players[Event.WeaponPlayerName]then
-if TargetPlayerName~=nil then
-self:_AddPlayerFromUnit(TargetUNIT)
-end
-self:T("Hitting Scenery")
-if TargetCategory then
-local Player=self.Players[Event.WeaponPlayerName]
-Player.Hit[TargetCategory]=Player.Hit[TargetCategory]or{}
-Player.Hit[TargetCategory][TargetUnitName]=Player.Hit[TargetCategory][TargetUnitName]or{}
-local PlayerHit=Player.Hit[TargetCategory][TargetUnitName]
-PlayerHit.Score=PlayerHit.Score or 0
-PlayerHit.Penalty=PlayerHit.Penalty or 0
-PlayerHit.ScoreHit=PlayerHit.ScoreHit or 0
-PlayerHit.PenaltyHit=PlayerHit.PenaltyHit or 0
-PlayerHit.TimeStamp=PlayerHit.TimeStamp or 0
-PlayerHit.UNIT=PlayerHit.UNIT or TargetUNIT
-if PlayerHit.UNIT.ThreatType==nil then
-PlayerHit.ThreatLevel,PlayerHit.ThreatType=PlayerHit.UNIT:GetThreatLevel()
-if PlayerHit.ThreatType==nil then
-PlayerHit.ThreatLevel=1
-PlayerHit.ThreatType="Unknown"
-end
-else
-PlayerHit.ThreatLevel=PlayerHit.UNIT.ThreatLevel
-PlayerHit.ThreatType=PlayerHit.UNIT.ThreatType
-end
-if timer.getTime()-PlayerHit.TimeStamp>1 then
-PlayerHit.TimeStamp=timer.getTime()
-local Score=0
-if InitCoalition then
-if InitCoalition==TargetCoalition then
-local Penalty=10
-Player.Penalty=Player.Penalty+Penalty
-PlayerHit.Penalty=PlayerHit.Penalty+Penalty
-PlayerHit.PenaltyHit=PlayerHit.PenaltyHit+1*self.ScaleDestroyPenalty
-MESSAGE
-:NewType(self.DisplayMessagePrefix.."Player '"..Event.WeaponPlayerName.."' hit friendly target "..
-TargetUnitCategory.." ( "..TargetType.." ) "..
-"Penalty: -"..Penalty.." = "..Player.Score-Player.Penalty,
-MESSAGE.Type.Update
-)
-:ToAllIf(self:IfMessagesHit()and self:IfMessagesToAll())
-:ToCoalitionIf(Event.WeaponCoalition,self:IfMessagesHit()and self:IfMessagesToCoalition())
-self:ScoreCSV(Event.WeaponPlayerName,TargetPlayerName,"HIT_PENALTY",1,-10,Event.WeaponName,Event.WeaponCoalition,Event.WeaponCategory,Event.WeaponTypeName,TargetUnitName,TargetUnitCoalition,TargetUnitCategory,TargetUnitType)
-else
-PlayerHit.ScoreHit=PlayerHit.ScoreHit+1
-MESSAGE:NewType(self.DisplayMessagePrefix.."Player '"..Event.WeaponPlayerName.."' hit enemy target "..TargetUnitCategory.." ( "..TargetType.." ) "..
-"Score: "..PlayerHit.Score..". Score Total:"..Player.Score-Player.Penalty,
-MESSAGE.Type.Update)
-:ToAllIf(self:IfMessagesHit()and self:IfMessagesToAll())
-:ToCoalitionIf(Event.WeaponCoalition,self:IfMessagesHit()and self:IfMessagesToCoalition())
-self:ScoreCSV(Event.WeaponPlayerName,TargetPlayerName,"HIT_SCORE",1,1,Event.WeaponName,Event.WeaponCoalition,Event.WeaponCategory,Event.WeaponTypeName,TargetUnitName,TargetUnitCoalition,TargetUnitCategory,TargetUnitType)
-end
-else
-MESSAGE:NewType(self.DisplayMessagePrefix.."Player '"..Event.WeaponPlayerName.."' hit scenery object.",
-MESSAGE.Type.Update)
-:ToAllIf(self:IfMessagesHit()and self:IfMessagesToAll())
-:ToCoalitionIf(InitCoalition,self:IfMessagesHit()and self:IfMessagesToCoalition())
-self:ScoreCSV(Event.WeaponPlayerName,"","HIT_SCORE",1,0,Event.WeaponName,Event.WeaponCoalition,Event.WeaponCategory,Event.WeaponTypeName,TargetUnitName,"","Scenery",TargetUnitType)
-end
-end
-end
-end
-end
-end
-function SCORING:_EventOnDeadOrCrash(Event)
-self:F({Event})
-local TargetUnit=nil
-local TargetGroup=nil
-local TargetUnitName=""
-local TargetGroupName=""
-local TargetPlayerName=""
-local TargetCoalition=nil
-local TargetCategory=nil
-local TargetType=nil
-local TargetUnitCoalition=nil
-local TargetUnitCategory=nil
-local TargetUnitType=nil
-if Event.IniDCSUnit then
-TargetUnit=Event.IniUnit
-TargetUnitName=Event.IniDCSUnitName
-TargetGroup=Event.IniDCSGroup
-TargetGroupName=Event.IniDCSGroupName
-TargetPlayerName=Event.IniPlayerName
-TargetCoalition=Event.IniCoalition
-TargetCategory=Event.IniCategory
-TargetType=Event.IniTypeName
-TargetUnitCoalition=_SCORINGCoalition[TargetCoalition]
-TargetUnitCategory=_SCORINGCategory[TargetCategory]
-TargetUnitType=TargetType
-self:T({TargetUnitName,TargetGroupName,TargetPlayerName,TargetCoalition,TargetCategory,TargetType})
-end
-for PlayerName,Player in pairs(self.Players)do
-if Player then
-self:T("Something got destroyed")
-local InitUnitName=Player.UnitName
-local InitUnitType=Player.UnitType
-local InitCoalition=Player.UnitCoalition
-local InitCategory=Player.UnitCategory
-local InitUnitCoalition=_SCORINGCoalition[InitCoalition]
-local InitUnitCategory=_SCORINGCategory[InitCategory]
-self:T({InitUnitName,InitUnitType,InitUnitCoalition,InitCoalition,InitUnitCategory,InitCategory})
-local Destroyed=false
-if Player and Player.Hit and Player.Hit[TargetCategory]and Player.Hit[TargetCategory][TargetUnitName]and Player.Hit[TargetCategory][TargetUnitName].TimeStamp~=0 and(TargetUnit.BirthTime==nil or Player.Hit[TargetCategory][TargetUnitName].TimeStamp>TargetUnit.BirthTime)then
-local TargetThreatLevel=Player.Hit[TargetCategory][TargetUnitName].ThreatLevel
-local TargetThreatType=Player.Hit[TargetCategory][TargetUnitName].ThreatType
-Player.Destroy[TargetCategory]=Player.Destroy[TargetCategory]or{}
-Player.Destroy[TargetCategory][TargetType]=Player.Destroy[TargetCategory][TargetType]or{}
-local TargetDestroy=Player.Destroy[TargetCategory][TargetType]
-TargetDestroy.Score=TargetDestroy.Score or 0
-TargetDestroy.ScoreDestroy=TargetDestroy.ScoreDestroy or 0
-TargetDestroy.Penalty=TargetDestroy.Penalty or 0
-TargetDestroy.PenaltyDestroy=TargetDestroy.PenaltyDestroy or 0
-if TargetCoalition then
-if InitCoalition==TargetCoalition then
-local ThreatLevelTarget=TargetThreatLevel
-local ThreatTypeTarget=TargetThreatType
-local ThreatLevelPlayer=Player.ThreatLevel/10+1
-local ThreatPenalty=math.ceil((ThreatLevelTarget/ThreatLevelPlayer)*self.ScaleDestroyPenalty/10)
-self:F({ThreatLevel=ThreatPenalty,ThreatLevelTarget=ThreatLevelTarget,ThreatTypeTarget=ThreatTypeTarget,ThreatLevelPlayer=ThreatLevelPlayer})
-Player.Penalty=Player.Penalty+ThreatPenalty
-TargetDestroy.Penalty=TargetDestroy.Penalty+ThreatPenalty
-TargetDestroy.PenaltyDestroy=TargetDestroy.PenaltyDestroy+1
-if Player.HitPlayers[TargetPlayerName]then
-MESSAGE:NewType(self.DisplayMessagePrefix.."Player '"..PlayerName.."' destroyed friendly player '"..TargetPlayerName.."' "..TargetUnitCategory.." ( "..ThreatTypeTarget.." ) "..
-"Penalty: -"..ThreatPenalty.." = "..Player.Score-Player.Penalty,
-MESSAGE.Type.Information)
-:ToAllIf(self:IfMessagesDestroy()and self:IfMessagesToAll())
-:ToCoalitionIf(InitCoalition,self:IfMessagesDestroy()and self:IfMessagesToCoalition())
-else
-MESSAGE:NewType(self.DisplayMessagePrefix.."Player '"..PlayerName.."' destroyed friendly target "..TargetUnitCategory.." ( "..ThreatTypeTarget.." ) "..
-"Penalty: -"..ThreatPenalty.." = "..Player.Score-Player.Penalty,
-MESSAGE.Type.Information)
-:ToAllIf(self:IfMessagesDestroy()and self:IfMessagesToAll())
-:ToCoalitionIf(InitCoalition,self:IfMessagesDestroy()and self:IfMessagesToCoalition())
-end
-self:ScoreCSV(PlayerName,TargetPlayerName,"DESTROY_PENALTY",1,ThreatPenalty,InitUnitName,InitUnitCoalition,InitUnitCategory,InitUnitType,TargetUnitName,TargetUnitCoalition,TargetUnitCategory,TargetUnitType)
-Destroyed=true
-else
-local ThreatLevelTarget=TargetThreatLevel
-local ThreatTypeTarget=TargetThreatType
-local ThreatLevelPlayer=Player.ThreatLevel/10+1
-local ThreatScore=math.ceil((ThreatLevelTarget/ThreatLevelPlayer)*self.ScaleDestroyScore/10)
-self:F({ThreatLevel=ThreatScore,ThreatLevelTarget=ThreatLevelTarget,ThreatTypeTarget=ThreatTypeTarget,ThreatLevelPlayer=ThreatLevelPlayer})
-Player.Score=Player.Score+ThreatScore
-TargetDestroy.Score=TargetDestroy.Score+ThreatScore
-TargetDestroy.ScoreDestroy=TargetDestroy.ScoreDestroy+1
-if Player.HitPlayers[TargetPlayerName]then
-MESSAGE:NewType(self.DisplayMessagePrefix.."Player '"..PlayerName.."' destroyed enemy player '"..TargetPlayerName.."' "..TargetUnitCategory.." ( "..ThreatTypeTarget.." ) "..
-"Score: +"..ThreatScore.." = "..Player.Score-Player.Penalty,
-MESSAGE.Type.Information)
-:ToAllIf(self:IfMessagesDestroy()and self:IfMessagesToAll())
-:ToCoalitionIf(InitCoalition,self:IfMessagesDestroy()and self:IfMessagesToCoalition())
-else
-MESSAGE:NewType(self.DisplayMessagePrefix.."Player '"..PlayerName.."' destroyed enemy "..TargetUnitCategory.." ( "..ThreatTypeTarget.." ) "..
-"Score: +"..ThreatScore.." = "..Player.Score-Player.Penalty,
-MESSAGE.Type.Information)
-:ToAllIf(self:IfMessagesDestroy()and self:IfMessagesToAll())
-:ToCoalitionIf(InitCoalition,self:IfMessagesDestroy()and self:IfMessagesToCoalition())
-end
-self:ScoreCSV(PlayerName,TargetPlayerName,"DESTROY_SCORE",1,ThreatScore,InitUnitName,InitUnitCoalition,InitUnitCategory,InitUnitType,TargetUnitName,TargetUnitCoalition,TargetUnitCategory,TargetUnitType)
-Destroyed=true
-local UnitName=TargetUnit:GetName()
-local Score=self.ScoringObjects[UnitName]
-if Score then
-Player.Score=Player.Score+Score
-TargetDestroy.Score=TargetDestroy.Score+Score
-MESSAGE:NewType(self.DisplayMessagePrefix.."Special target '"..TargetUnitCategory.." ( "..ThreatTypeTarget.." ) ".." destroyed! "..
-"Player '"..PlayerName.."' receives an extra "..Score.." points! Total: "..Player.Score-Player.Penalty,
-MESSAGE.Type.Information)
-:ToAllIf(self:IfMessagesScore()and self:IfMessagesToAll())
-:ToCoalitionIf(InitCoalition,self:IfMessagesScore()and self:IfMessagesToCoalition())
-self:ScoreCSV(PlayerName,TargetPlayerName,"DESTROY_SCORE",1,Score,InitUnitName,InitUnitCoalition,InitUnitCategory,InitUnitType,TargetUnitName,TargetUnitCoalition,TargetUnitCategory,TargetUnitType)
-Destroyed=true
-end
-for ZoneName,ScoreZoneData in pairs(self.ScoringZones)do
-self:F({ScoringZone=ScoreZoneData})
-local ScoreZone=ScoreZoneData.ScoreZone
-local Score=ScoreZoneData.Score
-if ScoreZone:IsVec2InZone(TargetUnit:GetVec2())then
-Player.Score=Player.Score+Score
-TargetDestroy.Score=TargetDestroy.Score+Score
-MESSAGE:NewType(self.DisplayMessagePrefix.."Target destroyed in zone '"..ScoreZone:GetName().."'."..
-"Player '"..PlayerName.."' receives an extra "..Score.." points! ".."Total: "..Player.Score-Player.Penalty,
-MESSAGE.Type.Information)
-:ToAllIf(self:IfMessagesZone()and self:IfMessagesToAll())
-:ToCoalitionIf(InitCoalition,self:IfMessagesZone()and self:IfMessagesToCoalition())
-self:ScoreCSV(PlayerName,TargetPlayerName,"DESTROY_SCORE",1,Score,InitUnitName,InitUnitCoalition,InitUnitCategory,InitUnitType,TargetUnitName,TargetUnitCoalition,TargetUnitCategory,TargetUnitType)
-Destroyed=true
-end
-end
-end
-else
-for ZoneName,ScoreZoneData in pairs(self.ScoringZones)do
-self:F({ScoringZone=ScoreZoneData})
-local ScoreZone=ScoreZoneData.ScoreZone
-local Score=ScoreZoneData.Score
-if ScoreZone:IsVec2InZone(TargetUnit:GetVec2())then
-Player.Score=Player.Score+Score
-TargetDestroy.Score=TargetDestroy.Score+Score
-MESSAGE:NewType(self.DisplayMessagePrefix.."Scenery destroyed in zone '"..ScoreZone:GetName().."'."..
-"Player '"..PlayerName.."' receives an extra "..Score.." points! ".."Total: "..Player.Score-Player.Penalty,
-MESSAGE.Type.Information)
-:ToAllIf(self:IfMessagesZone()and self:IfMessagesToAll())
-:ToCoalitionIf(InitCoalition,self:IfMessagesZone()and self:IfMessagesToCoalition())
-self:ScoreCSV(PlayerName,"","DESTROY_SCORE",1,Score,InitUnitName,InitUnitCoalition,InitUnitCategory,InitUnitType,TargetUnitName,"","Scenery",TargetUnitType)
-Destroyed=true
-end
-end
-end
-if Destroyed then
-Player.Hit[TargetCategory][TargetUnitName].TimeStamp=0
-end
-end
-end
-end
-end
-function SCORING:ReportDetailedPlayerHits(PlayerName)
-local ScoreMessage=""
-local PlayerScore=0
-local PlayerPenalty=0
-local PlayerData=self.Players[PlayerName]
-if PlayerData then
-self:T("Score Player: "..PlayerName)
-local InitUnitCoalition=_SCORINGCoalition[PlayerData.UnitCoalition]
-local InitUnitCategory=_SCORINGCategory[PlayerData.UnitCategory]
-local InitUnitType=PlayerData.UnitType
-local InitUnitName=PlayerData.UnitName
-local ScoreMessageHits=""
-for CategoryID,CategoryName in pairs(_SCORINGCategory)do
-self:T(CategoryName)
-if PlayerData.Hit[CategoryID]then
-self:T("Hit scores exist for player "..PlayerName)
-local Score=0
-local ScoreHit=0
-local Penalty=0
-local PenaltyHit=0
-for UnitName,UnitData in pairs(PlayerData.Hit[CategoryID])do
-Score=Score+UnitData.Score
-ScoreHit=ScoreHit+UnitData.ScoreHit
-Penalty=Penalty+UnitData.Penalty
-PenaltyHit=UnitData.PenaltyHit
-end
-local ScoreMessageHit=string.format("%s: %d ",CategoryName,Score-Penalty)
-self:T(ScoreMessageHit)
-ScoreMessageHits=ScoreMessageHits..ScoreMessageHit
-PlayerScore=PlayerScore+Score
-PlayerPenalty=PlayerPenalty+Penalty
-else
-end
-end
-if ScoreMessageHits~=""then
-ScoreMessage="Hits: "..ScoreMessageHits
-end
-end
-return ScoreMessage,PlayerScore,PlayerPenalty
-end
-function SCORING:ReportDetailedPlayerDestroys(PlayerName)
-local ScoreMessage=""
-local PlayerScore=0
-local PlayerPenalty=0
-local PlayerData=self.Players[PlayerName]
-if PlayerData then
-self:T("Score Player: "..PlayerName)
-local InitUnitCoalition=_SCORINGCoalition[PlayerData.UnitCoalition]
-local InitUnitCategory=_SCORINGCategory[PlayerData.UnitCategory]
-local InitUnitType=PlayerData.UnitType
-local InitUnitName=PlayerData.UnitName
-local ScoreMessageDestroys=""
-for CategoryID,CategoryName in pairs(_SCORINGCategory)do
-if PlayerData.Destroy[CategoryID]then
-self:T("Destroy scores exist for player "..PlayerName)
-local Score=0
-local ScoreDestroy=0
-local Penalty=0
-local PenaltyDestroy=0
-for UnitName,UnitData in pairs(PlayerData.Destroy[CategoryID])do
-self:F({UnitData=UnitData})
-if UnitData~={}then
-Score=Score+UnitData.Score
-ScoreDestroy=ScoreDestroy+UnitData.ScoreDestroy
-Penalty=Penalty+UnitData.Penalty
-PenaltyDestroy=PenaltyDestroy+UnitData.PenaltyDestroy
-end
-end
-local ScoreMessageDestroy=string.format(" %s: %d ",CategoryName,Score-Penalty)
-self:T(ScoreMessageDestroy)
-ScoreMessageDestroys=ScoreMessageDestroys..ScoreMessageDestroy
-PlayerScore=PlayerScore+Score
-PlayerPenalty=PlayerPenalty+Penalty
-else
-end
-end
-if ScoreMessageDestroys~=""then
-ScoreMessage="Destroys: "..ScoreMessageDestroys
-end
-end
-return ScoreMessage,PlayerScore,PlayerPenalty
-end
-function SCORING:ReportDetailedPlayerCoalitionChanges(PlayerName)
-local ScoreMessage=""
-local PlayerScore=0
-local PlayerPenalty=0
-local PlayerData=self.Players[PlayerName]
-if PlayerData then
-self:T("Score Player: "..PlayerName)
-local InitUnitCoalition=_SCORINGCoalition[PlayerData.UnitCoalition]
-local InitUnitCategory=_SCORINGCategory[PlayerData.UnitCategory]
-local InitUnitType=PlayerData.UnitType
-local InitUnitName=PlayerData.UnitName
-local ScoreMessageCoalitionChangePenalties=""
-if PlayerData.PenaltyCoalition~=0 then
-ScoreMessageCoalitionChangePenalties=ScoreMessageCoalitionChangePenalties..string.format(" -%d (%d changed)",PlayerData.Penalty,PlayerData.PenaltyCoalition)
-PlayerPenalty=PlayerPenalty+PlayerData.Penalty
-end
-if ScoreMessageCoalitionChangePenalties~=""then
-ScoreMessage=ScoreMessage.."Coalition Penalties: "..ScoreMessageCoalitionChangePenalties
-end
-end
-return ScoreMessage,PlayerScore,PlayerPenalty
-end
-function SCORING:ReportDetailedPlayerGoals(PlayerName)
-local ScoreMessage=""
-local PlayerScore=0
-local PlayerPenalty=0
-local PlayerData=self.Players[PlayerName]
-if PlayerData then
-self:T("Score Player: "..PlayerName)
-local InitUnitCoalition=_SCORINGCoalition[PlayerData.UnitCoalition]
-local InitUnitCategory=_SCORINGCategory[PlayerData.UnitCategory]
-local InitUnitType=PlayerData.UnitType
-local InitUnitName=PlayerData.UnitName
-local ScoreMessageGoal=""
-local ScoreGoal=0
-local ScoreTask=0
-for GoalName,GoalData in pairs(PlayerData.Goals)do
-ScoreGoal=ScoreGoal+GoalData.Score
-ScoreMessageGoal=ScoreMessageGoal.."'"..GoalName.."':"..GoalData.Score.."; "
-end
-PlayerScore=PlayerScore+ScoreGoal
-if ScoreMessageGoal~=""then
-ScoreMessage="Goals: "..ScoreMessageGoal
-end
-end
-return ScoreMessage,PlayerScore,PlayerPenalty
-end
-function SCORING:ReportDetailedPlayerMissions(PlayerName)
-local ScoreMessage=""
-local PlayerScore=0
-local PlayerPenalty=0
-local PlayerData=self.Players[PlayerName]
-if PlayerData then
-self:T("Score Player: "..PlayerName)
-local InitUnitCoalition=_SCORINGCoalition[PlayerData.UnitCoalition]
-local InitUnitCategory=_SCORINGCategory[PlayerData.UnitCategory]
-local InitUnitType=PlayerData.UnitType
-local InitUnitName=PlayerData.UnitName
-local ScoreMessageMission=""
-local ScoreMission=0
-local ScoreTask=0
-for MissionName,MissionData in pairs(PlayerData.Mission)do
-ScoreMission=ScoreMission+MissionData.ScoreMission
-ScoreTask=ScoreTask+MissionData.ScoreTask
-ScoreMessageMission=ScoreMessageMission.."'"..MissionName.."'; "
-end
-PlayerScore=PlayerScore+ScoreMission+ScoreTask
-if ScoreMessageMission~=""then
-ScoreMessage="Tasks: "..ScoreTask.." Mission: "..ScoreMission.." ( "..ScoreMessageMission..")"
-end
-end
-return ScoreMessage,PlayerScore,PlayerPenalty
-end
-function SCORING:ReportScoreGroupSummary(PlayerGroup)
-local PlayerMessage=""
-self:T("Report Score Group Summary")
-local PlayerUnits=PlayerGroup:GetUnits()
-for UnitID,PlayerUnit in pairs(PlayerUnits)do
-local PlayerUnit=PlayerUnit
-local PlayerName=PlayerUnit:GetPlayerName()
-if PlayerName then
-local ReportHits,ScoreHits,PenaltyHits=self:ReportDetailedPlayerHits(PlayerName)
-ReportHits=ReportHits~=""and"\n- "..ReportHits or ReportHits
-self:F({ReportHits,ScoreHits,PenaltyHits})
-local ReportDestroys,ScoreDestroys,PenaltyDestroys=self:ReportDetailedPlayerDestroys(PlayerName)
-ReportDestroys=ReportDestroys~=""and"\n- "..ReportDestroys or ReportDestroys
-self:F({ReportDestroys,ScoreDestroys,PenaltyDestroys})
-local ReportCoalitionChanges,ScoreCoalitionChanges,PenaltyCoalitionChanges=self:ReportDetailedPlayerCoalitionChanges(PlayerName)
-ReportCoalitionChanges=ReportCoalitionChanges~=""and"\n- "..ReportCoalitionChanges or ReportCoalitionChanges
-self:F({ReportCoalitionChanges,ScoreCoalitionChanges,PenaltyCoalitionChanges})
-local ReportGoals,ScoreGoals,PenaltyGoals=self:ReportDetailedPlayerGoals(PlayerName)
-ReportGoals=ReportGoals~=""and"\n- "..ReportGoals or ReportGoals
-self:F({ReportGoals,ScoreGoals,PenaltyGoals})
-local ReportMissions,ScoreMissions,PenaltyMissions=self:ReportDetailedPlayerMissions(PlayerName)
-ReportMissions=ReportMissions~=""and"\n- "..ReportMissions or ReportMissions
-self:F({ReportMissions,ScoreMissions,PenaltyMissions})
-local PlayerScore=ScoreHits+ScoreDestroys+ScoreCoalitionChanges+ScoreGoals+ScoreMissions
-local PlayerPenalty=PenaltyHits+PenaltyDestroys+PenaltyCoalitionChanges+PenaltyGoals+PenaltyMissions
-PlayerMessage=string.format("Player '%s' Score = %d ( %d Score, -%d Penalties )",
-PlayerName,
-PlayerScore-PlayerPenalty,
-PlayerScore,
-PlayerPenalty
-)
-MESSAGE:NewType(PlayerMessage,MESSAGE.Type.Detailed):ToGroup(PlayerGroup)
-end
-end
-end
-function SCORING:ReportScoreGroupDetailed(PlayerGroup)
-local PlayerMessage=""
-self:T("Report Score Group Detailed")
-local PlayerUnits=PlayerGroup:GetUnits()
-for UnitID,PlayerUnit in pairs(PlayerUnits)do
-local PlayerUnit=PlayerUnit
-local PlayerName=PlayerUnit:GetPlayerName()
-if PlayerName then
-local ReportHits,ScoreHits,PenaltyHits=self:ReportDetailedPlayerHits(PlayerName)
-ReportHits=ReportHits~=""and"\n- "..ReportHits or ReportHits
-self:F({ReportHits,ScoreHits,PenaltyHits})
-local ReportDestroys,ScoreDestroys,PenaltyDestroys=self:ReportDetailedPlayerDestroys(PlayerName)
-ReportDestroys=ReportDestroys~=""and"\n- "..ReportDestroys or ReportDestroys
-self:F({ReportDestroys,ScoreDestroys,PenaltyDestroys})
-local ReportCoalitionChanges,ScoreCoalitionChanges,PenaltyCoalitionChanges=self:ReportDetailedPlayerCoalitionChanges(PlayerName)
-ReportCoalitionChanges=ReportCoalitionChanges~=""and"\n- "..ReportCoalitionChanges or ReportCoalitionChanges
-self:F({ReportCoalitionChanges,ScoreCoalitionChanges,PenaltyCoalitionChanges})
-local ReportGoals,ScoreGoals,PenaltyGoals=self:ReportDetailedPlayerGoals(PlayerName)
-ReportGoals=ReportGoals~=""and"\n- "..ReportGoals or ReportGoals
-self:F({ReportGoals,ScoreGoals,PenaltyGoals})
-local ReportMissions,ScoreMissions,PenaltyMissions=self:ReportDetailedPlayerMissions(PlayerName)
-ReportMissions=ReportMissions~=""and"\n- "..ReportMissions or ReportMissions
-self:F({ReportMissions,ScoreMissions,PenaltyMissions})
-local PlayerScore=ScoreHits+ScoreDestroys+ScoreCoalitionChanges+ScoreGoals+ScoreMissions
-local PlayerPenalty=PenaltyHits+PenaltyDestroys+PenaltyCoalitionChanges+PenaltyGoals+PenaltyMissions
-PlayerMessage=
-string.format("Player '%s' Score = %d ( %d Score, -%d Penalties )%s%s%s%s%s",
-PlayerName,
-PlayerScore-PlayerPenalty,
-PlayerScore,
-PlayerPenalty,
-ReportHits,
-ReportDestroys,
-ReportCoalitionChanges,
-ReportGoals,
-ReportMissions
-)
-MESSAGE:NewType(PlayerMessage,MESSAGE.Type.Detailed):ToGroup(PlayerGroup)
-end
-end
-end
-function SCORING:ReportScoreAllSummary(PlayerGroup)
-local PlayerMessage=""
-self:T({"Summary Score Report of All Players",Players=self.Players})
-for PlayerName,PlayerData in pairs(self.Players)do
-self:T({PlayerName=PlayerName,PlayerGroup=PlayerGroup})
-if PlayerName then
-local ReportHits,ScoreHits,PenaltyHits=self:ReportDetailedPlayerHits(PlayerName)
-ReportHits=ReportHits~=""and"\n- "..ReportHits or ReportHits
-self:F({ReportHits,ScoreHits,PenaltyHits})
-local ReportDestroys,ScoreDestroys,PenaltyDestroys=self:ReportDetailedPlayerDestroys(PlayerName)
-ReportDestroys=ReportDestroys~=""and"\n- "..ReportDestroys or ReportDestroys
-self:F({ReportDestroys,ScoreDestroys,PenaltyDestroys})
-local ReportCoalitionChanges,ScoreCoalitionChanges,PenaltyCoalitionChanges=self:ReportDetailedPlayerCoalitionChanges(PlayerName)
-ReportCoalitionChanges=ReportCoalitionChanges~=""and"\n- "..ReportCoalitionChanges or ReportCoalitionChanges
-self:F({ReportCoalitionChanges,ScoreCoalitionChanges,PenaltyCoalitionChanges})
-local ReportGoals,ScoreGoals,PenaltyGoals=self:ReportDetailedPlayerGoals(PlayerName)
-ReportGoals=ReportGoals~=""and"\n- "..ReportGoals or ReportGoals
-self:F({ReportGoals,ScoreGoals,PenaltyGoals})
-local ReportMissions,ScoreMissions,PenaltyMissions=self:ReportDetailedPlayerMissions(PlayerName)
-ReportMissions=ReportMissions~=""and"\n- "..ReportMissions or ReportMissions
-self:F({ReportMissions,ScoreMissions,PenaltyMissions})
-local PlayerScore=ScoreHits+ScoreDestroys+ScoreCoalitionChanges+ScoreGoals+ScoreMissions
-local PlayerPenalty=PenaltyHits+PenaltyDestroys+PenaltyCoalitionChanges+PenaltyGoals+PenaltyMissions
-PlayerMessage=
-string.format("Player '%s' Score = %d ( %d Score, -%d Penalties )",
-PlayerName,
-PlayerScore-PlayerPenalty,
-PlayerScore,
-PlayerPenalty
-)
-MESSAGE:NewType(PlayerMessage,MESSAGE.Type.Overview):ToGroup(PlayerGroup)
-end
-end
-end
-function SCORING:SecondsToClock(sSeconds)
-local nSeconds=sSeconds
-if nSeconds==0 then
-return"00:00:00";
-else
-local nHours=string.format("%02.f",math.floor(nSeconds/3600));
-local nMins=string.format("%02.f",math.floor(nSeconds/60-(nHours*60)));
-local nSecs=string.format("%02.f",math.floor(nSeconds-nHours*3600-nMins*60));
-return nHours..":"..nMins..":"..nSecs
-end
-end
-function SCORING:OpenCSV(ScoringCSV)
-self:F(ScoringCSV)
-if lfs and io and os and self.AutoSave then
-if ScoringCSV then
-self.ScoringCSV=ScoringCSV
-local fdir=lfs.writedir()..[[Logs\]]..self.ScoringCSV.." "..os.date("%Y-%m-%d %H-%M-%S")..".csv"
-self.CSVFile,self.err=io.open(fdir,"w+")
-if not self.CSVFile then
-error("Error: Cannot open CSV file in "..lfs.writedir())
-end
-self.CSVFile:write('"GameName","RunTime","Time","PlayerName","TargetPlayerName","ScoreType","PlayerUnitCoalition","PlayerUnitCategory","PlayerUnitType","PlayerUnitName","TargetUnitCoalition","TargetUnitCategory","TargetUnitType","TargetUnitName","Times","Score"\n')
-self.RunTime=os.date("%y-%m-%d_%H-%M-%S")
-else
-error("A string containing the CSV file name must be given.")
-end
-else
-self:F("The MissionScripting.lua file has not been changed to allow lfs, io and os modules to be used...")
-end
-return self
-end
-function SCORING:ScoreCSV(PlayerName,TargetPlayerName,ScoreType,ScoreTimes,ScoreAmount,PlayerUnitName,PlayerUnitCoalition,PlayerUnitCategory,PlayerUnitType,TargetUnitName,TargetUnitCoalition,TargetUnitCategory,TargetUnitType)
-local ScoreTime=self:SecondsToClock(timer.getTime())
-PlayerName=PlayerName:gsub('"','_')
-TargetPlayerName=TargetPlayerName or""
-TargetPlayerName=TargetPlayerName:gsub('"','_')
-if PlayerUnitName and PlayerUnitName~=''then
-local PlayerUnit=Unit.getByName(PlayerUnitName)
-if PlayerUnit then
-if not PlayerUnitCategory then
-PlayerUnitCategory=_SCORINGCategory[PlayerUnit:getDesc().category]
-end
-if not PlayerUnitCoalition then
-PlayerUnitCoalition=_SCORINGCoalition[PlayerUnit:getCoalition()]
-end
-if not PlayerUnitType then
-PlayerUnitType=PlayerUnit:getTypeName()
-end
-else
-PlayerUnitName=''
-PlayerUnitCategory=''
-PlayerUnitCoalition=''
-PlayerUnitType=''
-end
-else
-PlayerUnitName=''
-PlayerUnitCategory=''
-PlayerUnitCoalition=''
-PlayerUnitType=''
-end
-TargetUnitCoalition=TargetUnitCoalition or""
-TargetUnitCategory=TargetUnitCategory or""
-TargetUnitType=TargetUnitType or""
-TargetUnitName=TargetUnitName or""
-if lfs and io and os and self.AutoSave then
-self.CSVFile:write(
-'"'..self.GameName..'"'..','..
-'"'..self.RunTime..'"'..','..
-''..ScoreTime..''..','..
-'"'..PlayerName..'"'..','..
-'"'..TargetPlayerName..'"'..','..
-'"'..ScoreType..'"'..','..
-'"'..PlayerUnitCoalition..'"'..','..
-'"'..PlayerUnitCategory..'"'..','..
-'"'..PlayerUnitType..'"'..','..
-'"'..PlayerUnitName..'"'..','..
-'"'..TargetUnitCoalition..'"'..','..
-'"'..TargetUnitCategory..'"'..','..
-'"'..TargetUnitType..'"'..','..
-'"'..TargetUnitName..'"'..','..
-''..ScoreTimes..''..','..
-''..ScoreAmount
-)
-self.CSVFile:write("\n")
-end
-end
-function SCORING:CloseCSV()
-if lfs and io and os and self.AutoSave then
-self.CSVFile:close()
-end
-end
-function SCORING:SwitchAutoSave(OnOff)
-self.AutoSave=OnOff
-return self
-end
-SEAD={
-ClassName="SEAD",
-TargetSkill={
-Average={Evade=30,DelayOn={40,60}},
-Good={Evade=20,DelayOn={30,50}},
-High={Evade=15,DelayOn={20,40}},
-Excellent={Evade=10,DelayOn={10,30}}
-},
-SEADGroupPrefixes={},
-SuppressedGroups={},
-EngagementRange=75,
-Padding=10,
-CallBack=nil,
-UseCallBack=false,
-debug=false,
-}
-SEAD.Harms={
-["AGM_88"]="AGM_88",
-["AGM_122"]="AGM_122",
-["AGM_84"]="AGM_84",
-["AGM_45"]="AGM_45",
-["ALARM"]="ALARM",
-["LD-10"]="LD-10",
-["X_58"]="X_58",
-["X_28"]="X_28",
-["X_25"]="X_25",
-["X_31"]="X_31",
-["Kh25"]="Kh25",
-["BGM_109"]="BGM_109",
-["AGM_154"]="AGM_154",
-["HY-2"]="HY-2",
-["ADM_141A"]="ADM_141A",
-}
-SEAD.HarmData={
-["AGM_88"]={150,3},
-["AGM_45"]={12,2},
-["AGM_122"]={16.5,2.3},
-["AGM_84"]={280,0.8},
-["ALARM"]={45,2},
-["LD-10"]={60,4},
-["X_58"]={70,4},
-["X_28"]={80,2.5},
-["X_25"]={25,0.76},
-["X_31"]={150,3},
-["Kh25"]={25,0.8},
-["BGM_109"]={460,0.705},
-["AGM_154"]={130,0.61},
-["HY-2"]={90,1},
-["ADM_141A"]={126,0.6},
-}
-function SEAD:New(SEADGroupPrefixes,Padding)
-local self=BASE:Inherit(self,FSM:New())
-self:T(SEADGroupPrefixes)
-if type(SEADGroupPrefixes)=='table'then
-for SEADGroupPrefixID,SEADGroupPrefix in pairs(SEADGroupPrefixes)do
-self.SEADGroupPrefixes[SEADGroupPrefix]=SEADGroupPrefix
-end
-else
-self.SEADGroupPrefixes[SEADGroupPrefixes]=SEADGroupPrefixes
-end
-local padding=Padding or 10
-if padding<10 then padding=10 end
-self.Padding=padding
-self.UseEmissionsOnOff=true
-self.debug=false
-self.CallBack=nil
-self.UseCallBack=false
-self:HandleEvent(EVENTS.Shot,self.HandleEventShot)
-self:SetStartState("Running")
-self:AddTransition("*","ManageEvasion","*")
-self:AddTransition("*","CalculateHitZone","*")
-self:I("*** SEAD - Started Version 0.4.5")
-return self
-end
-function SEAD:UpdateSet(SEADGroupPrefixes)
-self:T(SEADGroupPrefixes)
-if type(SEADGroupPrefixes)=='table'then
-for SEADGroupPrefixID,SEADGroupPrefix in pairs(SEADGroupPrefixes)do
-self.SEADGroupPrefixes[SEADGroupPrefix]=SEADGroupPrefix
-end
-else
-self.SEADGroupPrefixes[SEADGroupPrefixes]=SEADGroupPrefixes
-end
-return self
-end
-function SEAD:SetEngagementRange(range)
-self:T({range})
-range=range or 75
-if range<0 or range>100 then
-range=75
-end
-self.EngagementRange=range
-self:T(string.format("*** SEAD - Engagement range set to %s",range))
-return self
-end
-function SEAD:SetPadding(Padding)
-self:T({Padding})
-local padding=Padding or 10
-if padding<10 then padding=10 end
-self.Padding=padding
-return self
-end
-function SEAD:SwitchEmissions(Switch)
-self:T({Switch})
-self.UseEmissionsOnOff=Switch
-return self
-end
-function SEAD:AddCallBack(Object)
-self:T({Class=Object.ClassName})
-self.CallBack=Object
-self.UseCallBack=true
-return self
-end
-function SEAD:_CheckHarms(WeaponName)
-self:T({WeaponName})
-local hit=false
-local name=""
-for _,_name in pairs(SEAD.Harms)do
-if string.find(WeaponName,_name,1,true)then
-hit=true
-name=_name
-break
-end
-end
-return hit,name
-end
-function SEAD:_GetDistance(_point1,_point2)
-self:T("_GetDistance")
-if _point1 and _point2 then
-local distance1=_point1:Get2DDistance(_point2)
-local distance2=_point1:DistanceFromPointVec2(_point2)
-if distance1 and type(distance1)=="number"then
-return distance1
-elseif distance2 and type(distance2)=="number"then
-return distance2
-else
-self:E("*****Cannot calculate distance!")
-self:E({_point1,_point2})
-return-1
-end
-else
-self:E("******Cannot calculate distance!")
-self:E({_point1,_point2})
-return-1
-end
-end
-function SEAD:onafterCalculateHitZone(From,Event,To,SEADWeapon,pos0,height,SEADGroup,SEADWeaponName)
-self:T("**** Calculating hit zone for "..(SEADWeaponName or"None"))
-if SEADWeapon and SEADWeapon:isExist()then
-local position=SEADWeapon:getPosition()
-local mheight=height
-local wph=math.atan2(position.x.z,position.x.x)
-if wph<0 then
-wph=wph+2*math.pi
-end
-wph=math.deg(wph)
-local wpndata=SEAD.HarmData["AGM_88"]
-if string.find(SEADWeaponName,"154",1)then
-wpndata=SEAD.HarmData["AGM_154"]
-end
-local mveloc=math.floor(wpndata[2]*340.29)
-local c1=(2*mheight*9.81)/(mveloc^2)
-local c2=(mveloc^2)/9.81
-local Ropt=c2*math.sqrt(c1+1)
-if height<=5000 then
-Ropt=Ropt*0.72
-elseif height<=7500 then
-Ropt=Ropt*0.82
-elseif height<=10000 then
-Ropt=Ropt*0.87
-elseif height<=12500 then
-Ropt=Ropt*0.98
-end
-for n=1,3 do
-local dist=Ropt-((n-1)*20000)
-local predpos=pos0:Translate(dist,wph)
-if predpos then
-local targetzone=ZONE_RADIUS:New("Target Zone",predpos:GetVec2(),20000)
-if self.debug then
-predpos:MarkToAll(string.format("height=%dm | heading=%d | velocity=%ddeg | Ropt=%dm",mheight,wph,mveloc,Ropt),false)
-targetzone:DrawZone(coalition.side.BLUE,{0,0,1},0.2,nil,nil,3,true)
-end
-local seadset=SET_GROUP:New():FilterPrefixes(self.SEADGroupPrefixes):FilterZones({targetzone}):FilterOnce()
-local tgtcoord=targetzone:GetRandomPointVec2()
-local tgtgrp=seadset:GetRandom()
-local _targetgroup=nil
-local _targetgroupname="none"
-local _targetskill="Random"
-if tgtgrp and tgtgrp:IsAlive()then
-_targetgroup=tgtgrp
-_targetgroupname=tgtgrp:GetName()
-_targetskill=tgtgrp:GetUnit(1):GetSkill()
-self:T("*** Found Target = ".._targetgroupname)
-self:ManageEvasion(_targetskill,_targetgroup,pos0,"AGM_88",SEADGroup,20)
-end
-end
-end
-end
-return self
-end
-function SEAD:onafterManageEvasion(From,Event,To,_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup,timeoffset,Weapon)
-local timeoffset=timeoffset or 0
-if _targetskill=="Random"then
-local Skills={"Average","Good","High","Excellent"}
-_targetskill=Skills[math.random(1,4)]
-end
-if self.TargetSkill[_targetskill]then
-local _evade=math.random(1,100)
-if(_evade>self.TargetSkill[_targetskill].Evade)then
-self:T("*** SEAD - Evading")
-local _targetpos=_targetgroup:GetCoordinate()
-local _distance=self:_GetDistance(SEADPlanePos,_targetpos)
-local hit,data=self:_CheckHarms(SEADWeaponName)
-local wpnspeed=666
-local reach=10
-if hit then
-local wpndata=SEAD.HarmData[data]
-reach=wpndata[1]*1.1
-local mach=wpndata[2]
-wpnspeed=math.floor(mach*340.29)
-if Weapon then
-wpnspeed=Weapon:GetSpeed()
-self:T(string.format("*** SEAD - Weapon Speed from WEAPON: %f m/s",wpnspeed))
-end
-end
-local _tti=math.floor(_distance/wpnspeed)-timeoffset
-if _distance>0 then
-_distance=math.floor(_distance/1000)
-else
-_distance=0
-end
-self:T(string.format("*** SEAD - target skill %s, distance %dkm, reach %dkm, tti %dsec",_targetskill,_distance,reach,_tti))
-if reach>=_distance then
-self:T("*** SEAD - Shot in Reach")
-local function SuppressionStart(args)
-self:T(string.format("*** SEAD - %s Radar Off & Relocating",args[2]))
-local grp=args[1]
-local name=args[2]
-local attacker=args[3]
-if self.UseEmissionsOnOff then
-grp:EnableEmission(false)
-end
-grp:OptionAlarmStateGreen()
-grp:RelocateGroundRandomInRadius(20,300,false,false,"Diamond")
-if self.UseCallBack then
-local object=self.CallBack
-object:SeadSuppressionStart(grp,name,attacker)
-end
-end
-local function SuppressionStop(args)
-self:T(string.format("*** SEAD - %s Radar On",args[2]))
-local grp=args[1]
-local name=args[2]
-if self.UseEmissionsOnOff then
-grp:EnableEmission(true)
-end
-grp:OptionAlarmStateRed()
-grp:OptionEngageRange(self.EngagementRange)
-self.SuppressedGroups[name]=false
-if self.UseCallBack then
-local object=self.CallBack
-object:SeadSuppressionEnd(grp,name)
-end
-end
-local delay=math.random(self.TargetSkill[_targetskill].DelayOn[1],self.TargetSkill[_targetskill].DelayOn[2])
-if delay>_tti then delay=delay/2 end
-if _tti>600 then delay=_tti-90 end
-local SuppressionStartTime=timer.getTime()+delay
-local SuppressionEndTime=timer.getTime()+delay+_tti+self.Padding+delay
-local _targetgroupname=_targetgroup:GetName()
-if not self.SuppressedGroups[_targetgroupname]then
-self:T(string.format("*** SEAD - %s | Parameters TTI %ds | Switch-Off in %ds",_targetgroupname,_tti,delay))
-timer.scheduleFunction(SuppressionStart,{_targetgroup,_targetgroupname,SEADGroup},SuppressionStartTime)
-timer.scheduleFunction(SuppressionStop,{_targetgroup,_targetgroupname},SuppressionEndTime)
-self.SuppressedGroups[_targetgroupname]=true
-if self.UseCallBack then
-local object=self.CallBack
-object:SeadSuppressionPlanned(_targetgroup,_targetgroupname,SuppressionStartTime,SuppressionEndTime,SEADGroup)
-end
-end
-end
-end
-end
-return self
-end
-function SEAD:HandleEventShot(EventData)
-self:T({EventData.id})
-local SEADPlane=EventData.IniUnit
-local SEADGroup=EventData.IniGroup
-local SEADPlanePos=SEADPlane:GetCoordinate()
-local SEADUnit=EventData.IniDCSUnit
-local SEADUnitName=EventData.IniDCSUnitName
-local SEADWeapon=EventData.Weapon
-local SEADWeaponName=EventData.WeaponName
-local WeaponWrapper=WEAPON:New(EventData.Weapon)
-self:T("*** SEAD - Missile Launched = "..SEADWeaponName)
-if self:_CheckHarms(SEADWeaponName)then
-self:T('*** SEAD - Weapon Match')
-local _targetskill="Random"
-local _targetgroupname="none"
-local _target=EventData.Weapon:getTarget()
-if not _target or self.debug then
-self:E("***** SEAD - No target data for "..(SEADWeaponName or"None"))
-if string.find(SEADWeaponName,"AGM_88",1,true)or string.find(SEADWeaponName,"AGM_154",1,true)then
-self:I("**** Tracking AGM-88/154 with no target data.")
-local pos0=SEADPlane:GetCoordinate()
-local fheight=SEADPlane:GetHeight()
-self:__CalculateHitZone(20,SEADWeapon,pos0,fheight,SEADGroup,SEADWeaponName)
-end
-return self
-end
-local targetcat=Object.getCategory(_target)
-local _targetUnit=nil
-local _targetgroup=nil
-self:T(string.format("*** Targetcat = %d",targetcat))
-if targetcat==Object.Category.UNIT then
-self:T("*** Target Category UNIT")
-_targetUnit=UNIT:Find(_target)
-if _targetUnit and _targetUnit:IsAlive()then
-_targetgroup=_targetUnit:GetGroup()
-_targetgroupname=_targetgroup:GetName()
-local _targetUnitName=_targetUnit:GetName()
-_targetUnit:GetSkill()
-_targetskill=_targetUnit:GetSkill()
-end
-elseif targetcat==Object.Category.STATIC then
-self:T("*** Target Category STATIC")
-local seadset=SET_GROUP:New():FilterPrefixes(self.SEADGroupPrefixes):FilterOnce()
-local targetpoint=_target:getPoint()or{x=0,y=0,z=0}
-local tgtcoord=COORDINATE:NewFromVec3(targetpoint)
-local tgtgrp=seadset:FindNearestGroupFromPointVec2(tgtcoord)
-if tgtgrp and tgtgrp:IsAlive()then
-_targetgroup=tgtgrp
-_targetgroupname=tgtgrp:GetName()
-_targetskill=tgtgrp:GetUnit(1):GetSkill()
-self:T("*** Found Target = ".._targetgroupname)
-end
-end
-local SEADGroupFound=false
-for SEADGroupPrefixID,SEADGroupPrefix in pairs(self.SEADGroupPrefixes)do
-self:T("Target = ".._targetgroupname.." | Prefix = "..SEADGroupPrefix)
-if string.find(_targetgroupname,SEADGroupPrefix,1,true)then
-SEADGroupFound=true
-self:T('*** SEAD - Group Match Found')
-break
-end
-end
-if SEADGroupFound==true then
-if string.find(SEADWeaponName,"ADM_141",1,true)then
-self:__ManageEvasion(2,_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup,0,WeaponWrapper)
-else
-self:ManageEvasion(_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup,0,WeaponWrapper)
-end
-end
-end
-return self
-end
-SHORAD={
-ClassName="SHORAD",
-name="MyShorad",
-debug=false,
-Prefixes="",
-Radius=20000,
-Groupset=nil,
-Samset=nil,
-Coalition=nil,
-ActiveTimer=600,
-ActiveGroups={},
-lid="",
-DefendHarms=true,
-DefendMavs=true,
-DefenseLowProb=70,
-DefenseHighProb=90,
-UseEmOnOff=true,
-shootandscoot=false,
-SkateNumber=3,
-SkateZones=nil,
-minscootdist=100,
-minscootdist=3000,
-scootrandomcoord=false,
-}
-do
-SHORAD.Harms={
-["AGM_88"]="AGM_88",
-["AGM_122"]="AGM_122",
-["AGM_84"]="AGM_84",
-["AGM_45"]="AGM_45",
-["ALARM"]="ALARM",
-["LD-10"]="LD-10",
-["X_58"]="X_58",
-["X_28"]="X_28",
-["X_25"]="X_25",
-["X_31"]="X_31",
-["Kh25"]="Kh25",
-["HY-2"]="HY-2",
-["ADM_141A"]="ADM_141A",
-}
-SHORAD.Mavs={
-["AGM"]="AGM",
-["C-701"]="C-701",
-["Kh25"]="Kh25",
-["Kh29"]="Kh29",
-["Kh31"]="Kh31",
-["Kh66"]="Kh66",
-}
-function SHORAD:New(Name,ShoradPrefix,Samset,Radius,ActiveTimer,Coalition,UseEmOnOff)
-local self=BASE:Inherit(self,FSM:New())
-self:T({Name,ShoradPrefix,Samset,Radius,ActiveTimer,Coalition})
-local GroupSet=SET_GROUP:New():FilterPrefixes(ShoradPrefix):FilterCoalitions(Coalition):FilterCategoryGround():FilterStart()
-self.name=Name or"MyShorad"
-self.Prefixes=ShoradPrefix or"SAM SHORAD"
-self.Radius=Radius or 20000
-self.Coalition=Coalition or"blue"
-self.Samset=Samset or GroupSet
-self.ActiveTimer=ActiveTimer or 600
-self.ActiveGroups={}
-self.Groupset=GroupSet
-self.DefendHarms=true
-self.DefendMavs=true
-self.DefenseLowProb=70
-self.DefenseHighProb=90
-self.UseEmOnOff=true
-if UseEmOnOff==false then self.UseEmOnOff=UseEmOnOff end
-self:I("*** SHORAD - Started Version 0.3.4")
-self.lid=string.format("SHORAD %s | ",self.name)
-self:_InitState()
-self:HandleEvent(EVENTS.Shot,self.HandleEventShot)
-self:SetStartState("Running")
-self:AddTransition("*","WakeUpShorad","*")
-self:AddTransition("*","CalculateHitZone","*")
-self:AddTransition("*","ShootAndScoot","*")
-return self
-end
-function SHORAD:_InitState()
-self:T(self.lid.." _InitState")
-local table={}
-local set=self.Groupset
-self:T({set=set})
-local aliveset=set:GetAliveSet()
-for _,_group in pairs(aliveset)do
-if self.UseEmOnOff then
-_group:EnableEmission(false)
-_group:OptionAlarmStateRed()
-else
-_group:OptionAlarmStateGreen()
-end
-_group:OptionDisperseOnAttack(30)
-end
-for i=1,100 do
-math.random()
-end
-return self
-end
-function SHORAD:AddScootZones(ZoneSet,Number,Random,Formation)
-self:T(self.lid.." AddScootZones")
-self.SkateZones=ZoneSet
-self.SkateNumber=Number or 3
-self.shootandscoot=true
-self.scootrandomcoord=Random
-self.scootformation=Formation or"Cone"
-return self
-end
-function SHORAD:SwitchDebug(onoff)
-self:T({onoff})
-if onoff then
-self:SwitchDebugOn()
-else
-self:SwitchDebugOff()
-end
-return self
-end
-function SHORAD:SwitchDebugOn()
-self.debug=true
-BASE:TraceOn()
-BASE:TraceClass("SHORAD")
-return self
-end
-function SHORAD:SwitchDebugOff()
-self.debug=false
-BASE:TraceOff()
-return self
-end
-function SHORAD:SwitchHARMDefense(onoff)
-self:T({onoff})
-local onoff=onoff or true
-self.DefendHarms=onoff
-return self
-end
-function SHORAD:SwitchAGMDefense(onoff)
-self:T({onoff})
-local onoff=onoff or true
-self.DefendMavs=onoff
-return self
-end
-function SHORAD:SetDefenseLimits(low,high)
-self:T({low,high})
-local low=low or 70
-local high=high or 90
-if(low<0)or(low>100)or(low>high)then
-low=70
-end
-if(high<0)or(high>100)or(high0 then
-local nammo=group:GetAmmunition()
-if nammo==0 then
-self:OutOfAmmo()
-end
-self:StatusReport(false)
-if self:GetState()~="Stopped"then
-self:__Status(-30)
-end
-else
-self:Stop()
-end
-else
-self:Stop()
-end
-end
-function SUPPRESSION:onafterHit(Controllable,From,Event,To,Unit,AttackUnit)
-self:_EventFromTo("onafterHit",Event,From,To)
-if From=="CombatReady"or From=="Suppressed"then
-self:_Suppress()
-end
-local life_min,life_max,life_ave,life_ave0,groupstrength=self:_GetLife()
-local Damage=100-life_ave0
-local RetreatCondition=Damage>=self.RetreatDamage-0.01 and self.RetreatZone
-local Pflee=(self.PmaxFlee-self.PminFlee)/self.RetreatDamage*math.min(Damage,self.RetreatDamage)+self.PminFlee
-local P=math.random(0,100)
-local FleeCondition=P Prand ==> Flee)\n",Controllable:GetName(),Pflee,P)
-self:T(self.lid..text)
-if Damage>=99.9 then
-return
-end
-if RetreatCondition then
-self:Retreat()
-elseif FleeCondition then
-if self.FallbackON and AttackUnit:IsGround()then
-self:FallBack(AttackUnit)
-elseif self.TakecoverON then
-local Hideout=self.hideout
-if self.hideout==nil then
-Hideout=self:_SearchHideout()
-end
-self:TakeCover(Hideout)
-end
-end
-end
-function SUPPRESSION:onbeforeRecovered(Controllable,From,Event,To)
-self:_EventFromTo("onbeforeRecovered",Event,From,To)
-local Tnow=timer.getTime()
-self:T(self.lid..string.format("onbeforeRecovered: Time now: %d - Time over: %d",Tnow,self.TsuppressionOver))
-if Tnow>=self.TsuppressionOver then
-return true
-else
-return false
-end
-end
-function SUPPRESSION:onafterRecovered(Controllable,From,Event,To)
-self:_EventFromTo("onafterRecovered",Event,From,To)
-if Controllable and Controllable:IsAlive()then
-local text=string.format("Group %s has recovered!",Controllable:GetName())
-MESSAGE:New(text,10):ToAllIf(self.Debug)
-self:T(self.lid..text)
-self:_SetROE()
-if self.flare or self.Debug then
-Controllable:FlareGreen()
-end
-end
-end
-function SUPPRESSION:onafterFightBack(Controllable,From,Event,To)
-self:_EventFromTo("onafterFightBack",Event,From,To)
-self:_SetROE()
-self:_SetAlarmState()
-local group=Controllable
-local Waypoints=group:GetTemplateRoutePoints()
-group:Route(Waypoints,5)
-end
-function SUPPRESSION:onbeforeFallBack(Controllable,From,Event,To,AttackUnit)
-self:_EventFromTo("onbeforeFallBack",Event,From,To)
-if From=="FallingBack"then
-return false
-else
-return true
-end
-end
-function SUPPRESSION:onafterFallBack(Controllable,From,Event,To,AttackUnit)
-self:_EventFromTo("onafterFallback",Event,From,To)
-self:T(self.lid..string.format("Group %s is falling back after %d hits.",Controllable:GetName(),self.Nhit))
-local ACoord=AttackUnit:GetCoordinate()
-local DCoord=Controllable:GetCoordinate()
-local heading=self:_Heading(ACoord,DCoord)
-if self.FallbackHeading then
-heading=self.FallbackHeading
-end
-local Coord=DCoord:Translate(self.FallbackDist,heading)
-if self.Debug then
-local MarkerID=Coord:MarkToAll("Fall back position for group "..Controllable:GetName())
-end
-if self.smoke or self.Debug then
-Coord:SmokeBlue()
-end
-self:_SetROE(SUPPRESSION.ROE.Hold)
-self:_SetAlarmState(SUPPRESSION.AlarmState.Auto)
-self:_Run(Coord,self.Speed,self.Formation,self.FallbackWait)
-end
-function SUPPRESSION:onbeforeTakeCover(Controllable,From,Event,To,Hideout)
-self:_EventFromTo("onbeforeTakeCover",Event,From,To)
-if From=="TakingCover"then
-return false
-end
-if Hideout~=nil then
-return true
-else
-return false
-end
-end
-function SUPPRESSION:onafterTakeCover(Controllable,From,Event,To,Hideout)
-self:_EventFromTo("onafterTakeCover",Event,From,To)
-if self.Debug then
-local MarkerID=Hideout:MarkToAll(string.format("Hideout for group %s",Controllable:GetName()))
-end
-if self.smoke or self.Debug then
-Hideout:SmokeBlue()
-end
-self:_SetROE(SUPPRESSION.ROE.Hold)
-self:_SetAlarmState(SUPPRESSION.AlarmState.Green)
-self:_Run(Hideout,self.Speed,self.Formation,self.TakecoverWait)
-end
-function SUPPRESSION:onafterOutOfAmmo(Controllable,From,Event,To)
-self:_EventFromTo("onafterOutOfAmmo",Event,From,To)
-self:I(self.lid..string.format("Out of ammo!"))
-if self.RetreatZone then
-self:Retreat()
-end
-end
-function SUPPRESSION:onbeforeRetreat(Controllable,From,Event,To)
-self:_EventFromTo("onbeforeRetreat",Event,From,To)
-if From=="Retreating"then
-local text=string.format("Group %s is already retreating.",tostring(Controllable:GetName()))
-self:T2(self.lid..text)
-return false
-else
-return true
-end
-end
-function SUPPRESSION:onafterRetreat(Controllable,From,Event,To)
-self:_EventFromTo("onafterRetreat",Event,From,To)
-local text=string.format("Group %s is retreating! Alarm state green.",Controllable:GetName())
-MESSAGE:New(text,10):ToAllIf(self.Debug)
-self:T(self.lid..text)
-local ZoneCoord=self.RetreatZone:GetRandomCoordinate()
-local ZoneVec2=ZoneCoord:GetVec2()
-if self.smoke or self.Debug then
-ZoneCoord:SmokeBlue()
-end
-if self.Debug then
-self.RetreatZone:SmokeZone(SMOKECOLOR.Red,12)
-end
-self:_SetROE(SUPPRESSION.ROE.Hold)
-self:_SetAlarmState(SUPPRESSION.AlarmState.Green)
-self:_Run(ZoneCoord,self.Speed,self.Formation,self.RetreatWait)
-end
-function SUPPRESSION:onbeforeRetreated(Controllable,From,Event,To)
-self:_EventFromTo("onbeforeRetreated",Event,From,To)
-local inzone=self.RetreatZone:IsVec3InZone(Controllable:GetVec3())
-return inzone
-end
-function SUPPRESSION:onafterRetreated(Controllable,From,Event,To)
-self:_EventFromTo("onafterRetreated",Event,From,To)
-self:_SetROE(SUPPRESSION.ROE.Return)
-self:_SetAlarmState(SUPPRESSION.AlarmState.Auto)
-end
-function SUPPRESSION:onafterDead(Controllable,From,Event,To)
-self:_EventFromTo("onafterDead",Event,From,To)
-local group=self.Controllable
-if group then
-local nunits=group:CountAliveUnits()
-local text=string.format("Group %s: One of our units just died! %d units left.",self.Controllable:GetName(),nunits)
-MESSAGE:New(text,10):ToAllIf(self.Debug)
-self:T(self.lid..text)
-if nunits==0 then
-self:Stop()
-end
-else
-self:Stop()
-end
-end
-function SUPPRESSION:onafterStop(Controllable,From,Event,To)
-self:_EventFromTo("onafterStop",Event,From,To)
-local text=string.format("Stopping SUPPRESSION for group %s",self.Controllable:GetName())
-MESSAGE:New(text,10):ToAllIf(self.Debug)
-self:I(self.lid..text)
-self.CallScheduler:Clear()
-if self.mooseevents then
-self:UnHandleEvent(EVENTS.Dead)
-self:UnHandleEvent(EVENTS.Hit)
-else
-world.removeEventHandler(self)
-end
-end
-function SUPPRESSION:onEvent(Event)
-if Event==nil or Event.initiator==nil or Unit.getByName(Event.initiator:getName())==nil then
-return true
-end
-local EventData={}
-if Event.initiator then
-EventData.IniDCSUnit=Event.initiator
-EventData.IniUnitName=Event.initiator:getName()
-EventData.IniDCSGroup=Event.initiator:getGroup()
-EventData.IniGroupName=Event.initiator:getGroup():getName()
-EventData.IniGroup=GROUP:FindByName(EventData.IniGroupName)
-EventData.IniUnit=UNIT:FindByName(EventData.IniUnitName)
-end
-if Event.target then
-EventData.TgtDCSUnit=Event.target
-EventData.TgtUnitName=Event.target:getName()
-EventData.TgtDCSGroup=Event.target:getGroup()
-EventData.TgtGroupName=Event.target:getGroup():getName()
-EventData.TgtGroup=GROUP:FindByName(EventData.TgtGroupName)
-EventData.TgtUnit=UNIT:FindByName(EventData.TgtUnitName)
-end
-if Event.id==world.event.S_EVENT_HIT then
-self:_OnEventHit(EventData)
-end
-if Event.id==world.event.S_EVENT_DEAD then
-self:_OnEventDead(EventData)
-end
-end
-function SUPPRESSION:_OnEventHit(EventData)
-self:F3(EventData)
-local GroupNameSelf=self.Controllable:GetName()
-local GroupNameTgt=EventData.TgtGroupName
-local TgtUnit=EventData.TgtUnit
-local tgt=EventData.TgtDCSUnit
-local IniUnit=EventData.IniUnit
-if GroupNameTgt==GroupNameSelf then
-self:T(self.lid..string.format("Hit event at t = %5.1f",timer.getTime()))
-if self.flare or self.Debug then
-TgtUnit:FlareRed()
-end
-self.Nhit=self.Nhit+1
-self:T(self.lid..string.format("Group %s has just been hit %d times.",self.Controllable:GetName(),self.Nhit))
-local life=tgt:getLife()/(tgt:getLife0()+1)*100
-self:T2(self.lid..string.format("Target unit life = %5.1f",life))
-self:__Hit(3,TgtUnit,IniUnit)
-end
-end
-function SUPPRESSION:_OnEventDead(EventData)
-local GroupNameSelf=self.Controllable:GetName()
-local GroupNameIni=EventData.IniGroupName
-if GroupNameIni==GroupNameSelf then
-local IniUnit=EventData.IniUnit
-local IniUnitName=EventData.IniUnitName
-if EventData.IniUnit then
-self:T2(self.lid..string.format("Group %s: Dead MOOSE unit DOES exist! Unit name %s.",GroupNameIni,IniUnitName))
-else
-self:T2(self.lid..string.format("Group %s: Dead MOOSE unit DOES NOT not exist! Unit name %s.",GroupNameIni,IniUnitName))
-end
-if EventData.IniDCSUnit then
-self:T2(self.lid..string.format("Group %s: Dead DCS unit DOES exist! Unit name %s.",GroupNameIni,IniUnitName))
-else
-self:T2(self.lid..string.format("Group %s: Dead DCS unit DOES NOT exist! Unit name %s.",GroupNameIni,IniUnitName))
-end
-if IniUnit and(self.flare or self.Debug)then
-IniUnit:FlareWhite()
-self:T(self.lid..string.format("Flare Dead MOOSE unit."))
-end
-if EventData.IniDCSUnit and(self.flare or self.Debug)then
-local p=EventData.IniDCSUnit:getPosition().p
-trigger.action.signalFlare(p,trigger.flareColor.Yellow,0)
-self:T(self.lid..string.format("Flare Dead DCS unit."))
-end
-self:Status()
-self:__Dead(0.1)
-end
-end
-function SUPPRESSION:_Suppress()
-local Tnow=timer.getTime()
-local Controllable=self.Controllable
-self:_SetROE(SUPPRESSION.ROE.Hold)
-local sigma=(self.Tsuppress_max-self.Tsuppress_min)/4
-local Tsuppress=self:_Random_Gaussian(self.Tsuppress_ave,sigma,self.Tsuppress_min,self.Tsuppress_max)
-local renew=true
-if self.TsuppressionOver~=nil then
-if Tsuppress+Tnow>self.TsuppressionOver then
-self.TsuppressionOver=Tnow+Tsuppress
-else
-renew=false
-end
-else
-self.TsuppressionOver=Tnow+Tsuppress
-end
-if renew then
-self:__Recovered(self.TsuppressionOver-Tnow)
-end
-local text=string.format("Group %s is suppressed for %d seconds. Suppression ends at %d:%02d.",Controllable:GetName(),Tsuppress,self.TsuppressionOver/60,self.TsuppressionOver%60)
-MESSAGE:New(text,10):ToAllIf(self.Debug)
-self:T(self.lid..text)
-end
-function SUPPRESSION:_Run(fin,speed,formation,wait)
-speed=speed or 20
-formation=formation or ENUMS.Formation.Vehicle.OffRoad
-wait=wait or 30
-local group=self.Controllable
-if group and group:IsAlive()then
-local ini=group:GetCoordinate()
-local dist=ini:Get2DDistance(fin)
-local heading=self:_Heading(ini,fin)
-local wp={}
-local tasks={}
-wp[1]=ini:WaypointGround(speed,formation)
-if self.Debug then
-local MarkerID=ini:MarkToAll(string.format("Waypoing %d of group %s (initial)",#wp,self.Controllable:GetName()))
-end
-local ConditionWait=group:TaskCondition(nil,nil,nil,nil,wait,nil)
-local TaskHold=group:TaskHold()
-local TaskComboFin={}
-TaskComboFin[#TaskComboFin+1]=group:TaskFunction("SUPPRESSION._Passing_Waypoint",self,#wp,true)
-TaskComboFin[#TaskComboFin+1]=group:TaskControlled(TaskHold,ConditionWait)
-wp[#wp+1]=fin:WaypointGround(speed,formation,TaskComboFin)
-if self.Debug then
-local MarkerID=fin:MarkToAll(string.format("Waypoing %d of group %s (final)",#wp,self.Controllable:GetName()))
-end
-group:Route(wp)
-else
-self:E(self.lid..string.format("ERROR: Group is not alive!"))
-end
-end
-function SUPPRESSION._Passing_Waypoint(group,Fsm,i,final)
-local text=string.format("Group %s passing waypoint %d (final=%s)",group:GetName(),i,tostring(final))
-MESSAGE:New(text,10):ToAllIf(Fsm.Debug)
-if Fsm.Debug then
-env.info(Fsm.lid..text)
-end
-if final then
-if Fsm:is("Retreating")then
-Fsm:Retreated()
-else
-Fsm:FightBack()
-end
-end
-end
-function SUPPRESSION:_SearchHideout()
-local Zone=ZONE_GROUP:New("Zone_Hiding",self.Controllable,self.TakecoverRange)
-local gpos=self.Controllable:GetCoordinate()
-Zone:Scan(Object.Category.SCENERY)
-local hideouts={}
-for SceneryTypeName,SceneryData in pairs(Zone:GetScannedScenery())do
-for SceneryName,SceneryObject in pairs(SceneryData)do
-local SceneryObject=SceneryObject
-local spos=SceneryObject:GetCoordinate()
-local distance=spos:Get2DDistance(gpos)
-if self.Debug then
-local MarkerID=SceneryObject:GetCoordinate():MarkToAll(string.format("%s scenery object %s",self.Controllable:GetName(),SceneryObject:GetTypeName()))
-local text=string.format("%s scenery: %s, Coord %s",self.Controllable:GetName(),SceneryObject:GetTypeName(),SceneryObject:GetCoordinate():ToStringLLDMS())
-self:T2(self.lid..text)
-end
-table.insert(hideouts,{object=SceneryObject,distance=distance})
-end
-end
-local Hideout=nil
-if#hideouts>0 then
-self:T(self.lid.."Number of hideouts "..#hideouts)
-local _sort=function(a,b)return a.distancelife_max then
-life_max=life
-end
-life_ave=life_ave+life
-if self.Debug then
-local text=string.format("n=%02d: Life = %3.1f, Life0 = %3.1f, min=%3.1f, max=%3.1f, ave=%3.1f, group=%3.1f",n,unit:GetLife(),unit:GetLife0(),life_min,life_max,life_ave/n,groupstrength)
-self:T2(self.lid..text)
-end
-end
-end
-if n==0 then
-return 0,0,0,0,0
-end
-life_ave0=life_ave/self.IniGroupStrength
-life_ave=life_ave/n
-return life_min,life_max,life_ave,life_ave0,groupstrength
-else
-return 0,0,0,0,0
-end
-end
-function SUPPRESSION:_Heading(a,b)
-local dx=b.x-a.x
-local dy=b.z-a.z
-local angle=math.deg(math.atan2(dy,dx))
-if angle<0 then
-angle=360+angle
-end
-return angle
-end
-function SUPPRESSION:_Random_Gaussian(x0,sigma,xmin,xmax)
-sigma=sigma or 5
-local r
-local gotit=false
-local i=0
-while not gotit do
-local x1=math.random()
-local x2=math.random()
-r=math.sqrt(-2*sigma*sigma*math.log(x1))*math.cos(2*math.pi*x2)+x0
-i=i+1
-if(r>=xmin and r<=xmax)or i>100 then
-gotit=true
-end
-end
-return r
-end
-function SUPPRESSION:_SetROE(roe)
-local group=self.Controllable
-roe=roe or self.DefaultROE
-self.CurrentROE=roe
-if roe==SUPPRESSION.ROE.Free then
-group:OptionROEOpenFire()
-elseif roe==SUPPRESSION.ROE.Hold then
-group:OptionROEHoldFire()
-elseif roe==SUPPRESSION.ROE.Return then
-group:OptionROEReturnFire()
-else
-self:E(self.lid.."Unknown ROE requested: "..tostring(roe))
-group:OptionROEOpenFire()
-self.CurrentROE=SUPPRESSION.ROE.Free
-end
-local text=string.format("Group %s now has ROE %s.",self.Controllable:GetName(),self.CurrentROE)
-self:T(self.lid..text)
-end
-function SUPPRESSION:_SetAlarmState(state)
-local group=self.Controllable
-state=state or self.DefaultAlarmState
-self.CurrentAlarmState=state
-if state==SUPPRESSION.AlarmState.Auto then
-group:OptionAlarmStateAuto()
-elseif state==SUPPRESSION.AlarmState.Green then
-group:OptionAlarmStateGreen()
-elseif state==SUPPRESSION.AlarmState.Red then
-group:OptionAlarmStateRed()
-else
-self:E(self.lid.."Unknown alarm state requested: "..tostring(state))
-group:OptionAlarmStateAuto()
-self.CurrentAlarmState=SUPPRESSION.AlarmState.Auto
-end
-local text=string.format("Group %s now has Alarm State %s.",self.Controllable:GetName(),self.CurrentAlarmState)
-self:T(self.lid..text)
-end
-function SUPPRESSION:_EventFromTo(BA,Event,From,To)
-local text=string.format("\n%s: %s EVENT %s: %s --> %s",BA,self.Controllable:GetName(),Event,From,To)
-self:T2(self.lid..text)
-end
-WAREHOUSE={
-ClassName="WAREHOUSE",
-Debug=false,
-verbosity=0,
-lid=nil,
-Report=true,
-warehouse=nil,
-alias=nil,
-zone=nil,
-airbase=nil,
-airbasename=nil,
-road=nil,
-rail=nil,
-spawnzone=nil,
-uid=nil,
-dTstatus=30,
-queueid=0,
-stock={},
-queue={},
-pending={},
-transporting={},
-delivered={},
-defending={},
-portzone=nil,
-harborzone=nil,
-shippinglanes={},
-offroadpaths={},
-autodefence=false,
-spawnzonemaxdist=5000,
-autosave=false,
-autosavepath=nil,
-autosavefile=nil,
-saveparking=false,
-isUnit=false,
-isShip=false,
-lowfuelthresh=0.15,
-respawnafterdestroyed=false,
-respawndelay=nil,
-}
-WAREHOUSE.Descriptor={
-GROUPNAME="templatename",
-UNITTYPE="unittype",
-ATTRIBUTE="attribute",
-CATEGORY="category",
-ASSIGNMENT="assignment",
-ASSETLIST="assetlist,"
-}
-WAREHOUSE.Attribute={
-AIR_TRANSPORTPLANE="Air_TransportPlane",
-AIR_AWACS="Air_AWACS",
-AIR_FIGHTER="Air_Fighter",
-AIR_BOMBER="Air_Bomber",
-AIR_TANKER="Air_Tanker",
-AIR_TRANSPORTHELO="Air_TransportHelo",
-AIR_ATTACKHELO="Air_AttackHelo",
-AIR_UAV="Air_UAV",
-AIR_OTHER="Air_OtherAir",
-GROUND_APC="Ground_APC",
-GROUND_TRUCK="Ground_Truck",
-GROUND_INFANTRY="Ground_Infantry",
-GROUND_IFV="Ground_IFV",
-GROUND_ARTILLERY="Ground_Artillery",
-GROUND_TANK="Ground_Tank",
-GROUND_TRAIN="Ground_Train",
-GROUND_EWR="Ground_EWR",
-GROUND_AAA="Ground_AAA",
-GROUND_SAM="Ground_SAM",
-GROUND_OTHER="Ground_OtherGround",
-NAVAL_AIRCRAFTCARRIER="Naval_AircraftCarrier",
-NAVAL_WARSHIP="Naval_WarShip",
-NAVAL_ARMEDSHIP="Naval_ArmedShip",
-NAVAL_UNARMEDSHIP="Naval_UnarmedShip",
-NAVAL_OTHER="Naval_OtherNaval",
-OTHER_UNKNOWN="Other_Unknown",
-}
-WAREHOUSE.TransportType={
-AIRPLANE="Air_TransportPlane",
-HELICOPTER="Air_TransportHelo",
-APC="Ground_APC",
-TRAIN="Ground_Train",
-SHIP="Naval_UnarmedShip",
-AIRCRAFTCARRIER="Naval_AircraftCarrier",
-WARSHIP="Naval_WarShip",
-ARMEDSHIP="Naval_ArmedShip",
-SELFPROPELLED="Selfpropelled",
-}
-WAREHOUSE.Quantity={
-ALL="all",
-THREEQUARTERS="3/4",
-HALF="1/2",
-THIRD="1/3",
-QUARTER="1/4",
-}
-_WAREHOUSEDB={
-AssetID=0,
-Assets={},
-WarehouseID=0,
-Warehouses={}
-}
-WAREHOUSE.version="1.0.2a"
-function WAREHOUSE:New(warehouse,alias)
-local self=BASE:Inherit(self,FSM:New())
-if type(warehouse)=="string"then
-local warehousename=warehouse
-warehouse=UNIT:FindByName(warehousename)
-if warehouse==nil then
-warehouse=STATIC:FindByName(warehousename,true)
-end
-end
-if warehouse==nil then
-env.error("ERROR: Warehouse does not exist!")
-return nil
-end
-if warehouse:IsInstanceOf("STATIC")then
-self.isUnit=false
-elseif warehouse:IsInstanceOf("UNIT")then
-self.isUnit=true
-if warehouse:IsShip()then
-self.isShip=true
-end
-else
-env.error("ERROR: Warehouse is neither STATIC nor UNIT object!")
-return nil
-end
-self.alias=alias or warehouse:GetName()
-self.lid=string.format("WAREHOUSE %s | ",self.alias)
-self:I(self.lid..string.format("Adding warehouse v%s for structure %s [isUnit=%s, isShip=%s]",WAREHOUSE.version,warehouse:GetName(),tostring(self:IsUnit()),tostring(self:IsShip())))
-self.warehouse=warehouse
-_WAREHOUSEDB.WarehouseID=_WAREHOUSEDB.WarehouseID+1
-self.uid=_WAREHOUSEDB.WarehouseID
-self.coalition=self.warehouse:GetCoalition()
-self.countryid=self.warehouse:GetCountry()
-local _airbase=self:GetCoordinate():GetClosestAirbase(nil,self:GetCoalition())
-if _airbase and _airbase:GetCoordinate():Get2DDistance(self:GetCoordinate())<=5000 then
-self:SetAirbase(_airbase)
-end
-if self.isShip then
-self.zone=ZONE_AIRBASE:New(self.warehouse:GetName(),1000)
-self.spawnzone=ZONE_AIRBASE:New(self.warehouse:GetName(),1000)
-else
-self.zone=ZONE_RADIUS:New(string.format("Warehouse zone %s",self.warehouse:GetName()),warehouse:GetVec2(),500)
-self.spawnzone=ZONE_RADIUS:New(string.format("Warehouse %s spawn zone",self.warehouse:GetName()),warehouse:GetVec2(),250)
-end
-self:SetMarker(true)
-self:SetReportOff()
-self:SetRunwayRepairtime()
-self.allowSpawnOnClientSpots=false
-_WAREHOUSEDB.Warehouses[self.uid]=self
-self:SetStartState("NotReadyYet")
-self:AddTransition("NotReadyYet","Load","Loaded")
-self:AddTransition("Stopped","Load","Loaded")
-self:AddTransition("NotReadyYet","Start","Running")
-self:AddTransition("Loaded","Start","Running")
-self:AddTransition("*","Status","*")
-self:AddTransition("*","AddAsset","*")
-self:AddTransition("*","NewAsset","*")
-self:AddTransition("*","AddRequest","*")
-self:AddTransition("Running","Request","*")
-self:AddTransition("Running","RequestSpawned","*")
-self:AddTransition("Attacked","Request","*")
-self:AddTransition("*","Unloaded","*")
-self:AddTransition("*","AssetSpawned","*")
-self:AddTransition("*","AssetLowFuel","*")
-self:AddTransition("*","Arrived","*")
-self:AddTransition("*","Delivered","*")
-self:AddTransition("Running","SelfRequest","*")
-self:AddTransition("Attacked","SelfRequest","*")
-self:AddTransition("Running","Pause","Paused")
-self:AddTransition("Paused","Unpause","Running")
-self:AddTransition("*","Stop","Stopped")
-self:AddTransition("Stopped","Restart","Running")
-self:AddTransition("Loaded","Restart","Running")
-self:AddTransition("*","Save","*")
-self:AddTransition("*","Attacked","Attacked")
-self:AddTransition("Attacked","Defeated","Running")
-self:AddTransition("*","ChangeCountry","*")
-self:AddTransition("Attacked","Captured","Running")
-self:AddTransition("*","AirbaseCaptured","*")
-self:AddTransition("*","AirbaseRecaptured","*")
-self:AddTransition("*","RunwayDestroyed","*")
-self:AddTransition("*","RunwayRepaired","*")
-self:AddTransition("*","AssetDead","*")
-self:AddTransition("*","Destroyed","Destroyed")
-self:AddTransition("Destroyed","Respawn","Running")
-return self
-end
-function WAREHOUSE:SetDebugOn()
-self.Debug=true
-return self
-end
-function WAREHOUSE:SetDebugOff()
-self.Debug=false
-return self
-end
-function WAREHOUSE:SetReportOn()
-self.Report=true
-return self
-end
-function WAREHOUSE:SetReportOff()
-self.Report=false
-return self
-end
-function WAREHOUSE:SetSafeParkingOn()
-self.safeparking=true
-return self
-end
-function WAREHOUSE:SetSafeParkingOff()
-self.safeparking=false
-return self
-end
-function WAREHOUSE:SetAllowSpawnOnClientParking()
-self.allowSpawnOnClientSpots=true
-return self
-end
-function WAREHOUSE:SetLowFuelThreshold(threshold)
-self.lowfuelthresh=threshold or 0.15
-return self
-end
-function WAREHOUSE:SetStatusUpdate(timeinterval)
-self.dTstatus=timeinterval
-return self
-end
-function WAREHOUSE:SetVerbosityLevel(VerbosityLevel)
-self.verbosity=VerbosityLevel or 0
-return self
-end
-function WAREHOUSE:SetSpawnZone(zone,maxdist)
-self.spawnzone=zone
-self.spawnzonemaxdist=maxdist or 5000
-return self
-end
-function WAREHOUSE:GetSpawnZone()
-return self.spawnzone
-end
-function WAREHOUSE:SetWarehouseZone(zone)
-self.zone=zone
-return self
-end
-function WAREHOUSE:GetWarehouseZone()
-return self.zone
-end
-function WAREHOUSE:SetAutoDefenceOn()
-self.autodefence=true
-return self
-end
-function WAREHOUSE:SetAutoDefenceOff()
-self.autodefence=false
-return self
-end
-function WAREHOUSE:SetParkingIDs(ParkingIDs)
-if type(ParkingIDs)~="table"then
-ParkingIDs={ParkingIDs}
-end
-self.parkingIDs=ParkingIDs
-return self
-end
-function WAREHOUSE:_CheckParkingValid(spot)
-if self.parkingIDs==nil then
-return true
-end
-for _,id in pairs(self.parkingIDs or{})do
-if spot.TerminalID==id then
-return true
-end
-end
-return false
-end
-function WAREHOUSE:_CheckParkingAsset(spot,asset)
-if asset.parkingIDs==nil then
-return true
-end
-for _,id in pairs(asset.parkingIDs or{})do
-if spot.TerminalID==id then
-return true
-end
-end
-return false
-end
-function WAREHOUSE:SetSaveOnMissionEnd(path,filename)
-self.autosave=true
-self.autosavepath=path
-self.autosavefile=filename
-return self
-end
-function WAREHOUSE:SetMarker(switch)
-if switch==false then
-self.markerOn=false
-else
-self.markerOn=true
-end
-return self
-end
-function WAREHOUSE:SetRespawnAfterDestroyed(delay)
-self.respawnafterdestroyed=true
-self.respawndelay=delay
-return self
-end
-function WAREHOUSE:SetAirbase(airbase)
-self.airbase=airbase
-if airbase~=nil then
-self.airbasename=airbase:GetName()
-else
-self.airbasename=nil
-end
-return self
-end
-function WAREHOUSE:SetRoadConnection(coordinate)
-if coordinate then
-self.road=coordinate:GetClosestPointToRoad()
-else
-self.road=false
-end
-return self
-end
-function WAREHOUSE:SetRailConnection(coordinate)
-if coordinate then
-self.rail=coordinate:GetClosestPointToRoad(true)
-else
-self.rail=false
-end
-return self
-end
-function WAREHOUSE:SetPortZone(zone)
-self.portzone=zone
-return self
-end
-function WAREHOUSE:SetHarborZone(zone)
-self.harborzone=zone
-return self
-end
-function WAREHOUSE:AddShippingLane(remotewarehouse,group,oneway)
-if self.portzone==nil or remotewarehouse.portzone==nil then
-local text=string.format("ERROR: Sending or receiving warehouse does not have a port zone defined. Adding shipping lane not possible!")
-self:_ErrorMessage(text,5)
-return self
-end
-local startcoord=self.portzone:GetRandomCoordinate()
-local finalcoord=remotewarehouse.portzone:GetRandomCoordinate()
-local lane=self:_NewLane(group,startcoord,finalcoord)
-if self.Debug then
-for i=1,#lane do
-local coord=lane[i]
-local text=string.format("Shipping lane %s to %s. Point %d.",self.alias,remotewarehouse.alias,i)
-coord:MarkToCoalition(text,self:GetCoalition())
-end
-end
-local remotename=remotewarehouse.warehouse:GetName()
-if self.shippinglanes[remotename]==nil then
-self.shippinglanes[remotename]={}
-end
-table.insert(self.shippinglanes[remotename],lane)
-if not oneway then
-remotewarehouse:AddShippingLane(self,group,true)
-end
-return self
-end
-function WAREHOUSE:AddOffRoadPath(remotewarehouse,group,oneway)
-local startcoord=self.spawnzone:GetRandomCoordinate()
-local finalcoord=remotewarehouse.spawnzone:GetRandomCoordinate()
-local path=self:_NewLane(group,startcoord,finalcoord)
-if path==nil then
-self:E(self.lid.."ERROR: Offroad path could not be added. Group present in ME?")
-return
-end
-if path and self.Debug then
-for i=1,#path do
-local coord=path[i]
-local text=string.format("Off road path from %s to %s. Point %d.",self.alias,remotewarehouse.alias,i)
-coord:MarkToCoalition(text,self:GetCoalition())
-end
-end
-local remotename=remotewarehouse.warehouse:GetName()
-if self.offroadpaths[remotename]==nil then
-self.offroadpaths[remotename]={}
-end
-table.insert(self.offroadpaths[remotename],path)
-if not oneway then
-remotewarehouse:AddOffRoadPath(self,group,true)
-end
-return self
-end
-function WAREHOUSE:_NewLane(group,startcoord,finalcoord)
-local lane=nil
-if group then
-local lanepoints=group:GetTemplateRoutePoints()
-local laneF=lanepoints[1]
-local laneL=lanepoints[#lanepoints]
-local coordF=COORDINATE:New(laneF.x,0,laneF.y)
-local coordL=COORDINATE:New(laneL.x,0,laneL.y)
-local distF=startcoord:Get2DDistance(coordF)
-local distL=startcoord:Get2DDistance(coordL)
-lane={}
-if distF0 then
-local samecoalition=anycoalition or Coalition==warehouse:GetCoalition()
-if samecoalition and not(warehouse:IsNotReadyYet()or warehouse:IsStopped()or warehouse:IsDestroyed())then
-local nassets=warehouse:GetNumberOfAssets(Descriptor,DescriptorValue)
-local enough=true
-if Descriptor and DescriptorValue then
-enough=nassets>=MinAssets
-end
-if enough and(distmin==nil or dist=1 then
-local FSMstate=self:GetState()
-local coalition=self:GetCoalitionName()
-local country=self:GetCountryName()
-self:I(self.lid..string.format("State=%s %s [%s]: Assets=%d, Requests: waiting=%d, pending=%d",FSMstate,country,coalition,#self.stock,#self.queue,#self.pending))
-end
-self:_JobDone()
-self:_DisplayStatus()
-self:_CheckConquered()
-if self:IsRunwayOperational()==false then
-local Trepair=self:GetRunwayRepairtime()
-self:I(self.lid..string.format("Runway destroyed! Will be repaired in %d sec",Trepair))
-if Trepair==0 then
-self:RunwayRepaired()
-end
-end
-self:_CheckRequestConsistancy(self.queue)
-if self:IsRunning()or self:IsAttacked()then
-local request=self:_CheckQueue()
-if request then
-self:Request(request)
-end
-end
-if self.verbosity>2 then
-self:_PrintQueue(self.queue,"Queue waiting")
-self:_PrintQueue(self.pending,"Queue pending")
-end
-self:_UpdateWarehouseMarkText()
-if self.Debug then
-self:_DisplayStockItems(self.stock)
-end
-self:__Status(-self.dTstatus)
-end
-function WAREHOUSE:_JobDone()
-local done={}
-for _,request in pairs(self.pending)do
-local request=request
-if request.born then
-local ncargo=0
-if request.cargogroupset then
-ncargo=request.cargogroupset:Count()
-end
-local ntransport=0
-if request.transportgroupset then
-ntransport=request.transportgroupset:Count()
-end
-local ncargotot=request.nasset
-local ncargodelivered=request.ndelivered
-local ncargodead=ncargotot-ncargodelivered-ncargo
-local ntransporttot=request.ntransport
-local ntransporthome=request.ntransporthome
-local ntransportdead=ntransporttot-ntransporthome-ntransport
-local text=string.format("Request id=%d: Cargo: Ntot=%d, Nalive=%d, Ndelivered=%d, Ndead=%d | Transport: Ntot=%d, Nalive=%d, Nhome=%d, Ndead=%d",
-request.uid,ncargotot,ncargo,ncargodelivered,ncargodead,ntransporttot,ntransport,ntransporthome,ntransportdead)
-self:T(self.lid..text)
-if ncargo==0 then
-if not self.delivered[request.uid]then
-self:Delivered(request)
-end
-if ntransport==0 then
-if self.verbosity>=1 then
-local text=string.format("Warehouse %s: Job on request id=%d for warehouse %s done!\n",self.alias,request.uid,request.warehouse.alias)
-text=text..string.format("- %d of %d assets delivered. Casualties %d.",ncargodelivered,ncargotot,ncargodead)
-if request.ntransport>0 then
-text=text..string.format("\n- %d of %d transports returned home. Casualties %d.",ntransporthome,ntransporttot,ntransportdead)
-end
-self:_InfoMessage(text,20)
-end
-table.insert(done,request)
-else
-for _,_group in pairs(request.transportgroupset:GetSetObjects())do
-local group=_group
-if group and group:IsAlive()then
-local category=group:GetCategory()
-local speed=group:GetVelocityKMH()
-local notmoving=speed<1
-local airbase=group:GetCoordinate():GetClosestAirbase():GetName()
-local athomebase=self.airbase and self.airbase:GetName()==airbase
-local onground=not group:InAir()
-local inspawnzone=group:IsPartlyOrCompletelyInZone(self.spawnzone)
-local ishome=false
-if category==Group.Category.GROUND or category==Group.Category.HELICOPTER then
-ishome=inspawnzone and onground and notmoving
-elseif category==Group.Category.AIRPLANE then
-ishome=athomebase and onground and notmoving
-end
-local text=string.format("Group %s: speed=%d km/h, onground=%s , airbase=%s, spawnzone=%s ==> ishome=%s",group:GetName(),speed,tostring(onground),airbase,tostring(inspawnzone),tostring(ishome))
-self:T(self.lid..text)
-if ishome then
-local text=string.format("Warehouse %s: Transport group arrived back home and no cargo left for request id=%d.\nSending transport group %s back to stock.",self.alias,request.uid,group:GetName())
-self:T(self.lid..text)
-if self.Debug then
-group:SmokeRed()
-end
-self:Arrived(group)
-end
-end
-end
-end
-else
-if ntransport==0 and request.ntransport>0 then
-local ncargoalive=0
-for _,_group in pairs(request.cargogroupset:GetSetObjects())do
-local groupname=_group:GetName()
-local group=GROUP:FindByName(groupname.."#CARGO")
-if group and group:IsAlive()then
-if group:IsPartlyOrCompletelyInZone(self.spawnzone)then
-if self.Debug then
-group:SmokeBlue()
-end
-self:AddAsset(group)
-ncargoalive=ncargoalive+1
-end
-end
-end
-self:_InfoMessage(string.format("Warehouse %s: All transports of request id=%s dead! Putting remaining %s cargo assets back into warehouse!",self.alias,request.uid,ncargoalive))
-end
-end
-end
-end
-for _,request in pairs(done)do
-self:_DeleteQueueItem(request,self.pending)
-end
-end
-function WAREHOUSE:_CheckAssetStatus()
-local function _CheckGroup(_request,_group)
-local request=_request
-local group=_group
-if group and group:IsAlive()then
-local category=group:GetCategory()
-for _,_unit in pairs(group:GetUnits())do
-local unit=_unit
-if unit and unit:IsAlive()then
-local unitid=unit:GetID()
-local life9=unit:GetLife()
-local life0=unit:GetLife0()
-local life=life9/life0*100
-local speed=unit:GetVelocityMPS()
-local onground=unit:InAir()
-local problem=false
-if life<10 then
-self:T(string.format("Unit %s is heavily damaged!",unit:GetName()))
-end
-if speed<1 and unit:GetSpeedMax()>1 and onground then
-self:T(string.format("Unit %s is not moving!",unit:GetName()))
-problem=true
-end
-if problem then
-if request.assetproblem[unitid]then
-local deltaT=timer.getAbsTime()-request.assetproblem[unitid]
-if deltaT>300 then
-unit:Destroy()
-end
-else
-request.assetproblem[unitid]=timer.getAbsTime()
-end
-end
-end
-end
-end
-end
-for _,request in pairs(self.pending)do
-local request=request
-if request.cargogroupset then
-for _,_group in pairs(request.cargogroupset:GetSet())do
-local group=_group
-_CheckGroup(request,group)
-end
-end
-if request.transportgroupset then
-for _,group in pairs(request.transportgroupset:GetSet())do
-_CheckGroup(request,group)
-end
-end
-end
-end
-function WAREHOUSE:onafterAddAsset(From,Event,To,group,ngroups,forceattribute,forcecargobay,forceweight,loadradius,skill,liveries,assignment,other)
-self:T({group=group,ngroups=ngroups,forceattribute=forceattribute,forcecargobay=forcecargobay,forceweight=forceweight})
-local n=ngroups or 1
-if type(group)=="string"then
-group=GROUP:FindByName(group)
-end
-if liveries and type(liveries)=="string"then
-liveries={liveries}
-end
-if group then
-local wid,aid,rid=self:_GetIDsFromGroup(group)
-if wid and aid and rid then
-local warehouse=self:FindWarehouseInDB(wid)
-if warehouse then
-local request=warehouse:_GetRequestOfGroup(group,warehouse.pending)
-if request then
-local istransport=warehouse:_GroupIsTransport(group,request)
-if istransport==true then
-request.ntransporthome=request.ntransporthome+1
-request.transportgroupset:Remove(group:GetName(),true)
-local ntrans=request.transportgroupset:Count()
-self:T2(warehouse.lid..string.format("Transport %d of %s returned home. TransportSet=%d",request.ntransporthome,tostring(request.ntransport),ntrans))
-elseif istransport==false then
-request.ndelivered=request.ndelivered+1
-local namewo=self:_GetNameWithOut(group)
-request.cargogroupset:Remove(namewo,true)
-local ncargo=request.cargogroupset:Count()
-self:T2(warehouse.lid..string.format("Cargo %s: %d of %s delivered. CargoSet=%d",namewo,request.ndelivered,tostring(request.nasset),ncargo))
-else
-self:E(warehouse.lid..string.format("WARNING: Group %s is neither cargo nor transport! Need to investigate...",group:GetName()))
-end
-if assignment==nil and request.assignment~=nil then
-assignment=request.assignment
-end
-end
-end
-local asset=self:FindAssetInDB(group)
-if asset~=nil then
-self:_DebugMessage(string.format("Warehouse %s: Adding KNOWN asset uid=%d with attribute=%s to stock.",self.alias,asset.uid,asset.attribute),5)
-if liveries then
-if type(liveries)=="table"then
-asset.livery=liveries[math.random(#liveries)]
-else
-asset.livery=liveries
-end
-end
-asset.skill=skill or asset.skill
-asset.wid=self.uid
-asset.rid=nil
-asset.spawned=false
-asset.requested=false
-asset.isReserved=false
-asset.iscargo=nil
-asset.arrived=nil
-if group:IsAlive()==true then
-asset.damage=asset.life0-group:GetLife()
-end
-table.insert(self.stock,asset)
-self:__NewAsset(0.1,asset,assignment or"")
-else
-self:_ErrorMessage(string.format("ERROR: Known asset could not be found in global warehouse db!"),0)
-end
-else
-self:_DebugMessage(string.format("Warehouse %s: Adding %d NEW assets of group %s to stock",self.alias,n,tostring(group:GetName())),5)
-local assets=self:_RegisterAsset(group,n,forceattribute,forcecargobay,forceweight,loadradius,liveries,skill,assignment)
-for _,asset in pairs(assets)do
-asset.wid=self.uid
-asset.rid=nil
-table.insert(self.stock,asset)
-self:__NewAsset(0.1,asset,assignment or"")
-end
-end
-if group:IsAlive()==true then
-self:_DebugMessage(string.format("Removing group %s",group:GetName()),5)
-local opsgroup=_DATABASE:GetOpsGroup(group:GetName())
-if opsgroup then
-opsgroup:Despawn(0,true)
-opsgroup:__Stop(-0.01)
-else
-group:Destroy()
-end
-else
-local opsgroup=_DATABASE:GetOpsGroup(group:GetName())
-if opsgroup then
-opsgroup:Stop()
-end
-end
-else
-self:E(self.lid.."ERROR: Unknown group added as asset!")
-self:E({unknowngroup=group})
-end
-end
-function WAREHOUSE:_RegisterAsset(group,ngroups,forceattribute,forcecargobay,forceweight,loadradius,liveries,skill,assignment)
-self:F({groupname=group:GetName(),ngroups=ngroups,forceattribute=forceattribute,forcecargobay=forcecargobay,forceweight=forceweight})
-local n=ngroups or 1
-local function _GetObjectSize(DCSdesc)
-if DCSdesc.box then
-local x=DCSdesc.box.max.x-DCSdesc.box.min.x
-local y=DCSdesc.box.max.y-DCSdesc.box.min.y
-local z=DCSdesc.box.max.z-DCSdesc.box.min.z
-return math.max(x,z),x,y,z
-end
-return 0,0,0,0
-end
-local templategroupname=group:GetName()
-local Descriptors=group:GetUnit(1):GetDesc()
-local Category=group:GetCategory()
-local TypeName=group:GetTypeName()
-local SpeedMax=group:GetSpeedMax()
-local RangeMin=group:GetRange()
-local smax,sx,sy,sz=_GetObjectSize(Descriptors)
-local weight=0
-local cargobay={}
-local cargobaytot=0
-local cargobaymax=0
-local weights={}
-for _i,_unit in pairs(group:GetUnits())do
-local unit=_unit
-local Desc=unit:GetDesc()
-local unitweight=forceweight or Desc.massEmpty
-if unitweight then
-weight=weight+unitweight
-weights[_i]=unitweight
-end
-local cargomax=0
-local massfuel=Desc.fuelMassMax or 0
-local massempty=Desc.massEmpty or 0
-local massmax=Desc.massMax or 0
-cargomax=massmax-massfuel-massempty
-self:T3(self.lid..string.format("Unit name=%s: mass empty=%.1f kg, fuel=%.1f kg, max=%.1f kg ==> cargo=%.1f kg",unit:GetName(),unitweight,massfuel,massmax,cargomax))
-local bay=forcecargobay or unit:GetCargoBayFreeWeight()
-table.insert(cargobay,bay)
-cargobaytot=cargobaytot+bay
-if bay>cargobaymax then
-cargobaymax=bay
-end
-end
-local attribute=forceattribute or self:_GetAttribute(group)
-local assets={}
-for i=1,n do
-local asset={}
-_WAREHOUSEDB.AssetID=_WAREHOUSEDB.AssetID+1
-asset.uid=_WAREHOUSEDB.AssetID
-asset.templatename=templategroupname
-asset.template=UTILS.DeepCopy(_DATABASE.Templates.Groups[templategroupname].Template)
-asset.category=Category
-asset.unittype=TypeName
-asset.nunits=#asset.template.units
-asset.range=RangeMin
-asset.speedmax=SpeedMax
-asset.size=smax
-asset.weight=weight
-asset.weights=weights
-asset.DCSdesc=Descriptors
-asset.attribute=attribute
-asset.cargobay=cargobay
-asset.cargobaytot=cargobaytot
-asset.cargobaymax=cargobaymax
-asset.loadradius=loadradius
-if liveries then
-asset.livery=liveries[math.random(#liveries)]
-end
-asset.skill=skill
-asset.assignment=assignment
-asset.spawned=false
-asset.requested=false
-asset.isReserved=false
-asset.life0=group:GetLife0()
-asset.damage=0
-asset.spawngroupname=string.format("%s_AID-%d",templategroupname,asset.uid)
-if i==1 then
-self:_AssetItemInfo(asset)
-end
-_WAREHOUSEDB.Assets[asset.uid]=asset
-table.insert(assets,asset)
-end
-return assets
-end
-function WAREHOUSE:_AssetItemInfo(asset)
-local text=string.format("\nNew asset with id=%d for warehouse %s:\n",asset.uid,self.alias)
-text=text..string.format("Spawngroup name= %s\n",asset.spawngroupname)
-text=text..string.format("Template name = %s\n",asset.templatename)
-text=text..string.format("Unit type = %s\n",asset.unittype)
-text=text..string.format("Attribute = %s\n",asset.attribute)
-text=text..string.format("Category = %d\n",asset.category)
-text=text..string.format("Units # = %d\n",asset.nunits)
-text=text..string.format("Speed max = %5.2f km/h\n",asset.speedmax)
-text=text..string.format("Range max = %5.2f km\n",asset.range/1000)
-text=text..string.format("Size max = %5.2f m\n",asset.size)
-text=text..string.format("Weight total = %5.2f kg\n",asset.weight)
-text=text..string.format("Cargo bay tot = %5.2f kg\n",asset.cargobaytot)
-text=text..string.format("Cargo bay max = %5.2f kg\n",asset.cargobaymax)
-text=text..string.format("Load radius = %s m\n",tostring(asset.loadradius))
-text=text..string.format("Skill = %s\n",tostring(asset.skill))
-text=text..string.format("Livery = %s",tostring(asset.livery))
-self:I(self.lid..text)
-self:T({DCSdesc=asset.DCSdesc})
-self:T3({Template=asset.template})
-end
-function WAREHOUSE:onafterNewAsset(From,Event,To,asset,assignment)
-self:T(self.lid..string.format("New asset %s id=%d with assignment %s.",tostring(asset.templatename),asset.uid,tostring(assignment)))
-end
-function WAREHOUSE:onbeforeAddRequest(From,Event,To,warehouse,AssetDescriptor,AssetDescriptorValue,nAsset,TransportType,nTransport,Assignment,Prio)
-local okay=true
-if AssetDescriptor==WAREHOUSE.Descriptor.ATTRIBUTE then
-local gotit=false
-for _,attribute in pairs(WAREHOUSE.Attribute)do
-if AssetDescriptorValue==attribute then
-gotit=true
-end
-end
-if not gotit then
-self:_ErrorMessage("ERROR: Invalid request. Asset attribute is unknown!",5)
-okay=false
-end
-elseif AssetDescriptor==WAREHOUSE.Descriptor.CATEGORY then
-local gotit=false
-for _,category in pairs(Group.Category)do
-if AssetDescriptorValue==category then
-gotit=true
-end
-end
-if not gotit then
-self:_ErrorMessage("ERROR: Invalid request. Asset category is unknown!",5)
-okay=false
-end
-elseif AssetDescriptor==WAREHOUSE.Descriptor.GROUPNAME then
-if type(AssetDescriptorValue)~="string"then
-self:_ErrorMessage("ERROR: Invalid request. Asset template name must be passed as a string!",5)
-okay=false
-end
-elseif AssetDescriptor==WAREHOUSE.Descriptor.UNITTYPE then
-if type(AssetDescriptorValue)~="string"then
-self:_ErrorMessage("ERROR: Invalid request. Asset unit type must be passed as a string!",5)
-okay=false
-end
-elseif AssetDescriptor==WAREHOUSE.Descriptor.ASSIGNMENT then
-if type(AssetDescriptorValue)~="string"then
-self:_ErrorMessage("ERROR: Invalid request. Asset assignment type must be passed as a string!",5)
-okay=false
-end
-elseif AssetDescriptor==WAREHOUSE.Descriptor.ASSETLIST then
-if type(AssetDescriptorValue)~="table"then
-self:_ErrorMessage("ERROR: Invalid request. Asset assignment type must be passed as a table!",5)
-okay=false
-end
-else
-self:_ErrorMessage("ERROR: Invalid request. Asset descriptor is not ATTRIBUTE, CATEGORY, GROUPNAME, UNITTYPE or ASSIGNMENT!",5)
-okay=false
-end
-if self:IsStopped()then
-self:_ErrorMessage("ERROR: Invalid request. Warehouse is stopped!",0)
-okay=false
-end
-if self:IsDestroyed()and not self.respawnafterdestroyed then
-self:_ErrorMessage("ERROR: Invalid request. Warehouse is destroyed!",0)
-okay=false
-end
-return okay
-end
-function WAREHOUSE:onafterAddRequest(From,Event,To,warehouse,AssetDescriptor,AssetDescriptorValue,nAsset,TransportType,nTransport,Prio,Assignment)
-nAsset=nAsset or 1
-TransportType=TransportType or WAREHOUSE.TransportType.SELFPROPELLED
-Prio=Prio or 50
-if nTransport==nil then
-if TransportType==WAREHOUSE.TransportType.SELFPROPELLED then
-nTransport=0
-else
-nTransport=1
-end
-end
-local toself=false
-if self.warehouse:GetName()==warehouse.warehouse:GetName()then
-toself=true
-end
-self.queueid=self.queueid+1
-local request={
-uid=self.queueid,
-prio=Prio,
-warehouse=warehouse,
-assetdesc=AssetDescriptor,
-assetdescval=AssetDescriptorValue,
-nasset=nAsset,
-transporttype=TransportType,
-ntransport=nTransport,
-assignment=tostring(Assignment),
-airbase=warehouse:GetAirbase(),
-category=warehouse:GetAirbaseCategory(),
-ndelivered=0,
-ntransporthome=0,
-assets={},
-toself=toself,
-}
-table.insert(self.queue,request)
-local descval="assetlist"
-if request.assetdesc==WAREHOUSE.Descriptor.ASSETLIST then
-else
-descval=tostring(request.assetdescval)
-end
-local text=string.format("Warehouse %s: New request from warehouse %s.\nDescriptor %s=%s, #assets=%s; Transport=%s, #transports=%s.",
-self.alias,warehouse.alias,request.assetdesc,descval,tostring(request.nasset),request.transporttype,tostring(request.ntransport))
-self:_DebugMessage(text,5)
-end
-function WAREHOUSE:onbeforeRequest(From,Event,To,Request)
-self:T3({warehouse=self.alias,request=Request})
-local distance=self:GetCoordinate():Get2DDistance(Request.warehouse:GetCoordinate())
-local _assets=Request.cargoassets
-if Request.nasset==0 then
-local text=string.format("Warehouse %s: Request denied! Zero assets were requested.",self.alias)
-self:_InfoMessage(text,10)
-return false
-end
-for _,_asset in pairs(_assets)do
-local asset=_asset
-if asset.range=1 then
-local text=string.format("Warehouse %s: Processing request id=%d from warehouse %s.\n",self.alias,Request.uid,Request.warehouse.alias)
-text=text..string.format("Requested %s assets of %s=%s.\n",tostring(Request.nasset),Request.assetdesc,Request.assetdesc==WAREHOUSE.Descriptor.ASSETLIST and"Asset list"or Request.assetdescval)
-text=text..string.format("Transports %s of type %s.",tostring(Request.ntransport),tostring(Request.transporttype))
-self:_InfoMessage(text,5)
-end
-Request.timestamp=timer.getAbsTime()
-self:_SpawnAssetRequest(Request)
-local _assetstock=Request.transportassets
-local Parking={}
-if Request.transportcategory==Group.Category.AIRPLANE or Request.transportcategory==Group.Category.HELICOPTER then
-Parking=self:_FindParkingForAssets(self.airbase,_assetstock)
-end
-local _transportassets={}
-for i=1,Request.ntransport do
-local _assetitem=_assetstock[i]
-local _alias=_assetitem.spawngroupname
-_assetitem.rid=Request.uid
-_assetitem.spawned=false
-_assetitem.iscargo=false
-_assetitem.arrived=false
-local spawngroup=nil
-Request.assets[_assetitem.uid]=_assetitem
-if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE then
-spawngroup=self:_SpawnAssetAircraft(_alias,_assetitem,Request,Parking[_assetitem.uid],true)
-elseif Request.transporttype==WAREHOUSE.TransportType.HELICOPTER then
-spawngroup=self:_SpawnAssetAircraft(_alias,_assetitem,Request,Parking[_assetitem.uid],false)
-elseif Request.transporttype==WAREHOUSE.TransportType.APC then
-spawngroup=self:_SpawnAssetGroundNaval(_alias,_assetitem,Request,self.spawnzone)
-elseif Request.transporttype==WAREHOUSE.TransportType.TRAIN then
-self:_ErrorMessage("ERROR: Cargo transport by train not supported yet!")
-return
-elseif Request.transporttype==WAREHOUSE.TransportType.SHIP or Request.transporttype==WAREHOUSE.TransportType.NAVALCARRIER
-or Request.transporttype==WAREHOUSE.TransportType.ARMEDSHIP or Request.transporttype==WAREHOUSE.TransportType.WARSHIP then
-spawngroup=self:_SpawnAssetGroundNaval(_alias,_assetitem,Request,self.portzone)
-elseif Request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then
-self:_ErrorMessage("ERROR: Transport type selfpropelled was already handled above. We should not get here!")
-return
-else
-self:_ErrorMessage("ERROR: Unknown transport type!")
-return
-end
-if spawngroup then
-self:__AssetSpawned(0.01,spawngroup,_assetitem,Request)
-end
-end
-Request.assetproblem={}
-table.insert(self.pending,Request)
-self:_DeleteQueueItem(Request,self.queue)
-end
-function WAREHOUSE:onafterRequestSpawned(From,Event,To,Request,CargoGroupSet,TransportGroupSet)
-local _cargotype=Request.cargoattribute
-local _cargocategory=Request.cargocategory
-if Request.toself then
-self:_DebugMessage(string.format("Selfrequest! Current status %s",self:GetState()))
-self:__SelfRequest(1,CargoGroupSet,Request)
-return
-end
-if Request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then
-self:T2(self.lid..string.format("Got selfpropelled request for %d assets.",CargoGroupSet:Count()))
-for _,_group in pairs(CargoGroupSet:GetSetObjects())do
-local group=_group
-if _cargocategory==Group.Category.GROUND then
-self:T2(self.lid..string.format("Route ground group %s.",group:GetName()))
-local ToCoordinate=Request.warehouse.spawnzone:GetRandomCoordinate()
-if self.Debug then
-ToCoordinate:MarkToAll(string.format("Destination of group %s",group:GetName()))
-end
-self:_RouteGround(group,Request)
-elseif _cargocategory==Group.Category.AIRPLANE or _cargocategory==Group.Category.HELICOPTER then
-self:T2(self.lid..string.format("Route airborne group %s.",group:GetName()))
-self:_RouteAir(group)
-elseif _cargocategory==Group.Category.SHIP then
-self:T2(self.lid..string.format("Route naval group %s.",group:GetName()))
-self:_RouteNaval(group,Request)
-elseif _cargocategory==Group.Category.TRAIN then
-self:T2(self.lid..string.format("Route train group %s.",group:GetName()))
-self:_RouteTrain(group,Request.warehouse.rail)
-else
-self:E(self.lid..string.format("ERROR: unknown category %s for self propelled cargo %s!",tostring(_cargocategory),tostring(group:GetName())))
-end
-end
-Request.transportgroupset=TransportGroupSet
-return
-end
-local _boardradius=500
-if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE then
-_boardradius=5000
-elseif Request.transporttype==WAREHOUSE.TransportType.HELICOPTER then
-elseif Request.transporttype==WAREHOUSE.TransportType.APC then
-elseif Request.transporttype==WAREHOUSE.TransportType.SHIP or Request.transporttype==WAREHOUSE.TransportType.AIRCRAFTCARRIER
-or Request.transporttype==WAREHOUSE.TransportType.ARMEDSHIP or Request.transporttype==WAREHOUSE.TransportType.WARSHIP then
-_boardradius=6000
-end
-local CargoGroups=SET_CARGO:New()
-for _,_group in pairs(CargoGroupSet:GetSetObjects())do
-local asset=self:FindAssetInDB(_group)
-local cargogroup=CARGO_GROUP:New(_group,_cargotype,_group:GetName(),_boardradius,asset.loadradius)
-cargogroup:SetWeight(asset.weight)
-CargoGroups:AddCargo(cargogroup)
-end
-local CargoTransport
-if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE then
-local PickupAirbaseSet=SET_ZONE:New():AddZone(ZONE_AIRBASE:New(self.airbase:GetName()))
-local DeployAirbaseSet=SET_ZONE:New():AddZone(ZONE_AIRBASE:New(Request.airbase:GetName()))
-CargoTransport=AI_CARGO_DISPATCHER_AIRPLANE:New(TransportGroupSet,CargoGroups,PickupAirbaseSet,DeployAirbaseSet)
-CargoTransport:SetHomeZone(ZONE_AIRBASE:New(self.airbase:GetName()))
-elseif Request.transporttype==WAREHOUSE.TransportType.HELICOPTER then
-local PickupZoneSet=SET_ZONE:New():AddZone(self.spawnzone)
-local DeployZoneSet=SET_ZONE:New():AddZone(Request.warehouse.spawnzone)
-CargoTransport=AI_CARGO_DISPATCHER_HELICOPTER:New(TransportGroupSet,CargoGroups,PickupZoneSet,DeployZoneSet)
-CargoTransport:SetHomeZone(self.spawnzone)
-elseif Request.transporttype==WAREHOUSE.TransportType.APC then
-local PickupZoneSet=SET_ZONE:New():AddZone(self.spawnzone)
-local DeployZoneSet=SET_ZONE:New():AddZone(Request.warehouse.spawnzone)
-CargoTransport=AI_CARGO_DISPATCHER_APC:New(TransportGroupSet,CargoGroups,PickupZoneSet,DeployZoneSet,0)
-CargoTransport:SetHomeZone(self.spawnzone)
-elseif Request.transporttype==WAREHOUSE.TransportType.SHIP or Request.transporttype==WAREHOUSE.TransportType.AIRCRAFTCARRIER
-or Request.transporttype==WAREHOUSE.TransportType.ARMEDSHIP or Request.transporttype==WAREHOUSE.TransportType.WARSHIP then
-local PickupZoneSet=SET_ZONE:New():AddZone(self.portzone)
-PickupZoneSet:AddZone(self.harborzone)
-local DeployZoneSet=SET_ZONE:New():AddZone(Request.warehouse.harborzone)
-local remotename=Request.warehouse.warehouse:GetName()
-local ShippingLane=self.shippinglanes[remotename][math.random(#self.shippinglanes[remotename])]
-CargoTransport=AI_CARGO_DISPATCHER_SHIP:New(TransportGroupSet,CargoGroups,PickupZoneSet,DeployZoneSet,ShippingLane)
-CargoTransport:SetHomeZone(self.portzone)
-else
-self:E(self.lid.."ERROR: Unknown transporttype!")
-end
-local pickupouter=200
-local pickupinner=0
-local deployouter=200
-local deployinner=0
-if Request.transporttype==WAREHOUSE.TransportType.SHIP or Request.transporttype==WAREHOUSE.TransportType.AIRCRAFTCARRIER
-or Request.transporttype==WAREHOUSE.TransportType.ARMEDSHIP or Request.transporttype==WAREHOUSE.TransportType.WARSHIP then
-pickupouter=1000
-pickupinner=20
-deployouter=1000
-deployinner=0
-else
-pickupouter=200
-pickupinner=0
-if self.spawnzone.Radius~=nil then
-pickupouter=self.spawnzone.Radius
-pickupinner=20
-end
-deployouter=200
-deployinner=0
-if self.spawnzone.Radius~=nil then
-deployouter=Request.warehouse.spawnzone.Radius
-deployinner=20
-end
-end
-CargoTransport:SetPickupRadius(pickupouter,pickupinner)
-CargoTransport:SetDeployRadius(deployouter,deployinner)
-Request.carriercargo={}
-for _,carriergroup in pairs(TransportGroupSet:GetSetObjects())do
-local asset=self:FindAssetInDB(carriergroup)
-for _i,_carrierunit in pairs(carriergroup:GetUnits())do
-local carrierunit=_carrierunit
-Request.carriercargo[carrierunit:GetName()]={}
-local cargobay=asset.cargobay[_i]
-carrierunit:SetCargoBayWeightLimit(cargobay)
-self:T2(self.lid..string.format("Cargo bay weight limit of carrier unit %s: %.1f kg.",carrierunit:GetName(),carrierunit:GetCargoBayFreeWeight()))
-end
-end
-function CargoTransport:OnAfterPickedUp(From,Event,To,Carrier,PickupZone)
-local warehouse=Carrier:GetState(Carrier,"WAREHOUSE")
-local text=string.format("Carrier group %s picked up at pickup zone %s.",Carrier:GetName(),PickupZone:GetName())
-warehouse:T(warehouse.lid..text)
-end
-function CargoTransport:OnAfterDeployed(From,Event,To,Carrier,DeployZone)
-local warehouse=Carrier:GetState(Carrier,"WAREHOUSE")
-end
-function CargoTransport:OnAfterHome(From,Event,To,Carrier,Coordinate,Speed,Height,HomeZone)
-local warehouse=Carrier:GetState(Carrier,"WAREHOUSE")
-local text=string.format("Carrier group %s going home to zone %s.",Carrier:GetName(),HomeZone:GetName())
-warehouse:T(warehouse.lid..text)
-end
-function CargoTransport:OnAfterLoaded(From,Event,To,Carrier,Cargo,CarrierUnit,PickupZone)
-local warehouse=Carrier:GetState(Carrier,"WAREHOUSE")
-local text=string.format("Carrier group %s loaded cargo %s into unit %s in pickup zone %s",Carrier:GetName(),Cargo:GetName(),CarrierUnit:GetName(),PickupZone:GetName())
-warehouse:T(warehouse.lid..text)
-local group=Cargo:GetObject()
-local request=warehouse:_GetRequestOfGroup(group,warehouse.pending)
-table.insert(request.carriercargo[CarrierUnit:GetName()],warehouse:_GetNameWithOut(Cargo:GetName()))
-end
-function CargoTransport:OnAfterUnloaded(From,Event,To,Carrier,Cargo,CarrierUnit,DeployZone)
-local warehouse=Carrier:GetState(Carrier,"WAREHOUSE")
-local group=Cargo:GetObject()
-local text=string.format("Cargo group %s was unloaded from carrier unit %s.",tostring(group:GetName()),tostring(CarrierUnit:GetName()))
-warehouse:T(warehouse.lid..text)
-warehouse:Arrived(group)
-end
-function CargoTransport:OnAfterBackHome(From,Event,To,Carrier)
-local carrier=Carrier
-local warehouse=carrier:GetState(carrier,"WAREHOUSE")
-carrier:SmokeWhite()
-local text=string.format("Carrier %s is back home at warehouse %s.",tostring(Carrier:GetName()),tostring(warehouse.warehouse:GetName()))
-MESSAGE:New(text,5):ToAllIf(warehouse.Debug)
-warehouse:I(warehouse.lid..text)
-warehouse:__Arrived(1,Carrier)
-end
-CargoTransport:__Start(5)
-end
-function WAREHOUSE:onafterUnloaded(From,Event,To,group)
-self:_DebugMessage(string.format("Cargo %s unloaded!",tostring(group:GetName())),5)
-if group and group:IsAlive()then
-if self.Debug then
-group:SmokeWhite()
-end
-local speedmax=group:GetSpeedMax()
-if group:IsGround()then
-if speedmax>1 then
-group:RouteGroundTo(self.spawnzone:GetRandomCoordinate(),speedmax*0.5,AI.Task.VehicleFormation.RANK,3)
-else
-self:Arrived(group)
-end
-elseif group:IsAir()then
-self:Arrived(group)
-elseif group:IsShip()then
-self:Arrived(group)
-end
-else
-self:E(self.lid..string.format("ERROR unloaded Cargo group is not alive!"))
-end
-end
-function WAREHOUSE:onbeforeArrived(From,Event,To,group)
-local asset=self:FindAssetInDB(group)
-if asset then
-if asset.flightgroup and not asset.arrived then
-asset.arrived=true
-return false
-end
-if asset.arrived==true then
-return false
-else
-asset.arrived=true
-return true
-end
-end
-end
-function WAREHOUSE:onafterArrived(From,Event,To,group)
-if self.Debug then
-group:SmokeOrange()
-end
-local request=self:_GetRequestOfGroup(group,self.pending)
-if request then
-local warehouse=request.warehouse
-local istransport=self:_GroupIsTransport(group,request)
-if istransport==true then
-warehouse=self
-elseif istransport==false then
-warehouse=request.warehouse
-else
-self:E(self.lid..string.format("ERROR: Group %s is neither cargo nor transport",group:GetName()))
-return
-end
-self:_DebugMessage(string.format("Group %s arrived at warehouse %s!",tostring(group:GetName()),warehouse.alias),5)
-if group:IsGround()and group:GetSpeedMax()>1 then
-group:RouteGroundTo(warehouse:GetCoordinate(),group:GetSpeedMax()*0.3,"Off Road")
-end
-self:T(self.lid.."Asset arrived at warehouse adding in 60 sec")
-warehouse:__AddAsset(60,group)
-end
-end
-function WAREHOUSE:onafterDelivered(From,Event,To,request)
-if self.verbosity>=1 then
-local text=string.format("Warehouse %s: All assets delivered to warehouse %s!",self.alias,request.warehouse.alias)
-self:_InfoMessage(text,5)
-end
-if self.Debug then
-self:_Fireworks(request.warehouse:GetCoordinate())
-end
-self.delivered[request.uid]=true
-end
-function WAREHOUSE:onafterSelfRequest(From,Event,To,groupset,request)
-self:_DebugMessage(string.format("Assets spawned at warehouse %s after self request!",self.alias))
-for _,_group in pairs(groupset:GetSetObjects())do
-local group=_group
-if self.Debug then
-group:FlareGreen()
-end
-end
-if self:IsAttacked()then
-if self.autodefence then
-for _,_group in pairs(groupset:GetSetObjects())do
-local group=_group
-local speedmax=group:GetSpeedMax()
-if group:IsGround()and speedmax>1 and group:IsNotInZone(self.zone)then
-group:RouteGroundTo(self.zone:GetRandomCoordinate(),0.8*speedmax,"Off Road")
-end
-end
-end
-table.insert(self.defending,request)
-end
-end
-function WAREHOUSE:onafterAttacked(From,Event,To,Coalition,Country)
-local text=string.format("Warehouse %s: We are under attack!",self.alias)
-self:_InfoMessage(text)
-if self.Debug then
-self:GetCoordinate():SmokeOrange()
-end
-if self.autodefence then
-local nground=self:GetNumberOfAssets(WAREHOUSE.Descriptor.CATEGORY,Group.Category.GROUND)
-local text=string.format("Warehouse auto defence activated.\n")
-if nground>0 then
-text=text..string.format("Deploying all %d ground assets.",nground)
-self:AddRequest(self,WAREHOUSE.Descriptor.CATEGORY,Group.Category.GROUND,WAREHOUSE.Quantity.ALL,nil,nil,0,"AutoDefence")
-else
-text=text..string.format("No ground assets currently available.")
-end
-self:_InfoMessage(text)
-else
-local text=string.format("Warehouse auto defence inactive.")
-self:I(self.lid..text)
-end
-end
-function WAREHOUSE:onafterDefeated(From,Event,To)
-local text=string.format("Warehouse %s: Enemy attack was defeated!",self.alias)
-self:_InfoMessage(text)
-if self.Debug then
-self:GetCoordinate():SmokeGreen()
-end
-if self.autodefence then
-for _,request in pairs(self.defending)do
-for _,_group in pairs(request.cargogroupset:GetSetObjects())do
-local group=_group
-local speed=group:GetSpeedMax()
-if group:IsGround()and speed>1 then
-group:RouteGroundTo(self:GetCoordinate(),speed*0.3)
-end
-self:__AddAsset(60,group)
-end
-end
-self.defending=nil
-self.defending={}
-end
-end
-function WAREHOUSE:onafterRespawn(From,Event,To)
-local text=string.format("Respawning warehouse %s",self.alias)
-self:_InfoMessage(text)
-self.warehouse:ReSpawn()
-end
-function WAREHOUSE:onbeforeChangeCountry(From,Event,To,Country)
-local currentCountry=self:GetCountry()
-local text=string.format("Warehouse %s: request to change country %d-->%d",self.alias,currentCountry,Country)
-self:_DebugMessage(text,10)
-if currentCountry~=Country then
-return true
-end
-return false
-end
-function WAREHOUSE:onafterChangeCountry(From,Event,To,Country)
-local CoalitionOld=self:GetCoalition()
-self.warehouse:ReSpawn(Country)
-local CoalitionNew=self:GetCoalition()
-self.queue=nil
-self.queue={}
-if self.airbasename then
-local airbase=AIRBASE:FindByName(self.airbasename)
-local airbaseCoalition=airbase:GetCoalition()
-if CoalitionNew==airbaseCoalition then
-self.airbase=airbase
-else
-self.airbase=nil
-end
-end
-if self.Debug then
-if CoalitionNew==coalition.side.RED then
-self:GetCoordinate():SmokeRed()
-elseif CoalitionNew==coalition.side.BLUE then
-self:GetCoordinate():SmokeBlue()
-end
-end
-end
-function WAREHOUSE:onbeforeCaptured(From,Event,To,Coalition,Country)
-self:ChangeCountry(Country)
-end
-function WAREHOUSE:onafterCaptured(From,Event,To,Coalition,Country)
-local text=string.format("Warehouse %s: We were captured by enemy coalition (side=%d)!",self.alias,Coalition)
-self:_InfoMessage(text)
-end
-function WAREHOUSE:onafterAirbaseCaptured(From,Event,To,Coalition)
-local text=string.format("Warehouse %s: Our airbase %s was captured by the enemy (coalition=%d)!",self.alias,self.airbasename,Coalition)
-self:_InfoMessage(text)
-if self.Debug then
-if Coalition==coalition.side.RED then
-self.airbase:GetCoordinate():SmokeRed()
-elseif Coalition==coalition.side.BLUE then
-self.airbase:GetCoordinate():SmokeBlue()
-end
-end
-self.airbase=nil
-end
-function WAREHOUSE:onafterAirbaseRecaptured(From,Event,To,Coalition)
-local text=string.format("Warehouse %s: We recaptured our airbase %s from the enemy (coalition=%d)!",self.alias,self.airbasename,Coalition)
-self:_InfoMessage(text)
-self.airbase=AIRBASE:FindByName(self.airbasename)
-if self.Debug then
-if Coalition==coalition.side.RED then
-self.airbase:GetCoordinate():SmokeRed()
-elseif Coalition==coalition.side.BLUE then
-self.airbase:GetCoordinate():SmokeBlue()
-end
-end
-end
-function WAREHOUSE:onafterRunwayDestroyed(From,Event,To)
-local text=string.format("Warehouse %s: Runway %s destroyed!",self.alias,self.airbasename)
-self:_InfoMessage(text)
-self.runwaydestroyed=timer.getAbsTime()
-end
-function WAREHOUSE:onafterRunwayRepaired(From,Event,To)
-local text=string.format("Warehouse %s: Runway %s repaired!",self.alias,self.airbasename)
-self:_InfoMessage(text)
-self.runwaydestroyed=nil
-end
-function WAREHOUSE:onafterAssetSpawned(From,Event,To,group,asset,request)
-local text=string.format("Asset %s from request id=%d was spawned!",asset.spawngroupname,request.uid)
-self:T(self.lid..text)
-asset.spawned=true
-asset.spawngroupname=group:GetName()
-self:_DeleteStockItem(asset)
-if asset.iscargo==true then
-request.cargogroupset=request.cargogroupset or SET_GROUP:New()
-request.cargogroupset:AddGroup(group)
-else
-request.transportgroupset=request.transportgroupset or SET_GROUP:New()
-request.transportgroupset:AddGroup(group)
-end
-group:SetState(group,"WAREHOUSE",self)
-local n=0
-for _,_asset in pairs(request.assets)do
-local assetitem=_asset
-self:T(self.lid..string.format("Asset %s spawned %s as %s",assetitem.templatename,tostring(assetitem.spawned),tostring(assetitem.spawngroupname)))
-if assetitem.spawned then
-n=n+1
-else
-end
-end
-if n==request.nasset+request.ntransport then
-self:T(self.lid..string.format("All assets %d (ncargo=%d + ntransport=%d) of request rid=%d spawned. Calling RequestSpawned",n,request.nasset,request.ntransport,request.uid))
-self:RequestSpawned(request,request.cargogroupset,request.transportgroupset)
-else
-self:T(self.lid..string.format("Not all assets %d (ncargo=%d + ntransport=%d) of request rid=%d spawned YET",n,request.nasset,request.ntransport,request.uid))
-end
-end
-function WAREHOUSE:onafterAssetDead(From,Event,To,asset,request)
-if asset and request then
-local text=string.format("Asset %s from request id=%d is dead!",asset.templatename,request.uid)
-self:T(self.lid..text)
-local groupname=asset.spawngroupname
-local NoTriggerEvent=true
-if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then
-if request.cargogroupset then
-request.cargogroupset:Remove(groupname,NoTriggerEvent)
-self:T(self.lid..string.format("Removed selfpropelled cargo %s: ncargo=%d.",groupname,request.cargogroupset:Count()))
-else
-self:E(self.lid..string.format("ERROR: cargogroupset is nil for request ID=%s!",tostring(request.uid)))
-end
-else
-local istransport=not asset.iscargo
-if istransport==true then
-request.transportgroupset:Remove(groupname,NoTriggerEvent)
-self:T(self.lid..string.format("Removed transport %s: ntransport=%d",groupname,request.transportgroupset:Count()))
-elseif istransport==false then
-request.cargogroupset:Remove(groupname,NoTriggerEvent)
-self:T(self.lid..string.format("Removed transported cargo %s outside carrier: ncargo=%d",groupname,request.cargogroupset:Count()))
-else
-end
-end
-else
-self:E(self.lid.."ERROR: Asset and/or Request is nil in onafterAssetDead")
-end
-end
-function WAREHOUSE:onafterDestroyed(From,Event,To)
-local text=string.format("Warehouse %s was destroyed! Assets lost %d. Respawn=%s",self.alias,#self.stock,tostring(self.respawnafterdestroyed))
-self:_InfoMessage(text)
-if self.respawnafterdestroyed then
-if self.respawndelay then
-self:Pause()
-self:__Respawn(self.respawndelay)
-else
-self:Respawn()
-end
-else
-for k,_ in pairs(self.queue)do
-self.queue[k]=nil
-end
-for k,_ in pairs(self.stock)do
-end
-for k=#self.stock,1,-1 do
-self.stock[k]=nil
-end
-end
-end
-function WAREHOUSE:onafterSave(From,Event,To,path,filename)
-local function _savefile(filename,data)
-local f=assert(io.open(filename,"wb"))
-f:write(data)
-f:close()
-end
-filename=filename or string.format("WAREHOUSE-%d_%s.txt",self.uid,self.alias)
-if path~=nil then
-filename=path.."\\"..filename
-end
-local text=string.format("Saving warehouse assets to file %s",filename)
-MESSAGE:New(text,30):ToAllIf(self.Debug or self.Report)
-self:I(self.lid..text)
-local warehouseassets=""
-warehouseassets=warehouseassets..string.format("coalition=%d\n",self:GetCoalition())
-warehouseassets=warehouseassets..string.format("country=%d\n",self:GetCountry())
-for _,_asset in pairs(self.stock)do
-local asset=_asset
-local assetstring=""
-for key,value in pairs(asset)do
-if key=="templatename"or key=="attribute"or key=="cargobay"or key=="weight"or key=="loadradius"or key=="livery"or key=="skill"or key=="assignment"then
-local name
-if type(value)=="table"then
-name=string.format("%s=%s;",key,value[1])
-else
-name=string.format("%s=%s;",key,value)
-end
-assetstring=assetstring..name
-end
-self:I(string.format("Loaded asset: %s",assetstring))
-end
-warehouseassets=warehouseassets..assetstring.."\n"
-end
-_savefile(filename,warehouseassets)
-end
-function WAREHOUSE:onbeforeLoad(From,Event,To,path,filename)
-local function _fileexists(name)
-local f=io.open(name,"r")
-if f~=nil then
-io.close(f)
-return true
-else
-return false
-end
-end
-filename=filename or string.format("WAREHOUSE-%d_%s.txt",self.uid,self.alias)
-if path~=nil then
-filename=path.."\\"..filename
-end
-local exists=_fileexists(filename)
-if exists then
-return true
-else
-self:_ErrorMessage(string.format("ERROR: file %s does not exist! Cannot load assets.",filename),60)
-return false
-end
-end
-function WAREHOUSE:onafterLoad(From,Event,To,path,filename)
-local function _loadfile(filename)
-local f=assert(io.open(filename,"rb"))
-local data=f:read("*all")
-f:close()
-return data
-end
-filename=filename or string.format("WAREHOUSE-%d_%s.txt",self.uid,self.alias)
-if path~=nil then
-filename=path.."\\"..filename
-end
-local text=string.format("Loading warehouse assets from file %s",filename)
-MESSAGE:New(text,30):ToAllIf(self.Debug or self.Report)
-self:I(self.lid..text)
-local data=_loadfile(filename)
-local assetdata=UTILS.Split(data,"\n")
-local Coalition
-local Country
-local assets={}
-for _,asset in pairs(assetdata)do
-local descriptors=UTILS.Split(asset,";")
-local asset={}
-local isasset=false
-for _,descriptor in pairs(descriptors)do
-local keyval=UTILS.Split(descriptor,"=")
-if#keyval==2 then
-if keyval[1]=="coalition"then
-Coalition=tonumber(keyval[2])
-elseif keyval[1]=="country"then
-Country=tonumber(keyval[2])
-else
-isasset=true
-local key=keyval[1]
-local val=keyval[2]
-if val=="nil"then
-val=nil
-end
-if key=="cargobay"or key=="weight"or key=="loadradius"then
-asset[key]=tonumber(val)
-else
-asset[key]=val
-end
-end
-end
-end
-if isasset then
-table.insert(assets,asset)
-end
-end
-if Country~=self:GetCountry()then
-self:T(self.lid..string.format("Changing warehouse country %d-->%d on loading assets.",self:GetCountry(),Country))
-self:ChangeCountry(Country)
-end
-for _,_asset in pairs(assets)do
-local asset=_asset
-local group=GROUP:FindByName(asset.templatename)
-if group then
-self:AddAsset(group,1,asset.attribute,asset.cargobay,asset.weight,asset.loadradius,asset.skill,asset.livery,asset.assignment)
-else
-self:E(string.format("ERROR: Group %s doest not exit. Cannot be loaded as asset.",tostring(asset.templatename)))
-end
-end
-end
-function WAREHOUSE:_SpawnAssetRequest(Request)
-self:F2({requestUID=Request.uid})
-local cargoassets=Request.cargoassets
-local Parking={}
-if Request.cargocategory==Group.Category.AIRPLANE or Request.cargocategory==Group.Category.HELICOPTER then
-Parking=self:_FindParkingForAssets(self.airbase,cargoassets)or{}
-end
-local UnControlled=true
-for i=1,#cargoassets do
-local asset=cargoassets[i]
-if not asset.spawned then
-asset.iscargo=true
-asset.rid=Request.uid
-local _alias=asset.spawngroupname
-Request.assets[asset.uid]=asset
-local _group=nil
-if asset.category==Group.Category.GROUND then
-_group=self:_SpawnAssetGroundNaval(_alias,asset,Request,self.spawnzone,Request.lateActivation)
-elseif asset.category==Group.Category.AIRPLANE or asset.category==Group.Category.HELICOPTER then
-if Parking[asset.uid]then
-_group=self:_SpawnAssetAircraft(_alias,asset,Request,Parking[asset.uid],UnControlled,Request.lateActivation)
-else
-_group=self:_SpawnAssetAircraft(_alias,asset,Request,nil,UnControlled,Request.lateActivation)
-end
-elseif asset.category==Group.Category.TRAIN then
-if self.rail then
-_group=self:_SpawnAssetGroundNaval(_alias,asset,Request,self.spawnzone,Request.lateActivation)
-end
-elseif asset.category==Group.Category.SHIP then
-_group=self:_SpawnAssetGroundNaval(_alias,asset,Request,self.portzone,Request.lateActivation)
-else
-self:E(self.lid.."ERROR: Unknown asset category!")
-end
-if _group then
-self:__AssetSpawned(0.01,_group,asset,Request)
-end
-end
-end
-end
-function WAREHOUSE:_SpawnAssetGroundNaval(alias,asset,request,spawnzone,lateactivated)
-if asset and(asset.category==Group.Category.GROUND or asset.category==Group.Category.SHIP or asset.category==Group.Category.TRAIN)then
-local template=self:_SpawnAssetPrepareTemplate(asset,alias)
-template.route.points[1]={}
-local coord=spawnzone:GetRandomCoordinate()
-if asset.category==Group.Category.TRAIN then
-coord=self.rail
-end
-for i=1,#template.units do
-local unit=template.units[i]
-local SX=unit.x or 0
-local SY=unit.y or 0
-local BX=asset.template.route.points[1].x
-local BY=asset.template.route.points[1].y
-local TX=coord.x+(SX-BX)
-local TY=coord.z+(SY-BY)
-template.units[i].x=TX
-template.units[i].y=TY
-if asset.livery then
-unit.livery_id=asset.livery
-end
-if asset.skill then
-unit.skill=asset.skill
-end
-end
-template.lateActivation=lateactivated
-template.route.points[1].x=coord.x
-template.route.points[1].y=coord.z
-template.x=coord.x
-template.y=coord.z
-template.alt=coord.y
-local group=_DATABASE:Spawn(template)
-return group
-end
-return nil
-end
-function WAREHOUSE:_SpawnAssetAircraft(alias,asset,request,parking,uncontrolled,lateactivated)
-if asset and asset.category==Group.Category.AIRPLANE or asset.category==Group.Category.HELICOPTER then
-local template=self:_SpawnAssetPrepareTemplate(asset,alias)
-local _type=COORDINATE.WaypointType.TakeOffParking
-local _action=COORDINATE.WaypointAction.FromParkingArea
-if asset.takeoffType and asset.takeoffType==COORDINATE.WaypointType.TakeOffParkingHot then
-_type=COORDINATE.WaypointType.TakeOffParkingHot
-_action=COORDINATE.WaypointAction.FromParkingAreaHot
-uncontrolled=false
-end
-local airstart=asset.takeoffType and asset.takeoffType==COORDINATE.WaypointType.TurningPoint or false
-if airstart then
-_type=COORDINATE.WaypointType.TurningPoint
-_action=COORDINATE.WaypointAction.TurningPoint
-uncontrolled=false
-end
-if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then
-if request.toself then
-local coord=self.airbase:GetCoordinate()
-if airstart then
-coord:SetAltitude(math.random(1000,2000))
-end
-local wp=coord:WaypointAir("RADIO",_type,_action,0,false,self.airbase,{},"Parking")
-template.route.points={wp}
-else
-template.route.points=self:_GetFlightplan(asset,self.airbase,request.warehouse.airbase)
-end
-else
-template.route.points[1]=self.airbase:GetCoordinate():WaypointAir("BARO",_type,_action,0,true,self.airbase,nil,"Spawnpoint")
-end
-local AirbaseID=self.airbase:GetID()
-local AirbaseCategory=self:GetAirbaseCategory()
-if AirbaseCategory==Airbase.Category.HELIPAD or AirbaseCategory==Airbase.Category.SHIP then
-else
-if#parking<#template.units and not airstart then
-local text=string.format("ERROR: Not enough parking! Free parking = %d < %d aircraft to be spawned.",#parking,#template.units)
-self:_DebugMessage(text)
-return nil
-end
-end
-for i=1,#template.units do
-local unit=template.units[i]
-if AirbaseCategory==Airbase.Category.HELIPAD or AirbaseCategory==Airbase.Category.SHIP then
-local coord=self.airbase:GetCoordinate()
-unit.x=coord.x
-unit.y=coord.z
-unit.alt=coord.y
-if airstart then
-unit.alt=math.random(1000,2000)
-end
-unit.parking_id=nil
-unit.parking=nil
-else
-local coord=nil
-local terminal=nil
-if airstart then
-coord=self.airbase:GetCoordinate():SetAltitude(math.random(1000,2000))
-else
-coord=parking[i].Coordinate
-terminal=parking[i].TerminalID
-end
-if self.Debug then
-local text=string.format("Spawnplace unit %s terminal %d.",unit.name,terminal)
-coord:MarkToAll(text)
-env.info(text)
-end
-unit.x=coord.x
-unit.y=coord.z
-unit.alt=coord.y
-unit.parking_id=nil
-unit.parking=terminal
-end
-if asset.livery then
-unit.livery_id=asset.livery
-end
-if asset.skill then
-unit.skill=asset.skill
-end
-if asset.payload then
-unit.payload=asset.payload.pylons
-end
-if asset.modex then
-unit.onboard_num=asset.modex[i]
-end
-if asset.callsign then
-unit.callsign=asset.callsign[i]
-end
-end
-template.x=template.units[1].x
-template.y=template.units[1].y
-template.uncontrolled=uncontrolled
-self:T2({airtemplate=template})
-local group=_DATABASE:Spawn(template)
-return group
-end
-return nil
-end
-function WAREHOUSE:_SpawnAssetPrepareTemplate(asset,alias)
-local template=UTILS.DeepCopy(asset.template)
-template.name=alias
-template.CoalitionID=self:GetCoalition()
-template.CountryID=self:GetCountry()
-template.groupId=nil
-template.lateActivation=false
-if asset.missionTask then
-self:T(self.lid..string.format("Setting mission task to %s",tostring(asset.missionTask)))
-template.task=asset.missionTask
-end
-template.route={}
-template.route.routeRelativeTOT=true
-template.route.points={}
-for i=1,#template.units do
-local unit=template.units[i]
-unit.unitId=nil
-unit.name=string.format("%s-%02d",template.name,i)
-end
-return template
-end
-function WAREHOUSE:_RouteGround(group,request)
-if group and group:IsAlive()then
-local _speed=group:GetSpeedMax()*0.7
-local Waypoints={}
-local hasoffroad=self:HasConnectionOffRoad(request.warehouse,self.Debug)
-if hasoffroad then
-local remotename=request.warehouse.warehouse:GetName()
-local path=self.offroadpaths[remotename][math.random(#self.offroadpaths[remotename])]
-for i=1,#path do
-local coord=path[i]
-local Waypoint=coord:WaypointGround(_speed,"Off Road")
-table.insert(Waypoints,Waypoint)
-end
-else
-Waypoints=group:TaskGroundOnRoad(request.warehouse.road,_speed,"Off Road",false,self.road)
-local FromWP=group:GetCoordinate():WaypointGround(_speed,"Off Road")
-table.insert(Waypoints,1,FromWP)
-end
-for n,wp in ipairs(Waypoints)do
-local tf=self:_SimpleTaskFunctionWP("warehouse:_PassingWaypoint",group,n,#Waypoints)
-group:SetTaskWaypoint(wp,tf)
-end
-group:Route(Waypoints,1)
-group:OptionROEReturnFire()
-group:OptionAlarmStateGreen()
-end
-end
-function WAREHOUSE:_RouteNaval(group,request)
-if group and group:IsAlive()then
-local _speed=group:GetSpeedMax()*0.8
-local remotename=request.warehouse.warehouse:GetName()
-local lane=self.shippinglanes[remotename][math.random(#self.shippinglanes[remotename])]
-if lane then
-local Waypoints={}
-for i=1,#lane do
-local coord=lane[i]
-local Waypoint=coord:WaypointGround(_speed)
-table.insert(Waypoints,Waypoint)
-end
-local TaskFunction=self:_SimpleTaskFunction("warehouse:_Arrived",group)
-local Waypoint=Waypoints[#Waypoints]
-group:SetTaskWaypoint(Waypoint,TaskFunction)
-group:Route(Waypoints,1)
-group:OptionROEReturnFire()
-else
-self:E(self.lid..string.format("ERROR: No shipping lane defined for Naval asset!"))
-end
-end
-end
-function WAREHOUSE:_RouteAir(aircraft)
-if aircraft and aircraft:IsAlive()~=nil then
-self:T2(self.lid..string.format("RouteAir aircraft group %s alive=%s",aircraft:GetName(),tostring(aircraft:IsAlive())))
-if self.flightcontrol then
-local fg=FLIGHTGROUP:New(aircraft)
-fg:SetReadyForTakeoff(true)
-else
-aircraft:StartUncontrolled(math.random(60))
-end
-self:T2(self.lid..string.format("RouteAir aircraft group %s alive=%s (after start command)",aircraft:GetName(),tostring(aircraft:IsAlive())))
-aircraft:OptionROEReturnFire()
-aircraft:OptionROTPassiveDefense()
-else
-self:E(string.format("ERROR: aircraft %s cannot be routed since it does not exist or is not alive %s!",tostring(aircraft:GetName()),tostring(aircraft:IsAlive())))
-end
-end
-function WAREHOUSE:_RouteTrain(Group,Coordinate,Speed)
-if Group and Group:IsAlive()then
-local _speed=Speed or Group:GetSpeedMax()*0.6
-local Waypoints=Group:TaskGroundOnRailRoads(Coordinate,Speed)
-local TaskFunction=self:_SimpleTaskFunction("warehouse:_Arrived",Group)
-local Waypoint=Waypoints[#Waypoints]
-Group:SetTaskWaypoint(Waypoint,TaskFunction)
-Group:Route(Waypoints,1)
-end
-end
-function WAREHOUSE:_Arrived(group)
-self:_DebugMessage(string.format("Group %s arrived!",tostring(group:GetName())))
-if group then
-self:__Arrived(1,group)
-end
-end
-function WAREHOUSE:_PassingWaypoint(group,n,N)
-self:T(self.lid..string.format("Group %s passing waypoint %d of %d!",tostring(group:GetName()),n,N))
-if n==N then
-self:__Arrived(1,group)
-end
-end
-function WAREHOUSE:GetAssetByID(id)
-if id then
-return _WAREHOUSEDB.Assets[id]
-else
-return nil
-end
-end
-function WAREHOUSE:GetAssetByName(GroupName)
-local name=self:_GetNameWithOut(GroupName)
-local _,aid,_=self:_GetIDsFromGroup(GROUP:FindByName(name))
-if aid then
-return _WAREHOUSEDB.Assets[aid]
-else
-return nil
-end
-end
-function WAREHOUSE:GetRequestByID(id)
-if id then
-for _,_request in pairs(self.queue)do
-local request=_request
-if request.uid==id then
-return request,true
-end
-end
-for _,_request in pairs(self.pending)do
-local request=_request
-if request.uid==id then
-return request,false
-end
-end
-end
-return nil,nil
-end
-function WAREHOUSE:_OnEventBirth(EventData)
-self:T3(self.lid..string.format("Warehouse %s (id=%s) captured event birth!",self.alias,self.uid))
-if EventData and EventData.IniGroup then
-local group=EventData.IniGroup
-local wid,aid,rid=self:_GetIDsFromGroup(group)
-if wid==self.uid then
-local asset=self:GetAssetByID(aid)
-local request=self:GetRequestByID(rid)
-if asset and request then
-self:T(self.lid..string.format("Warehouse %s captured event birth of request ID=%d, asset ID=%d, unit %s spawned=%s",self.alias,request.uid,asset.uid,EventData.IniUnitName,tostring(asset.spawned)))
-request.born=true
-else
-self:E(self.lid..string.format("ERROR: Either asset AID=%s or request RID=%s are nil in event birth of unit %s",tostring(aid),tostring(rid),tostring(EventData.IniUnitName)))
-end
-else
-end
-end
-end
-function WAREHOUSE:_OnEventEngineStartup(EventData)
-self:T3(self.lid..string.format("Warehouse %s captured event engine startup!",self.alias))
-if EventData and EventData.IniGroup then
-local group=EventData.IniGroup
-local wid,aid,rid=self:_GetIDsFromGroup(group)
-if wid==self.uid then
-self:T(self.lid..string.format("Warehouse %s captured event engine startup of its asset unit %s.",self.alias,EventData.IniUnitName))
-end
-end
-end
-function WAREHOUSE:_OnEventTakeOff(EventData)
-self:T3(self.lid..string.format("Warehouse %s captured event takeoff!",self.alias))
-if EventData and EventData.IniGroup then
-local group=EventData.IniGroup
-local wid,aid,rid=self:_GetIDsFromGroup(group)
-if wid==self.uid then
-self:T(self.lid..string.format("Warehouse %s captured event takeoff of its asset unit %s.",self.alias,EventData.IniUnitName))
-end
-end
-end
-function WAREHOUSE:_OnEventLanding(EventData)
-self:T3(self.lid..string.format("Warehouse %s captured event landing!",self.alias))
-if EventData and EventData.IniGroup then
-local group=EventData.IniGroup
-local wid,aid,rid=self:_GetIDsFromGroup(group)
-if wid~=nil and wid==self.uid then
-self:T(self.lid..string.format("Warehouse %s captured event landing of its asset unit %s.",self.alias,EventData.IniUnitName))
-end
-end
-end
-function WAREHOUSE:_OnEventEngineShutdown(EventData)
-self:T3(self.lid..string.format("Warehouse %s captured event engine shutdown!",self.alias))
-if EventData and EventData.IniGroup then
-local group=EventData.IniGroup
-local wid,aid,rid=self:_GetIDsFromGroup(group)
-if wid==self.uid then
-self:T(self.lid..string.format("Warehouse %s captured event engine shutdown of its asset unit %s.",self.alias,EventData.IniUnitName))
-end
-end
-end
-function WAREHOUSE:_OnEventArrived(EventData)
-if EventData and EventData.IniUnit then
-local unit=EventData.IniUnit
-if unit and unit:IsAlive()==true and unit:InAir()==false then
-local group=EventData.IniGroup
-local wid,aid,rid=self:_GetIDsFromGroup(group)
-if wid~=nil and aid~=nil and rid~=nil then
-if self.uid==wid then
-local request=self:_GetRequestOfGroup(group,self.pending)
-if request then
-local istransport=self:_GroupIsTransport(group,request)
-local closest=group:GetCoordinate():GetClosestAirbase()
-local rightairbase=closest:GetName()==request.warehouse:GetAirbase():GetName()
-if istransport==false and rightairbase then
-local nunits=#group:GetUnits()
-local dt=10*(nunits-1)+1
-if self.verbosity>=1 then
-local text=string.format("Air asset group %s from warehouse %s arrived at its destination. Trigger Arrived event in %d sec",group:GetName(),self.alias,dt)
-self:_InfoMessage(text)
-end
-self:__Arrived(dt,group)
-end
-end
-end
-else
-self:T3(string.format("Group that arrived did not belong to a warehouse. Warehouse ID=%s, Asset ID=%s, Request ID=%s.",tostring(wid),tostring(aid),tostring(rid)))
-end
-end
-end
-end
-function WAREHOUSE:_OnEventCrashOrDead(EventData)
-self:T3(self.lid..string.format("Warehouse %s captured event dead or crash!",self.alias))
-if EventData then
-if EventData.IniUnitName then
-local warehousename=self.warehouse:GetName()
-if EventData.IniUnitName==warehousename then
-self:_DebugMessage(string.format("Warehouse %s alias %s was destroyed!",warehousename,self.alias))
-self:Destroyed()
-end
-if self.airbase and self.airbasename and self.airbasename==EventData.IniUnitName then
-if self:IsRunwayOperational()then
-self:RunwayDestroyed()
-else
-self.runwaydestroyed=timer.getAbsTime()
-end
-end
-end
-self:T2(self.lid..string.format("Warehouse %s captured event dead or crash or unit %s",self.alias,tostring(EventData.IniUnitName)))
-if EventData.IniGroup then
-local group=EventData.IniGroup
-local wid,aid,rid=self:_GetIDsFromGroup(group)
-if wid==self.uid then
-self:T(self.lid..string.format("Warehouse %s captured event dead or crash of its asset unit %s",self.alias,EventData.IniUnitName))
-for _,request in pairs(self.pending)do
-local request=request
-if request.uid==rid then
-self:_UnitDead(EventData.IniUnit,EventData.IniGroup,request)
-end
-end
-end
-end
-end
-end
-function WAREHOUSE:_UnitDead(deadunit,deadgroup,request)
-self:F(self.lid.."FF unit dead "..deadunit:GetName())
-local opsgroup=_DATABASE:FindOpsGroup(deadgroup)
-if opsgroup then
-return nil
-end
-local nalive=deadgroup:CountAliveUnits()
-local groupdead=false
-if nalive>0 then
-groupdead=false
-else
-groupdead=true
-end
-local asset=self:FindAssetInDB(deadgroup)
-local unitname=self:_GetNameWithOut(deadunit)
-local groupname=self:_GetNameWithOut(deadgroup)
-if groupdead then
-self:T(self.lid..string.format("Group %s (transport=%s) is dead!",groupname,tostring(self:_GroupIsTransport(deadgroup,request))))
-if self.Debug then
-deadgroup:SmokeWhite()
-end
-self:AssetDead(asset,request)
-end
-local NoTriggerEvent=true
-if request.transporttype~=WAREHOUSE.TransportType.SELFPROPELLED then
-if not asset.iscargo then
-local cargogroupnames=request.carriercargo[unitname]
-if cargogroupnames then
-for _,cargoname in pairs(cargogroupnames)do
-request.cargogroupset:Remove(cargoname,NoTriggerEvent)
-self:T(self.lid..string.format("Removed transported cargo %s inside dead carrier %s: ncargo=%d",cargoname,unitname,request.cargogroupset:Count()))
-end
-end
-else
-self:E(self.lid..string.format("ERROR: Group %s is neither cargo nor transport!",deadgroup:GetName()))
-end
-end
-end
-function WAREHOUSE:_OnEventBaseCaptured(EventData)
-self:T3(self.lid..string.format("Warehouse %s captured event base captured!",self.alias))
-if self.airbasename==nil then
-return
-end
-if EventData and EventData.Place then
-local airbase=EventData.Place
-if EventData.PlaceName==self.airbasename then
-local NewCoalitionAirbase=airbase:GetCoalition()
-self:T(self.lid..string.format("Airbase of warehouse %s (coalition ID=%d) was captured! New owner coalition ID=%d.",self.alias,self:GetCoalition(),NewCoalitionAirbase))
-if self.airbase==nil then
-if NewCoalitionAirbase==self:GetCoalition()then
-self:AirbaseRecaptured(NewCoalitionAirbase)
-end
-else
-if NewCoalitionAirbase~=self:GetCoalition()then
-self:AirbaseCaptured(NewCoalitionAirbase)
-end
-end
-end
-end
-end
-function WAREHOUSE:_OnEventMissionEnd(EventData)
-self:T3(self.lid..string.format("Warehouse %s captured event mission end!",self.alias))
-if self.autosave then
-self:Save(self.autosavepath,self.autosavefile)
-end
-end
-function WAREHOUSE:_CheckConquered()
-local coord=self.zone:GetCoordinate()
-local radius=self.zone:GetRadius()
-local gotunits,_,_,units,_,_=coord:ScanObjects(radius,true,false,false)
-local Nblue=0
-local Nred=0
-local Nneutral=0
-local CountryBlue=nil
-local CountryRed=nil
-local CountryNeutral=nil
-if gotunits then
-for _,_unit in pairs(units)do
-local unit=_unit
-local distance=coord:Get2DDistance(unit:GetCoordinate())
-if unit:IsGround()and unit:IsAlive()and distance<=radius then
-local _coalition=unit:GetCoalition()
-local _country=unit:GetCountry()
-self:T2(self.lid..string.format("Unit %s in warehouse zone of radius=%d m. Coalition=%d, country=%d. Distance = %d m.",unit:GetName(),radius,_coalition,_country,distance))
-if _coalition==coalition.side.BLUE then
-Nblue=Nblue+1
-CountryBlue=_country
-elseif _coalition==coalition.side.RED then
-Nred=Nred+1
-CountryRed=_country
-else
-Nneutral=Nneutral+1
-CountryNeutral=_country
-end
-end
-end
-end
-self:T(self.lid..string.format("Ground troops in warehouse zone: blue=%d, red=%d, neutral=%d",Nblue,Nred,Nneutral))
-local newcoalition=self:GetCoalition()
-local newcountry=self:GetCountry()
-if Nblue>0 and Nred==0 and Nneutral==0 then
-newcoalition=coalition.side.BLUE
-newcountry=CountryBlue
-elseif Nblue==0 and Nred>0 and Nneutral==0 then
-newcoalition=coalition.side.RED
-newcountry=CountryRed
-elseif Nblue==0 and Nred==0 and Nneutral>0 then
-end
-if self:IsAttacked()and newcoalition~=self:GetCoalition()then
-self:Captured(newcoalition,newcountry)
-return
-end
-if self:GetCoalition()==coalition.side.BLUE then
-if self:IsRunning()and Nred>0 then
-self:Attacked(coalition.side.RED,CountryRed)
-end
-if self:IsAttacked()and Nred==0 then
-self:Defeated()
-end
-elseif self:GetCoalition()==coalition.side.RED then
-if self:IsRunning()and Nblue>0 then
-self:Attacked(coalition.side.BLUE,CountryBlue)
-end
-if self:IsAttacked()and Nblue==0 then
-self:Defeated()
-end
-elseif self:GetCoalition()==coalition.side.NEUTRAL then
-if self:IsRunning()and Nred>0 then
-self:Attacked(coalition.side.RED,CountryRed)
-elseif self:IsRunning()and Nblue>0 then
-self:Attacked(coalition.side.BLUE,CountryBlue)
-end
-end
-end
-function WAREHOUSE:_CheckAirbaseOwner()
-if self.airbasename then
-local airbase=AIRBASE:FindByName(self.airbasename)
-local airbasecurrentcoalition=airbase:GetCoalition()
-if self.airbase then
-if self:GetCoalition()~=airbasecurrentcoalition then
-self.airbase=nil
-end
-else
-if self:GetCoalition()==airbasecurrentcoalition then
-self.airbase=airbase
-end
-end
-end
-end
-function WAREHOUSE:_CheckRequestConsistancy(queue)
-self:T3(self.lid..string.format("Number of queued requests = %d",#queue))
-local invalid={}
-for _,_request in pairs(queue)do
-local request=_request
-self:T2(self.lid..string.format("Checking request id=%d.",request.uid))
-local valid=true
-if request.nasset==0 then
-self:E(self.lid..string.format("ERROR: INVALID request. Request for zero assets not possible. Can happen when, e.g. \"all\" ground assets are requests but none in stock."))
-valid=false
-end
-if self:GetCoalition()~=request.warehouse:GetCoalition()then
-self:E(self.lid..string.format("ERROR: INVALID request. Requesting warehouse is of wrong coalition! Own coalition %s != %s of requesting warehouse.",self:GetCoalitionName(),request.warehouse:GetCoalitionName()))
-valid=false
-end
-if request.warehouse:IsStopped()then
-self:E(self.lid..string.format("ERROR: INVALID request. Requesting warehouse is stopped!"))
-valid=false
-end
-if request.warehouse:IsDestroyed()and not self.respawnafterdestroyed then
-self:E(self.lid..string.format("ERROR: INVALID request. Requesting warehouse is destroyed!"))
-valid=false
-end
-if valid==false then
-self:E(self.lid..string.format("Got invalid request id=%d.",request.uid))
-table.insert(invalid,request)
-else
-self:T3(self.lid..string.format("Got valid request id=%d.",request.uid))
-end
-end
-for _,_request in pairs(invalid)do
-self:E(self.lid..string.format("Deleting INVALID request id=%d.",_request.uid))
-self:_DeleteQueueItem(_request,self.queue)
-end
-end
-function WAREHOUSE:_CheckRequestValid(request)
-local _assets,_nassets,_enough=self:_FilterStock(self.stock,request.assetdesc,request.assetdescval,request.nasset)
-if#_assets==0 then
-return true
-end
-local nasset=request.nasset
-if type(request.nasset)=="string"then
-nasset=self:_QuantityRel2Abs(request.nasset,_nassets)
-end
-local text=string.format("Request valid? Number of assets: requested=%s=%d, selected=%d, total=%d, enough=%s.",tostring(request.nasset),nasset,#_assets,_nassets,tostring(_enough))
-self:T(text)
-local asset=_assets[1]
-local asset_plane=asset.category==Group.Category.AIRPLANE
-local asset_helo=asset.category==Group.Category.HELICOPTER
-local asset_ground=asset.category==Group.Category.GROUND
-local asset_train=asset.category==Group.Category.TRAIN
-local asset_naval=asset.category==Group.Category.SHIP
-local asset_air=asset_helo or asset_plane
-local valid=true
-local requestcategory=request.warehouse:GetAirbaseCategory()
-if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then
-if asset_air then
-if asset_plane then
-if requestcategory==Airbase.Category.HELIPAD or self:GetAirbaseCategory()==Airbase.Category.HELIPAD then
-self:E("ERROR: Incorrect request. Asset airplane requested but warehouse or requestor is HELIPAD/FARP!")
-valid=false
-end
-elseif asset_helo then
-if self:GetAirbaseCategory()==-1 or requestcategory==-1 then
-self:E("ERROR: Incorrect request. Helos need a AIRBASE/HELIPAD/SHIP as home/destination base!")
-valid=false
-end
-end
-if self.airbase==nil or request.airbase==nil then
-self:E("ERROR: Incorrect request. Either warehouse or requesting warehouse does not have any kind of airbase!")
-valid=false
-else
-local termtype_dep=asset.terminalType or self:_GetTerminal(asset.attribute,self:GetAirbaseCategory())
-local termtype_des=asset.terminalType or self:_GetTerminal(asset.attribute,request.warehouse:GetAirbaseCategory())
-local np_departure=self.airbase:GetParkingSpotsNumber(termtype_dep)
-local np_destination=request.airbase:GetParkingSpotsNumber(termtype_des)
-self:T(string.format("Asset attribute = %s, DEPARTURE: terminal type = %d, spots = %d, DESTINATION: terminal type = %d, spots = %d",asset.attribute,termtype_dep,np_departure,termtype_des,np_destination))
-if np_departure0 then
-local asset=_assets[1]
-_assetattribute=_assets[1].attribute
-_assetcategory=_assets[1].category
-_assetairstart=_assets[1].takeoffType and _assets[1].takeoffType==COORDINATE.WaypointType.TurningPoint or false
-if _assetcategory==Group.Category.AIRPLANE or _assetcategory==Group.Category.HELICOPTER then
-if self.airbase and self.airbase:GetCoalition()==self:GetCoalition()then
-if self.airbase.storage then
-local nS=self.airbase.storage:GetAmount(asset.unittype)
-local nA=asset.nunits*request.nasset
-if nS NOT enough to spawn the requested %d asset units (%d groups)",
-self.alias,nS,asset.unittype,nA,request.nasset)
-self:_InfoMessage(text,5)
-return false
-end
-end
-if self:IsRunwayOperational()or _assetairstart then
-if _assetairstart then
-else
-local Parking=self:_FindParkingForAssets(self.airbase,_assets)
-if Parking==nil then
-local text=string.format("Warehouse %s: Request denied! Not enough free parking spots for all requested assets at the moment.",self.alias)
-self:_InfoMessage(text,5)
-return false
-end
-end
-else
-local text=string.format("Warehouse %s: Request denied! Runway is still destroyed",self.alias)
-self:_InfoMessage(text,5)
-return false
-end
-else
-local text=string.format("Warehouse %s: Request denied! No airbase",self.alias)
-self:_InfoMessage(text,5)
-return false
-end
-end
-request.cargoassets=_assets
-end
-if request.transporttype~=WAREHOUSE.TransportType.SELFPROPELLED then
-_transports=self:_GetTransportsForAssets(request)
-if#_transports>0 then
-local _transportattribute=_transports[1].attribute
-local _transportcategory=_transports[1].category
-if _transportcategory==Group.Category.AIRPLANE or _transportcategory==Group.Category.HELICOPTER then
-if self.airbase and self.airbase:GetCoalition()==self:GetCoalition()then
-if self:IsRunwayOperational()then
-local Parking=self:_FindParkingForAssets(self.airbase,_transports)
-if Parking==nil then
-local text=string.format("Warehouse %s: Request denied! Not enough free parking spots for all transports at the moment.",self.alias)
-self:_InfoMessage(text,5)
-return false
-end
-else
-local text=string.format("Warehouse %s: Request denied! Runway is still destroyed",self.alias)
-self:_InfoMessage(text,5)
-return false
-end
-else
-local text=string.format("Warehouse %s: Request denied! No airbase currently!",self.alias)
-self:_InfoMessage(text,5)
-return false
-end
-end
-else
-local text=string.format("Warehouse %s: Request denied! Not enough transport carriers available at the moment.",self.alias)
-self:_InfoMessage(text,5)
-return false
-end
-else
-if _assetcategory==Group.Category.GROUND then
-local dist=self.warehouse:GetCoordinate():Get2DDistance(self.spawnzone:GetCoordinate())
-if dist>self.spawnzonemaxdist then
-local text=string.format("Warehouse %s: Request denied! Not close enough to spawn zone. Distance = %d m. We need to be at least within %d m range to spawn.",self.alias,dist,self.spawnzonemaxdist)
-self:_InfoMessage(text,5)
-return false
-end
-elseif _assetcategory==Group.Category.AIRPLANE or _assetcategory==Group.Category.HELICOPTER then
-end
-end
-request.cargoassets=_assets
-request.cargoattribute=_assets[1].attribute
-request.cargocategory=_assets[1].category
-request.nasset=#_assets
-local text=string.format("Selected cargo assets, attibute=%s, category=%d:\n",request.cargoattribute,request.cargocategory)
-for _i,_asset in pairs(_assets)do
-local asset=_asset
-text=text..string.format("%d) name=%s, type=%s, category=%d, #units=%d",_i,asset.templatename,asset.unittype,asset.category,asset.nunits)
-end
-self:T(self.lid..text)
-if request.transporttype~=WAREHOUSE.TransportType.SELFPROPELLED then
-request.transportassets=_transports
-request.transportattribute=_transports[1].attribute
-request.transportcategory=_transports[1].category
-request.ntransport=#_transports
-local text=string.format("Selected transport assets, attibute=%s, category=%d:\n",request.transportattribute,request.transportcategory)
-for _i,_asset in pairs(_transports)do
-local asset=_asset
-text=text..string.format("%d) name=%s, type=%s, category=%d, #units=%d\n",_i,asset.templatename,asset.unittype,asset.category,asset.nunits)
-end
-self:T(self.lid..text)
-end
-return true
-end
-function WAREHOUSE:_GetTransportsForAssets(request)
-local transports=self:_FilterStock(self.stock,WAREHOUSE.Descriptor.ATTRIBUTE,request.transporttype,nil,true)
-local cargoassets=UTILS.DeepCopy(request.cargoassets)
-local cargoset=request.transportcargoset
-local function sort_transports(a,b)
-return a.cargobaymax>b.cargobaymax
-end
-local function sort_cargoassets(a,b)
-return a.weight>b.weight
-end
-table.sort(transports,sort_transports)
-table.sort(cargoassets,sort_cargoassets)
-self:T2(self.lid.."Transport capability:")
-local totalbay=0
-for i=1,#transports do
-local transport=transports[i]
-for j=1,transport.nunits do
-totalbay=totalbay+transport.cargobay[j]
-self:T2(self.lid..string.format("Cargo bay = %d (unit=%d)",transport.cargobay[j],j))
-end
-end
-self:T2(self.lid..string.format("Total capacity = %d",totalbay))
-self:T2(self.lid.."Cargo weight:")
-local totalcargoweight=0
-for i=1,#cargoassets do
-local asset=cargoassets[i]
-totalcargoweight=totalcargoweight+asset.weight
-self:T2(self.lid..string.format("weight = %d",asset.weight))
-end
-self:T2(self.lid..string.format("Total weight = %d",totalcargoweight))
-local used_transports={}
-for i=1,#transports do
-local transport=transports[i]
-local putintocarrier={}
-local used=false
-for k=1,transport.nunits do
-local cargobay=transport.cargobay[k]
-for j,asset in pairs(cargoassets)do
-local asset=asset
-local delta=cargobay-asset.weight
-if delta>=0 then
-cargobay=cargobay-asset.weight
-self:T3(self.lid..string.format("%s unit %d loads cargo uid=%d: bayempty=%02d, bayloaded = %02d - weight=%02d",transport.templatename,k,asset.uid,transport.cargobay[k],cargobay,asset.weight))
-table.insert(putintocarrier,j)
-used=true
-else
-self:T2(self.lid..string.format("Carrier unit %s too small for cargo asset %s ==> cannot be used! Cargo bay - asset weight = %d kg",transport.templatename,asset.templatename,delta))
-end
-end
-end
-for j=#putintocarrier,1,-1 do
-local nput=putintocarrier[j]
-local cargo=cargoassets[nput]
-if cargo then
-self:T2(self.lid..string.format("Cargo id=%d assigned for carrier id=%d",cargo.uid,transport.uid))
-table.remove(cargoassets,nput)
-end
-end
-if used then
-table.insert(used_transports,transport)
-end
-local ntrans=self:_QuantityRel2Abs(request.ntransport,#transports)
-if#used_transports>=ntrans then
-request.ntransport=#used_transports
-break
-end
-end
-local text=string.format("Used Transports for request %d to warehouse %s:\n",request.uid,request.warehouse.alias)
-local totalcargobay=0
-for _i,_transport in pairs(used_transports)do
-local transport=_transport
-text=text..string.format("%d) %s: cargobay tot = %d kg, cargobay max = %d kg, nunits=%d\n",_i,transport.unittype,transport.cargobaytot,transport.cargobaymax,transport.nunits)
-totalcargobay=totalcargobay+transport.cargobaytot
-end
-text=text..string.format("Total cargo bay capacity = %.1f kg\n",totalcargobay)
-text=text..string.format("Total cargo weight = %.1f kg\n",totalcargoweight)
-text=text..string.format("Minimum number of runs = %.1f",totalcargoweight/totalcargobay)
-self:_DebugMessage(text)
-return used_transports
-end
-function WAREHOUSE:_QuantityRel2Abs(relative,ntot)
-local nabs=0
-if type(relative)=="string"then
-if relative==WAREHOUSE.Quantity.ALL then
-nabs=ntot
-elseif relative==WAREHOUSE.Quantity.THREEQUARTERS then
-nabs=UTILS.Round(ntot*3/4)
-elseif relative==WAREHOUSE.Quantity.HALF then
-nabs=UTILS.Round(ntot/2)
-elseif relative==WAREHOUSE.Quantity.THIRD then
-nabs=UTILS.Round(ntot/3)
-elseif relative==WAREHOUSE.Quantity.QUARTER then
-nabs=UTILS.Round(ntot/4)
-else
-nabs=math.min(1,ntot)
-end
-else
-nabs=relative
-end
-self:T2(self.lid..string.format("Relative %s: tot=%d, abs=%.2f",tostring(relative),ntot,nabs))
-return nabs
-end
-function WAREHOUSE:_CheckQueue()
-self:_SortQueue()
-local request=nil
-local invalid={}
-local gotit=false
-for _,_qitem in ipairs(self.queue)do
-local qitem=_qitem
-local valid=self:_CheckRequestValid(qitem)
-local okay=false
-if valid then
-okay=self:_CheckRequestNow(qitem)
-else
-table.insert(invalid,qitem)
-end
-if okay and valid and not gotit then
-request=qitem
-gotit=true
-break
-end
-end
-for _,_request in pairs(invalid)do
-self:T(self.lid..string.format("Deleting invalid request id=%d.",_request.uid))
-self:_DeleteQueueItem(_request,self.queue)
-end
-return request
-end
-function WAREHOUSE:_SimpleTaskFunction(Function,group)
-self:F2({Function})
-local warehouse=self.warehouse:GetName()
-local groupname=group:GetName()
-local DCSScript={}
-DCSScript[#DCSScript+1]=string.format('local mygroup = GROUP:FindByName(\"%s\") ',groupname)
-if self.isUnit then
-DCSScript[#DCSScript+1]=string.format("local mywarehouse = UNIT:FindByName(\"%s\") ",warehouse)
-else
-DCSScript[#DCSScript+1]=string.format("local mywarehouse = STATIC:FindByName(\"%s\") ",warehouse)
-end
-DCSScript[#DCSScript+1]=string.format('local warehouse = mywarehouse:GetState(mywarehouse, \"WAREHOUSE\") ')
-DCSScript[#DCSScript+1]=string.format('%s(mygroup)',Function)
-local DCSTask=CONTROLLABLE.TaskWrappedAction(self,CONTROLLABLE.CommandDoScript(self,table.concat(DCSScript)))
-return DCSTask
-end
-function WAREHOUSE:_SimpleTaskFunctionWP(Function,group,n,N)
-self:F2({Function})
-local warehouse=self.warehouse:GetName()
-local groupname=group:GetName()
-local DCSScript={}
-DCSScript[#DCSScript+1]=string.format('local mygroup = GROUP:FindByName(\"%s\") ',groupname)
-if self.isUnit then
-DCSScript[#DCSScript+1]=string.format("local mywarehouse = UNIT:FindByName(\"%s\") ",warehouse)
-else
-DCSScript[#DCSScript+1]=string.format("local mywarehouse = STATIC:FindByName(\"%s\") ",warehouse)
-end
-DCSScript[#DCSScript+1]=string.format('local warehouse = mywarehouse:GetState(mywarehouse, \"WAREHOUSE\") ')
-DCSScript[#DCSScript+1]=string.format('%s(mygroup, %d, %d)',Function,n,N)
-local DCSTask=CONTROLLABLE.TaskWrappedAction(self,CONTROLLABLE.CommandDoScript(self,table.concat(DCSScript)))
-return DCSTask
-end
-function WAREHOUSE:_GetTerminal(_attribute,_category)
-local _terminal=AIRBASE.TerminalType.OpenBig
-if _attribute==WAREHOUSE.Attribute.AIR_FIGHTER or _attribute==WAREHOUSE.Attribute.AIR_UAV then
-_terminal=AIRBASE.TerminalType.FighterAircraft
-elseif _attribute==WAREHOUSE.Attribute.AIR_BOMBER or _attribute==WAREHOUSE.Attribute.AIR_TRANSPORTPLANE or _attribute==WAREHOUSE.Attribute.AIR_TANKER or _attribute==WAREHOUSE.Attribute.AIR_AWACS then
-_terminal=AIRBASE.TerminalType.OpenBig
-elseif _attribute==WAREHOUSE.Attribute.AIR_TRANSPORTHELO or _attribute==WAREHOUSE.Attribute.AIR_ATTACKHELO then
-_terminal=AIRBASE.TerminalType.HelicopterUsable
-else
-end
-if _category==Airbase.Category.SHIP then
-if not(_attribute==WAREHOUSE.Attribute.AIR_TRANSPORTHELO or _attribute==WAREHOUSE.Attribute.AIR_ATTACKHELO)then
-_terminal=AIRBASE.TerminalType.OpenMedOrBig
-end
-end
-return _terminal
-end
-function WAREHOUSE:_FindParkingForAssets(airbase,assets)
-local scanradius=25
-local scanunits=true
-local scanstatics=true
-local scanscenery=false
-local verysafe=false
-local function _overlap(l1,l2,dist)
-local safedist=(l1/2+l2/2)*1.05
-local safe=(dist>safedist)
-self:T3(string.format("l1=%.1f l2=%.1f s=%.1f d=%.1f ==> safe=%s",l1,l2,safedist,dist,tostring(safe)))
-return safe
-end
-local function _clients()
-local coords={}
-if not self.allowSpawnOnClientSpots then
-local clients=_DATABASE.CLIENTS
-for clientname,client in pairs(clients)do
-local template=_DATABASE:GetGroupTemplateFromUnitName(clientname)
-local units=template.units
-for i,unit in pairs(units)do
-local coord=COORDINATE:New(unit.x,unit.alt,unit.y)
-coords[unit.name]=coord
-end
-end
-end
-return coords
-end
-local parkingdata=airbase.parking
-local obstacles={}
-self.clientcoords=self.clientcoords or _clients()
-for clientname,_coord in pairs(self.clientcoords)do
-table.insert(obstacles,{coord=_coord,size=15,name=clientname,type="client"})
-end
-for _,parkingspot in pairs(parkingdata)do
-local _spot=parkingspot.Coordinate
-local _termid=parkingspot.TerminalID
-local _,_,_,_units,_statics,_sceneries=_spot:ScanObjects(scanradius,scanunits,scanstatics,scanscenery)
-for _,_unit in pairs(_units)do
-local unit=_unit
-local _coord=unit:GetVec3()
-local _size=self:_GetObjectSize(unit:GetDCSObject())
-local _name=unit:GetName()
-if unit and unit:IsAlive()then
-table.insert(obstacles,{coord=_coord,size=_size,name=_name,type="unit"})
-end
-end
-for _,static in pairs(_statics)do
-local _coord=static:getPoint()
-local _name=static:getName()
-local _size=self:_GetObjectSize(static)
-table.insert(obstacles,{coord=_coord,size=_size,name=_name,type="static"})
-end
-for _,scenery in pairs(_sceneries)do
-local _coord=scenery:getPoint()
-local _name=scenery:getTypeName()
-local _size=self:_GetObjectSize(scenery)
-table.insert(obstacles,{coord=_coord,size=_size,name=_name,type="scenery"})
-end
-end
-local parking={}
-for _,asset in pairs(assets)do
-local _asset=asset
-if not _asset.spawned then
-local terminaltype=asset.terminalType or self:_GetTerminal(asset.attribute,self:GetAirbaseCategory())
-parking[_asset.uid]={}
-for i=1,_asset.nunits do
-local assetname=_asset.spawngroupname.."-"..tostring(i)
-local gotit=false
-for _,_parkingspot in pairs(parkingdata)do
-local parkingspot=_parkingspot
-local valid=true
-if asset.parkingIDs then
-valid=self:_CheckParkingAsset(parkingspot,asset)
-else
-local validTerminal=AIRBASE._CheckTerminalType(parkingspot.TerminalType,terminaltype)
-local validParking=self:_CheckParkingValid(parkingspot)
-local validBWlist=airbase:_CheckParkingLists(parkingspot.TerminalID)
-valid=validTerminal and validParking and validBWlist
-end
-if valid then
-local _spot=parkingspot.Coordinate
-local _termid=parkingspot.TerminalID
-local free=true
-local problem=nil
-for _,obstacle in pairs(obstacles)do
-local dist=_spot:Get2DDistance(obstacle.coord)
-local safe=_overlap(_asset.size,obstacle.size,dist)
-if not safe then
-self:T3(self.lid..string.format("FF asset=%s (id=%d): spot id=%d dist=%.1fm is NOT SAFE",assetname,_asset.uid,_termid,dist))
-free=false
-problem=obstacle
-problem.dist=dist
-break
-else
-end
-end
-if free then
-table.insert(parking[_asset.uid],parkingspot)
-self:T(self.lid..string.format("Parking spot %d is free for asset %s [id=%d]!",_termid,assetname,_asset.uid))
-table.insert(obstacles,{coord=_spot,size=_asset.size,name=assetname,type="asset"})
-gotit=true
-break
-else
-if self.Debug then
-local coord=problem.coord
-local text=string.format("Obstacle %s [type=%s] blocking spot=%d! Size=%.1f m and distance=%.1f m.",problem.name,problem.type,_termid,problem.size,problem.dist)
-self:I(self.lid..text)
-coord:MarkToAll(string.format(text))
-else
-self:T(self.lid..string.format("Parking spot %d is occupied or not big enough!",_termid))
-end
-end
-else
-self:T2(self.lid..string.format("Terminal ID=%d: type=%s not supported",parkingspot.TerminalID,parkingspot.TerminalType))
-end
-end
-if not gotit then
-self:I(self.lid..string.format("WARNING: No free parking spot for asset %s [id=%d]",assetname,_asset.uid))
-return nil
-end
-end
-end
-end
-return parking
-end
-function WAREHOUSE:_GetRequestOfGroup(group,queue)
-local wid,aid,rid=self:_GetIDsFromGroup(group)
-for _,_request in pairs(queue)do
-local request=_request
-if request.uid==rid then
-return request
-end
-end
-end
-function WAREHOUSE:_GroupIsTransport(group,request)
-local asset=self:FindAssetInDB(group)
-if asset and asset.iscargo~=nil then
-return not asset.iscargo
-else
-local groupname=self:_GetNameWithOut(group)
-if request.transportgroupset then
-local transporters=request.transportgroupset:GetSetObjects()
-for _,transport in pairs(transporters)do
-if transport:GetName()==groupname then
-return true
-end
-end
-end
-if request.cargogroupset then
-local cargos=request.cargogroupset:GetSetObjects()
-for _,cargo in pairs(cargos)do
-if self:_GetNameWithOut(cargo)==groupname then
-return false
-end
-end
-end
-end
-return nil
-end
-function WAREHOUSE:_GetNameWithOut(group)
-local groupname=type(group)=="string"and group or group:GetName()
-if groupname:find("CARGO")then
-local name=groupname:gsub("#CARGO","")
-return name
-else
-return groupname
-end
-end
-function WAREHOUSE:_GetIDsFromGroup(group)
-if group then
-local groupname=group:GetName()
-local wid,aid,rid=self:_GetIDsFromGroupName(groupname)
-return wid,aid,rid
-else
-self:E("WARNING: Group not found in GetIDsFromGroup() function!")
-end
-end
-function WAREHOUSE:_GetIDsFromGroupName(groupname)
-local function analyse(text)
-local unspawned=UTILS.Split(text,"#")[1]
-local keywords=UTILS.Split(unspawned,"_")
-local _wid=nil
-local _aid=nil
-local _rid=nil
-for _,keys in pairs(keywords)do
-local str=UTILS.Split(keys,"-")
-local key=str[1]
-local val=str[2]
-if key:find("WID")then
-_wid=tonumber(val)
-elseif key:find("AID")then
-_aid=tonumber(val)
-elseif key:find("RID")then
-_rid=tonumber(val)
-end
-end
-return _wid,_aid,_rid
-end
-local wid,aid,rid=analyse(groupname)
-local asset=self:GetAssetByID(aid)
-if asset then
-wid=asset.wid
-rid=asset.rid
-end
-self:T3(self.lid..string.format("Group Name = %s",tostring(groupname)))
-self:T3(self.lid..string.format("Warehouse ID = %s",tostring(wid)))
-self:T3(self.lid..string.format("Asset ID = %s",tostring(aid)))
-self:T3(self.lid..string.format("Request ID = %s",tostring(rid)))
-return wid,aid,rid
-end
-function WAREHOUSE:FilterStock(descriptor,attribute,nmax,mobile)
-return self:_FilterStock(self.stock,descriptor,attribute,nmax,mobile)
-end
-function WAREHOUSE:_FilterStock(stock,descriptor,attribute,nmax,mobile)
-nmax=nmax or WAREHOUSE.Quantity.ALL
-if mobile==nil then
-mobile=false
-end
-local filtered={}
-if descriptor==WAREHOUSE.Descriptor.ASSETLIST then
-local ntot=0
-for _,_rasset in pairs(attribute)do
-local rasset=_rasset
-for _,_asset in ipairs(stock)do
-local asset=_asset
-if rasset.uid==asset.uid then
-table.insert(filtered,asset)
-break
-end
-end
-end
-return filtered,#filtered,#filtered>=#attribute
-end
-local ntot=0
-for _,_asset in ipairs(stock)do
-local asset=_asset
-local ismobile=asset.speedmax>0
-if asset[descriptor]==attribute then
-if(mobile==true and ismobile)or mobile==false then
-ntot=ntot+1
-end
-end
-end
-if ntot==0 then
-return filtered,ntot,false
-end
-nmax=self:_QuantityRel2Abs(nmax,ntot)
-for _i,_asset in ipairs(stock)do
-local asset=_asset
-if asset[descriptor]==attribute then
-if(mobile and asset.speedmax>0)or(not mobile)then
-table.insert(filtered,asset)
-if nmax~=nil and#filtered>=nmax then
-return filtered,ntot,true
-end
-end
-end
-end
-return filtered,ntot,ntot>=nmax
-end
-function WAREHOUSE:_HasAttribute(group,attribute)
-if group then
-local groupattribute=self:_GetAttribute(group)
-return groupattribute==attribute
-end
-return false
-end
-function WAREHOUSE:_GetAttribute(group)
-local attribute=WAREHOUSE.Attribute.OTHER_UNKNOWN
-if group then
-local transportplane=group:HasAttribute("Transports")and group:HasAttribute("Planes")
-local awacs=group:HasAttribute("AWACS")
-local fighter=group:HasAttribute("Fighters")or group:HasAttribute("Interceptors")or group:HasAttribute("Multirole fighters")or(group:HasAttribute("Bombers")and not group:HasAttribute("Strategic bombers"))
-local bomber=group:HasAttribute("Strategic bombers")
-local tanker=group:HasAttribute("Tankers")
-local uav=group:HasAttribute("UAVs")
-local transporthelo=group:HasAttribute("Transport helicopters")
-local attackhelicopter=group:HasAttribute("Attack helicopters")
-local apc=group:HasAttribute("APC")
-local truck=group:HasAttribute("Trucks")and group:GetCategory()==Group.Category.GROUND
-local infantry=group:HasAttribute("Infantry")
-local ifv=group:HasAttribute("IFV")
-local artillery=group:HasAttribute("Artillery")
-local tank=group:HasAttribute("Old Tanks")or group:HasAttribute("Modern Tanks")
-local aaa=group:HasAttribute("AAA")
-local ewr=group:HasAttribute("EWR")
-local sam=group:HasAttribute("SAM elements")and(not group:HasAttribute("AAA"))
-local train=group:GetCategory()==Group.Category.TRAIN
-local aircraftcarrier=group:HasAttribute("Aircraft Carriers")
-local warship=group:HasAttribute("Heavy armed ships")
-local armedship=group:HasAttribute("Armed ships")or group:HasAttribute("Armed Ship")
-local unarmedship=group:HasAttribute("Unarmed ships")
-if transportplane then
-attribute=WAREHOUSE.Attribute.AIR_TRANSPORTPLANE
-elseif awacs then
-attribute=WAREHOUSE.Attribute.AIR_AWACS
-elseif fighter then
-attribute=WAREHOUSE.Attribute.AIR_FIGHTER
-elseif bomber then
-attribute=WAREHOUSE.Attribute.AIR_BOMBER
-elseif tanker then
-attribute=WAREHOUSE.Attribute.AIR_TANKER
-elseif transporthelo then
-attribute=WAREHOUSE.Attribute.AIR_TRANSPORTHELO
-elseif attackhelicopter then
-attribute=WAREHOUSE.Attribute.AIR_ATTACKHELO
-elseif uav then
-attribute=WAREHOUSE.Attribute.AIR_UAV
-elseif apc then
-attribute=WAREHOUSE.Attribute.GROUND_APC
-elseif ifv then
-attribute=WAREHOUSE.Attribute.GROUND_IFV
-elseif infantry then
-attribute=WAREHOUSE.Attribute.GROUND_INFANTRY
-elseif artillery then
-attribute=WAREHOUSE.Attribute.GROUND_ARTILLERY
-elseif tank then
-attribute=WAREHOUSE.Attribute.GROUND_TANK
-elseif aaa then
-attribute=WAREHOUSE.Attribute.GROUND_AAA
-elseif ewr then
-attribute=WAREHOUSE.Attribute.GROUND_EWR
-elseif sam then
-attribute=WAREHOUSE.Attribute.GROUND_SAM
-elseif truck then
-attribute=WAREHOUSE.Attribute.GROUND_TRUCK
-elseif train then
-attribute=WAREHOUSE.Attribute.GROUND_TRAIN
-elseif aircraftcarrier then
-attribute=WAREHOUSE.Attribute.NAVAL_AIRCRAFTCARRIER
-elseif warship then
-attribute=WAREHOUSE.Attribute.NAVAL_WARSHIP
-elseif armedship then
-attribute=WAREHOUSE.Attribute.NAVAL_ARMEDSHIP
-elseif unarmedship then
-attribute=WAREHOUSE.Attribute.NAVAL_UNARMEDSHIP
-else
-if group:IsGround()then
-attribute=WAREHOUSE.Attribute.GROUND_OTHER
-elseif group:IsShip()then
-attribute=WAREHOUSE.Attribute.NAVAL_OTHER
-elseif group:IsAir()then
-attribute=WAREHOUSE.Attribute.AIR_OTHER
-else
-attribute=WAREHOUSE.Attribute.OTHER_UNKNOWN
-end
-end
-end
-return attribute
-end
-function WAREHOUSE:_GetObjectSize(DCSobject)
-local DCSdesc=DCSobject:getDesc()
-if DCSdesc.box then
-local x=DCSdesc.box.max.x+math.abs(DCSdesc.box.min.x)
-local y=DCSdesc.box.max.y+math.abs(DCSdesc.box.min.y)
-local z=DCSdesc.box.max.z+math.abs(DCSdesc.box.min.z)
-return math.max(x,z),x,y,z
-end
-return 0,0,0,0
-end
-function WAREHOUSE:GetStockInfo(stock)
-local _data={}
-for _j,_attribute in pairs(WAREHOUSE.Attribute)do
-local n=0
-for _i,_item in pairs(stock)do
-local _ite=_item
-if _ite.attribute==_attribute then
-n=n+1
-end
-end
-_data[_attribute]=n
-end
-return _data
-end
-function WAREHOUSE:_DeleteStockItem(stockitem)
-for i=1,#self.stock do
-local item=self.stock[i]
-if item.uid==stockitem.uid then
-table.remove(self.stock,i)
-break
-end
-end
-end
-function WAREHOUSE:_DeleteQueueItem(qitem,queue)
-self:F({qitem=qitem,queue=queue})
-for i=1,#queue do
-local _item=queue[i]
-if _item.uid==qitem.uid then
-self:T(self.lid..string.format("Deleting queue item id=%d.",qitem.uid))
-table.remove(queue,i)
-break
-end
-end
-end
-function WAREHOUSE:_DeleteQueueItemByID(qitemID,queue)
-for i=1,#queue do
-local _item=queue[i]
-if _item.uid==qitemID then
-self:T(self.lid..string.format("Deleting queue item id=%d.",qitemID))
-table.remove(queue,i)
-break
-end
-end
-end
-function WAREHOUSE:_SortQueue()
-self:F3()
-local function _sort(a,b)
-return(a.prio=2 then
-local total="Empty"
-if#queue>0 then
-total=string.format("Total = %d",#queue)
-end
-local text=string.format("%s at %s: %s",name,self.alias,total)
-for i,qitem in ipairs(queue)do
-local qitem=qitem
-local uid=qitem.uid
-local prio=qitem.prio
-local clock="N/A"
-if qitem.timestamp then
-clock=tostring(UTILS.SecondsToClock(qitem.timestamp))
-end
-local assignment=tostring(qitem.assignment)
-local requestor=qitem.warehouse.alias
-local airbasename=qitem.warehouse:GetAirbaseName()
-local requestorAirbaseCat=qitem.warehouse:GetAirbaseCategory()
-local assetdesc=qitem.assetdesc
-local assetdescval=qitem.assetdescval
-if assetdesc==WAREHOUSE.Descriptor.ASSETLIST then
-assetdescval="Asset list"
-end
-local nasset=tostring(qitem.nasset)
-local ndelivered=tostring(qitem.ndelivered)
-local ncargogroupset="N/A"
-if qitem.cargogroupset then
-ncargogroupset=tostring(qitem.cargogroupset:Count())
-end
-local transporttype="N/A"
-if qitem.transporttype then
-transporttype=qitem.transporttype
-end
-local ntransport="N/A"
-if qitem.ntransport then
-ntransport=tostring(qitem.ntransport)
-end
-local ntransportalive="N/A"
-if qitem.transportgroupset then
-ntransportalive=tostring(qitem.transportgroupset:Count())
-end
-local ntransporthome="N/A"
-if qitem.ntransporthome then
-ntransporthome=tostring(qitem.ntransporthome)
-end
-text=text..string.format(
-"\n%d) UID=%d, Prio=%d, Clock=%s, Assignment=%s | Requestor=%s [Airbase=%s, category=%d] | Assets(%s)=%s: #requested=%s / #alive=%s / #delivered=%s | Transport=%s: #requested=%s / #alive=%s / #home=%s",
-i,uid,prio,clock,assignment,requestor,airbasename,requestorAirbaseCat,assetdesc,assetdescval,nasset,ncargogroupset,ndelivered,transporttype,ntransport,ntransportalive,ntransporthome)
-end
-if#queue==0 then
-self:I(self.lid..text)
-else
-if total~="Empty"then
-self:I(self.lid..text)
-end
-end
-end
-end
-function WAREHOUSE:_DisplayStatus()
-if self.verbosity>=3 then
-local text=string.format("\n------------------------------------------------------\n")
-text=text..string.format("Warehouse %s status: %s\n",self.alias,self:GetState())
-text=text..string.format("------------------------------------------------------\n")
-text=text..string.format("Coalition name = %s\n",self:GetCoalitionName())
-text=text..string.format("Country name = %s\n",self:GetCountryName())
-text=text..string.format("Airbase name = %s (category=%d)\n",self:GetAirbaseName(),self:GetAirbaseCategory())
-text=text..string.format("Queued requests = %d\n",#self.queue)
-text=text..string.format("Pending requests = %d\n",#self.pending)
-text=text..string.format("------------------------------------------------------\n")
-text=text..self:_GetStockAssetsText()
-self:I(text)
-end
-end
-function WAREHOUSE:_GetStockAssetsText(messagetoall)
-local _data=self:GetStockInfo(self.stock)
-local text="Stock:\n"
-local total=0
-for _attribute,_count in pairs(_data)do
-if _count>0 then
-local attribute=tostring(UTILS.Split(_attribute,"_")[2])
-text=text..string.format("%s = %d\n",attribute,_count)
-total=total+_count
-end
-end
-text=text..string.format("===================\n")
-text=text..string.format("Total = %d\n",total)
-text=text..string.format("------------------------------------------------------\n")
-MESSAGE:New(text,10):ToAllIf(messagetoall)
-return text
-end
-function WAREHOUSE:_UpdateWarehouseMarkText()
-if self.markerOn then
-local text=string.format("Warehouse state: %s\nTotal assets in stock %d:\n",self:GetState(),#self.stock)
-for _attribute,_count in pairs(self:GetStockInfo(self.stock)or{})do
-if _count>0 then
-local attribute=tostring(UTILS.Split(_attribute,"_")[2])
-text=text..string.format("%s=%d, ",attribute,_count)
-end
-end
-local coordinate=self:GetCoordinate()
-local coalition=self:GetCoalition()
-if not self.markerWarehouse then
-self.markerWarehouse=MARKER:New(coordinate,text):ToCoalition(coalition)
-else
-local refresh=false
-if self.markerWarehouse.text~=text then
-self.markerWarehouse.text=text
-refresh=true
-end
-if self.markerWarehouse.coordinate~=coordinate then
-self.markerWarehouse.coordinate=coordinate
-refresh=true
-end
-if self.markerWarehouse.coalition~=coalition then
-self.markerWarehouse.coalition=coalition
-refresh=true
-end
-if refresh then
-self.markerWarehouse:Refresh()
-end
-end
-end
-end
-function WAREHOUSE:_DisplayStockItems(stock)
-local text=self.lid..string.format("Warehouse %s stock assets:",self.alias)
-for _i,_stock in pairs(stock)do
-local mystock=_stock
-local name=mystock.templatename
-local category=mystock.category
-local cargobaymax=mystock.cargobaymax
-local cargobaytot=mystock.cargobaytot
-local nunits=mystock.nunits
-local range=mystock.range
-local size=mystock.size
-local speed=mystock.speedmax
-local uid=mystock.uid
-local unittype=mystock.unittype
-local weight=mystock.weight
-local attribute=mystock.attribute
-text=text..string.format("\n%02d) uid=%d, name=%s, unittype=%s, category=%d, attribute=%s, nunits=%d, speed=%.1f km/h, range=%.1f km, size=%.1f m, weight=%.1f kg, cargobax max=%.1f kg tot=%.1f kg",
-_i,uid,name,unittype,category,attribute,nunits,speed,range/1000,size,weight,cargobaymax,cargobaytot)
-end
-self:T3(text)
-end
-function WAREHOUSE:_Fireworks(coord)
-coord=coord or self:GetCoordinate()
-for i=1,91 do
-local color=math.random(0,3)
-coord:Flare(color,i-1)
-end
-end
-function WAREHOUSE:_InfoMessage(text,duration)
-duration=duration or 20
-if duration>0 and self.Debug or self.Report then
-MESSAGE:New(text,duration):ToCoalition(self:GetCoalition())
-end
-self:I(self.lid..text)
-end
-function WAREHOUSE:_DebugMessage(text,duration)
-duration=duration or 20
-if self.Debug and duration>0 then
-MESSAGE:New(text,duration):ToAllIf(self.Debug)
-end
-self:T(self.lid..text)
-end
-function WAREHOUSE:_ErrorMessage(text,duration)
-duration=duration or 20
-if duration>0 then
-MESSAGE:New(text,duration):ToAll()
-end
-self:E(self.lid..text)
-end
-function WAREHOUSE:_GetMaxHeight(D,alphaC,alphaD,Hdep,Hdest,Deltahhold)
-local Hhold=Hdest+Deltahhold
-local hdest=Hdest-Hdep
-local hhold=hdest+Deltahhold
-local Dp=math.sqrt(D^2+hhold^2)
-local alphaS=math.atan(hdest/D)
-local alphaH=math.atan(hhold/D)
-local alphaCp=alphaC-alphaH
-local alphaDp=alphaD+alphaH
-local gammap=math.pi-alphaCp-alphaDp
-local sCp=Dp*math.sin(alphaDp)/math.sin(gammap)
-local sDp=Dp*math.sin(alphaCp)/math.sin(gammap)
-local hmax=sCp*math.sin(alphaC)
-if self.Debug then
-env.info(string.format("Hdep = %.3f km",Hdep/1000))
-env.info(string.format("Hdest = %.3f km",Hdest/1000))
-env.info(string.format("DetaHold= %.3f km",Deltahhold/1000))
-env.info()
-env.info(string.format("D = %.3f km",D/1000))
-env.info(string.format("Dp = %.3f km",Dp/1000))
-env.info()
-env.info(string.format("alphaC = %.3f Deg",math.deg(alphaC)))
-env.info(string.format("alphaCp = %.3f Deg",math.deg(alphaCp)))
-env.info()
-env.info(string.format("alphaD = %.3f Deg",math.deg(alphaD)))
-env.info(string.format("alphaDp = %.3f Deg",math.deg(alphaDp)))
-env.info()
-env.info(string.format("alphaS = %.3f Deg",math.deg(alphaS)))
-env.info(string.format("alphaH = %.3f Deg",math.deg(alphaH)))
-env.info()
-env.info(string.format("sCp = %.3f km",sCp/1000))
-env.info(string.format("sDp = %.3f km",sDp/1000))
-env.info()
-env.info(string.format("hmax = %.3f km",hmax/1000))
-env.info()
-local hdescent=hmax-hhold
-local dClimb=hmax/math.tan(alphaC)
-local dDescent=(hmax-hhold)/math.tan(alphaD)
-local dCruise=D-dClimb-dDescent
-env.info(string.format("hmax = %.3f km",hmax/1000))
-env.info(string.format("hdescent = %.3f km",hdescent/1000))
-env.info(string.format("Dclimb = %.3f km",dClimb/1000))
-env.info(string.format("Dcruise = %.3f km",dCruise/1000))
-env.info(string.format("Ddescent = %.3f km",dDescent/1000))
-env.info()
-end
-return hmax
-end
-function WAREHOUSE:_GetFlightplan(asset,departure,destination)
-local Vmax=asset.speedmax/3.6
-local Range=asset.range
-local category=asset.category
-local ceiling=asset.DCSdesc.Hmax
-local Vymax=asset.DCSdesc.VyMax
-local VxCruiseMax=0.90*Vmax
-local VxCruiseMin=math.min(VxCruiseMax*0.70,166)
-local VxCruise=UTILS.RandomGaussian((VxCruiseMax-VxCruiseMin)/2+VxCruiseMin,(VxCruiseMax-VxCruiseMax)/4,VxCruiseMin,VxCruiseMax)
-local VxClimb=math.min(Vmax*0.90,200)
-local VxDescent=math.min(Vmax*0.60,140)
-local VxHolding=VxDescent*0.9
-local VxFinal=VxHolding*0.9
-local VyClimb=math.min(7.6,Vymax)
-local AlphaClimb=math.rad(4)
-local AlphaDescent=math.rad(4)
-local FLcruise_expect=150*RAT.unit.FL2m
-if category==Group.Category.HELICOPTER then
-FLcruise_expect=1000
-end
-local Pdeparture=departure:GetCoordinate()
-local H_departure=Pdeparture.y
-local Pdestination=destination:GetCoordinate()
-local H_destination=Pdestination.y
-local Rhmin=5000
-local Rhmax=10000
-if category==Group.Category.HELICOPTER then
-Rhmin=500
-Rhmax=1000
-end
-local Pholding=Pdestination:GetRandomCoordinateInRadius(Rhmax,Rhmin)
-local d_holding=Pholding:Get2DDistance(Pdestination)
-local H_holding=Pholding.y
-local heading=Pdeparture:HeadingTo(Pholding)
-local d_total=Pdeparture:Get2DDistance(Pholding)
-local h_holding=1200
-if category==Group.Category.HELICOPTER then
-h_holding=150
-end
-h_holding=UTILS.Randomize(h_holding,0.2)
-local DeltaholdingMax=self:_GetMaxHeight(d_total,AlphaClimb,AlphaDescent,H_departure,H_holding,0)
-if h_holding>DeltaholdingMax then
-h_holding=math.abs(DeltaholdingMax)
-end
-local Hh_holding=H_holding+h_holding
-local h_max=self:_GetMaxHeight(d_total,AlphaClimb,AlphaDescent,H_departure,H_holding,h_holding)
-local FLmax=h_max+H_departure
-local FLmin=math.max(H_departure,Hh_holding)
-FLmax=math.min(FLmax,ceiling)
-if FLmin>FLmax then
-FLmin=FLmax
-end
-if FLcruise_expectFLmax then
-FLcruise_expect=FLmax
-end
-local FLcruise=UTILS.RandomGaussian(FLcruise_expect,math.abs(FLmax-FLmin)/4,FLmin,FLmax)
-local h_climb=FLcruise-H_departure
-local h_descent=FLcruise-Hh_holding
-local d_climb=h_climb/math.tan(AlphaClimb)
-local d_descent=h_descent/math.tan(AlphaDescent)
-local d_cruise=d_total-d_climb-d_descent
-local text=string.format("Flight plan:\n")
-text=text..string.format("Vx max = %.2f km/h\n",Vmax*3.6)
-text=text..string.format("Vx climb = %.2f km/h\n",VxClimb*3.6)
-text=text..string.format("Vx cruise = %.2f km/h\n",VxCruise*3.6)
-text=text..string.format("Vx descent = %.2f km/h\n",VxDescent*3.6)
-text=text..string.format("Vx holding = %.2f km/h\n",VxHolding*3.6)
-text=text..string.format("Vx final = %.2f km/h\n",VxFinal*3.6)
-text=text..string.format("Vy max = %.2f m/s\n",Vymax)
-text=text..string.format("Vy climb = %.2f m/s\n",VyClimb)
-text=text..string.format("Alpha Climb = %.2f Deg\n",math.deg(AlphaClimb))
-text=text..string.format("Alpha Descent = %.2f Deg\n",math.deg(AlphaDescent))
-text=text..string.format("Dist climb = %.3f km\n",d_climb/1000)
-text=text..string.format("Dist cruise = %.3f km\n",d_cruise/1000)
-text=text..string.format("Dist descent = %.3f km\n",d_descent/1000)
-text=text..string.format("Dist total = %.3f km\n",d_total/1000)
-text=text..string.format("h_climb = %.3f km\n",h_climb/1000)
-text=text..string.format("h_desc = %.3f km\n",h_descent/1000)
-text=text..string.format("h_holding = %.3f km\n",h_holding/1000)
-text=text..string.format("h_max = %.3f km\n",h_max/1000)
-text=text..string.format("FL min = %.3f km\n",FLmin/1000)
-text=text..string.format("FL expect = %.3f km\n",FLcruise_expect/1000)
-text=text..string.format("FL cruise * = %.3f km\n",FLcruise/1000)
-text=text..string.format("FL max = %.3f km\n",FLmax/1000)
-text=text..string.format("Ceiling = %.3f km\n",ceiling/1000)
-text=text..string.format("Max range = %.3f km\n",Range/1000)
-self:T(self.lid..text)
-if d_cruise<0 then
-d_cruise=100
-end
-local wp={}
-local c={}
-local _type=COORDINATE.WaypointType.TakeOffParking
-local _action=COORDINATE.WaypointAction.FromParkingArea
-if asset.takeoffType and asset.takeoffType==COORDINATE.WaypointType.TakeOffParkingHot then
-_type=COORDINATE.WaypointType.TakeOffParkingHot
-_action=COORDINATE.WaypointAction.FromParkingAreaHot
-else
-end
-c[#c+1]=Pdeparture
-wp[#wp+1]=Pdeparture:WaypointAir("RADIO",_type,_action,VxClimb*3.6,true,departure,nil,"Departure")
-local Pcruise=Pdeparture:Translate(d_climb,heading)
-Pcruise.y=FLcruise
-c[#c+1]=Pcruise
-wp[#wp+1]=Pcruise:WaypointAir("BARO",COORDINATE.WaypointType.TurningPoint,COORDINATE.WaypointAction.TurningPoint,VxCruise*3.6,true,nil,nil,"Cruise")
-local Pdescent=Pcruise:Translate(d_cruise,heading)
-Pdescent.y=FLcruise
-c[#c+1]=Pdescent
-wp[#wp+1]=Pdescent:WaypointAir("BARO",COORDINATE.WaypointType.TurningPoint,COORDINATE.WaypointAction.TurningPoint,VxDescent*3.6,true,nil,nil,"Descent")
-Pholding.y=H_holding+h_holding
-c[#c+1]=Pholding
-wp[#wp+1]=Pholding:WaypointAir("BARO",COORDINATE.WaypointType.TurningPoint,COORDINATE.WaypointAction.TurningPoint,VxHolding*3.6,true,nil,nil,"Holding")
-c[#c+1]=Pdestination
-wp[#wp+1]=Pdestination:WaypointAir("RADIO",COORDINATE.WaypointType.Land,COORDINATE.WaypointAction.Landing,VxFinal*3.6,true,destination,nil,"Final Destination")
-if self.Debug then
-for i,coord in pairs(c)do
-local coord=coord
-local dist=0
-if i>1 then
-dist=coord:Get2DDistance(c[i-1])
-end
-coord:MarkToAll(string.format("Waypoint %i, distance = %.2f km",i,dist/1000))
-end
-end
-return wp,c
-end
-do
-ZONE_CAPTURE_COALITION={
-ClassName="ZONE_CAPTURE_COALITION",
-MarkBlue=nil,
-MarkRed=nil,
-StartInterval=nil,
-RepeatInterval=nil,
-HitsOn=nil,
-HitTimeLast=nil,
-HitTimeAttackOver=nil,
-MarkOn=nil,
-}
-function ZONE_CAPTURE_COALITION:New(Zone,Coalition,UnitCategories,ObjectCategories)
-local self=BASE:Inherit(self,ZONE_GOAL_COALITION:New(Zone,Coalition,UnitCategories))
-self:F({Zone=Zone,Coalition=Coalition,UnitCategories=UnitCategories,ObjectCategories=ObjectCategories})
-self:SetObjectCategories(ObjectCategories)
-self:SetSmokeZone(false)
-self:SetMarkZone(true)
-self:SetStartState("Empty")
-do
-end
-do
-end
-do
-end
-do
-end
-self:AddTransition("*","Guard","Guarded")
-self:AddTransition("*","Empty","Empty")
-self:AddTransition({"Guarded","Empty"},"Attack","Attacked")
-self:AddTransition({"Guarded","Attacked","Empty"},"Capture","Captured")
-_EVENTDISPATCHER:CreateEventNewZoneGoal(self)
-return self
-end
-function ZONE_CAPTURE_COALITION:Start(StartInterval,RepeatInterval)
-self.StartInterval=StartInterval or 1
-self.RepeatInterval=RepeatInterval or 15
-if self.ScheduleStatusZone then
-self:ScheduleStop(self.ScheduleStatusZone)
-end
-self.ScheduleStatusZone=self:ScheduleRepeat(self.StartInterval,self.RepeatInterval,0.1,nil,self.StatusZone,self)
-self:HandleEvent(EVENTS.Hit,self.OnEventHit)
-self:Mark()
-return self
-end
-function ZONE_CAPTURE_COALITION:Stop()
-if self.ScheduleStatusZone then
-self:ScheduleStop(self.ScheduleStatusZone)
-end
-if self.SmokeScheduler then
-self:ScheduleStop(self.SmokeScheduler)
-end
-self:UnHandleEvent(EVENTS.Hit)
-end
-function ZONE_CAPTURE_COALITION:SetMonitorHits(Switch,TimeAttackOver)
-self.HitsOn=Switch
-self.HitTimeAttackOver=TimeAttackOver or 5*60
-return self
-end
-function ZONE_CAPTURE_COALITION:SetMarkZone(Switch)
-if Switch==nil or Switch==true then
-self.MarkOn=true
-else
-self.MarkOn=false
-end
-return self
-end
-function ZONE_CAPTURE_COALITION:OnEventHit(EventData)
-if self.HitsOn then
-local UnitHit=EventData.TgtUnit
-if UnitHit and UnitHit.ClassName~="SCENERY"then
-if UnitHit and UnitHit:IsInZone(self)and UnitHit:GetCoalition()==self.Coalition then
-self.HitTimeLast=timer.getTime()
-if self:GetState()~="Attacked"then
-self:F2("Hit ==> Attack")
-self:Attack()
-end
-end
-end
-end
-end
-function ZONE_CAPTURE_COALITION:onafterGuard()
-self:F2("After Guard")
-if self.SmokeZone and not self.SmokeScheduler then
-self.SmokeScheduler=self:ScheduleRepeat(self.StartInterval,self.RepeatInterval,0.1,nil,self.StatusSmoke,self)
-end
-end
-function ZONE_CAPTURE_COALITION:onenterGuarded()
-self:F2("Enter Guarded")
-self:Mark()
-end
-function ZONE_CAPTURE_COALITION:onenterCaptured()
-self:F2("Enter Captured")
-local NewCoalition=self:GetScannedCoalition()
-self:F({NewCoalition=NewCoalition})
-self:SetCoalition(NewCoalition)
-self:Mark()
-self.Goal:Achieved()
-end
-function ZONE_CAPTURE_COALITION:onenterEmpty()
-self:F2("Enter Empty")
-self:Mark()
-end
-function ZONE_CAPTURE_COALITION:onenterAttacked()
-self:F2("Enter Attacked")
-self:Mark()
-end
-function ZONE_CAPTURE_COALITION:IsEmpty()
-local IsEmpty=self:IsNoneInZone()
-self:F({IsEmpty=IsEmpty})
-return IsEmpty
-end
-function ZONE_CAPTURE_COALITION:IsGuarded()
-local IsGuarded=self:IsAllInZoneOfCoalition(self.Coalition)
-self:F({IsGuarded=IsGuarded})
-return IsGuarded
-end
-function ZONE_CAPTURE_COALITION:IsCaptured()
-local IsCaptured=self:IsAllInZoneOfOtherCoalition(self.Coalition)
-self:F({IsCaptured=IsCaptured})
-return IsCaptured
-end
-function ZONE_CAPTURE_COALITION:IsAttacked()
-local IsAttacked=self:IsSomeInZoneOfCoalition(self.Coalition)
-self:F({IsAttacked=IsAttacked})
-return IsAttacked
-end
-function ZONE_CAPTURE_COALITION:StatusZone()
-local State=self:GetState()
-self:GetParent(self,ZONE_CAPTURE_COALITION).StatusZone(self)
-local Tnow=timer.getTime()
-if State~="Guarded"and self:IsGuarded()then
-if self.HitTimeLast==nil or Tnow>=self.HitTimeLast+self.HitTimeAttackOver then
-self:Guard()
-self.HitTimeLast=nil
-end
-end
-if State~="Empty"and self:IsEmpty()then
-self:Empty()
-end
-if State~="Attacked"and self:IsAttacked()then
-self:Attack()
-end
-if State~="Captured"and self:IsCaptured()then
-self:Capture()
-end
-local unitset=self:GetScannedSetUnit()
-local nRed=0
-local nBlue=0
-for _,object in pairs(unitset:GetSet())do
-local coal=object:GetCoalition()
-if object:IsAlive()then
-if coal==coalition.side.RED then
-nRed=nRed+1
-elseif coal==coalition.side.BLUE then
-nBlue=nBlue+1
-end
-end
-end
-if false then
-local text=string.format("CAPTURE ZONE %s: Owner=%s (Previous=%s): #blue=%d, #red=%d, Status %s",self:GetZoneName(),self:GetCoalitionName(),UTILS.GetCoalitionName(self:GetPreviousCoalition()),nBlue,nRed,State)
-local NewState=self:GetState()
-if NewState~=State then
-text=text..string.format(" --> %s",NewState)
-end
-self:I(text)
-end
-end
-function ZONE_CAPTURE_COALITION:Mark()
-if self.MarkOn then
-local Coord=self:GetCoordinate()
-local ZoneName=self:GetZoneName()
-local State=self:GetState()
-if self.MarkRed then
-Coord:RemoveMark(self.MarkRed)
-end
-if self.MarkBlue then
-Coord:RemoveMark(self.MarkBlue)
-end
-if self.Coalition==coalition.side.BLUE then
-self.MarkBlue=Coord:MarkToCoalitionBlue("Coalition: Blue\nGuard Zone: "..ZoneName.."\nStatus: "..State)
-self.MarkRed=Coord:MarkToCoalitionRed("Coalition: Blue\nCapture Zone: "..ZoneName.."\nStatus: "..State)
-elseif self.Coalition==coalition.side.RED then
-self.MarkRed=Coord:MarkToCoalitionRed("Coalition: Red\nGuard Zone: "..ZoneName.."\nStatus: "..State)
-self.MarkBlue=Coord:MarkToCoalitionBlue("Coalition: Red\nCapture Zone: "..ZoneName.."\nStatus: "..State)
-else
-self.MarkRed=Coord:MarkToCoalitionRed("Coalition: Neutral\nCapture Zone: "..ZoneName.."\nStatus: "..State)
-self.MarkBlue=Coord:MarkToCoalitionBlue("Coalition: Neutral\nCapture Zone: "..ZoneName.."\nStatus: "..State)
-end
-end
-end
-end
-do
-ZONE_GOAL={
-ClassName="ZONE_GOAL",
-Goal=nil,
-SmokeTime=nil,
-SmokeScheduler=nil,
-SmokeColor=nil,
-SmokeZone=nil,
-}
-function ZONE_GOAL:New(Zone)
-BASE:I({Zone=Zone})
-local self=BASE:Inherit(self,BASE:New())
-if type(Zone)=="string"then
-self=BASE:Inherit(self,ZONE_POLYGON:NewFromGroupName(Zone))
-else
-self=BASE:Inherit(self,ZONE_RADIUS:New(Zone:GetName(),Zone:GetVec2(),Zone:GetRadius()))
-self:F({Zone=Zone})
-end
-self.Goal=GOAL:New()
-self.SmokeTime=nil
-self:SetSmokeZone(true)
-self:AddTransition("*","DestroyedUnit","*")
-return self
-end
-function ZONE_GOAL:GetZone()
-return self
-end
-function ZONE_GOAL:GetZoneName()
-return self:GetName()
-end
-function ZONE_GOAL:SetSmokeZone(switch)
-self.SmokeZone=switch
-return self
-end
-function ZONE_GOAL:Smoke(SmokeColor)
-self:F({SmokeColor=SmokeColor})
-self.SmokeColor=SmokeColor
-end
-function ZONE_GOAL:Flare(FlareColor)
-self:FlareZone(FlareColor,30)
-end
-function ZONE_GOAL:onafterGuard()
-self:F("Guard")
-if self.SmokeZone and not self.SmokeScheduler then
-self.SmokeScheduler=self:ScheduleRepeat(1,1,0.1,nil,self.StatusSmoke,self)
-end
-end
-function ZONE_GOAL:StatusSmoke()
-self:F({self.SmokeTime,self.SmokeColor})
-if self.SmokeZone then
-local CurrentTime=timer.getTime()
-if self.SmokeTime==nil or self.SmokeTime+300<=CurrentTime then
-if self.SmokeColor then
-self:GetCoordinate():Smoke(self.SmokeColor)
-self.SmokeTime=CurrentTime
-end
-end
-end
-end
-function ZONE_GOAL:__Destroyed(EventData)
-self:F({"EventDead",EventData})
-self:F({EventData.IniUnit})
-if EventData.IniDCSUnit then
-local Vec3=EventData.IniDCSUnit:getPosition().p
-self:F({Vec3=Vec3})
-if Vec3 and self:IsVec3InZone(Vec3)then
-local PlayerHits=_DATABASE.HITS[EventData.IniUnitName]
-if PlayerHits then
-for PlayerName,PlayerHit in pairs(PlayerHits.Players or{})do
-self.Goal:AddPlayerContribution(PlayerName)
-self:DestroyedUnit(EventData.IniUnitName,PlayerName)
-end
-end
-end
-end
-end
-function ZONE_GOAL:MonitorDestroyedUnits()
-self:HandleEvent(EVENTS.Dead,self.__Destroyed)
-self:HandleEvent(EVENTS.Crash,self.__Destroyed)
-end
-end
-do
-ZONE_GOAL_CARGO={
-ClassName="ZONE_GOAL_CARGO",
-}
-ZONE_GOAL_CARGO.States={}
-function ZONE_GOAL_CARGO:New(Zone,Coalition)
-local self=BASE:Inherit(self,ZONE_GOAL:New(Zone))
-self:F({Zone=Zone,Coalition=Coalition})
-self:SetCoalition(Coalition)
-do
-end
-do
-end
-do
-end
-do
-end
-self:AddTransition("*","Guard","Guarded")
-self:AddTransition("*","Empty","Empty")
-self:AddTransition({"Guarded","Empty"},"Attack","Attacked")
-self:AddTransition({"Guarded","Attacked","Empty"},"Capture","Captured")
-return self
-end
-function ZONE_GOAL_CARGO:SetCoalition(Coalition)
-self.Coalition=Coalition
-end
-function ZONE_GOAL_CARGO:GetCoalition()
-return self.Coalition
-end
-function ZONE_GOAL_CARGO:GetCoalitionName()
-if self.Coalition==coalition.side.BLUE then
-return"Blue"
-end
-if self.Coalition==coalition.side.RED then
-return"Red"
-end
-if self.Coalition==coalition.side.NEUTRAL then
-return"Neutral"
-end
-return""
-end
-function ZONE_GOAL_CARGO:IsGuarded()
-local IsGuarded=self.Zone:IsAllInZoneOfCoalition(self.Coalition)
-self:F({IsGuarded=IsGuarded})
-return IsGuarded
-end
-function ZONE_GOAL_CARGO:IsEmpty()
-local IsEmpty=self.Zone:IsNoneInZone()
-self:F({IsEmpty=IsEmpty})
-return IsEmpty
-end
-function ZONE_GOAL_CARGO:IsCaptured()
-local IsCaptured=self.Zone:IsAllInZoneOfOtherCoalition(self.Coalition)
-self:F({IsCaptured=IsCaptured})
-return IsCaptured
-end
-function ZONE_GOAL_CARGO:IsAttacked()
-local IsAttacked=self.Zone:IsSomeInZoneOfCoalition(self.Coalition)
-self:F({IsAttacked=IsAttacked})
-return IsAttacked
-end
-function ZONE_GOAL_CARGO:Mark()
-local Coord=self.Zone:GetCoordinate()
-local ZoneName=self:GetZoneName()
-local State=self:GetState()
-if self.MarkRed and self.MarkBlue then
-self:F({MarkRed=self.MarkRed,MarkBlue=self.MarkBlue})
-Coord:RemoveMark(self.MarkRed)
-Coord:RemoveMark(self.MarkBlue)
-end
-if self.Coalition==coalition.side.BLUE then
-self.MarkBlue=Coord:MarkToCoalitionBlue("Guard Zone: "..ZoneName.."\nStatus: "..State)
-self.MarkRed=Coord:MarkToCoalitionRed("Capture Zone: "..ZoneName.."\nStatus: "..State)
-else
-self.MarkRed=Coord:MarkToCoalitionRed("Guard Zone: "..ZoneName.."\nStatus: "..State)
-self.MarkBlue=Coord:MarkToCoalitionBlue("Capture Zone: "..ZoneName.."\nStatus: "..State)
-end
-end
-function ZONE_GOAL_CARGO:onenterGuarded()
-if self.Coalition==coalition.side.BLUE then
-else
-end
-self:Mark()
-end
-function ZONE_GOAL_CARGO:onenterCaptured()
-local NewCoalition=self.Zone:GetCoalition()
-self:F({NewCoalition=NewCoalition})
-self:SetCoalition(NewCoalition)
-self:Mark()
-end
-function ZONE_GOAL_CARGO:onenterEmpty()
-self:Mark()
-end
-function ZONE_GOAL_CARGO:onenterAttacked()
-self:Mark()
-end
-function ZONE_GOAL_CARGO:onafterGuard()
-if not self.SmokeScheduler then
-self.SmokeScheduler=self:ScheduleRepeat(1,1,0.1,nil,self.StatusSmoke,self)
-end
-if not self.ScheduleStatusZone then
-self.ScheduleStatusZone=self:ScheduleRepeat(15,15,0.1,nil,self.StatusZone,self)
-end
-end
-function ZONE_GOAL_CARGO:IsCaptured()
-local IsCaptured=self.Zone:IsAllInZoneOfOtherCoalition(self.Coalition)
-self:F({IsCaptured=IsCaptured})
-return IsCaptured
-end
-function ZONE_GOAL_CARGO:IsAttacked()
-local IsAttacked=self.Zone:IsSomeInZoneOfCoalition(self.Coalition)
-self:F({IsAttacked=IsAttacked})
-return IsAttacked
-end
-function ZONE_GOAL_CARGO:StatusZone()
-local State=self:GetState()
-self:F({State=self:GetState()})
-self.Zone:Scan()
-if State~="Guarded"and self:IsGuarded()then
-self:Guard()
-end
-if State~="Empty"and self:IsEmpty()then
-self:Empty()
-end
-if State~="Attacked"and self:IsAttacked()then
-self:Attack()
-end
-if State~="Captured"and self:IsCaptured()then
-self:Capture()
-end
-end
-end
-do
-ZONE_GOAL_COALITION={
-ClassName="ZONE_GOAL_COALITION",
-Coalition=nil,
-PreviousCoalition=nil,
-UnitCategories=nil,
-ObjectCategories=nil,
-}
-ZONE_GOAL_COALITION.States={}
-function ZONE_GOAL_COALITION:New(Zone,Coalition,UnitCategories)
-if not Zone then
-BASE:E("ERROR: No Zone specified in ZONE_GOAL_COALITION!")
-return nil
-end
-local self=BASE:Inherit(self,ZONE_GOAL:New(Zone))
-self:F({Zone=Zone,Coalition=Coalition})
-self:SetCoalition(Coalition or coalition.side.NEUTRAL)
-self:SetUnitCategories(UnitCategories)
-self:SetObjectCategories()
-return self
-end
-function ZONE_GOAL_COALITION:SetCoalition(Coalition)
-self.PreviousCoalition=self.Coalition or Coalition
-self.Coalition=Coalition
-return self
-end
-function ZONE_GOAL_COALITION:SetUnitCategories(UnitCategories)
-if UnitCategories and type(UnitCategories)~="table"then
-UnitCategories={UnitCategories}
-end
-self.UnitCategories=UnitCategories or{Unit.Category.GROUND_UNIT}
-return self
-end
-function ZONE_GOAL_COALITION:SetObjectCategories(ObjectCategories)
-if ObjectCategories and type(ObjectCategories)~="table"then
-ObjectCategories={ObjectCategories}
-end
-self.ObjectCategories=ObjectCategories or{Object.Category.UNIT,Object.Category.STATIC}
-return self
-end
-function ZONE_GOAL_COALITION:GetCoalition()
-return self.Coalition
-end
-function ZONE_GOAL_COALITION:GetPreviousCoalition()
-return self.PreviousCoalition
-end
-function ZONE_GOAL_COALITION:GetCoalitionName()
-return UTILS.GetCoalitionName(self.Coalition)
-end
-function ZONE_GOAL_COALITION:StatusZone()
-local State=self:GetState()
-local text=string.format("Zone state=%s, Owner=%s, Scanning...",State,self:GetCoalitionName())
-self:F(text)
-self:Scan(self.ObjectCategories,self.UnitCategories)
-return self
-end
-end
-AIRBOSS={
-ClassName="AIRBOSS",
-Debug=false,
-lid=nil,
-theatre=nil,
-carrier=nil,
-carriertype=nil,
-carrierparam={},
-alias=nil,
-airbase=nil,
-waypoints={},
-currentwp=nil,
-beacon=nil,
-TACANon=nil,
-TACANchannel=nil,
-TACANmode=nil,
-TACANmorse=nil,
-ICLSon=nil,
-ICLSchannel=nil,
-ICLSmorse=nil,
-LSORadio=nil,
-LSOFreq=nil,
-LSOModu=nil,
-MarshalRadio=nil,
-MarshalFreq=nil,
-MarshalModu=nil,
-TowerFreq=nil,
-radiotimer=nil,
-zoneCCA=nil,
-zoneCCZ=nil,
-players={},
-menuadded={},
-BreakEntry={},
-BreakEarly={},
-BreakLate={},
-Abeam={},
-Ninety={},
-Wake={},
-Final={},
-Groove={},
-Platform={},
-DirtyUp={},
-Bullseye={},
-defaultcase=nil,
-case=nil,
-defaultoffset=nil,
-holdingoffset=nil,
-recoverytimes={},
-flights={},
-Qpattern={},
-Qmarshal={},
-Qwaiting={},
-Qspinning={},
-RQMarshal={},
-RQLSO={},
-TQMarshal=0,
-TQLSO=0,
-Nmaxpattern=nil,
-Nmaxmarshal=nil,
-NmaxSection=nil,
-NmaxStack=nil,
-handleai=nil,
-xtVoiceOvers=nil,
-xtVoiceOversAI=nil,
-tanker=nil,
-Corientation=nil,
-Corientlast=nil,
-Cposition=nil,
-defaultskill=nil,
-adinfinitum=nil,
-magvar=nil,
-Tcollapse=nil,
-recoverywindow=nil,
-usersoundradio=nil,
-Tqueue=nil,
-dTqueue=nil,
-dTstatus=nil,
-menumarkzones=nil,
-menusmokezones=nil,
-playerscores=nil,
-autosave=nil,
-autosavefile=nil,
-autosavepath=nil,
-marshalradius=nil,
-airbossnice=nil,
-staticweather=nil,
-windowcount=0,
-LSOdT=nil,
-senderac=nil,
-radiorelayLSO=nil,
-radiorelayMSH=nil,
-turnintowind=nil,
-detour=nil,
-squadsetAI=nil,
-excludesetAI=nil,
-menusingle=nil,
-collisiondist=nil,
-holdtimestamp=nil,
-Tmessage=nil,
-soundfolder=nil,
-soundfolderLSO=nil,
-soundfolderMSH=nil,
-despawnshutdown=nil,
-dTbeacon=nil,
-Tbeacon=nil,
-LSOCall=nil,
-MarshalCall=nil,
-lowfuelAI=nil,
-emergency=nil,
-respawnAI=nil,
-gle={},
-lue={},
-trapsheet=nil,
-trappath=nil,
-trapprefix=nil,
-initialmaxalt=nil,
-welcome=nil,
-skipperMenu=nil,
-skipperSpeed=nil,
-skipperTime=nil,
-skipperOffset=nil,
-skipperUturn=nil,
-}
-AIRBOSS.AircraftCarrier={
-AV8B="AV8BNA",
-HORNET="FA-18C_hornet",
-A4EC="A-4E-C",
-F14A="F-14A-135-GR",
-F14B="F-14B",
-F14A_AI="F-14A",
-FA18C="F/A-18C",
-T45C="T-45",
-S3B="S-3B",
-S3BTANKER="S-3B Tanker",
-E2D="E-2C",
-C2A="C2A_Greyhound",
-RHINOE="FA-18E",
-RHINOF="FA-18F",
-GROWLER="EA-18G",
-}
-AIRBOSS.CarrierType={
-ROOSEVELT="CVN_71",
-LINCOLN="CVN_72",
-WASHINGTON="CVN_73",
-TRUMAN="CVN_75",
-STENNIS="Stennis",
-FORRESTAL="Forrestal",
-VINSON="VINSON",
-HERMES="HERMES81",
-INVINCIBLE="hms_invincible",
-TARAWA="LHA_Tarawa",
-AMERICA="USS America LHA-6",
-JCARLOS="L61",
-CANBERRA="L02",
-KUZNETSOV="KUZNECOW",
-}
-AIRBOSS.PatternStep={
-UNDEFINED="Undefined",
-REFUELING="Refueling",
-SPINNING="Spinning",
-COMMENCING="Commencing",
-HOLDING="Holding",
-WAITING="Waiting for free Marshal stack",
-PLATFORM="Platform",
-ARCIN="Arc Turn In",
-ARCOUT="Arc Turn Out",
-DIRTYUP="Dirty Up",
-BULLSEYE="Bullseye",
-INITIAL="Initial",
-BREAKENTRY="Break Entry",
-EARLYBREAK="Early Break",
-LATEBREAK="Late Break",
-ABEAM="Abeam",
-NINETY="Ninety",
-WAKE="Wake",
-FINAL="Turn Final",
-GROOVE_XX="Groove X",
-GROOVE_IM="Groove In the Middle",
-GROOVE_IC="Groove In Close",
-GROOVE_AR="Groove At the Ramp",
-GROOVE_IW="Groove In the Wires",
-GROOVE_AL="Groove Abeam Landing Spot",
-GROOVE_LC="Groove Level Cross",
-BOLTER="Bolter Pattern",
-EMERGENCY="Emergency Landing",
-DEBRIEF="Debrief",
-}
-AIRBOSS.GroovePos={
-X0="X0",
-XX="XX",
-IM="IM",
-IC="IC",
-AR="AR",
-AL="AL",
-LC="LC",
-IW="IW",
-}
-AIRBOSS.Difficulty={
-EASY="Flight Student",
-NORMAL="Naval Aviator",
-HARD="TOPGUN Graduate",
-}
-AIRBOSS.MenuF10={}
-AIRBOSS.MenuF10Root=nil
-AIRBOSS.version="1.3.3"
-function AIRBOSS:New(carriername,alias)
-local self=BASE:Inherit(self,FSM:New())
-self:F2({carriername=carriername,alias=alias})
-self.carrier=UNIT:FindByName(carriername)
-if self.carrier==nil then
-local text=string.format("ERROR: Carrier unit %s could not be found! Make sure this UNIT is defined in the mission editor and check the spelling of the unit name carefully.",carriername)
-MESSAGE:New(text,120):ToAll()
-self:E(text)
-return nil
-end
-self.lid=string.format("AIRBOSS %s | ",carriername)
-self.theatre=env.mission.theatre
-self:T2(self.lid..string.format("Theatre = %s.",tostring(self.theatre)))
-self.carriertype=self.carrier:GetTypeName()
-self.alias=alias or carriername
-self.airbase=AIRBASE:FindByName(carriername)
-self.beacon=BEACON:New(self.carrier)
-self:_GetTowerFrequency()
-self.playerscores={}
-self:_InitWaypoints()
-self.currentwp=1
-self:_PatrolRoute()
-self:SetMarshalRadio()
-self:SetAirbossRadio()
-self:SetLSORadio()
-self:SetLSOCallInterval()
-self.radiotimer=SCHEDULER:New()
-self:SetMagneticDeclination()
-self:SetICLS()
-self:SetTACAN()
-self:SetBeaconRefresh()
-self:SetMaxLandingPattern()
-self:SetMaxMarshalStacks()
-self:SetMaxSectionSize()
-self:SetMaxFlightsPerStack()
-self:SetHandleAION()
-self:SetExtraVoiceOvers(false)
-self:SetExtraVoiceOversAI(false)
-self:SetAirbossNiceGuy()
-self:SetEmergencyLandings()
-self:SetDespawnOnEngineShutdown(false)
-self:SetRespawnAI(false)
-self:SetStaticWeather()
-self:SetRecoveryCase()
-self:SetRecoveryTurnTime()
-self:SetHoldingOffsetAngle()
-self:SetMarshalRadius()
-self:SetInitialMaxAlt()
-self:SetDefaultPlayerSkill(AIRBOSS.Difficulty.EASY)
-self:SetGlideslopeErrorThresholds()
-self:SetLineupErrorThresholds()
-self:SetCarrierControlledArea()
-self:SetCarrierControlledZone()
-self:SetPatrolAdInfinitum(true)
-self:SetCollisionDistance()
-self:SetQueueUpdateTime()
-self:SetStatusUpdateTime()
-self:SetDefaultMessageDuration()
-self:SetMenuMarkZones()
-self:SetMenuSmokeZones()
-self:SetMenuSingleCarrier(false)
-self:SetWelcomePlayers(true)
-self.landingcoord=COORDINATE:New(0,0,0)
-self.sterncoord=COORDINATE:New(0,0,0)
-self.landingspotcoord=COORDINATE:New(0,0,0)
-if self.carriertype==AIRBOSS.CarrierType.STENNIS then
-self:_InitNimitz()
-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.FORRESTAL then
-self:_InitForrestal()
-elseif self.carriertype==AIRBOSS.CarrierType.VINSON then
-self:_InitStennis()
-elseif self.carriertype==AIRBOSS.CarrierType.HERMES then
-self:_InitHermes()
-elseif self.carriertype==AIRBOSS.CarrierType.INVINCIBLE then
-self:_InitInvincible()
-elseif self.carriertype==AIRBOSS.CarrierType.TARAWA then
-self:_InitTarawa()
-elseif self.carriertype==AIRBOSS.CarrierType.AMERICA then
-self:_InitAmerica()
-elseif self.carriertype==AIRBOSS.CarrierType.JCARLOS then
-self:_InitJcarlos()
-elseif self.carriertype==AIRBOSS.CarrierType.CANBERRA then
-self:_InitCanberra()
-elseif self.carriertype==AIRBOSS.CarrierType.KUZNETSOV then
-self:_InitStennis()
-else
-self:E(self.lid..string.format("ERROR: Unknown carrier type %s!",tostring(self.carriertype)))
-return nil
-end
-self:_InitVoiceOvers()
-if false then
-self.Debug=true
-BASE:TraceOnOff(true)
-BASE:TraceClass(self.ClassName)
-BASE:TraceLevel(3)
-end
-if false then
-local case=3
-self.holdingoffset=30
-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:_GetZoneArcIn(case):SmokeZone(SMOKECOLOR.Blue,45)
-self:_GetZoneArcOut(case):SmokeZone(SMOKECOLOR.Blue,45)
-self:_GetZonePlatform(case):SmokeZone(SMOKECOLOR.Blue,45)
-self:_GetZoneCorridor(case):SmokeZone(SMOKECOLOR.Green,45)
-self:_GetZoneHolding(case,1):SmokeZone(SMOKECOLOR.White,45)
-self:_GetZoneHolding(case,2):SmokeZone(SMOKECOLOR.White,45)
-self:_GetZoneInitial(case):SmokeZone(SMOKECOLOR.Orange,45)
-self:_GetZoneCommence(case,1):SmokeZone(SMOKECOLOR.Red,45)
-self:_GetZoneCommence(case,2):SmokeZone(SMOKECOLOR.Red,45)
-self:_GetZoneAbeamLandingSpot():SmokeZone(SMOKECOLOR.Red,5)
-self:_GetZoneLandingSpot():SmokeZone(SMOKECOLOR.Red,5)
-end
-if false then
-local FB=self:GetFinalBearing(false)
-local hdg=self:GetHeading(false)
-local stern=self:_GetSternCoord()
-local bow=stern:Translate(self.carrierparam.totlength,hdg,true)
-local rwy=stern:Translate(self.carrierparam.rwylength,FB,true)
-local function flareme()
-self:GetCoordinate():FlareYellow()
-stern:FlareYellow()
-bow:FlareYellow()
-local r1=stern:Translate(self.carrierparam.rwywidth*0.5,FB+90,true)
-local r2=stern:Translate(self.carrierparam.rwywidth*0.5,FB-90,true)
-rwy:FlareRed()
-local cR=stern:Translate(self.carrierparam.totwidthstarboard,hdg+90,true)
-local cL=stern:Translate(self.carrierparam.totwidthport,hdg-90,true)
-if self.carrier:GetTypeName()~=AIRBOSS.CarrierType.INVINCIBLE or self.carrier:GetTypeName()~=AIRBOSS.CarrierType.HERMES or self.carrier:GetTypeName()~=AIRBOSS.CarrierType.TARAWA or self.carrier:GetTypeName()~=AIRBOSS.CarrierType.AMERICA or self.carrier:GetTypeName()~=AIRBOSS.CarrierType.JCARLOS or self.carrier:GetTypeName()~=AIRBOSS.CarrierType.CANBERRA then
-local w1=stern:Translate(self.carrierparam.wire1,FB,true)
-local w2=stern:Translate(self.carrierparam.wire2,FB,true)
-local w3=stern:Translate(self.carrierparam.wire3,FB,true)
-local w4=stern:Translate(self.carrierparam.wire4,FB,true)
-w1:FlareWhite()
-w2:FlareYellow()
-w3:FlareWhite()
-w4:FlareYellow()
-else
-local ALSPT=self:_GetZoneAbeamLandingSpot()
-ALSPT:FlareZone(FLARECOLOR.Red,5,nil,UTILS.FeetToMeters(120))
-local LSPT=self:_GetZoneLandingSpot()
-LSPT:FlareZone(FLARECOLOR.Green,5,nil,self.carrierparam.deckheight)
-local PLSC=self:_GetLandingSpotCoordinate()
-PLSC:FlareWhite()
-end
-local cbox=self:_GetZoneCarrierBox()
-local rbox=self:_GetZoneRunwayBox()
-cbox:FlareZone(FLARECOLOR.Green,5,nil,self.carrierparam.deckheight)
-rbox:FlareZone(FLARECOLOR.White,5,nil,self.carrierparam.deckheight)
-end
-SCHEDULER:New(nil,flareme,{},1,3,nil,180)
-end
-self:SetStartState("Stopped")
-self:AddTransition("Stopped","Load","Stopped")
-self:AddTransition("Stopped","Start","Idle")
-self:AddTransition("*","Idle","Idle")
-self:AddTransition("Idle","RecoveryStart","Recovering")
-self:AddTransition("Recovering","RecoveryStop","Idle")
-self:AddTransition("Recovering","RecoveryPause","Paused")
-self:AddTransition("Paused","RecoveryUnpause","Recovering")
-self:AddTransition("*","Status","*")
-self:AddTransition("*","RecoveryCase","*")
-self:AddTransition("*","PassingWaypoint","*")
-self:AddTransition("*","LSOGrade","*")
-self:AddTransition("*","Marshal","*")
-self:AddTransition("*","Save","*")
-self:AddTransition("*","Stop","Stopped")
-return self
-end
-function AIRBOSS:SetWelcomePlayers(Switch)
-self.welcome=Switch
-return self
-end
-function AIRBOSS:SetCarrierControlledArea(Radius)
-Radius=UTILS.NMToMeters(Radius or 50)
-self.zoneCCA=ZONE_UNIT:New("Carrier Controlled Area",self.carrier,Radius)
-return self
-end
-function AIRBOSS:SetCarrierControlledZone(Radius)
-Radius=UTILS.NMToMeters(Radius or 5)
-self.zoneCCZ=ZONE_UNIT:New("Carrier Controlled Zone",self.carrier,Radius)
-return self
-end
-function AIRBOSS:SetCollisionDistance(Distance)
-self.collisiondist=UTILS.NMToMeters(Distance or 5)
-return self
-end
-function AIRBOSS:SetRecoveryCase(Case)
-self.defaultcase=Case or 1
-self.case=self.defaultcase
-return self
-end
-function AIRBOSS:SetHoldingOffsetAngle(Offset)
-self.defaultoffset=Offset or 0
-self.holdingoffset=self.defaultoffset
-return self
-end
-function AIRBOSS:SetMenuRecovery(Duration,WindOnDeck,Uturn,Offset)
-self.skipperMenu=true
-self.skipperTime=Duration or 30
-self.skipperSpeed=WindOnDeck or 25
-self.skipperOffset=Offset or 30
-if Uturn then
-self.skipperUturn=true
-else
-self.skipperUturn=false
-end
-return self
-end
-function AIRBOSS:AddRecoveryWindow(starttime,stoptime,case,holdingoffset,turnintowind,speed,uturn)
-local Tnow=timer.getAbsTime()
-if starttime and type(starttime)=="number"then
-starttime=UTILS.SecondsToClock(Tnow+starttime)
-end
-if stoptime and type(stoptime)=="number"then
-stoptime=UTILS.SecondsToClock(Tnow+stoptime)
-end
-starttime=starttime or UTILS.SecondsToClock(Tnow)
-local Tstart=UTILS.ClockToSeconds(starttime)
-local Tstop=stoptime and UTILS.ClockToSeconds(stoptime)or Tstart+90*60
-if Tstart>Tstop then
-self:E(string.format("ERROR: Recovery stop time %s lies before recovery start time %s! Recovery window rejected.",UTILS.SecondsToClock(Tstart),UTILS.SecondsToClock(Tstop)))
-return self
-end
-if Tstop<=Tnow then
-string.format("WARNING: Recovery stop time %s already over. Tnow=%s! Recovery window rejected.",UTILS.SecondsToClock(Tstop),UTILS.SecondsToClock(Tnow))
-return self
-end
-case=case or self.defaultcase
-holdingoffset=holdingoffset or self.defaultoffset
-if case==1 then
-holdingoffset=0
-end
-self.windowcount=self.windowcount+1
-local recovery={}
-recovery.START=Tstart
-recovery.STOP=Tstop
-recovery.CASE=case
-recovery.OFFSET=holdingoffset
-recovery.OPEN=false
-recovery.OVER=false
-recovery.WIND=turnintowind
-recovery.SPEED=speed or 20
-recovery.ID=self.windowcount
-if uturn==nil or uturn==true then
-recovery.UTURN=true
-else
-recovery.UTURN=false
-end
-table.insert(self.recoverytimes,recovery)
-return recovery
-end
-function AIRBOSS:SetSquadronAI(SetGroup)
-self.squadsetAI=SetGroup
-return self
-end
-function AIRBOSS:SetExcludeAI(SetGroup)
-self.excludesetAI=SetGroup
-return self
-end
-function AIRBOSS:AddExcludeAI(Group)
-self.excludesetAI=self.excludesetAI or SET_GROUP:New()
-self.excludesetAI:AddGroup(Group)
-return self
-end
-function AIRBOSS:CloseCurrentRecoveryWindow(Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,self.CloseCurrentRecoveryWindow,self)
-else
-if self:IsRecovering()and self.recoverywindow and self.recoverywindow.OPEN then
-self:RecoveryStop()
-self.recoverywindow.OPEN=false
-self.recoverywindow.OVER=true
-self:DeleteRecoveryWindow(self.recoverywindow)
-end
-end
-end
-function AIRBOSS:DeleteAllRecoveryWindows(Delay)
-for _,recovery in pairs(self.recoverytimes)do
-self:I(self.lid..string.format("Deleting recovery window ID %s",tostring(recovery.ID)))
-self:DeleteRecoveryWindow(recovery,Delay)
-end
-return self
-end
-function AIRBOSS:GetRecoveryWindowByID(id)
-if id then
-for _,_window in pairs(self.recoverytimes)do
-local window=_window
-if window and window.ID==id then
-return window
-end
-end
-end
-return nil
-end
-function AIRBOSS:DeleteRecoveryWindow(Window,Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,self.DeleteRecoveryWindow,self,Window)
-else
-for i,_recovery in pairs(self.recoverytimes)do
-local recovery=_recovery
-if Window and Window.ID==recovery.ID then
-if Window.OPEN then
-self:RecoveryStop()
-else
-table.remove(self.recoverytimes,i)
-end
-end
-end
-end
-end
-function AIRBOSS:SetRecoveryTurnTime(Interval)
-self.dTturn=Interval or 300
-return self
-end
-function AIRBOSS:SetMPWireCorrection(Dcorr)
-self.mpWireCorrection=Dcorr or 12
-return self
-end
-function AIRBOSS:SetQueueUpdateTime(TimeInterval)
-self.dTqueue=TimeInterval or 30
-return self
-end
-function AIRBOSS:SetLSOCallInterval(TimeInterval)
-self.LSOdT=TimeInterval or 4
-return self
-end
-function AIRBOSS:SetAirbossNiceGuy(Switch)
-if Switch==true or Switch==nil then
-self.airbossnice=true
-else
-self.airbossnice=false
-end
-return self
-end
-function AIRBOSS:SetEmergencyLandings(Switch)
-if Switch==true or Switch==nil then
-self.emergency=true
-else
-self.emergency=false
-end
-return self
-end
-function AIRBOSS:SetDespawnOnEngineShutdown(Switch)
-if Switch==true or Switch==nil then
-self.despawnshutdown=true
-else
-self.despawnshutdown=false
-end
-return self
-end
-function AIRBOSS:SetRespawnAI(Switch)
-if Switch==true or Switch==nil then
-self.respawnAI=true
-else
-self.respawnAI=false
-end
-return self
-end
-function AIRBOSS:SetRefuelAI(LowFuelThreshold)
-self.lowfuelAI=LowFuelThreshold or 10
-return self
-end
-function AIRBOSS:SetInitialMaxAlt(MaxAltitude)
-self.initialmaxalt=UTILS.FeetToMeters(MaxAltitude or 1300)
-return self
-end
-function AIRBOSS:SetSoundfilesFolder(FolderPath)
-if FolderPath then
-local lastchar=string.sub(FolderPath,-1)
-if lastchar~="/"then
-FolderPath=FolderPath.."/"
-end
-end
-self.soundfolder=FolderPath
-self:I(self.lid..string.format("Setting sound files folder to: %s",self.soundfolder))
-return self
-end
-function AIRBOSS:SetStatusUpdateTime(TimeInterval)
-self.dTstatus=TimeInterval or 0.5
-return self
-end
-function AIRBOSS:SetDefaultMessageDuration(Duration)
-self.Tmessage=Duration or 10
-return self
-end
-function AIRBOSS:SetGlideslopeErrorThresholds(_max,_min,High,HIGH,Low,LOW)
-if self.carriertype==AIRBOSS.CarrierType.INVINCIBLE or self.carriertype==AIRBOSS.CarrierType.HERMES or self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS or self.carriertype==AIRBOSS.CarrierType.CANBERRA then
-self.gle._max=_max or 0.7
-self.gle.High=High or 1.4
-self.gle.HIGH=HIGH or 1.9
-self.gle._min=_min or-0.5
-self.gle.Low=Low or-1.2
-self.gle.LOW=LOW or-1.5
-else
-self.gle._max=_max or 0.4
-self.gle.High=High or 0.8
-self.gle.HIGH=HIGH or 1.5
-self.gle._min=_min or-0.3
-self.gle.Low=Low or-0.6
-self.gle.LOW=LOW or-0.9
-end
-return self
-end
-function AIRBOSS:SetLineupErrorThresholds(_max,_min,Left,LeftMed,LEFT,Right,RightMed,RIGHT)
-if self.carriertype==AIRBOSS.CarrierType.INVINCIBLE or self.carriertype==AIRBOSS.CarrierType.HERMES or self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS or self.carriertype==AIRBOSS.CarrierType.CANBERRA then
-self.lue._max=_max or 1.8
-self.lue._min=_min or-1.8
-self.lue.Left=Left or-2.8
-self.lue.LeftMed=LeftMed or-3.8
-self.lue.LEFT=LEFT or-4.5
-self.lue.Right=Right or 2.8
-self.lue.RightMed=RightMed or 3.8
-self.lue.RIGHT=RIGHT or 4.5
-else
-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
-end
-return self
-end
-function AIRBOSS:SetMarshalRadius(Radius)
-self.marshalradius=UTILS.NMToMeters(Radius or 2.8)
-return self
-end
-function AIRBOSS:SetMenuSingleCarrier(Switch)
-if Switch==true or Switch==nil then
-self.menusingle=true
-else
-self.menusingle=false
-end
-return self
-end
-function AIRBOSS:SetMenuMarkZones(Switch)
-if Switch==nil or Switch==true then
-self.menumarkzones=true
-else
-self.menumarkzones=false
-end
-return self
-end
-function AIRBOSS:SetMenuSmokeZones(Switch)
-if Switch==nil or Switch==true then
-self.menusmokezones=true
-else
-self.menusmokezones=false
-end
-return self
-end
-function AIRBOSS:SetTrapSheet(Path,Prefix)
-if io then
-self.trapsheet=true
-self.trappath=Path
-self.trapprefix=Prefix
-else
-self:E(self.lid.."ERROR: io is not desanitized. Cannot save trap sheet.")
-end
-return self
-end
-function AIRBOSS:SetStaticWeather(Switch)
-if Switch==nil or Switch==true then
-self.staticweather=true
-else
-self.staticweather=false
-end
-return self
-end
-function AIRBOSS:SetTACANoff()
-self.TACANon=false
-return self
-end
-function AIRBOSS:SetTACAN(Channel,Mode,MorseCode)
-self.TACANchannel=Channel or 74
-self.TACANmode=Mode or"X"
-self.TACANmorse=MorseCode or"STN"
-self.TACANon=true
-return self
-end
-function AIRBOSS:SetICLSoff()
-self.ICLSon=false
-return self
-end
-function AIRBOSS:SetICLS(Channel,MorseCode)
-self.ICLSchannel=Channel or 1
-self.ICLSmorse=MorseCode or"STN"
-self.ICLSon=true
-return self
-end
-function AIRBOSS:SetBeaconRefresh(TimeInterval)
-self.dTbeacon=TimeInterval or(20*60)
-return self
-end
-function AIRBOSS:EnableSRS(PathToSRS,Port,Culture,Gender,Voice,GoogleCreds,Volume,AltBackend)
-local Frequency=self.AirbossRadio.frequency
-local Modulation=self.AirbossRadio.modulation
-self.SRS=MSRS:New(PathToSRS,Frequency,Modulation,Volume,AltBackend)
-self.SRS:SetCoalition(self:GetCoalition())
-self.SRS:SetCoordinate(self:GetCoordinate())
-self.SRS:SetCulture(Culture or"en-US")
-self.SRS:SetGender(Gender or"male")
-self.SRS:SetPath(PathToSRS)
-self.SRS:SetPort(Port or 5002)
-self.SRS:SetLabel(self.AirbossRadio.alias or"AIRBOSS")
-self.SRS:SetCoordinate(self.carrier:GetCoordinate())
-if GoogleCreds then
-self.SRS:SetGoogle(GoogleCreds)
-end
-if Voice then
-self.SRS:SetVoice(Voice)
-end
-self.SRS:SetVolume(Volume or 1.0)
-self.SRSQ=MSRSQUEUE:New("AIRBOSS")
-self.SRSQ:SetTransmitOnlyWithPlayers(true)
-if not self.PilotRadio then
-self:SetSRSPilotVoice()
-end
-return self
-end
-function AIRBOSS:SetLSORadio(Frequency,Modulation,Voice,Gender,Culture)
-self.LSOFreq=(Frequency or 264)
-Modulation=Modulation or"AM"
-if Modulation=="FM"then
-self.LSOModu=radio.modulation.FM
-else
-self.LSOModu=radio.modulation.AM
-end
-self.LSORadio={}
-self.LSORadio.frequency=self.LSOFreq
-self.LSORadio.modulation=self.LSOModu
-self.LSORadio.alias="LSO"
-self.LSORadio.voice=Voice
-self.LSORadio.gender=Gender or"male"
-self.LSORadio.culture=Culture or"en-US"
-return self
-end
-function AIRBOSS:SetAirbossRadio(Frequency,Modulation,Voice,Gender,Culture)
-self.AirbossFreq=Frequency or self:_GetTowerFrequency()or 127.5
-Modulation=Modulation or"AM"
-if type(Modulation)=="table"then
-self.AirbossModu=Modulation
-else
-if Modulation=="FM"then
-self.AirbossModu=radio.modulation.FM
-else
-self.AirbossModu=radio.modulation.AM
-end
-end
-self.AirbossRadio={}
-self.AirbossRadio.frequency=self.AirbossFreq
-self.AirbossRadio.modulation=self.AirbossModu
-self.AirbossRadio.alias="AIRBOSS"
-self.AirbossRadio.voice=Voice
-self.AirbossRadio.gender=Gender or"male"
-self.AirbossRadio.culture=Culture or"en-US"
-return self
-end
-function AIRBOSS:SetMarshalRadio(Frequency,Modulation,Voice,Gender,Culture)
-self.MarshalFreq=Frequency or 305
-Modulation=Modulation or"AM"
-if Modulation=="FM"then
-self.MarshalModu=radio.modulation.FM
-else
-self.MarshalModu=radio.modulation.AM
-end
-self.MarshalRadio={}
-self.MarshalRadio.frequency=self.MarshalFreq
-self.MarshalRadio.modulation=self.MarshalModu
-self.MarshalRadio.alias="MARSHAL"
-self.MarshalRadio.voice=Voice
-self.MarshalRadio.gender=Gender or"male"
-self.MarshalRadio.culture=Culture or"en-US"
-return self
-end
-function AIRBOSS:SetRadioUnitName(unitname)
-self.senderac=unitname
-return self
-end
-function AIRBOSS:SetRadioRelayLSO(unitname)
-self.radiorelayLSO=unitname
-return self
-end
-function AIRBOSS:SetRadioRelayMarshal(unitname)
-self.radiorelayMSH=unitname
-return self
-end
-function AIRBOSS:SetUserSoundRadio()
-self.usersoundradio=true
-return self
-end
-function AIRBOSS:SoundCheckLSO(delay)
-if delay and delay>0 then
-self:ScheduleOnce(delay,AIRBOSS.SoundCheckLSO,self)
-else
-local text="Playing LSO sound files:"
-for _name,_call in pairs(self.LSOCall)do
-local call=_call
-text=text..string.format("\nFile=%s.%s, duration=%.2f sec, loud=%s, subtitle=\"%s\".",call.file,call.suffix,call.duration,tostring(call.loud),call.subtitle)
-self:RadioTransmission(self.LSORadio,call,false)
-if call.loud then
-self:RadioTransmission(self.LSORadio,call,true)
-end
-end
-self:T(self.lid..text)
-end
-end
-function AIRBOSS:SoundCheckMarshal(delay)
-if delay and delay>0 then
-self:ScheduleOnce(delay,AIRBOSS.SoundCheckMarshal,self)
-else
-local text="Playing Marshal sound files:"
-for _name,_call in pairs(self.MarshalCall)do
-local call=_call
-text=text..string.format("\nFile=%s.%s, duration=%.2f sec, loud=%s, subtitle=\"%s\".",call.file,call.suffix,call.duration,tostring(call.loud),call.subtitle)
-self:RadioTransmission(self.MarshalRadio,call,false)
-if call.loud then
-self:RadioTransmission(self.MarshalRadio,call,true)
-end
-end
-self:T(self.lid..text)
-end
-end
-function AIRBOSS:SetMaxLandingPattern(nmax)
-nmax=nmax or 4
-nmax=math.max(nmax,1)
-nmax=math.min(nmax,6)
-self.Nmaxpattern=nmax
-return self
-end
-function AIRBOSS:SetMaxMarshalStacks(nmax)
-self.Nmaxmarshal=nmax or 3
-self.Nmaxmarshal=math.max(self.Nmaxmarshal,1)
-return self
-end
-function AIRBOSS:SetMaxSectionSize(nmax)
-nmax=nmax or 2
-nmax=math.max(nmax,1)
-nmax=math.min(nmax,4)
-self.NmaxSection=nmax-1
-return self
-end
-function AIRBOSS:SetMaxFlightsPerStack(nmax)
-nmax=nmax or 2
-nmax=math.max(nmax,1)
-nmax=math.min(nmax,4)
-self.NmaxStack=nmax
-return self
-end
-function AIRBOSS:SetHandleAION()
-self.handleai=true
-return self
-end
-function AIRBOSS:SetExtraVoiceOvers(status)
-self.xtVoiceOvers=status
-return self
-end
-function AIRBOSS:SetExtraVoiceOversAI(status)
-self.xtVoiceOversAI=status
-return self
-end
-function AIRBOSS:SetHandleAIOFF()
-self.handleai=false
-return self
-end
-function AIRBOSS:SetRecoveryTanker(recoverytanker)
-self.tanker=recoverytanker
-return self
-end
-function AIRBOSS:SetAWACS(awacs)
-self.awacs=awacs
-return self
-end
-function AIRBOSS:SetDefaultPlayerSkill(skill)
-self.defaultskill=skill or AIRBOSS.Difficulty.NORMAL
-local gotit=false
-for _,_skill in pairs(AIRBOSS.Difficulty)do
-if _skill==self.defaultskill then
-gotit=true
-end
-end
-if not gotit then
-self.defaultskill=AIRBOSS.Difficulty.NORMAL
-self:E(self.lid..string.format("ERROR: Invalid default skill = %s. Resetting to Naval Aviator.",tostring(skill)))
-end
-return self
-end
-function AIRBOSS:SetAutoSave(path,filename)
-self.autosave=true
-self.autosavepath=path
-self.autosavefile=filename
-return self
-end
-function AIRBOSS:SetDebugModeON()
-self.Debug=true
-return self
-end
-function AIRBOSS:SetPatrolAdInfinitum(switch)
-if switch==false then
-self.adinfinitum=false
-else
-self.adinfinitum=true
-end
-return self
-end
-function AIRBOSS:SetMagneticDeclination(declination)
-self.magvar=declination or UTILS.GetMagneticDeclination()
-return self
-end
-function AIRBOSS:SetDebugModeOFF()
-self.Debug=false
-return self
-end
-function AIRBOSS:SetFunkManOn(Port,Host)
-self.funkmanSocket=SOCKET:New(Port,Host)
-return self
-end
-function AIRBOSS:GetNextRecoveryTime(InSeconds)
-if self.recoverywindow then
-if InSeconds then
-return self.recoverywindow.START,self.recoverywindow.STOP
-else
-return UTILS.SecondsToClock(self.recoverywindow.START),UTILS.SecondsToClock(self.recoverywindow.STOP)
-end
-else
-if InSeconds then
-return-1,-1
-else
-return"?","?"
-end
-end
-end
-function AIRBOSS:IsRecovering()
-return self:is("Recovering")
-end
-function AIRBOSS:IsIdle()
-return self:is("Idle")
-end
-function AIRBOSS:IsPaused()
-return self:is("Paused")
-end
-function AIRBOSS:_ActivateBeacons()
-self:T(self.lid..string.format("Activating Beacons (TACAN=%s, ICLS=%s)",tostring(self.TACANon),tostring(self.ICLSon)))
-if self.TACANon then
-self:I(self.lid..string.format("Activating TACAN Channel %d%s (%s)",self.TACANchannel,self.TACANmode,self.TACANmorse))
-self.beacon:ActivateTACAN(self.TACANchannel,self.TACANmode,self.TACANmorse,true)
-end
-if self.ICLSon then
-self:I(self.lid..string.format("Activating ICLS Channel %d (%s)",self.ICLSchannel,self.ICLSmorse))
-self.beacon:ActivateICLS(self.ICLSchannel,self.ICLSmorse)
-end
-self.Tbeacon=timer.getTime()
-end
-function AIRBOSS:onafterStart(From,Event,To)
-self:I(self.lid..string.format("Starting AIRBOSS v%s for carrier unit %s of type %s on map %s",AIRBOSS.version,self.carrier:GetName(),self.carriertype,self.theatre))
-self:_ActivateBeacons()
-self.Cposition=self:GetCoordinate()
-self.Corientation=self.carrier:GetOrientationX()
-self.Corientlast=self.Corientation
-self.Tpupdate=timer.getTime()
-if#self.recoverytimes==0 and false then
-local Topen=timer.getAbsTime()+15*60
-local Tclose=Topen+3*60*60
-self:AddRecoveryWindow(UTILS.SecondsToClock(Topen),UTILS.SecondsToClock(Tclose))
-end
-self:_CheckRecoveryTimes()
-self.Tqueue=timer.getTime()-60
-self:HandleEvent(EVENTS.Birth)
-self:HandleEvent(EVENTS.Land)
-self:HandleEvent(EVENTS.EngineShutdown)
-self:HandleEvent(EVENTS.Takeoff)
-self:HandleEvent(EVENTS.Crash)
-self:HandleEvent(EVENTS.Ejection)
-self:HandleEvent(EVENTS.PlayerLeaveUnit,self._PlayerLeft)
-self:HandleEvent(EVENTS.MissionEnd)
-self:HandleEvent(EVENTS.RemoveUnit)
-self.StatusTimer=TIMER:New(self._Status,self):Start(2,0.5)
-self:__Status(1)
-end
-function AIRBOSS:onafterStatus(From,Event,To)
-local time=timer.getTime()
-if time-self.Tqueue>self.dTqueue then
-local clock=UTILS.SecondsToClock(timer.getAbsTime())
-local eta=UTILS.SecondsToClock(self:_GetETAatNextWP())
-local hdg=self:GetHeading()
-local pos=self:GetCoordinate()
-local speed=self.carrier:GetVelocityKNOTS()
-local collision=false
-local holdtime=0
-if self.holdtimestamp then
-holdtime=timer.getTime()-self.holdtimestamp
-end
-local NextWP=self:_GetNextWaypoint()
-local ExpectedSpeed=UTILS.MpsToKnots(NextWP:GetVelocity())
-if speed<0.5 and ExpectedSpeed>0 and not(self.detour or self.turnintowind)then
-if not self.holdtimestamp then
-self:E(self.lid..string.format("Carrier came to an unexpected standstill. Trying to re-route in 3 min. Speed=%.1f knots, expected=%.1f knots",speed,ExpectedSpeed))
-self.holdtimestamp=timer.getTime()
-else
-if holdtime>3*60 then
-local coord=self:GetCoordinate():Translate(500,hdg+10)
-self:CarrierResumeRoute(coord)
-self.holdtimestamp=nil
-end
-end
-end
-local text=string.format("Time %s - Status %s (case=%d) - Speed=%.1f kts - Heading=%d - WP=%d - ETA=%s - Turning=%s - Collision Warning=%s - Detour=%s - Turn Into Wind=%s - Holdtime=%d sec",clock,self:GetState(),self.case,speed,hdg,self.currentwp,eta,tostring(self.turning),tostring(collision),tostring(self.detour),tostring(self.turnintowind),holdtime)
-self:T(self.lid..text)
-text="Players:"
-local i=0
-for _name,_player in pairs(self.players)do
-i=i+1
-local player=_player
-text=text..string.format("\n%d.) %s: Step=%s, Unit=%s, Airframe=%s",i,tostring(player.name),tostring(player.step),tostring(player.unitname),tostring(player.actype))
-end
-if i==0 then
-text=text.." none"
-end
-self:T(self.lid..text)
-if collision then
-if self.turnintowind then
-self:CarrierResumeRoute(self.Creturnto)
-if self:IsRecovering()and self.recoverywindow and self.recoverywindow.WIND then
-self.recoverywindow.WIND=false
-end
-end
-end
-self:_CheckRecoveryTimes()
-self:_ScanCarrierZone()
-self:_CheckQueue()
-self:_CheckCarrierTurning()
-self:_CheckPatternUpdate()
-self.Tqueue=time
-end
-if time-self.Tbeacon>self.dTbeacon then
-self:_ActivateBeacons()
-end
-self:__Status(-30)
-end
-function AIRBOSS:_Status()
-self:_CheckPlayerStatus()
-self:_CheckAIStatus()
-end
-function AIRBOSS:_CheckAIStatus()
-for _,_flight in pairs(self.Qmarshal)do
-local flight=_flight
-if flight.ai then
-local fuel=flight.group:GetFuelMin()*100
-local text=string.format("Group %s fuel=%.1f %%",flight.groupname,fuel)
-self:T3(self.lid..text)
-if self.lowfuelAI and fuel=recovery.START then
-if time0 then
-local extmin=5*npattern
-recovery.STOP=recovery.STOP+extmin*60
-local text=string.format("We still got flights in the pattern.\nRecovery time prolonged by %d minutes.\nNow get your act together and no more bolters!",extmin)
-self:MessageToPattern(text,"AIRBOSS","99",10,false,nil)
-else
-self:RecoveryStop()
-state="closing now"
-recovery.OPEN=false
-recovery.OVER=true
-end
-else
-state="closed"
-end
-end
-else
-state="in the future"
-if nextwindow==nil then
-nextwindow=recovery
-state="next in line"
-end
-end
-text=text..string.format("\n- Start=%s Stop=%s Case=%d Offset=%d Open=%s Closed=%s Status=\"%s\"",Cstart,Cstop,recovery.CASE,recovery.OFFSET,tostring(recovery.OPEN),tostring(recovery.OVER),state)
-end
-self:T(self.lid..text)
-self.recoverywindow=nil
-if self:IsIdle()then
-if nextwindow then
-self:RecoveryCase(nextwindow.CASE,nextwindow.OFFSET)
-if nextwindow.WIND and nextwindow.START-time5
-local _,vwind=self:GetWind()
-if vwind<0.1 then
-uturn=false
-end
-if not nextwindow.UTURN then
-uturn=false
-end
-self:T(self.lid..string.format("Heading=%03d°, Wind=%03d° %.1f kts, Delta=%03d° ==> U-turn=%s",hdg,wind,UTILS.MpsToKnots(vwind),delta,tostring(uturn)))
-local t=math.max(nextwindow.STOP-nextwindow.START+self.dTturn,60*60*24)
-local v=UTILS.KnotsToMps(nextwindow.SPEED)
-local vmax=self.carrier:GetSpeedMax()/3.6
-v=math.min(v,vmax)
-self:CarrierTurnIntoWind(t,v,uturn)
-end
-self.recoverywindow=nextwindow
-else
-self:RecoveryCase()
-end
-else
-if currwindow then
-self.recoverywindow=currwindow
-else
-self.recoverywindow=nextwindow
-end
-end
-self:T2({"FF",recoverywindow=self.recoverywindow})
-end
-function AIRBOSS:_GetFlightLead(flight)
-if flight.name~=flight.seclead then
-local lead=self.players[flight.seclead]
-return lead,false
-else
-return flight,true
-end
-end
-function AIRBOSS:onbeforeRecoveryCase(From,Event,To,Case,Offset)
-Case=Case or self.defaultcase
-Offset=Offset or self.defaultoffset
-if Case==self.case and Offset==self.holdingoffset then
-return false
-end
-return true
-end
-function AIRBOSS:onafterRecoveryCase(From,Event,To,Case,Offset)
-Case=Case or self.defaultcase
-Offset=Offset or self.defaultoffset
-local text=string.format("Switching recovery case %d ==> %d",self.case,Case)
-if Case>1 then
-text=text..string.format(" Holding offset angle %d degrees.",Offset)
-end
-MESSAGE:New(text,20,self.alias):ToAllIf(self.Debug)
-self:T(self.lid..text)
-self.case=Case
-self.holdingoffset=Offset
-for _,_flight in pairs(self.flights)do
-local flight=_flight
-if not(self:_InQueue(self.Qmarshal,flight.group)or self:_InQueue(self.Qpattern,flight.group))then
-if flight.name~=flight.seclead then
-local lead=self.players[flight.seclead]
-if lead and not(self:_InQueue(self.Qmarshal,lead.group)or self:_InQueue(self.Qpattern,lead.group))then
-flight.case=self.case
-end
-else
-flight.case=self.case
-end
-end
-end
-end
-function AIRBOSS:onafterRecoveryStart(From,Event,To,Case,Offset)
-Case=Case or self.defaultcase
-Offset=Offset or self.defaultoffset
-self:_MarshalCallRecoveryStart(Case)
-self:RecoveryCase(Case,Offset)
-end
-function AIRBOSS:onafterRecoveryStop(From,Event,To)
-self:T(self.lid..string.format("Stopping aircraft recovery."))
-self:_MarshalCallRecoveryStopped(self.case)
-if self.turnintowind then
-local coord=self.Creturnto
-if self.recoverywindow and self.recoverywindow.UTURN==false then
-coord=nil
-end
-self:CarrierResumeRoute(coord)
-end
-if self.recoverywindow and self.recoverywindow.OPEN==true then
-self.recoverywindow.OPEN=false
-self.recoverywindow.OVER=true
-self:DeleteRecoveryWindow(self.recoverywindow)
-end
-self:_CheckRecoveryTimes()
-end
-function AIRBOSS:onafterRecoveryPause(From,Event,To,duration)
-self:T(self.lid..string.format("Pausing aircraft recovery."))
-if duration then
-self:__RecoveryUnpause(duration)
-local clock=UTILS.SecondsToClock(timer.getAbsTime()+duration)
-self:_MarshalCallRecoveryPausedResumedAt(clock)
-else
-local text=string.format("aircraft recovery is paused until further notice.")
-self:_MarshalCallRecoveryPausedNotice()
-end
-end
-function AIRBOSS:onafterRecoveryUnpause(From,Event,To)
-self:T(self.lid..string.format("Unpausing aircraft recovery."))
-self:_MarshalCallResumeRecovery()
-end
-function AIRBOSS:onafterPassingWaypoint(From,Event,To,n)
-self:I(self.lid..string.format("Carrier passed waypoint %d.",n))
-end
-function AIRBOSS:onafterIdle(From,Event,To)
-self:T(self.lid..string.format("Carrier goes to idle."))
-end
-function AIRBOSS:onafterStop(From,Event,To)
-self:I(self.lid..string.format("Stopping airboss script."))
-self:UnHandleEvent(EVENTS.Birth)
-self:UnHandleEvent(EVENTS.Land)
-self:UnHandleEvent(EVENTS.EngineShutdown)
-self:UnHandleEvent(EVENTS.Takeoff)
-self:UnHandleEvent(EVENTS.Crash)
-self:UnHandleEvent(EVENTS.Ejection)
-self:UnHandleEvent(EVENTS.PlayerLeaveUnit)
-self:UnHandleEvent(EVENTS.MissionEnd)
-self.CallScheduler:Clear()
-end
-function AIRBOSS:_InitStennis()
-self.carrierparam.sterndist=-153
-self.carrierparam.deckheight=18.30
-self.carrierparam.totlength=310
-self.carrierparam.totwidthport=40
-self.carrierparam.totwidthstarboard=30
-self.carrierparam.rwyangle=-9.1359
-self.carrierparam.rwylength=225
-self.carrierparam.rwywidth=20
-self.carrierparam.wire1=46
-self.carrierparam.wire2=46+12
-self.carrierparam.wire3=46+24
-self.carrierparam.wire4=46+35
-self.carrierparam.landingdist=self.carrierparam.sterndist+self.carrierparam.wire3
-self.Platform.name="Platform 5k"
-self.Platform.Xmin=-UTILS.NMToMeters(22)
-self.Platform.Xmax=nil
-self.Platform.Zmin=-UTILS.NMToMeters(30)
-self.Platform.Zmax=UTILS.NMToMeters(30)
-self.Platform.LimitXmin=nil
-self.Platform.LimitXmax=nil
-self.Platform.LimitZmin=nil
-self.Platform.LimitZmax=nil
-self.DirtyUp.name="Dirty Up"
-self.DirtyUp.Xmin=-UTILS.NMToMeters(21)
-self.DirtyUp.Xmax=nil
-self.DirtyUp.Zmin=-UTILS.NMToMeters(30)
-self.DirtyUp.Zmax=UTILS.NMToMeters(30)
-self.DirtyUp.LimitXmin=nil
-self.DirtyUp.LimitXmax=nil
-self.DirtyUp.LimitZmin=nil
-self.DirtyUp.LimitZmax=nil
-self.Bullseye.name="Bullseye"
-self.Bullseye.Xmin=-UTILS.NMToMeters(11)
-self.Bullseye.Xmax=nil
-self.Bullseye.Zmin=-UTILS.NMToMeters(30)
-self.Bullseye.Zmax=UTILS.NMToMeters(30)
-self.Bullseye.LimitXmin=nil
-self.Bullseye.LimitXmax=nil
-self.Bullseye.LimitZmin=nil
-self.Bullseye.LimitZmax=nil
-self.BreakEntry.name="Break Entry"
-self.BreakEntry.Xmin=-UTILS.NMToMeters(4)
-self.BreakEntry.Xmax=nil
-self.BreakEntry.Zmin=-UTILS.NMToMeters(0.5)
-self.BreakEntry.Zmax=UTILS.NMToMeters(1.5)
-self.BreakEntry.LimitXmin=0
-self.BreakEntry.LimitXmax=nil
-self.BreakEntry.LimitZmin=nil
-self.BreakEntry.LimitZmax=nil
-self.BreakEarly.name="Early Break"
-self.BreakEarly.Xmin=-UTILS.NMToMeters(1)
-self.BreakEarly.Xmax=UTILS.NMToMeters(5)
-self.BreakEarly.Zmin=-UTILS.NMToMeters(2)
-self.BreakEarly.Zmax=UTILS.NMToMeters(1)
-self.BreakEarly.LimitXmin=0
-self.BreakEarly.LimitXmax=nil
-self.BreakEarly.LimitZmin=-UTILS.NMToMeters(0.2)
-self.BreakEarly.LimitZmax=nil
-self.BreakLate.name="Late Break"
-self.BreakLate.Xmin=-UTILS.NMToMeters(1)
-self.BreakLate.Xmax=UTILS.NMToMeters(5)
-self.BreakLate.Zmin=-UTILS.NMToMeters(2)
-self.BreakLate.Zmax=UTILS.NMToMeters(1)
-self.BreakLate.LimitXmin=0
-self.BreakLate.LimitXmax=nil
-self.BreakLate.LimitZmin=-UTILS.NMToMeters(0.8)
-self.BreakLate.LimitZmax=nil
-self.Abeam.name="Abeam Position"
-self.Abeam.Xmin=-UTILS.NMToMeters(5)
-self.Abeam.Xmax=UTILS.NMToMeters(5)
-self.Abeam.Zmin=-UTILS.NMToMeters(2)
-self.Abeam.Zmax=500
-self.Abeam.LimitXmin=-200
-self.Abeam.LimitXmax=nil
-self.Abeam.LimitZmin=nil
-self.Abeam.LimitZmax=nil
-self.Ninety.name="Ninety"
-self.Ninety.Xmin=-UTILS.NMToMeters(4)
-self.Ninety.Xmax=0
-self.Ninety.Zmin=-UTILS.NMToMeters(2)
-self.Ninety.Zmax=nil
-self.Ninety.LimitXmin=nil
-self.Ninety.LimitXmax=nil
-self.Ninety.LimitZmin=nil
-self.Ninety.LimitZmax=-UTILS.NMToMeters(0.6)
-self.Wake.name="Wake"
-self.Wake.Xmin=-UTILS.NMToMeters(4)
-self.Wake.Xmax=0
-self.Wake.Zmin=-2000
-self.Wake.Zmax=nil
-self.Wake.LimitXmin=nil
-self.Wake.LimitXmax=nil
-self.Wake.LimitZmin=0
-self.Wake.LimitZmax=nil
-self.Final.name="Final"
-self.Final.Xmin=-UTILS.NMToMeters(4)
-self.Final.Xmax=0
-self.Final.Zmin=-2000
-self.Final.Zmax=nil
-self.Final.LimitXmin=nil
-self.Final.LimitXmax=nil
-self.Final.LimitZmin=nil
-self.Final.LimitZmax=nil
-self.Groove.name="Groove"
-self.Groove.Xmin=-UTILS.NMToMeters(4)
-self.Groove.Xmax=nil
-self.Groove.Zmin=-UTILS.NMToMeters(2)
-self.Groove.Zmax=UTILS.NMToMeters(2)
-self.Groove.LimitXmin=nil
-self.Groove.LimitXmax=nil
-self.Groove.LimitZmin=nil
-self.Groove.LimitZmax=nil
-end
-function AIRBOSS:_InitNimitz()
-self:_InitStennis()
-self.carrierparam.sterndist=-164
-self.carrierparam.deckheight=20.1494
-self.carrierparam.totlength=332.8
-self.carrierparam.totwidthport=45
-self.carrierparam.totwidthstarboard=35
-self.carrierparam.rwyangle=-9.1359
-self.carrierparam.rwylength=250
-self.carrierparam.rwywidth=25
-self.carrierparam.wire1=55
-self.carrierparam.wire2=67
-self.carrierparam.wire3=79
-self.carrierparam.wire4=92
-self.carrierparam.landingdist=self.carrierparam.sterndist+self.carrierparam.wire3
-end
-function AIRBOSS:_InitForrestal()
-self:_InitNimitz()
-self.carrierparam.sterndist=-135.5
-self.carrierparam.deckheight=20
-self.carrierparam.totlength=315
-self.carrierparam.totwidthport=45
-self.carrierparam.totwidthstarboard=35
-self.carrierparam.rwyangle=-9.1359
-self.carrierparam.rwylength=212
-self.carrierparam.rwywidth=25
-self.carrierparam.wire1=44
-self.carrierparam.wire2=54
-self.carrierparam.wire3=64
-self.carrierparam.wire4=74
-self.carrierparam.landingdist=self.carrierparam.sterndist+self.carrierparam.wire3
-end
-function AIRBOSS:_InitHermes()
-self:_InitStennis()
-self.carrierparam.sterndist=-105
-self.carrierparam.deckheight=12
-self.carrierparam.totlength=228.19
-self.carrierparam.totwidthport=20.5
-self.carrierparam.totwidthstarboard=24.5
-self.carrierparam.rwyangle=0
-self.carrierparam.rwylength=215
-self.carrierparam.rwywidth=13
-self.carrierparam.wire1=nil
-self.carrierparam.wire2=nil
-self.carrierparam.wire3=nil
-self.carrierparam.wire4=nil
-self.carrierparam.landingspot=69
-self.carrierparam.landingdist=self.carrierparam.sterndist+self.carrierparam.landingspot
-self.BreakLate.name="Late Break"
-self.BreakLate.Xmin=-UTILS.NMToMeters(1)
-self.BreakLate.Xmax=UTILS.NMToMeters(5)
-self.BreakLate.Zmin=-UTILS.NMToMeters(1.6)
-self.BreakLate.Zmax=UTILS.NMToMeters(1)
-self.BreakLate.LimitXmin=0
-self.BreakLate.LimitXmax=nil
-self.BreakLate.LimitZmin=-UTILS.NMToMeters(0.5)
-self.BreakLate.LimitZmax=nil
-end
-function AIRBOSS:_InitInvincible()
-self:_InitStennis()
-self.carrierparam.sterndist=-105
-self.carrierparam.deckheight=12
-self.carrierparam.totlength=228.19
-self.carrierparam.totwidthport=20.5
-self.carrierparam.totwidthstarboard=24.5
-self.carrierparam.rwyangle=0
-self.carrierparam.rwylength=215
-self.carrierparam.rwywidth=13
-self.carrierparam.wire1=nil
-self.carrierparam.wire2=nil
-self.carrierparam.wire3=nil
-self.carrierparam.wire4=nil
-self.carrierparam.landingspot=69
-self.carrierparam.landingdist=self.carrierparam.sterndist+self.carrierparam.landingspot
-self.BreakLate.name="Late Break"
-self.BreakLate.Xmin=-UTILS.NMToMeters(1)
-self.BreakLate.Xmax=UTILS.NMToMeters(5)
-self.BreakLate.Zmin=-UTILS.NMToMeters(1.6)
-self.BreakLate.Zmax=UTILS.NMToMeters(1)
-self.BreakLate.LimitXmin=0
-self.BreakLate.LimitXmax=nil
-self.BreakLate.LimitZmin=-UTILS.NMToMeters(0.5)
-self.BreakLate.LimitZmax=nil
-end
-function AIRBOSS:_InitTarawa()
-self:_InitStennis()
-self.carrierparam.sterndist=-125
-self.carrierparam.deckheight=21
-self.carrierparam.totlength=245
-self.carrierparam.totwidthport=10
-self.carrierparam.totwidthstarboard=25
-self.carrierparam.rwyangle=0
-self.carrierparam.rwylength=225
-self.carrierparam.rwywidth=15
-self.carrierparam.wire1=nil
-self.carrierparam.wire2=nil
-self.carrierparam.wire3=nil
-self.carrierparam.wire4=nil
-self.carrierparam.landingspot=57
-self.carrierparam.landingdist=self.carrierparam.sterndist+self.carrierparam.landingspot
-self.BreakLate.name="Late Break"
-self.BreakLate.Xmin=-UTILS.NMToMeters(1)
-self.BreakLate.Xmax=UTILS.NMToMeters(5)
-self.BreakLate.Zmin=-UTILS.NMToMeters(1.6)
-self.BreakLate.Zmax=UTILS.NMToMeters(1)
-self.BreakLate.LimitXmin=0
-self.BreakLate.LimitXmax=nil
-self.BreakLate.LimitZmin=-UTILS.NMToMeters(0.5)
-self.BreakLate.LimitZmax=nil
-end
-function AIRBOSS:_InitAmerica()
-self:_InitStennis()
-self.carrierparam.sterndist=-125
-self.carrierparam.deckheight=20
-self.carrierparam.totlength=257
-self.carrierparam.totwidthport=11
-self.carrierparam.totwidthstarboard=25
-self.carrierparam.rwyangle=0
-self.carrierparam.rwylength=240
-self.carrierparam.rwywidth=15
-self.carrierparam.wire1=nil
-self.carrierparam.wire2=nil
-self.carrierparam.wire3=nil
-self.carrierparam.wire4=nil
-self.carrierparam.landingspot=59
-self.carrierparam.landingdist=self.carrierparam.sterndist+self.carrierparam.landingspot
-self.BreakLate.name="Late Break"
-self.BreakLate.Xmin=-UTILS.NMToMeters(1)
-self.BreakLate.Xmax=UTILS.NMToMeters(5)
-self.BreakLate.Zmin=-UTILS.NMToMeters(1.6)
-self.BreakLate.Zmax=UTILS.NMToMeters(1)
-self.BreakLate.LimitXmin=0
-self.BreakLate.LimitXmax=nil
-self.BreakLate.LimitZmin=-UTILS.NMToMeters(0.5)
-self.BreakLate.LimitZmax=nil
-end
-function AIRBOSS:_InitJcarlos()
-self:_InitStennis()
-self.carrierparam.sterndist=-125
-self.carrierparam.deckheight=20
-self.carrierparam.totlength=231
-self.carrierparam.totwidthport=10
-self.carrierparam.totwidthstarboard=22
-self.carrierparam.rwyangle=0
-self.carrierparam.rwylength=202
-self.carrierparam.rwywidth=14
-self.carrierparam.wire1=nil
-self.carrierparam.wire2=nil
-self.carrierparam.wire3=nil
-self.carrierparam.wire4=nil
-self.carrierparam.landingspot=89
-self.carrierparam.landingdist=self.carrierparam.sterndist+self.carrierparam.landingspot
-self.BreakLate.name="Late Break"
-self.BreakLate.Xmin=-UTILS.NMToMeters(1)
-self.BreakLate.Xmax=UTILS.NMToMeters(5)
-self.BreakLate.Zmin=-UTILS.NMToMeters(1.6)
-self.BreakLate.Zmax=UTILS.NMToMeters(1)
-self.BreakLate.LimitXmin=0
-self.BreakLate.LimitXmax=nil
-self.BreakLate.LimitZmin=-UTILS.NMToMeters(0.5)
-self.BreakLate.LimitZmax=nil
-end
-function AIRBOSS:_InitCanberra()
-self:_InitJcarlos()
-end
-function AIRBOSS:SetVoiceOversMarshalByGabriella(mizfolder)
-if mizfolder then
-local lastchar=string.sub(mizfolder,-1)
-if lastchar~="/"then
-mizfolder=mizfolder.."/"
-end
-self.soundfolderMSH=mizfolder
-else
-self.soundfolderMSH=self.soundfolder
-end
-self:I(self.lid..string.format("Marshal Gabriella reporting for duty! Soundfolder=%s",tostring(self.soundfolderMSH)))
-self.MarshalCall.AFFIRMATIVE.duration=0.65
-self.MarshalCall.ALTIMETER.duration=0.60
-self.MarshalCall.BRC.duration=0.67
-self.MarshalCall.CARRIERTURNTOHEADING.duration=1.62
-self.MarshalCall.CASE.duration=0.30
-self.MarshalCall.CHARLIETIME.duration=0.77
-self.MarshalCall.CLEAREDFORRECOVERY.duration=0.93
-self.MarshalCall.DECKCLOSED.duration=0.73
-self.MarshalCall.DEGREES.duration=0.48
-self.MarshalCall.EXPECTED.duration=0.50
-self.MarshalCall.FLYNEEDLES.duration=0.89
-self.MarshalCall.HOLDATANGELS.duration=0.81
-self.MarshalCall.HOURS.duration=0.41
-self.MarshalCall.MARSHALRADIAL.duration=0.95
-self.MarshalCall.N0.duration=0.41
-self.MarshalCall.N1.duration=0.30
-self.MarshalCall.N2.duration=0.34
-self.MarshalCall.N3.duration=0.31
-self.MarshalCall.N4.duration=0.34
-self.MarshalCall.N5.duration=0.30
-self.MarshalCall.N6.duration=0.33
-self.MarshalCall.N7.duration=0.38
-self.MarshalCall.N8.duration=0.35
-self.MarshalCall.N9.duration=0.35
-self.MarshalCall.NEGATIVE.duration=0.60
-self.MarshalCall.NEWFB.duration=0.95
-self.MarshalCall.OPS.duration=0.23
-self.MarshalCall.POINT.duration=0.38
-self.MarshalCall.RADIOCHECK.duration=1.27
-self.MarshalCall.RECOVERY.duration=0.60
-self.MarshalCall.RECOVERYOPSSTOPPED.duration=1.25
-self.MarshalCall.RECOVERYPAUSEDNOTICE.duration=2.55
-self.MarshalCall.RECOVERYPAUSEDRESUMED.duration=2.55
-self.MarshalCall.REPORTSEEME.duration=0.87
-self.MarshalCall.RESUMERECOVERY.duration=1.55
-self.MarshalCall.ROGER.duration=0.50
-self.MarshalCall.SAYNEEDLES.duration=0.82
-self.MarshalCall.STACKFULL.duration=5.70
-self.MarshalCall.STARTINGRECOVERY.duration=1.61
-end
-function AIRBOSS:SetVoiceOversMarshalByRaynor(mizfolder)
-if mizfolder then
-local lastchar=string.sub(mizfolder,-1)
-if lastchar~="/"then
-mizfolder=mizfolder.."/"
-end
-self.soundfolderMSH=mizfolder
-else
-self.soundfolderMSH=self.soundfolder
-end
-self:I(self.lid..string.format("Marshal Raynor reporting for duty! Soundfolder=%s",tostring(self.soundfolderMSH)))
-self.MarshalCall.AFFIRMATIVE.duration=0.70
-self.MarshalCall.ALTIMETER.duration=0.60
-self.MarshalCall.BRC.duration=0.60
-self.MarshalCall.CARRIERTURNTOHEADING.duration=1.87
-self.MarshalCall.CASE.duration=0.60
-self.MarshalCall.CHARLIETIME.duration=0.81
-self.MarshalCall.CLEAREDFORRECOVERY.duration=1.21
-self.MarshalCall.DECKCLOSED.duration=0.86
-self.MarshalCall.DEGREES.duration=0.55
-self.MarshalCall.EXPECTED.duration=0.61
-self.MarshalCall.FLYNEEDLES.duration=0.90
-self.MarshalCall.HOLDATANGELS.duration=0.91
-self.MarshalCall.HOURS.duration=0.54
-self.MarshalCall.MARSHALRADIAL.duration=0.80
-self.MarshalCall.N0.duration=0.38
-self.MarshalCall.N1.duration=0.30
-self.MarshalCall.N2.duration=0.30
-self.MarshalCall.N3.duration=0.30
-self.MarshalCall.N4.duration=0.32
-self.MarshalCall.N5.duration=0.41
-self.MarshalCall.N6.duration=0.48
-self.MarshalCall.N7.duration=0.51
-self.MarshalCall.N8.duration=0.38
-self.MarshalCall.N9.duration=0.34
-self.MarshalCall.NEGATIVE.duration=0.60
-self.MarshalCall.NEWFB.duration=1.10
-self.MarshalCall.OPS.duration=0.46
-self.MarshalCall.POINT.duration=0.21
-self.MarshalCall.RADIOCHECK.duration=0.95
-self.MarshalCall.RECOVERY.duration=0.63
-self.MarshalCall.RECOVERYOPSSTOPPED.duration=1.36
-self.MarshalCall.RECOVERYPAUSEDNOTICE.duration=2.8
-self.MarshalCall.RECOVERYPAUSEDRESUMED.duration=2.75
-self.MarshalCall.REPORTSEEME.duration=1.06
-self.MarshalCall.RESUMERECOVERY.duration=1.41
-self.MarshalCall.ROGER.duration=0.41
-self.MarshalCall.SAYNEEDLES.duration=0.79
-self.MarshalCall.STACKFULL.duration=4.70
-self.MarshalCall.STARTINGRECOVERY.duration=2.06
-end
-function AIRBOSS:SetVoiceOversLSOByRaynor(mizfolder)
-if mizfolder then
-local lastchar=string.sub(mizfolder,-1)
-if lastchar~="/"then
-mizfolder=mizfolder.."/"
-end
-self.soundfolderLSO=mizfolder
-else
-self.soundfolderLSO=self.soundfolder
-end
-self:I(self.lid..string.format("LSO Raynor reporting for duty! Soundfolder=%s",tostring(self.soundfolderLSO)))
-self.LSOCall.BOLTER.duration=0.75
-self.LSOCall.CALLTHEBALL.duration=0.625
-self.LSOCall.CHECK.duration=0.40
-self.LSOCall.CLEAREDTOLAND.duration=0.85
-self.LSOCall.COMELEFT.duration=0.60
-self.LSOCall.DEPARTANDREENTER.duration=1.10
-self.LSOCall.EXPECTHEAVYWAVEOFF.duration=1.30
-self.LSOCall.EXPECTSPOT75.duration=1.85
-self.LSOCall.EXPECTSPOT5.duration=1.3
-self.LSOCall.FAST.duration=0.75
-self.LSOCall.FOULDECK.duration=0.75
-self.LSOCall.HIGH.duration=0.65
-self.LSOCall.IDLE.duration=0.40
-self.LSOCall.LONGINGROOVE.duration=1.25
-self.LSOCall.LOW.duration=0.60
-self.LSOCall.N0.duration=0.38
-self.LSOCall.N1.duration=0.30
-self.LSOCall.N2.duration=0.30
-self.LSOCall.N3.duration=0.30
-self.LSOCall.N4.duration=0.32
-self.LSOCall.N5.duration=0.41
-self.LSOCall.N6.duration=0.48
-self.LSOCall.N7.duration=0.51
-self.LSOCall.N8.duration=0.38
-self.LSOCall.N9.duration=0.34
-self.LSOCall.PADDLESCONTACT.duration=0.91
-self.LSOCall.POWER.duration=0.45
-self.LSOCall.RADIOCHECK.duration=0.90
-self.LSOCall.RIGHTFORLINEUP.duration=0.70
-self.LSOCall.ROGERBALL.duration=0.72
-self.LSOCall.SLOW.duration=0.63
-self.LSOCall.STABILIZED.duration=0.75
-self.LSOCall.WAVEOFF.duration=0.55
-self.LSOCall.WELCOMEABOARD.duration=0.80
-end
-function AIRBOSS:SetVoiceOversLSOByFF(mizfolder)
-if mizfolder then
-local lastchar=string.sub(mizfolder,-1)
-if lastchar~="/"then
-mizfolder=mizfolder.."/"
-end
-self.soundfolderLSO=mizfolder
-else
-self.soundfolderLSO=self.soundfolder
-end
-self:I(self.lid..string.format("LSO FF reporting for duty! Soundfolder=%s",tostring(self.soundfolderLSO)))
-self.LSOCall.BOLTER.duration=0.75
-self.LSOCall.CALLTHEBALL.duration=0.60
-self.LSOCall.CHECK.duration=0.45
-self.LSOCall.CLEAREDTOLAND.duration=1.00
-self.LSOCall.COMELEFT.duration=0.60
-self.LSOCall.DEPARTANDREENTER.duration=1.10
-self.LSOCall.EXPECTHEAVYWAVEOFF.duration=1.20
-self.LSOCall.EXPECTSPOT75.duration=2.00
-self.LSOCall.EXPECTSPOT5.duration=1.3
-self.LSOCall.FAST.duration=0.70
-self.LSOCall.FOULDECK.duration=0.62
-self.LSOCall.HIGH.duration=0.65
-self.LSOCall.IDLE.duration=0.45
-self.LSOCall.LONGINGROOVE.duration=1.20
-self.LSOCall.LOW.duration=0.50
-self.LSOCall.N0.duration=0.40
-self.LSOCall.N1.duration=0.25
-self.LSOCall.N2.duration=0.37
-self.LSOCall.N3.duration=0.37
-self.LSOCall.N4.duration=0.39
-self.LSOCall.N5.duration=0.39
-self.LSOCall.N6.duration=0.40
-self.LSOCall.N7.duration=0.40
-self.LSOCall.N8.duration=0.37
-self.LSOCall.N9.duration=0.40
-self.LSOCall.PADDLESCONTACT.duration=1.00
-self.LSOCall.POWER.duration=0.50
-self.LSOCall.RADIOCHECK.duration=1.10
-self.LSOCall.RIGHTFORLINEUP.duration=0.80
-self.LSOCall.ROGERBALL.duration=1.00
-self.LSOCall.SLOW.duration=0.65
-self.LSOCall.SLOW.duration=0.59
-self.LSOCall.STABILIZED.duration=0.90
-self.LSOCall.WAVEOFF.duration=0.60
-self.LSOCall.WELCOMEABOARD.duration=1.00
-end
-function AIRBOSS:SetVoiceOversMarshalByFF(mizfolder)
-if mizfolder then
-local lastchar=string.sub(mizfolder,-1)
-if lastchar~="/"then
-mizfolder=mizfolder.."/"
-end
-self.soundfolderMSH=mizfolder
-else
-self.soundfolderMSH=self.soundfolder
-end
-self:I(self.lid..string.format("Marshal FF reporting for duty! Soundfolder=%s",tostring(self.soundfolderMSH)))
-self.MarshalCall.AFFIRMATIVE.duration=0.90
-self.MarshalCall.ALTIMETER.duration=0.85
-self.MarshalCall.BRC.duration=0.80
-self.MarshalCall.CARRIERTURNTOHEADING.duration=2.48
-self.MarshalCall.CASE.duration=0.40
-self.MarshalCall.CHARLIETIME.duration=0.90
-self.MarshalCall.CLEAREDFORRECOVERY.duration=1.25
-self.MarshalCall.DECKCLOSED.duration=1.10
-self.MarshalCall.DEGREES.duration=0.60
-self.MarshalCall.EXPECTED.duration=0.55
-self.MarshalCall.FLYNEEDLES.duration=0.90
-self.MarshalCall.HOLDATANGELS.duration=1.10
-self.MarshalCall.HOURS.duration=0.60
-self.MarshalCall.MARSHALRADIAL.duration=1.10
-self.MarshalCall.N0.duration=0.40
-self.MarshalCall.N1.duration=0.25
-self.MarshalCall.N2.duration=0.37
-self.MarshalCall.N3.duration=0.37
-self.MarshalCall.N4.duration=0.39
-self.MarshalCall.N5.duration=0.39
-self.MarshalCall.N6.duration=0.40
-self.MarshalCall.N7.duration=0.40
-self.MarshalCall.N8.duration=0.37
-self.MarshalCall.N9.duration=0.40
-self.MarshalCall.NEGATIVE.duration=0.80
-self.MarshalCall.NEWFB.duration=1.35
-self.MarshalCall.OPS.duration=0.48
-self.MarshalCall.POINT.duration=0.33
-self.MarshalCall.RADIOCHECK.duration=1.20
-self.MarshalCall.RECOVERY.duration=0.70
-self.MarshalCall.RECOVERYOPSSTOPPED.duration=1.65
-self.MarshalCall.RECOVERYPAUSEDNOTICE.duration=2.9
-self.MarshalCall.RECOVERYPAUSEDRESUMED.duration=3.40
-self.MarshalCall.REPORTSEEME.duration=0.95
-self.MarshalCall.RESUMERECOVERY.duration=1.75
-self.MarshalCall.ROGER.duration=0.53
-self.MarshalCall.SAYNEEDLES.duration=0.90
-self.MarshalCall.STACKFULL.duration=6.35
-self.MarshalCall.STARTINGRECOVERY.duration=2.65
-end
-function AIRBOSS:_InitVoiceOvers()
-self.LSOCall={
-BOLTER={file="LSO-BolterBolter",suffix="ogg",loud=false,subtitle="Bolter, Bolter",duration=0.75,subduration=5},
-CALLTHEBALL={file="LSO-CallTheBall",suffix="ogg",loud=false,subtitle="Call the ball",duration=0.6,subduration=2},
-CHECK={file="LSO-Check",suffix="ogg",loud=false,subtitle="Check",duration=0.45,subduration=2.5},
-CLEAREDTOLAND={file="LSO-ClearedToLand",suffix="ogg",loud=false,subtitle="Cleared to land",duration=1.0,subduration=5},
-COMELEFT={file="LSO-ComeLeft",suffix="ogg",loud=true,subtitle="Come left",duration=0.60,subduration=1},
-RADIOCHECK={file="LSO-RadioCheck",suffix="ogg",loud=false,subtitle="Paddles, radio check",duration=1.1,subduration=5},
-RIGHTFORLINEUP={file="LSO-RightForLineup",suffix="ogg",loud=true,subtitle="Right for line up",duration=0.80,subduration=1},
-HIGH={file="LSO-High",suffix="ogg",loud=true,subtitle="You're high",duration=0.65,subduration=1},
-LOW={file="LSO-Low",suffix="ogg",loud=true,subtitle="You're low",duration=0.50,subduration=1},
-POWER={file="LSO-Power",suffix="ogg",loud=true,subtitle="Power",duration=0.50,subduration=1},
-SLOW={file="LSO-Slow",suffix="ogg",loud=true,subtitle="You're slow",duration=0.65,subduration=1},
-FAST={file="LSO-Fast",suffix="ogg",loud=true,subtitle="You're fast",duration=0.70,subduration=1},
-ROGERBALL={file="LSO-RogerBall",suffix="ogg",loud=false,subtitle="Roger ball",duration=1.00,subduration=2},
-WAVEOFF={file="LSO-WaveOff",suffix="ogg",loud=false,subtitle="Wave off",duration=0.6,subduration=5},
-LONGINGROOVE={file="LSO-LongInTheGroove",suffix="ogg",loud=false,subtitle="You're long in the groove",duration=1.2,subduration=5},
-FOULDECK={file="LSO-FoulDeck",suffix="ogg",loud=false,subtitle="Foul deck",duration=0.62,subduration=5},
-DEPARTANDREENTER={file="LSO-DepartAndReenter",suffix="ogg",loud=false,subtitle="Depart and re-enter",duration=1.1,subduration=5},
-PADDLESCONTACT={file="LSO-PaddlesContact",suffix="ogg",loud=false,subtitle="Paddles, contact",duration=1.0,subduration=5},
-WELCOMEABOARD={file="LSO-WelcomeAboard",suffix="ogg",loud=false,subtitle="Welcome aboard",duration=1.0,subduration=5},
-EXPECTHEAVYWAVEOFF={file="LSO-ExpectHeavyWaveoff",suffix="ogg",loud=false,subtitle="Expect heavy waveoff",duration=1.2,subduration=5},
-EXPECTSPOT75={file="LSO-ExpectSpot75",suffix="ogg",loud=false,subtitle="Expect spot 7.5",duration=2.0,subduration=5},
-EXPECTSPOT5={file="LSO-ExpectSpot5",suffix="ogg",loud=false,subtitle="Expect spot 5",duration=1.3,subduration=5},
-STABILIZED={file="LSO-Stabilized",suffix="ogg",loud=false,subtitle="Stabilized",duration=0.9,subduration=5},
-IDLE={file="LSO-Idle",suffix="ogg",loud=false,subtitle="Idle",duration=0.45,subduration=5},
-N0={file="LSO-N0",suffix="ogg",loud=false,subtitle="",duration=0.40},
-N1={file="LSO-N1",suffix="ogg",loud=false,subtitle="",duration=0.25},
-N2={file="LSO-N2",suffix="ogg",loud=false,subtitle="",duration=0.37},
-N3={file="LSO-N3",suffix="ogg",loud=false,subtitle="",duration=0.37},
-N4={file="LSO-N4",suffix="ogg",loud=false,subtitle="",duration=0.39},
-N5={file="LSO-N5",suffix="ogg",loud=false,subtitle="",duration=0.39},
-N6={file="LSO-N6",suffix="ogg",loud=false,subtitle="",duration=0.40},
-N7={file="LSO-N7",suffix="ogg",loud=false,subtitle="",duration=0.40},
-N8={file="LSO-N8",suffix="ogg",loud=false,subtitle="",duration=0.37},
-N9={file="LSO-N9",suffix="ogg",loud=false,subtitle="",duration=0.40},
-CLICK={file="AIRBOSS-RadioClick",suffix="ogg",loud=false,subtitle="",duration=0.35},
-NOISE={file="AIRBOSS-Noise",suffix="ogg",loud=false,subtitle="",duration=3.6},
-SPINIT={file="AIRBOSS-SpinIt",suffix="ogg",loud=false,subtitle="",duration=0.73,subduration=5},
-}
-self.PilotCall={
-N0={file="PILOT-N0",suffix="ogg",loud=false,subtitle="",duration=0.40},
-N1={file="PILOT-N1",suffix="ogg",loud=false,subtitle="",duration=0.25},
-N2={file="PILOT-N2",suffix="ogg",loud=false,subtitle="",duration=0.37},
-N3={file="PILOT-N3",suffix="ogg",loud=false,subtitle="",duration=0.37},
-N4={file="PILOT-N4",suffix="ogg",loud=false,subtitle="",duration=0.39},
-N5={file="PILOT-N5",suffix="ogg",loud=false,subtitle="",duration=0.39},
-N6={file="PILOT-N6",suffix="ogg",loud=false,subtitle="",duration=0.40},
-N7={file="PILOT-N7",suffix="ogg",loud=false,subtitle="",duration=0.40},
-N8={file="PILOT-N8",suffix="ogg",loud=false,subtitle="",duration=0.37},
-N9={file="PILOT-N9",suffix="ogg",loud=false,subtitle="",duration=0.40},
-POINT={file="PILOT-Point",suffix="ogg",loud=false,subtitle="",duration=0.33},
-SKYHAWK={file="PILOT-Skyhawk",suffix="ogg",loud=false,subtitle="",duration=0.95,subduration=5},
-HARRIER={file="PILOT-Harrier",suffix="ogg",loud=false,subtitle="",duration=0.58,subduration=5},
-HAWKEYE={file="PILOT-Hawkeye",suffix="ogg",loud=false,subtitle="",duration=0.63,subduration=5},
-TOMCAT={file="PILOT-Tomcat",suffix="ogg",loud=false,subtitle="",duration=0.66,subduration=5},
-HORNET={file="PILOT-Hornet",suffix="ogg",loud=false,subtitle="",duration=0.56,subduration=5},
-VIKING={file="PILOT-Viking",suffix="ogg",loud=false,subtitle="",duration=0.61,subduration=5},
-BALL={file="PILOT-Ball",suffix="ogg",loud=false,subtitle="",duration=0.50,subduration=5},
-BINGOFUEL={file="PILOT-BingoFuel",suffix="ogg",loud=false,subtitle="",duration=0.80},
-GASATDIVERT={file="PILOT-GasAtDivert",suffix="ogg",loud=false,subtitle="",duration=1.80},
-GASATTANKER={file="PILOT-GasAtTanker",suffix="ogg",loud=false,subtitle="",duration=1.95},
-}
-self.MarshalCall={
-AFFIRMATIVE={file="MARSHAL-Affirmative",suffix="ogg",loud=false,subtitle="",duration=0.90},
-ALTIMETER={file="MARSHAL-Altimeter",suffix="ogg",loud=false,subtitle="",duration=0.85},
-BRC={file="MARSHAL-BRC",suffix="ogg",loud=false,subtitle="",duration=0.80},
-CARRIERTURNTOHEADING={file="MARSHAL-CarrierTurnToHeading",suffix="ogg",loud=false,subtitle="",duration=2.48,subduration=5},
-CASE={file="MARSHAL-Case",suffix="ogg",loud=false,subtitle="",duration=0.40},
-CHARLIETIME={file="MARSHAL-CharlieTime",suffix="ogg",loud=false,subtitle="",duration=0.90},
-CLEAREDFORRECOVERY={file="MARSHAL-ClearedForRecovery",suffix="ogg",loud=false,subtitle="",duration=1.25},
-DECKCLOSED={file="MARSHAL-DeckClosed",suffix="ogg",loud=false,subtitle="",duration=1.10,subduration=5},
-DEGREES={file="MARSHAL-Degrees",suffix="ogg",loud=false,subtitle="",duration=0.60},
-EXPECTED={file="MARSHAL-Expected",suffix="ogg",loud=false,subtitle="",duration=0.55},
-FLYNEEDLES={file="MARSHAL-FlyYourNeedles",suffix="ogg",loud=false,subtitle="Fly your needles",duration=0.9,subduration=5},
-HOLDATANGELS={file="MARSHAL-HoldAtAngels",suffix="ogg",loud=false,subtitle="",duration=1.10},
-HOURS={file="MARSHAL-Hours",suffix="ogg",loud=false,subtitle="",duration=0.60,subduration=5},
-MARSHALRADIAL={file="MARSHAL-MarshalRadial",suffix="ogg",loud=false,subtitle="",duration=1.10},
-N0={file="MARSHAL-N0",suffix="ogg",loud=false,subtitle="",duration=0.40},
-N1={file="MARSHAL-N1",suffix="ogg",loud=false,subtitle="",duration=0.25},
-N2={file="MARSHAL-N2",suffix="ogg",loud=false,subtitle="",duration=0.37},
-N3={file="MARSHAL-N3",suffix="ogg",loud=false,subtitle="",duration=0.37},
-N4={file="MARSHAL-N4",suffix="ogg",loud=false,subtitle="",duration=0.39},
-N5={file="MARSHAL-N5",suffix="ogg",loud=false,subtitle="",duration=0.39},
-N6={file="MARSHAL-N6",suffix="ogg",loud=false,subtitle="",duration=0.40},
-N7={file="MARSHAL-N7",suffix="ogg",loud=false,subtitle="",duration=0.40},
-N8={file="MARSHAL-N8",suffix="ogg",loud=false,subtitle="",duration=0.37},
-N9={file="MARSHAL-N9",suffix="ogg",loud=false,subtitle="",duration=0.40},
-NEGATIVE={file="MARSHAL-Negative",suffix="ogg",loud=false,subtitle="",duration=0.80,subduration=5},
-NEWFB={file="MARSHAL-NewFB",suffix="ogg",loud=false,subtitle="",duration=1.35},
-OPS={file="MARSHAL-Ops",suffix="ogg",loud=false,subtitle="",duration=0.48},
-POINT={file="MARSHAL-Point",suffix="ogg",loud=false,subtitle="",duration=0.33},
-RADIOCHECK={file="MARSHAL-RadioCheck",suffix="ogg",loud=false,subtitle="Radio check",duration=1.20,subduration=5},
-RECOVERY={file="MARSHAL-Recovery",suffix="ogg",loud=false,subtitle="",duration=0.70,subduration=5},
-RECOVERYOPSSTOPPED={file="MARSHAL-RecoveryOpsStopped",suffix="ogg",loud=false,subtitle="",duration=1.65,subduration=5},
-RECOVERYPAUSEDNOTICE={file="MARSHAL-RecoveryPausedNotice",suffix="ogg",loud=false,subtitle="aircraft recovery paused until further notice",duration=2.90,subduration=5},
-RECOVERYPAUSEDRESUMED={file="MARSHAL-RecoveryPausedResumed",suffix="ogg",loud=false,subtitle="",duration=3.40,subduration=5},
-REPORTSEEME={file="MARSHAL-ReportSeeMe",suffix="ogg",loud=false,subtitle="",duration=0.95},
-RESUMERECOVERY={file="MARSHAL-ResumeRecovery",suffix="ogg",loud=false,subtitle="resuming aircraft recovery",duration=1.75,subduraction=5},
-ROGER={file="MARSHAL-Roger",suffix="ogg",loud=false,subtitle="",duration=0.53,subduration=5},
-SAYNEEDLES={file="MARSHAL-SayNeedles",suffix="ogg",loud=false,subtitle="Say needles",duration=0.90,subduration=5},
-STACKFULL={file="MARSHAL-StackFull",suffix="ogg",loud=false,subtitle="Marshal Stack is currently full. Hold outside 10 NM zone and wait for further instructions",duration=6.35,subduration=10},
-STARTINGRECOVERY={file="MARSHAL-StartingRecovery",suffix="ogg",loud=false,subtitle="",duration=2.65,subduration=5},
-CLICK={file="AIRBOSS-RadioClick",suffix="ogg",loud=false,subtitle="",duration=0.35},
-NOISE={file="AIRBOSS-Noise",suffix="ogg",loud=false,subtitle="",duration=3.6},
-}
-self:SetVoiceOversLSOByRaynor()
-self:SetVoiceOversMarshalByRaynor()
-end
-function AIRBOSS:SetVoiceOver(radiocall,duration,subtitle,subduration,filename,suffix)
-radiocall.duration=duration
-radiocall.subtitle=subtitle or radiocall.subtitle
-radiocall.file=filename
-radiocall.suffix=suffix or".ogg"
-end
-function AIRBOSS:_GetAircraftAoA(playerData)
-local hornet=playerData.actype==AIRBOSS.AircraftCarrier.HORNET
-or playerData.actype==AIRBOSS.AircraftCarrier.RHINOE
-or playerData.actype==AIRBOSS.AircraftCarrier.RHINOF
-or playerData.actype==AIRBOSS.AircraftCarrier.GROWLER
-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
-local aoa={}
-if hornet then
-aoa.SLOW=9.8
-aoa.Slow=9.3
-aoa.OnSpeedMax=8.8
-aoa.OnSpeed=8.1
-aoa.OnSpeedMin=7.4
-aoa.Fast=6.9
-aoa.FAST=6.3
-elseif tomcat then
-aoa.SLOW=self:_AoAUnit2Deg(playerData,17.0)
-aoa.Slow=self:_AoAUnit2Deg(playerData,16.0)
-aoa.OnSpeedMax=self:_AoAUnit2Deg(playerData,15.5)
-aoa.OnSpeed=self:_AoAUnit2Deg(playerData,15.0)
-aoa.OnSpeedMin=self:_AoAUnit2Deg(playerData,14.5)
-aoa.Fast=self:_AoAUnit2Deg(playerData,14.0)
-aoa.FAST=self:_AoAUnit2Deg(playerData,13.0)
-elseif goshawk then
-aoa.SLOW=8.00
-aoa.Slow=7.75
-aoa.OnSpeedMax=7.25
-aoa.OnSpeed=7.00
-aoa.OnSpeedMin=6.75
-aoa.Fast=6.25
-aoa.FAST=6.00
-elseif skyhawk then
-aoa.SLOW=9.50
-aoa.Slow=9.25
-aoa.OnSpeedMax=9.00
-aoa.OnSpeed=8.75
-aoa.OnSpeedMin=8.50
-aoa.Fast=8.25
-aoa.FAST=8.00
-elseif harrier then
-aoa.SLOW=16.0
-aoa.Slow=13.5
-aoa.OnSpeedMax=12.5
-aoa.OnSpeed=10.0
-aoa.OnSpeedMin=9.5
-aoa.Fast=8.0
-aoa.FAST=7.5
-end
-return aoa
-end
-function AIRBOSS:_AoAUnit2Deg(playerData,aoaunits)
-local degrees=aoaunits
-if playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B then
-degrees=-10+50/30*aoaunits
-degrees=0.918*aoaunits-3.411
-elseif playerData.actype==AIRBOSS.AircraftCarrier.A4EC then
-degrees=0.5*aoaunits
-end
-return degrees
-end
-function AIRBOSS:_AoADeg2Units(playerData,degrees)
-local aoaunits=degrees
-if playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B then
-aoaunits=(degrees+10)*30/50
-aoaunits=1.089*degrees+3.715
-elseif playerData.actype==AIRBOSS.AircraftCarrier.A4EC then
-aoaunits=2*degrees
-end
-return aoaunits
-end
-function AIRBOSS:_GetAircraftParameters(playerData,step)
-step=step or playerData.step
-local hornet=playerData.actype==AIRBOSS.AircraftCarrier.HORNET
-or playerData.actype==AIRBOSS.AircraftCarrier.RHINOE
-or playerData.actype==AIRBOSS.AircraftCarrier.RHINOF
-or playerData.actype==AIRBOSS.AircraftCarrier.GROWLER
-local skyhawk=playerData.actype==AIRBOSS.AircraftCarrier.A4EC
-local tomcat=playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B
-local harrier=playerData.actype==AIRBOSS.AircraftCarrier.AV8B
-local goshawk=playerData.actype==AIRBOSS.AircraftCarrier.T45C
-local alt
-local aoa
-local dist
-local speed
-local aoaac=self:_GetAircraftAoA(playerData)
-if step==AIRBOSS.PatternStep.PLATFORM then
-alt=UTILS.FeetToMeters(5000)
-speed=UTILS.KnotsToMps(250)
-elseif step==AIRBOSS.PatternStep.ARCIN then
-if tomcat then
-speed=UTILS.KnotsToMps(150)
-else
-speed=UTILS.KnotsToMps(250)
-end
-elseif step==AIRBOSS.PatternStep.ARCOUT then
-if tomcat then
-speed=UTILS.KnotsToMps(150)
-else
-speed=UTILS.KnotsToMps(250)
-end
-elseif step==AIRBOSS.PatternStep.DIRTYUP then
-alt=UTILS.FeetToMeters(1200)
-elseif step==AIRBOSS.PatternStep.BULLSEYE then
-alt=UTILS.FeetToMeters(1200)
-dist=-UTILS.NMToMeters(3)
-aoa=aoaac.OnSpeed
-elseif step==AIRBOSS.PatternStep.INITIAL then
-if hornet or tomcat or harrier then
-alt=UTILS.FeetToMeters(800)
-speed=UTILS.KnotsToMps(350)
-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
-if hornet or tomcat or harrier then
-alt=UTILS.FeetToMeters(800)
-speed=UTILS.KnotsToMps(350)
-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 or goshawk then
-alt=UTILS.FeetToMeters(800)
-elseif skyhawk then
-alt=UTILS.FeetToMeters(600)
-end
-elseif step==AIRBOSS.PatternStep.LATEBREAK then
-if hornet or tomcat or harrier or goshawk then
-alt=UTILS.FeetToMeters(800)
-elseif skyhawk then
-alt=UTILS.FeetToMeters(600)
-end
-elseif step==AIRBOSS.PatternStep.ABEAM then
-if hornet or tomcat or harrier or goshawk then
-alt=UTILS.FeetToMeters(600)
-elseif skyhawk then
-alt=UTILS.FeetToMeters(500)
-end
-aoa=aoaac.OnSpeed
-if goshawk then
-dist=UTILS.NMToMeters(0.9)
-elseif harrier then
-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
-alt=UTILS.FeetToMeters(425)
-end
-aoa=aoaac.OnSpeed
-elseif step==AIRBOSS.PatternStep.WAKE then
-if hornet or goshawk then
-alt=UTILS.FeetToMeters(370)
-elseif tomcat then
-alt=UTILS.FeetToMeters(430)
-elseif skyhawk then
-alt=UTILS.FeetToMeters(370)
-end
-aoa=aoaac.OnSpeed
-elseif step==AIRBOSS.PatternStep.FINAL then
-if hornet or goshawk then
-alt=UTILS.FeetToMeters(300)
-elseif tomcat then
-alt=UTILS.FeetToMeters(360)
-elseif skyhawk then
-alt=UTILS.FeetToMeters(300)
-elseif harrier then
-alt=UTILS.FeetToMeters(312)
-end
-aoa=aoaac.OnSpeed
-end
-return alt,aoa,dist,speed
-end
-function AIRBOSS:_GetNextMarshalFight()
-for _,_flight in pairs(self.Qmarshal)do
-local flight=_flight
-local stack=flight.flag
-local Tmarshal=timer.getAbsTime()-flight.time
-local TmarshalMin=2*60
-if flight.ai then
-TmarshalMin=3*60
-end
-if flight.holding~=nil and Tmarshal>=TmarshalMin then
-if flight.case==1 and stack==1 or flight.case>1 then
-if flight.ai then
-return flight
-else
-if flight.step~=AIRBOSS.PatternStep.COMMENCING then
-return flight
-end
-end
-end
-end
-end
-return nil
-end
-function AIRBOSS:_CheckQueue()
-if self.Debug then
-self:_PrintQueue(self.flights,"All Flights")
-end
-self:_PrintQueue(self.Qmarshal,"Marshal")
-self:_PrintQueue(self.Qpattern,"Pattern")
-self:_PrintQueue(self.Qwaiting,"Waiting")
-self:_PrintQueue(self.Qspinning,"Spinning")
-if self.case>1 then
-for _,_flight in pairs(self.Qwaiting)do
-local flight=_flight
-local removed=self:_RemoveFlightFromQueue(self.Qwaiting,flight)
-if removed then
-local stack=self:_GetFreeStack(flight.ai)
-self:T(self.lid..string.format("Moving flight %s onboard %s from Waiting queue to Case %d Marshal stack %d",flight.groupname,flight.onboard,self.case,stack))
-if flight.ai then
-self:_MarshalAI(flight,stack)
-else
-self:_MarshalPlayer(flight,stack)
-end
-break
-end
-end
-end
-if not self:IsRecovering()then
-for _,_flight in pairs(self.Qmarshal)do
-local flight=_flight
-if(flight.case==1 and self.case>1)or(flight.case>1 and self.case==1)then
-local removed=self:_RemoveFlightFromQueue(self.Qmarshal,flight)
-if removed then
-local stack=self:_GetFreeStack(flight.ai)
-self:T(self.lid..string.format("Moving flight %s onboard %s from Marshal Case %d ==> %d Marshal stack %d",flight.groupname,flight.onboard,flight.case,self.case,stack))
-if flight.ai then
-self:_MarshalAI(flight,stack)
-else
-self:_MarshalPlayer(flight,stack)
-end
-break
-elseif flight.case~=self.case then
-flight.case=self.case
-end
-end
-end
-return
-end
-local _,npattern=self:_GetQueueInfo(self.Qpattern)
-local _,nspinning=self:_GetQueueInfo(self.Qspinning)
-local marshalflight=self:_GetNextMarshalFight()
-if marshalflight and npattern0 then
-local patternflight=self.Qpattern[#self.Qpattern]
-pcase=patternflight.case
-local npunits=self:_GetFlightUnits(patternflight,false)
-Tpattern=timer.getAbsTime()-patternflight.time
-self:T(self.lid..string.format("Pattern time of last group %s = %d seconds. # of units=%d.",patternflight.groupname,Tpattern,npunits))
-end
-local TpatternMin
-if pcase==1 then
-TpatternMin=2*60*npunits
-else
-TpatternMin=2*60*npunits
-end
-if Tpattern>TpatternMin then
-self:T(self.lid..string.format("Sending marshal flight %s to pattern.",marshalflight.groupname))
-self:_ClearForLanding(marshalflight)
-end
-end
-end
-function AIRBOSS:_ClearForLanding(flight)
-if flight.ai then
-self:_RemoveFlightFromMarshalQueue(flight,false)
-self:_LandAI(flight)
-self:_MarshalCallClearedForRecovery(flight.onboard,flight.case)
-if self.xtVoiceOversAI then
-local leader=flight.group:GetUnits()[1]
-self:_CommencingCall(leader,flight.onboard)
-end
-else
-if flight.step~=AIRBOSS.PatternStep.COMMENCING then
-self:_MarshalCallClearedForRecovery(flight.onboard,flight.case)
-flight.time=timer.getAbsTime()
-end
-self:_SetPlayerStep(flight,AIRBOSS.PatternStep.COMMENCING,3)
-end
-end
-function AIRBOSS:_SetPlayerStep(playerData,step,delay)
-if delay and delay>0 then
-self:ScheduleOnce(delay,self._SetPlayerStep,self,playerData,step)
-else
-if playerData then
-playerData.step=step
-playerData.warning=nil
-self:_StepHint(playerData)
-end
-end
-end
-function AIRBOSS:_ScanCarrierZone()
-local coord=self:GetCoordinate()
-local RCCZ=self.zoneCCA:GetRadius()
-self:T(self.lid..string.format("Scanning Carrier Controlled Area. Radius=%.1f NM.",UTILS.MetersToNM(RCCZ)))
-local _,_,_,unitscan=coord:ScanObjects(RCCZ,true,false,false)
-local insideCCA={}
-for _,_unit in pairs(unitscan)do
-local unit=_unit
-local airborne=unit:IsAir()
-local inzone=unit:IsInZone(self.zoneCCA)
-local friendly=self:GetCoalition()==unit:GetCoalition()
-local carrierac=self:_IsCarrierAircraft(unit)
-if airborne and inzone and friendly and carrierac then
-local group=unit:GetGroup()
-local groupname=group:GetName()
-if insideCCA[groupname]==nil then
-insideCCA[groupname]=group
-end
-end
-end
-for groupname,_group in pairs(insideCCA)do
-local group=_group
-local knownflight=self:_GetFlightFromGroupInQueue(group,self.flights)
-local actype=group:GetTypeName()
-if knownflight then
-self:T2(self.lid..string.format("Known flight group %s of type %s in CCA.",groupname,actype))
-if knownflight.ai and knownflight.flag==-100 and self.handleai then
-local putintomarshal=false
-local flight=_DATABASE:GetOpsGroup(groupname)
-if flight and flight:IsInbound()and flight.destbase:GetName()==self.carrier:GetName()then
-if flight.ishelo then
-else
-putintomarshal=true
-end
-flight.airboss=self
-end
-if putintomarshal then
-local stack=self:_GetFreeStack(knownflight.ai)
-local respawn=self.respawnAI
-if stack then
-self:_MarshalAI(knownflight,stack,respawn)
-else
-if not self:_InQueue(self.Qwaiting,knownflight.group)then
-self:_WaitAI(knownflight,respawn)
-end
-end
-break
-end
-end
-else
-if not self:_IsHuman(group)then
-self:_CreateFlightGroup(group)
-end
-end
-end
-local remove={}
-for _,_flight in pairs(self.flights)do
-local flight=_flight
-if insideCCA[flight.groupname]==nil then
-if flight.ai and not(self:_InQueue(self.Qmarshal,flight.group)or self:_InQueue(self.Qpattern,flight.group))then
-table.insert(remove,flight)
-end
-end
-end
-for _,flight in pairs(remove)do
-self:_RemoveFlightFromQueue(self.flights,flight)
-end
-end
-function AIRBOSS:_WaitPlayer(playerData)
-if playerData then
-local nwaiting=#self.Qwaiting
-self:_MarshalCallStackFull(playerData.onboard,nwaiting)
-table.insert(self.Qwaiting,playerData)
-playerData.time=timer.getAbsTime()
-playerData.step=AIRBOSS.PatternStep.WAITING
-playerData.warning=nil
-for _,_flight in pairs(playerData.section)do
-local flight=_flight
-flight.step=AIRBOSS.PatternStep.WAITING
-flight.time=timer.getAbsTime()
-flight.warning=nil
-end
-end
-end
-function AIRBOSS:_MarshalPlayer(playerData,stack)
-if playerData then
-self:_AddMarshalGroup(playerData,stack)
-self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.HOLDING)
-playerData.holding=nil
-for _,_flight in pairs(playerData.section)do
-local flight=_flight
-self:_SetPlayerStep(flight,AIRBOSS.PatternStep.HOLDING)
-flight.holding=nil
-flight.case=playerData.case
-flight.flag=stack
-self:Marshal(flight)
-end
-else
-self:E(self.lid.."ERROR: Could not add player to Marshal stack! playerData=nil")
-end
-end
-function AIRBOSS:_WaitAI(flight,respawn)
-flight.flag=-99
-table.insert(self.Qwaiting,flight)
-local group=flight.group
-local groupname=flight.groupname
-local speedOrbitMps=UTILS.KnotsToMps(274)
-local speedOrbitKmh=UTILS.KnotsToKmph(274)
-local speedTransit=UTILS.KnotsToKmph(370)
-local cv=self:GetCoordinate()
-local fc=group:GetCoordinate()
-local hdg=self:GetHeading(false)
-local hdgto=cv:HeadingTo(fc)
-local angels=math.random(6,10)
-local altitude=UTILS.FeetToMeters(angels*1000)
-local p0=cv:Translate(UTILS.NMToMeters(11),hdgto):Translate(UTILS.NMToMeters(5),hdg):SetAltitude(altitude)
-local wp={}
-wp[1]=group:GetCoordinate():WaypointAirTurningPoint(nil,speedTransit,{},"Current Position")
-local taskorbit=group:TaskOrbit(p0,altitude,speedOrbitMps)
-wp[#wp+1]=p0:WaypointAirTurningPoint(nil,speedOrbitKmh,{taskorbit},string.format("Waiting Orbit at Angels %d",angels))
-if self.Debug then
-p0:MarkToAll(string.format("Waiting Orbit of flight %s at Angels %s",groupname,angels))
-end
-if respawn then
-local Template=group:GetTemplate()
-Template.route.points=wp
-group=group:Respawn(Template,true)
-end
-group:WayPointInitialize(wp)
-group:Route(wp,1)
-end
-function AIRBOSS:_MarshalAI(flight,nstack,respawn)
-self:F2({flight=flight,nstack=nstack,respawn=respawn})
-if flight==nil or flight.group==nil then
-self:E(self.lid.."ERROR: flight or flight.group is nil.")
-return
-end
-if flight.group:GetCoordinate()==nil then
-self:E(self.lid.."ERROR: cannot get coordinate of flight group.")
-return
-end
-if not self:_InQueue(self.Qmarshal,flight.group)then
-if self.xtVoiceOversAI then
-local leader=flight.group:GetUnits()[1]
-self:_MarshallInboundCall(leader,flight.onboard)
-end
-self:_AddMarshalGroup(flight,nstack)
-end
-local case=flight.case
-local ostack=flight.flag
-local group=flight.group
-local groupname=flight.groupname
-flight.flag=nstack
-local Carrier=self:GetCoordinate()
-local hdg=self:GetHeading()
-local speedOrbitMps=UTILS.KnotsToMps(274)
-local speedOrbitKmh=UTILS.KnotsToKmph(274)
-local speedTransit=UTILS.KnotsToKmph(370)
-local altitude
-local p0
-local p1
-local p2
-altitude,p1,p2=self:_GetMarshalAltitude(nstack,case)
-local wp={}
-if not flight.holding then
-self:T(self.lid..string.format("Guiding AI flight %s to marshal stack %d-->%d.",groupname,ostack,nstack))
-wp[1]=group:GetCoordinate():WaypointAirTurningPoint(nil,speedTransit,{},"Current Position")
-local TaskArrivedHolding=flight.group:TaskFunction("AIRBOSS._ReachedHoldingZone",self,flight)
-if case==1 then
-local pE=Carrier:Translate(UTILS.NMToMeters(7),hdg-30):SetAltitude(altitude)
-p0=Carrier:Translate(UTILS.NMToMeters(5),hdg-135):SetAltitude(altitude)
-wp[#wp+1]=pE:WaypointAirTurningPoint(nil,speedTransit,{TaskArrivedHolding},"Entering Case I Marshal Pattern")
-else
-local radial=self:GetRadial(case,false,true)
-p0=p2:Translate(UTILS.NMToMeters(5),radial+90,true):Translate(UTILS.NMToMeters(5),radial,true)
-wp[#wp+1]=p0:WaypointAirTurningPoint(nil,speedTransit,{TaskArrivedHolding},"Entering Case II/III Marshal Pattern")
-end
-else
-self:T(self.lid..string.format("Updating AI flight %s at marshal stack %d-->%d.",groupname,ostack,nstack))
-wp[1]=group:GetCoordinate():WaypointAirTurningPoint(nil,speedOrbitKmh,{},"Current Position")
-p0=group:GetCoordinate():Translate(UTILS.NMToMeters(0.2),group:GetHeading(),true)
-end
-local taskorbit=group:TaskOrbit(p1,altitude,speedOrbitMps,p2)
-wp[#wp+1]=p0:WaypointAirTurningPoint(nil,speedOrbitKmh,{taskorbit},string.format("Marshal Orbit Stack %d",nstack))
-if self.Debug then
-p0:MarkToAll("WP P0 "..groupname)
-p1:MarkToAll("RT P1 "..groupname)
-p2:MarkToAll("RT P2 "..groupname)
-end
-if respawn then
-local Template=group:GetTemplate()
-Template.route.points=wp
-flight.group=group:Respawn(Template,true)
-end
-flight.group:WayPointInitialize(wp)
-flight.group:Route(wp,1)
-self:Marshal(flight)
-end
-function AIRBOSS:_RefuelAI(flight)
-local wp={}
-local CurrentSpeed=flight.group:GetVelocityKMH()
-wp[#wp+1]=flight.group:GetCoordinate():WaypointAirTurningPoint(nil,CurrentSpeed,{},"Current position")
-local refuelac=false
-local actype=flight.group:GetTypeName()
-if actype==AIRBOSS.AircraftCarrier.AV8B or
-actype==AIRBOSS.AircraftCarrier.F14A or
-actype==AIRBOSS.AircraftCarrier.F14B or
-actype==AIRBOSS.AircraftCarrier.F14A_AI or
-actype==AIRBOSS.AircraftCarrier.HORNET or
-actype==AIRBOSS.AircraftCarrier.RHINOE or
-actype==AIRBOSS.AircraftCarrier.RHINOF or
-actype==AIRBOSS.AircraftCarrier.GROWLER or
-actype==AIRBOSS.AircraftCarrier.FA18C or
-actype==AIRBOSS.AircraftCarrier.S3B or
-actype==AIRBOSS.AircraftCarrier.S3BTANKER then
-refuelac=true
-end
-local text=""
-if self.tanker and refuelac then
-local tankerpos=self.tanker.tanker:GetCoordinate()
-local TaskRefuel=flight.group:TaskRefueling()
-local TaskMarshal=flight.group:TaskFunction("AIRBOSS._TaskFunctionMarshalAI",self,flight)
-wp[#wp+1]=tankerpos:WaypointAirTurningPoint(nil,CurrentSpeed,{TaskRefuel,TaskMarshal},"Refueling")
-self:_MarshalCallGasAtTanker(flight.onboard)
-else
-local divertfield=self:GetCoordinate():GetClosestAirbase(Airbase.Category.AIRDROME,self:GetCoalition())
-if divertfield==nil then
-divertfield=self:GetCoordinate():GetClosestAirbase(Airbase.Category.AIRDROME,0)
-end
-if divertfield then
-local divertcoord=divertfield:GetCoordinate()
-wp[#wp+1]=divertcoord:WaypointAirLanding(UTILS.KnotsToKmph(300),divertfield,{},"Divert Field")
-self:_MarshalCallGasAtDivert(flight.onboard,divertfield:GetName())
-local Template=flight.group:GetTemplate()
-Template.route.points=wp
-flight.group=flight.group:Respawn(Template,true)
-else
-self:E(self.lid..string.format("WARNING: No recovery tanker or divert field available for group %s.",flight.groupname))
-flight.refueling=true
-return
-end
-end
-flight.group:WayPointInitialize(wp)
-flight.group:Route(wp,1)
-flight.refueling=true
-end
-function AIRBOSS:_LandAI(flight)
-self:T(self.lid..string.format("Landing AI flight %s.",flight.groupname))
-local Speed=UTILS.KnotsToKmph(200)
-if flight.actype==AIRBOSS.AircraftCarrier.HORNET
-or flight.actype==AIRBOSS.AircraftCarrier.FA18C
-or flight.actype==AIRBOSS.AircraftCarrier.RHINOE
-or flight.actype==AIRBOSS.AircraftCarrier.RHINOF
-or flight.actype==AIRBOSS.AircraftCarrier.GROWLER then
-Speed=UTILS.KnotsToKmph(200)
-elseif flight.actype==AIRBOSS.AircraftCarrier.E2D then
-Speed=UTILS.KnotsToKmph(150)
-elseif flight.actype==AIRBOSS.AircraftCarrier.F14A_AI or flight.actype==AIRBOSS.AircraftCarrier.F14A or flight.actype==AIRBOSS.AircraftCarrier.F14B then
-Speed=UTILS.KnotsToKmph(175)
-elseif flight.actype==AIRBOSS.AircraftCarrier.S3B or flight.actype==AIRBOSS.AircraftCarrier.S3BTANKER then
-Speed=UTILS.KnotsToKmph(140)
-end
-local Carrier=self:GetCoordinate()
-local hdg=self:GetHeading()
-local wp={}
-local CurrentSpeed=flight.group:GetVelocityKMH()
-wp[#wp+1]=flight.group:GetCoordinate():WaypointAirTurningPoint(nil,CurrentSpeed,{},"Current position")
-local alt=UTILS.FeetToMeters(800)
-wp[#wp+1]=Carrier:Translate(UTILS.NMToMeters(4),hdg-160):SetAltitude(alt):WaypointAirLanding(Speed,self.airbase,nil,"Landing")
-flight.group:WayPointInitialize(wp)
-flight.group:Route(wp,0)
-end
-function AIRBOSS:_GetMarshalAltitude(stack,case)
-if stack<=0 then
-return 0,nil,nil
-end
-case=case or self.case
-local Carrier=self:GetCoordinate()
-local angels0
-local Dist
-local p1=nil
-local p2=nil
-local nstack=stack-1
-if case==1 then
-angels0=2
-local hdg=self.carrier:GetHeading()
-p1=Carrier
-p2=Carrier:Translate(UTILS.NMToMeters(1.5),hdg)
-if self.carriertype==AIRBOSS.CarrierType.INVINCIBLE or self.carriertype==AIRBOSS.CarrierType.HERMES or self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS or self.carriertype==AIRBOSS.CarrierType.CANBERRA then
-p1=Carrier:Translate(UTILS.NMToMeters(1.0),hdg+90)
-p2=p1:Translate(2.5,hdg)
-end
-else
-angels0=6
-Dist=UTILS.NMToMeters(nstack+angels0+15)
-local radial=self:GetRadial(case,false,true)
-local l=UTILS.NMToMeters(10)
-p1=Carrier:Translate(Dist+l,radial)
-p2=Carrier:Translate(Dist,radial)
-end
-local altitude=UTILS.FeetToMeters((nstack+angels0)*1000)
-p1:SetAltitude(altitude,true)
-p2:SetAltitude(altitude,true)
-return altitude,p1,p2
-end
-function AIRBOSS:_GetCharlieTime(flightgroup)
-local stack=flightgroup.flag
-if stack<=0 then
-return nil
-end
-local Tnow=timer.getAbsTime()
-local Tcharlie=0
-local Trecovery=0
-if self.recoverywindow then
-Trecovery=math.max(self.recoverywindow.START-Tnow,0)
-else
-Trecovery=7*60
-end
-for _,_flight in pairs(self.Qmarshal)do
-local flight=_flight
-local mstack=flight.flag
-local Tarrive=0
-local Tholding=3*60
-if stack>0 and mstack>0 and mstack<=stack then
-if flight.holding==nil then
-local holdingzone=self:_GetZoneHolding(flight.case,1):GetCoordinate()
-local d0=holdingzone:Get2DDistance(flight.group:GetCoordinate())
-local v0=flight.group:GetVelocityMPS()
-Tarrive=d0/v0
-self:T3(self.lid..string.format("Tarrive=%.1f seconds, Clock %s",Tarrive,UTILS.SecondsToClock(Tnow+Tarrive)))
-else
-if mstack==1 then
-local tholding=timer.getAbsTime()-flight.time
-Tholding=math.max(3*60-tholding,0)
-end
-end
-local Tmin=math.max(Tarrive,Trecovery)
-Tcharlie=math.max(Tmin,Tcharlie)+Tholding
-end
-end
-Tcharlie=Tcharlie+Tnow
-local text=string.format("Charlie time for flight %s (%s) %s",flightgroup.onboard,flightgroup.groupname,UTILS.SecondsToClock(Tcharlie))
-MESSAGE:New(text,10,"DEBUG"):ToAllIf(self.Debug)
-self:T(self.lid..text)
-return Tcharlie
-end
-function AIRBOSS:_AddMarshalGroup(flight,stack)
-flight.flag=stack
-flight.case=self.case
-table.insert(self.Qmarshal,flight)
-local P=UTILS.hPa2inHg(self:GetCoordinate():GetPressure())
-local alt=self:_GetMarshalAltitude(stack,flight.case)
-local brc=self:GetBRC()
-if self.recoverywindow and self.recoverywindow.WIND then
-brc=self:GetBRCintoWind(self.recoverywindow.SPEED)
-end
-flight.Tcharlie=self:_GetCharlieTime(flight)
-local Ccharlie=UTILS.SecondsToClock(flight.Tcharlie)
-self:_MarshalCallArrived(flight.onboard,flight.case,brc,alt,Ccharlie,P)
-if self.TACANon and(not flight.ai)and flight.difficulty==AIRBOSS.Difficulty.EASY then
-local radial=self:GetRadial(flight.case,true,true,true)
-if flight.case==1 then
-radial=self:GetBRC()
-end
-local text=string.format("Select TACAN %03d°, channel %d%s (%s)",radial,self.TACANchannel,self.TACANmode,self.TACANmorse)
-self:MessageToPlayer(flight,text,nil,"")
-end
-end
-function AIRBOSS:_CollapseMarshalStack(flight,nopattern)
-self:F2({flight=flight,nopattern=nopattern})
-local case=flight.case
-local stack=flight.flag
-if stack<=0 then
-self:E(self.lid..string.format("ERROR: Flight %s is has stack value %d<0. Cannot collapse stack!",flight.groupname,stack))
-return
-end
-self.Tcollapse=timer.getTime()
-for _,_flight in pairs(self.Qmarshal)do
-local mflight=_flight
-if(case==1 and mflight.case==1)then
-local mstack=mflight.flag
-if mstack>stack then
-local newstack=self:_GetFreeStack(mflight.ai,mflight.case,true)
-if newstack and newstack %d.",mflight.groupname,mflight.case,mstack,newstack))
-if mflight.ai then
-self:_MarshalAI(mflight,newstack)
-else
-mflight.flag=newstack
-local angels=self:_GetAngels(self:_GetMarshalAltitude(newstack,case))
-if mflight.difficulty~=AIRBOSS.Difficulty.HARD then
-local text=string.format("descent to stack at Angels %d.",angels)
-self:MessageToPlayer(mflight,text,"MARSHAL")
-end
-mflight.time=timer.getAbsTime()
-for _,_sec in pairs(mflight.section)do
-local sec=_sec
-sec.flag=newstack
-sec.time=timer.getAbsTime()
-if sec.difficulty~=AIRBOSS.Difficulty.HARD then
-local text=string.format("descent to stack at Angels %d.",angels)
-self:MessageToPlayer(sec,text,"MARSHAL")
-end
-end
-end
-end
-end
-end
-end
-if nopattern then
-self:T(self.lid..string.format("Flight %s is leaving stack but not going to pattern.",flight.groupname))
-else
-local Tmarshal=UTILS.SecondsToClock(timer.getAbsTime()-flight.time)
-self:T(self.lid..string.format("Flight %s is leaving marshal after %s and going pattern.",flight.groupname,Tmarshal))
-self:_AddFlightToPatternQueue(flight)
-end
-flight.flag=-1
-flight.time=timer.getAbsTime()
-end
-function AIRBOSS:_GetFreeStack(ai,case,empty)
-case=case or self.case
-if case==1 then
-return self:_GetFreeStack_Old(ai,case,empty)
-end
-local nmaxstacks=100
-if case==1 then
-nmaxstacks=self.Nmaxmarshal
-end
-local stack={}
-for i=1,nmaxstacks do
-stack[i]=self.NmaxStack
-end
-local nmax=1
-for _,_flight in pairs(self.Qmarshal)do
-local flight=_flight
-if flight.case==case then
-local n=flight.flag
-if n>nmax then
-nmax=n
-end
-if n>0 then
-if flight.ai or flight.case>1 then
-stack[n]=0
-else
-stack[n]=stack[n]-1
-end
-else
-self:E(string.format("ERROR: Flight %s in marshal stack has stack value <= 0. Stack value is %d.",flight.groupname,n))
-end
-end
-end
-local nfree=nil
-if stack[nmax]==0 then
-if case==1 then
-if nmax>=nmaxstacks then
-nfree=nil
-else
-nfree=nmax+1
-end
-else
-nfree=nmax+1
-end
-elseif stack[nmax]==self.NmaxStack then
-self:E(self.lid..string.format("ERROR: Max occupied stack is empty. Should not happen! Nmax=%d, stack[nmax]=%d",nmax,stack[nmax]))
-nfree=nmax
-else
-if ai or empty or case>1 then
-nfree=nmax+1
-else
-nfree=nmax
-end
-end
-self:T(self.lid..string.format("Returning free stack %s",tostring(nfree)))
-return nfree
-end
-function AIRBOSS:_GetFreeStack_Old(ai,case,empty)
-case=case or self.case
-local nmaxstacks=100
-if case==1 then
-nmaxstacks=self.Nmaxmarshal
-end
-local stack={}
-for i=1,nmaxstacks do
-stack[i]=self.NmaxStack
-end
-for _,_flight in pairs(self.Qmarshal)do
-local flight=_flight
-if flight.case==case then
-local n=flight.flag
-if n>0 then
-if flight.ai or flight.case>1 then
-stack[n]=0
-else
-stack[n]=stack[n]-1
-end
-else
-self:E(string.format("ERROR: Flight %s in marshal stack has stack value <= 0. Stack value is %d.",flight.groupname,n))
-end
-end
-end
-local nfree=nil
-for i=1,nmaxstacks do
-self:T2(self.lid..string.format("FF Stack[%d]=%d",i,stack[i]))
-if ai or empty or case>1 then
-if stack[i]==self.NmaxStack then
-nfree=i
-return i
-end
-else
-if stack[i]>0 then
-nfree=i
-return i
-end
-end
-end
-return nfree
-end
-function AIRBOSS:_GetFlightUnits(flight,onground)
-local inair=true
-if onground==true then
-inair=false
-end
-local function countunits(_group,inair)
-local group=_group
-local units=group:GetUnits()
-local n=0
-if units then
-for _,_unit in pairs(units)do
-local unit=_unit
-if unit and unit:IsAlive()then
-if inair then
-if unit:InAir()then
-self:T2(self.lid..string.format("Unit %s is in AIR",unit:GetName()))
-n=n+1
-end
-else
-n=n+1
-end
-end
-end
-end
-return n
-end
-local nunits=countunits(flight.group,inair)
-local nsection=0
-for _,sec in pairs(flight.section)do
-local secflight=sec
-nsection=nsection+countunits(secflight.group,inair)
-end
-return nunits+nsection,nunits,nsection
-end
-function AIRBOSS:_GetQueueInfo(queue,case)
-local ngroup=0
-local Nunits=0
-for _,_flight in pairs(queue)do
-local flight=_flight
-if case then
-if(flight.case==case)or(case==23 and(flight.case==2 or flight.case==3))then
-local ntot,nunits,nsection=self:_GetFlightUnits(flight)
-Nunits=Nunits+ntot
-if ntot>0 then
-ngroup=ngroup+1
-end
-end
-else
-local ntot,nunits,nsection=self:_GetFlightUnits(flight)
-Nunits=Nunits+ntot
-if ntot>0 then
-ngroup=ngroup+1
-end
-end
-end
-return ngroup,Nunits
-end
-function AIRBOSS:_PrintQueue(queue,name)
-local Nqueue,nqueue=self:_GetQueueInfo(queue)
-local text=string.format("%s Queue N=%d (#%d), n=%d:",name,Nqueue,#queue,nqueue)
-if#queue==0 then
-text=text.." empty."
-else
-for i,_flight in pairs(queue)do
-local flight=_flight
-local clock=UTILS.SecondsToClock(timer.getAbsTime()-flight.time)
-local case=flight.case
-local stack=flight.flag
-local fuel=flight.group:GetFuelMin()*100
-local ai=tostring(flight.ai)
-local lead=flight.seclead
-local Nsec=#flight.section
-local actype=self:_GetACNickname(flight.actype)
-local onboard=flight.onboard
-local holding=tostring(flight.holding)
-local _,nunits,nsec=self:_GetFlightUnits(flight,false)
-text=text..string.format("\n[%d] %s*%d (%s): lead=%s (%d/%d), onboard=%s, flag=%d, case=%d, time=%s, fuel=%d, ai=%s, holding=%s",i,flight.groupname,nunits,actype,lead,nsec,Nsec,onboard,stack,case,clock,fuel,ai,holding)
-if stack>0 then
-local alt=UTILS.MetersToFeet(self:_GetMarshalAltitude(stack,case))
-text=text..string.format(" stackalt=%d ft",alt)
-end
-for j,_element in pairs(flight.elements)do
-local element=_element
-text=text..string.format("\n (%d) %s (%s): ai=%s, ballcall=%s, recovered=%s",j,element.onboard,element.unitname,tostring(element.ai),tostring(element.ballcall),tostring(element.recovered))
-end
-end
-end
-self:T(self.lid..text)
-end
-function AIRBOSS:_CreateFlightGroup(group)
-self:T(self.lid..string.format("Creating new flight for group %s of aircraft type %s.",group:GetName(),group:GetTypeName()))
-local flight={}
-if not self:_InQueue(self.flights,group)then
-local groupname=group:GetName()
-local human,playername=self:_IsHuman(group)
-flight.group=group
-flight.groupname=group:GetName()
-flight.nunits=#group:GetUnits()
-flight.time=timer.getAbsTime()
-flight.dist0=group:GetCoordinate():Get2DDistance(self:GetCoordinate())
-flight.flag=-100
-flight.ai=not human
-flight.actype=group:GetTypeName()
-flight.onboardnumbers=self:_GetOnboardNumbers(group)
-flight.seclead=flight.group:GetUnit(1):GetName()
-flight.section={}
-flight.ballcall=false
-flight.refueling=false
-flight.holding=nil
-flight.name=flight.group:GetUnit(1):GetName()
-flight.case=self.case
-local text=string.format("Flight elements of group %s:",flight.groupname)
-flight.elements={}
-local units=group:GetUnits()
-for i,_unit in pairs(units)do
-local unit=_unit
-local element={}
-element.unit=unit
-element.unitname=unit:GetName()
-element.onboard=flight.onboardnumbers[element.unitname]
-element.ballcall=false
-element.ai=not self:_IsHumanUnit(unit)
-element.recovered=nil
-text=text..string.format("\n[%d] %s onboard #%s, AI=%s",i,element.unitname,tostring(element.onboard),tostring(element.ai))
-table.insert(flight.elements,element)
-end
-self:T(self.lid..text)
-if flight.ai then
-local onboard=flight.onboardnumbers[flight.seclead]
-flight.onboard=onboard
-else
-flight.onboard=self:_GetOnboardNumberPlayer(group)
-end
-table.insert(self.flights,flight)
-else
-self:E(self.lid..string.format("ERROR: Flight group %s already exists in self.flights!",group:GetName()))
-return nil
-end
-return flight
-end
-function AIRBOSS:_NewPlayer(unitname)
-local playerunit,playername=self:_GetPlayerUnitAndName(unitname)
-if playerunit and playername then
-local group=playerunit:GetGroup()
-local playerData
-playerData=self:_CreateFlightGroup(group)
-if playerData then
-playerData.unit=playerunit
-playerData.unitname=unitname
-playerData.name=playername
-playerData.callsign=playerData.unit:GetCallsign()
-playerData.client=CLIENT:FindByName(unitname,nil,true)
-playerData.seclead=playername
-playerData.passes=0
-playerData.messages={}
-playerData.lastdebrief=playerData.lastdebrief or{}
-playerData.attitudemonitor=false
-if playerData.trapon==nil then
-playerData.trapon=self.trapsheet
-end
-playerData.difficulty=playerData.difficulty or self.defaultskill
-if playerData.subtitles==nil then
-playerData.subtitles=true
-end
-if playerData.showhints==nil then
-if playerData.difficulty==AIRBOSS.Difficulty.HARD then
-playerData.showhints=false
-else
-playerData.showhints=true
-end
-end
-playerData.points={}
-playerData=self:_InitPlayer(playerData)
-self.players[playername]=playerData
-self.playerscores[playername]=self.playerscores[playername]or{}
-if self.welcome then
-self:MessageToPlayer(playerData,string.format("Welcome, %s %s!",playerData.difficulty,playerData.name),string.format("AIRBOSS %s",self.alias),"",5)
-end
-end
-return playerData
-end
-return nil
-end
-function AIRBOSS:_InitPlayer(playerData,step)
-self:T(self.lid..string.format("Initializing player data for %s callsign %s.",playerData.name,playerData.callsign))
-playerData.step=step or AIRBOSS.PatternStep.UNDEFINED
-playerData.groove={}
-playerData.debrief={}
-playerData.trapsheet={}
-playerData.warning=nil
-playerData.holding=nil
-playerData.refueling=false
-playerData.valid=false
-playerData.lig=false
-playerData.wop=false
-playerData.waveoff=false
-playerData.wofd=false
-playerData.owo=false
-playerData.boltered=false
-playerData.hover=false
-playerData.stable=false
-playerData.landed=false
-playerData.Tlso=timer.getTime()
-playerData.Tgroove=nil
-playerData.TIG0=nil
-playerData.wire=nil
-playerData.flag=-100
-playerData.debriefschedulerID=nil
-if playerData.group:GetName():match("Groove")and playerData.passes==0 then
-self:MessageToPlayer(playerData,"Group name contains \"Groove\". Happy groove testing.")
-playerData.attitudemonitor=true
-playerData.step=AIRBOSS.PatternStep.FINAL
-self:_AddFlightToPatternQueue(playerData)
-self.dTstatus=0.1
-end
-return playerData
-end
-function AIRBOSS:_GetFlightFromGroupInQueue(group,queue)
-if group then
-local name=group:GetName()
-for i,_flight in pairs(queue)do
-local flight=_flight
-if flight.groupname==name then
-return flight,i
-end
-end
-self:T2(self.lid..string.format("WARNING: Flight group %s could not be found in queue.",name))
-end
-self:T2(self.lid..string.format("WARNING: Flight group could not be found in queue. Group is nil!"))
-return nil,nil
-end
-function AIRBOSS:_GetFlightElement(unitname)
-local unit=UNIT:FindByName(unitname)
-if unit then
-local flight=self:_GetFlightFromGroupInQueue(unit:GetGroup(),self.flights)
-if flight then
-for i,_element in pairs(flight.elements)do
-local element=_element
-if element.unit:GetName()==unitname then
-return element,i,flight
-end
-end
-self:T2(self.lid..string.format("WARNING: Flight element %s could not be found in flight group.",unitname,flight.groupname))
-end
-end
-return nil,nil,nil
-end
-function AIRBOSS:_RemoveFlightElement(unitname)
-local element,idx,flight=self:_GetFlightElement(unitname)
-if idx then
-table.remove(flight.elements,idx)
-return true
-else
-self:T("WARNING: Flight element could not be removed from flight group. Index=nil!")
-return nil
-end
-end
-function AIRBOSS:_InQueue(queue,group)
-local name=group:GetName()
-for _,_flight in pairs(queue)do
-local flight=_flight
-if name==flight.groupname then
-return true
-end
-end
-return false
-end
-function AIRBOSS:_RemoveDeadFlightGroups()
-for i=#self.flight,1,-1 do
-local flight=self.flights[i]
-if not flight.group:IsAlive()then
-self:T(string.format("Removing dead flight group %s from ALL flights table.",flight.groupname))
-table.remove(self.flights,i)
-end
-end
-for i=#self.Qmarshal,1,-1 do
-local flight=self.Qmarshal[i]
-if not flight.group:IsAlive()then
-self:T(string.format("Removing dead flight group %s from Marshal Queue table.",flight.groupname))
-table.remove(self.Qmarshal,i)
-end
-end
-for i=#self.Qpattern,1,-1 do
-local flight=self.Qpattern[i]
-if not flight.group:IsAlive()then
-self:T(string.format("Removing dead flight group %s from Pattern Queue table.",flight.groupname))
-table.remove(self.Qpattern,i)
-end
-end
-end
-function AIRBOSS:_GetLeadFlight(flight)
-local lead=flight
-if flight.name~=flight.seclead then
-lead=self.players[flight.seclead]
-end
-return lead
-end
-function AIRBOSS:_CheckSectionRecovered(flight)
-if flight==nil then
-return true
-end
-local lead=self:_GetLeadFlight(flight)
-for _,_element in pairs(lead.elements)do
-local element=_element
-if not element.recovered then
-return false
-end
-end
-for _,_section in pairs(lead.section)do
-local sectionmember=_section
-for _,_element in pairs(sectionmember.elements)do
-local element=_element
-if not element.recovered then
-return false
-end
-end
-end
-self:_RemoveFlightFromQueue(self.Qpattern,lead)
-if self:_InQueue(self.Qmarshal,lead.group)then
-self:E(self.lid..string.format("ERROR: lead flight group %s should not be in marshal queue",lead.groupname))
-self:_RemoveFlightFromMarshalQueue(lead,true)
-end
-if self:_InQueue(self.Qwaiting,lead.group)then
-self:E(self.lid..string.format("ERROR: lead flight group %s should not be in pattern queue",lead.groupname))
-self:_RemoveFlightFromQueue(self.Qwaiting,lead)
-end
-return true
-end
-function AIRBOSS:_AddFlightToPatternQueue(flight)
-table.insert(self.Qpattern,flight)
-flight.flag=-1
-flight.time=timer.getAbsTime()
-flight.recovered=false
-for _,elem in pairs(flight.elements)do
-elem.recoverd=false
-end
-for _,sec in pairs(flight.section)do
-sec.flag=-1
-sec.time=timer.getAbsTime()
-for _,elem in pairs(sec.elements)do
-elem.recoverd=false
-end
-end
-end
-function AIRBOSS:_RecoveredElement(unit)
-local element,idx,flight=self:_GetFlightElement(unit:GetName())
-if element then
-element.recovered=true
-end
-return flight
-end
-function AIRBOSS:_RemoveFlightFromMarshalQueue(flight,nopattern)
-local removed,idx=self:_RemoveFlightFromQueue(self.Qmarshal,flight)
-if removed then
-flight.holding=nil
-self:_CollapseMarshalStack(flight,nopattern)
-if flight.case==1 and#self.Qwaiting>0 then
-local nextflight=self.Qwaiting[1]
-local freestack=self:_GetFreeStack(nextflight.ai)
-if nextflight.ai then
-self:_MarshalAI(nextflight,freestack)
-else
-self:_MarshalPlayer(nextflight,freestack)
-end
-self:_RemoveFlightFromQueue(self.Qwaiting,nextflight)
-end
-end
-return removed,idx
-end
-function AIRBOSS:_RemoveFlightFromQueue(queue,flight)
-for i,_flight in pairs(queue)do
-local qflight=_flight
-if qflight.groupname==flight.groupname then
-self:T(self.lid..string.format("Removing flight group %s from queue.",flight.groupname))
-table.remove(queue,i)
-return true,i
-end
-end
-return false,nil
-end
-function AIRBOSS:_RemoveUnitFromFlight(unit)
-if unit and unit:IsInstanceOf("UNIT")then
-local group=unit:GetGroup()
-if group then
-local flight=self:_GetFlightFromGroupInQueue(group,self.flights)
-if flight then
-local removed=self:_RemoveFlightElement(unit:GetName())
-if removed then
-local _,nunits=self:_GetFlightUnits(flight,not flight.ai)
-local nelements=#flight.elements
-self:T(self.lid..string.format("Removed unit %s: nunits=%d, nelements=%d",unit:GetName(),nunits,nelements))
-if nunits==0 or nelements==0 then
-self:_RemoveFlight(flight)
-end
-end
-end
-end
-end
-end
-function AIRBOSS:_RemoveFlightFromSection(flight)
-if flight.name~=flight.seclead then
-local lead=self.players[flight.seclead]
-if lead then
-for i,sec in pairs(lead.section)do
-local sectionmember=sec
-if sectionmember.name==flight.name then
-table.remove(lead.section,i)
-break
-end
-end
-end
-end
-end
-function AIRBOSS:_UpdateFlightSection(flight)
-if flight.seclead==flight.name then
-if#flight.section>=1 then
-local newlead=flight.section[1]
-newlead.seclead=newlead.name
-for i=2,#flight.section do
-local member=flight.section[i]
-table.insert(newlead.section,member)
-member.seclead=newlead.name
-end
-end
-flight.section={}
-else
-self:_RemoveFlightFromSection(flight)
-end
-end
-function AIRBOSS:_RemoveFlight(flight,completely)
-self:F(self.lid..string.format("Removing flight %s, ai=%s completely=%s.",tostring(flight.groupname),tostring(flight.ai),tostring(completely)))
-self:_RemoveFlightFromMarshalQueue(flight,true)
-self:_RemoveFlightFromQueue(self.Qpattern,flight)
-self:_RemoveFlightFromQueue(self.Qwaiting,flight)
-self:_RemoveFlightFromQueue(self.Qspinning,flight)
-if flight.ai then
-self:_RemoveFlightFromQueue(self.flights,flight)
-else
-local grades=self.playerscores[flight.name]
-if grades and#grades>0 then
-while#grades>0 and grades[#grades].finalscore==nil do
-table.remove(grades,#grades)
-end
-end
-if completely then
-self:_UpdateFlightSection(flight)
-self:_RemoveFlightFromQueue(self.flights,flight)
-local playerdata=self.players[flight.name]
-if playerdata then
-self:T(self.lid..string.format("Removing player %s completely.",flight.name))
-self.players[flight.name]=nil
-end
-flight=nil
-else
-self:_SetPlayerStep(flight,AIRBOSS.PatternStep.UNDEFINED)
-for _,sectionmember in pairs(flight.section)do
-self:_SetPlayerStep(sectionmember,AIRBOSS.PatternStep.UNDEFINED)
-self:_RemoveFlightFromQueue(self.Qspinning,sectionmember)
-end
-self:_RemoveFlightFromSection(flight)
-end
-end
-end
-function AIRBOSS:_CheckPlayerStatus()
-for _playerName,_playerData in pairs(self.players)do
-local playerData=_playerData
-if playerData then
-local unit=playerData.unit
-if unit and unit:IsAlive()then
-if unit:IsInZone(self.zoneCCA)then
-if playerData.attitudemonitor then
-self:_AttitudeMonitor(playerData)
-end
-self:_CheckPlayerPatternDistance(playerData)
-self:_CheckFoulDeck(playerData)
-if playerData.step==AIRBOSS.PatternStep.UNDEFINED then
-elseif playerData.step==AIRBOSS.PatternStep.REFUELING then
-elseif playerData.step==AIRBOSS.PatternStep.SPINNING then
-self:_Spinning(playerData)
-elseif playerData.step==AIRBOSS.PatternStep.HOLDING then
-self:_Holding(playerData)
-elseif playerData.step==AIRBOSS.PatternStep.WAITING then
-self:_Waiting(playerData)
-elseif playerData.step==AIRBOSS.PatternStep.COMMENCING then
-self:_Commencing(playerData,true)
-elseif playerData.step==AIRBOSS.PatternStep.BOLTER then
-self:_BolterPattern(playerData)
-elseif playerData.step==AIRBOSS.PatternStep.PLATFORM then
-self:_Platform(playerData)
-elseif playerData.step==AIRBOSS.PatternStep.ARCIN then
-self:_ArcInTurn(playerData)
-elseif playerData.step==AIRBOSS.PatternStep.ARCOUT then
-self:_ArcOutTurn(playerData)
-elseif playerData.step==AIRBOSS.PatternStep.DIRTYUP then
-self:_DirtyUp(playerData)
-elseif playerData.step==AIRBOSS.PatternStep.BULLSEYE then
-self:_Bullseye(playerData)
-elseif playerData.step==AIRBOSS.PatternStep.INITIAL then
-self:_Initial(playerData)
-elseif playerData.step==AIRBOSS.PatternStep.BREAKENTRY then
-self:_BreakEntry(playerData)
-elseif playerData.step==AIRBOSS.PatternStep.EARLYBREAK then
-self:_Break(playerData,AIRBOSS.PatternStep.EARLYBREAK)
-elseif playerData.step==AIRBOSS.PatternStep.LATEBREAK then
-self:_Break(playerData,AIRBOSS.PatternStep.LATEBREAK)
-elseif playerData.step==AIRBOSS.PatternStep.ABEAM then
-self:_Abeam(playerData)
-elseif playerData.step==AIRBOSS.PatternStep.NINETY then
-self:_CheckForLongDownwind(playerData)
-self:_Ninety(playerData)
-elseif playerData.step==AIRBOSS.PatternStep.WAKE then
-self:_Wake(playerData)
-elseif playerData.step==AIRBOSS.PatternStep.EMERGENCY then
-self:_Final(playerData,true)
-elseif playerData.step==AIRBOSS.PatternStep.FINAL then
-self:_Final(playerData)
-elseif playerData.step==AIRBOSS.PatternStep.GROOVE_XX or
-playerData.step==AIRBOSS.PatternStep.GROOVE_IM or
-playerData.step==AIRBOSS.PatternStep.GROOVE_IC or
-playerData.step==AIRBOSS.PatternStep.GROOVE_AR or
-playerData.step==AIRBOSS.PatternStep.GROOVE_AL or
-playerData.step==AIRBOSS.PatternStep.GROOVE_LC or
-playerData.step==AIRBOSS.PatternStep.GROOVE_IW then
-self:_Groove(playerData)
-elseif playerData.step==AIRBOSS.PatternStep.DEBRIEF then
-playerData.debriefschedulerID=self:ScheduleOnce(5,self._Debrief,self,playerData)
-playerData.step=AIRBOSS.PatternStep.UNDEFINED
-else
-self:E(self.lid..string.format("ERROR: Unknown player step %s. Please report!",tostring(playerData.step)))
-end
-self:_CheckMissedStepOnEntry(playerData)
-else
-self:T2(self.lid.."WARNING: Player unit not inside the CCA!")
-end
-else
-self:T(self.lid.."WARNING: Player unit is not alive!")
-end
-end
-end
-end
-function AIRBOSS:_CheckMissedStepOnEntry(playerData)
-local rightcase=playerData.case>1
-local rightqueue=self:_InQueue(self.Qpattern,playerData.group)
-local rightflag=playerData.flag~=-42
-local step=playerData.step
-local missedstep=step==AIRBOSS.PatternStep.PLATFORM or step==AIRBOSS.PatternStep.ARCIN or step==AIRBOSS.PatternStep.ARCOUT or step==AIRBOSS.PatternStep.DIRTYUP
-if rightcase and rightqueue and rightflag then
-local zone=nil
-if playerData.case==2 and missedstep then
-zone=self:_GetZoneInitial(playerData.case)
-elseif playerData.case==3 and missedstep then
-zone=self:_GetZoneBullseye(playerData.case)
-end
-if zone then
-local inzone=playerData.unit:IsInZone(zone)
-local relheading=self:_GetRelativeHeading(playerData.unit,false)
-if inzone and math.abs(relheading)<60 then
-local text=string.format("you missed an important step in the pattern!\nYour next step would have been %s.",playerData.step)
-self:MessageToPlayer(playerData,text,"AIRBOSS",nil,5)
-if playerData.case==2 then
-playerData.step=AIRBOSS.PatternStep.INITIAL
-elseif playerData.case==3 then
-playerData.step=AIRBOSS.PatternStep.BULLSEYE
-end
-playerData.flag=-42
-end
-end
-end
-end
-function AIRBOSS:_SetTimeInGroove(playerData)
-if playerData.TIG0 then
-playerData.Tgroove=timer.getTime()-playerData.TIG0
-else
-playerData.Tgroove=999
-end
-end
-function AIRBOSS:_GetTimeInGroove(playerData)
-local Tgroove=999
-if playerData.TIG0 then
-Tgroove=timer.getTime()-playerData.TIG0
-end
-return Tgroove
-end
-function AIRBOSS:OnEventBirth(EventData)
-self:F3({eventbirth=EventData})
-if EventData==nil then
-self:E(self.lid.."ERROR: EventData=nil in event BIRTH!")
-self:E(EventData)
-return
-end
-if EventData.IniUnit==nil then
-self:E(self.lid.."ERROR: EventData.IniUnit=nil in event BIRTH!")
-self:E(EventData)
-return
-end
-if EventData.IniObjectCategory~=Object.Category.UNIT then return end
-local _unitName=EventData.IniUnitName
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-self:T(self.lid.."BIRTH: unit = "..tostring(EventData.IniUnitName))
-self:T(self.lid.."BIRTH: group = "..tostring(EventData.IniGroupName))
-self:T(self.lid.."BIRTH: player = "..tostring(_playername))
-if _unit and _playername then
-local _uid=_unit:GetID()
-local _group=_unit:GetGroup()
-local _callsign=_unit:GetCallsign()
-local text=string.format("Pilot %s, callsign %s entered unit %s of group %s.",_playername,_callsign,_unitName,_group:GetName())
-self:T(self.lid..text)
-MESSAGE:New(text,5):ToAllIf(self.Debug)
-local rightaircraft=self:_IsCarrierAircraft(_unit)
-if rightaircraft==false then
-local text=string.format("Player aircraft type %s not supported by AIRBOSS class.",_unit:GetTypeName())
-MESSAGE:New(text,30):ToAllIf(self.Debug)
-self:T2(self.lid..text)
-return
-end
-if self:GetCoalition()~=_unit:GetCoalition()then
-local text=string.format("Player entered aircraft of other coalition.")
-MESSAGE:New(text,30):ToAllIf(self.Debug)
-self:T(self.lid..text)
-return
-end
-self:_AddF10Commands(_unitName)
-self:ScheduleOnce(1,self._NewPlayer,self,_unitName)
-end
-end
-function AIRBOSS:OnEventLand(EventData)
-self:F3({eventland=EventData})
-if EventData==nil then
-self:E(self.lid.."ERROR: EventData=nil in event LAND!")
-self:E(EventData)
-return
-end
-if EventData.IniUnit==nil then
-self:E(self.lid.."ERROR: EventData.IniUnit=nil in event LAND!")
-self:E(EventData)
-return
-end
-local _unitName=EventData.IniUnitName
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-self:T(self.lid.."LAND: unit = "..tostring(EventData.IniUnitName))
-self:T(self.lid.."LAND: group = "..tostring(EventData.IniGroupName))
-self:T(self.lid.."LAND: player = "..tostring(_playername))
-local airbase=EventData.Place
-if airbase==nil then
-return
-end
-local airbasename=tostring(airbase:GetName())
-if airbasename==self.airbase:GetName()then
-local stern=self:_GetSternCoord()
-local zoneCarrier=self:_GetZoneCarrierBox()
-if _unit and _playername then
-local _uid=_unit:GetID()
-local _group=_unit:GetGroup()
-local _callsign=_unit:GetCallsign()
-local text=string.format("Player %s, callsign %s unit %s (ID=%d) of group %s landed at airbase %s",_playername,_callsign,_unitName,_uid,_group:GetName(),airbasename)
-self:T(self.lid..text)
-MESSAGE:New(text,5,"DEBUG"):ToAllIf(self.Debug)
-local playerData=self.players[_playername]
-if playerData==nil then
-self:E(self.lid..string.format("ERROR: playerData nil in landing event. unit=%s player=%s",tostring(_unitName),tostring(_playername)))
-return
-end
-if _unit:IsInZone(zoneCarrier)then
-if not playerData.valid then
-local text=string.format("you missed at least one important step in the pattern!\nYour next step would have been %s.\nThis pass is INVALID.",playerData.step)
-self:MessageToPlayer(playerData,text,"AIRBOSS",nil,30,true,5)
-self:_RemoveFlightFromMarshalQueue(playerData,true)
-self:_RemoveFlightFromQueue(self.Qpattern,playerData)
-self:_RemoveFlightFromQueue(self.Qwaiting,playerData)
-self:_RemoveFlightFromQueue(self.Qspinning,playerData)
-self:_InitPlayer(playerData)
-return
-end
-if playerData.landed then
-self:E(self.lid..string.format("Player %s just landed a second time.",_playername))
-else
-playerData.landed=true
-playerData.attitudemonitor=false
-local coord=playerData.unit:GetCoordinate()
-local X,Z,rho,phi=self:_GetDistances(_unit)
-local dist=coord:Get2DDistance(stern)
-if self.Debug and false then
-local lp=coord:MarkToAll("Landing coord.")
-coord:SmokeGreen()
-end
-self:_SetTimeInGroove(playerData)
-local text=string.format("Player %s AC type %s landed at dist=%.1f m. Tgroove=%.1f sec.",playerData.name,playerData.actype,dist,self:_GetTimeInGroove(playerData))
-text=text..string.format(" X=%.1f m, Z=%.1f m, rho=%.1f m.",X,Z,rho)
-self:T(self.lid..text)
-if self.carriertype==AIRBOSS.CarrierType.INVINCIBLE or self.carriertype==AIRBOSS.CarrierType.HERMES or self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS or self.carriertype==AIRBOSS.CarrierType.CANBERRA then
-self:RadioTransmission(self.LSORadio,self.LSOCall.IDLE,false,1,nil,true)
-self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.DEBRIEF)
-else
-self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.UNDEFINED)
-self:ScheduleOnce(1,self._Trapped,self,playerData)
-end
-end
-else
-if playerData then
-self:E(self.lid..string.format("Player %s did not land in carrier box zone. Maybe in the water near the carrier?",playerData.name))
-end
-end
-else
-if self.carriertype~=AIRBOSS.CarrierType.INVINCIBLE or self.carriertype~=AIRBOSS.CarrierType.HERMES or self.carriertype~=AIRBOSS.CarrierType.TARAWA or self.carriertype~=AIRBOSS.CarrierType.AMERICA or self.carriertype~=AIRBOSS.CarrierType.JCARLOS or self.carriertype~=AIRBOSS.CarrierType.CANBERRA then
-local coord=EventData.IniUnit:GetCoordinate()
-local dist=coord:Get2DDistance(self:GetCoordinate())
-local wire=self:_GetWire(coord,0)
-local _type=EventData.IniUnit:GetTypeName()
-local text=string.format("AI unit %s of type %s landed at dist=%.1f m. Trapped wire=%d.",_unitName,_type,dist,wire)
-self:T(self.lid..text)
-end
-local flight=self:_RecoveredElement(EventData.IniUnit)
-self:_CheckSectionRecovered(flight)
-end
-end
-end
-function AIRBOSS:OnEventEngineShutdown(EventData)
-self:F3({eventengineshutdown=EventData})
-if EventData==nil then
-self:E(self.lid.."ERROR: EventData=nil in event ENGINESHUTDOWN!")
-self:E(EventData)
-return
-end
-if EventData.IniUnit==nil then
-self:E(self.lid.."ERROR: EventData.IniUnit=nil in event ENGINESHUTDOWN!")
-self:E(EventData)
-return
-end
-local _unitName=EventData.IniUnitName
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-self:T3(self.lid.."ENGINESHUTDOWN: unit = "..tostring(EventData.IniUnitName))
-self:T3(self.lid.."ENGINESHUTDOWN: group = "..tostring(EventData.IniGroupName))
-self:T3(self.lid.."ENGINESHUTDOWN: player = "..tostring(_playername))
-if _unit and _playername then
-self:T(self.lid..string.format("Player %s shut down its engines!",_playername))
-else
-self:T(self.lid..string.format("AI unit %s shut down its engines!",_unitName))
-local flight=self:_GetFlightFromGroupInQueue(EventData.IniGroup,self.flights)
-if flight and flight.ai then
-local recovered=self:_CheckSectionRecovered(flight)
-if recovered then
-self:T(self.lid..string.format("AI group %s completely recovered. Despawning group after engine shutdown event as requested in 5 seconds.",tostring(EventData.IniGroupName)))
-self:_RemoveFlight(flight)
-local istanker=self.tanker and self.tanker.tanker:GetName()==EventData.IniGroupName
-local isawacs=self.awacs and self.awacs.tanker:GetName()==EventData.IniGroupName
-if self.despawnshutdown and not(istanker or isawacs)then
-EventData.IniGroup:Destroy(nil,5)
-end
-end
-end
-end
-end
-function AIRBOSS:OnEventTakeoff(EventData)
-self:F3({eventtakeoff=EventData})
-if EventData==nil then
-self:E(self.lid.."ERROR: EventData=nil in event TAKEOFF!")
-self:E(EventData)
-return
-end
-if EventData.IniUnit==nil then
-self:E(self.lid.."ERROR: EventData.IniUnit=nil in event TAKEOFF!")
-self:E(EventData)
-return
-end
-local _unitName=EventData.IniUnitName
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-self:T3(self.lid.."TAKEOFF: unit = "..tostring(EventData.IniUnitName))
-self:T3(self.lid.."TAKEOFF: group = "..tostring(EventData.IniGroupName))
-self:T3(self.lid.."TAKEOFF: player = "..tostring(_playername))
-local airbase=EventData.Place
-local airbasename="unknown"
-if airbase then
-airbasename=airbase:GetName()
-end
-if airbasename==self.airbase:GetName()then
-if _unit and _playername then
-self:T(self.lid..string.format("Player %s took off at %s!",_playername,airbasename))
-else
-self:T2(self.lid..string.format("AI unit %s took off at %s!",_unitName,airbasename))
-local flight=self:_GetFlightFromGroupInQueue(EventData.IniGroup,self.flights)
-if flight then
-for _,elem in pairs(flight.elements)do
-local element=elem
-element.ballcall=false
-element.recovered=nil
-end
-end
-end
-end
-end
-function AIRBOSS:OnEventCrash(EventData)
-self:F3({eventcrash=EventData})
-if EventData==nil then
-self:E(self.lid.."ERROR: EventData=nil in event CRASH!")
-self:E(EventData)
-return
-end
-if EventData.IniUnit==nil then
-self:E(self.lid.."ERROR: EventData.IniUnit=nil in event CRASH!")
-self:E(EventData)
-return
-end
-local _unitName=EventData.IniUnitName
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-self:T3(self.lid.."CRASH: unit = "..tostring(EventData.IniUnitName))
-self:T3(self.lid.."CRASH: group = "..tostring(EventData.IniGroupName))
-self:T3(self.lid.."CARSH: player = "..tostring(_playername))
-if _unit and _playername then
-self:T(self.lid..string.format("Player %s crashed!",_playername))
-local flight=self.players[_playername]
-if flight then
-self:_RemoveFlight(flight,true)
-end
-else
-self:T2(self.lid..string.format("AI unit %s crashed!",EventData.IniUnitName))
-self:_RemoveUnitFromFlight(EventData.IniUnit)
-end
-end
-function AIRBOSS:OnEventEjection(EventData)
-self:F3({eventland=EventData})
-if EventData==nil then
-self:E(self.lid.."ERROR: EventData=nil in event EJECTION!")
-self:E(EventData)
-return
-end
-if EventData.IniUnit==nil then
-self:E(self.lid.."ERROR: EventData.IniUnit=nil in event EJECTION!")
-self:E(EventData)
-return
-end
-local _unitName=EventData.IniUnitName
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-self:T3(self.lid.."EJECT: unit = "..tostring(EventData.IniUnitName))
-self:T3(self.lid.."EJECT: group = "..tostring(EventData.IniGroupName))
-self:T3(self.lid.."EJECT: player = "..tostring(_playername))
-if _unit and _playername then
-self:T(self.lid..string.format("Player %s ejected!",_playername))
-local flight=self.players[_playername]
-if flight then
-self:_RemoveFlight(flight,true)
-end
-else
-self:T(self.lid..string.format("AI unit %s ejected!",EventData.IniUnitName))
-self:_RemoveUnitFromFlight(EventData.IniUnit)
-local flight=self:_GetFlightFromGroupInQueue(EventData.IniGroup,self.flights)
-self:_CheckSectionRecovered(flight)
-end
-end
-function AIRBOSS:OnEventRemoveUnit(EventData)
-self:F3({eventland=EventData})
-if EventData==nil then
-self:E(self.lid.."ERROR: EventData=nil in event REMOVEUNIT!")
-self:E(EventData)
-return
-end
-if EventData.IniUnit==nil then
-self:E(self.lid.."ERROR: EventData.IniUnit=nil in event REMOVEUNIT!")
-self:E(EventData)
-return
-end
-local _unitName=EventData.IniUnitName
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-self:T3(self.lid.."EJECT: unit = "..tostring(EventData.IniUnitName))
-self:T3(self.lid.."EJECT: group = "..tostring(EventData.IniGroupName))
-self:T3(self.lid.."EJECT: player = "..tostring(_playername))
-if _unit and _playername then
-self:T(self.lid..string.format("Player %s removed!",_playername))
-local flight=self.players[_playername]
-if flight then
-self:_RemoveFlight(flight,true)
-end
-else
-self:T(self.lid..string.format("AI unit %s removed!",EventData.IniUnitName))
-self:_RemoveUnitFromFlight(EventData.IniUnit)
-local flight=self:_GetFlightFromGroupInQueue(EventData.IniGroup,self.flights)
-self:_CheckSectionRecovered(flight)
-end
-end
-function AIRBOSS:_PlayerLeft(EventData)
-self:F3({eventleave=EventData})
-if EventData==nil then
-self:E(self.lid.."ERROR: EventData=nil in event PLAYERLEFTUNIT!")
-self:E(EventData)
-return
-end
-if EventData.IniUnit==nil then
-self:E(self.lid.."ERROR: EventData.IniUnit=nil in event PLAYERLEFTUNIT!")
-self:E(EventData)
-return
-end
-local _unitName=EventData.IniUnitName
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-self:T3(self.lid.."PLAYERLEAVEUNIT: unit = "..tostring(EventData.IniUnitName))
-self:T3(self.lid.."PLAYERLEAVEUNIT: group = "..tostring(EventData.IniGroupName))
-self:T3(self.lid.."PLAYERLEAVEUNIT: player = "..tostring(_playername))
-if _unit and _playername then
-self:T(self.lid..string.format("Player %s left unit %s!",_playername,_unitName))
-local flight=self.players[_playername]
-if flight then
-self:_RemoveFlight(flight,true)
-end
-end
-end
-function AIRBOSS:OnEventMissionEnd(EventData)
-self:T3(self.lid.."Mission Ended")
-end
-function AIRBOSS:_Spinning(playerData)
-local SpinIt={}
-SpinIt.name="Spinning"
-SpinIt.Xmin=-UTILS.NMToMeters(6)
-SpinIt.Xmax=UTILS.NMToMeters(5)
-SpinIt.Zmin=-UTILS.NMToMeters(6)
-SpinIt.Zmax=UTILS.NMToMeters(2)
-SpinIt.LimitXmin=-100
-SpinIt.LimitXmax=nil
-SpinIt.LimitZmin=-UTILS.NMToMeters(1)
-SpinIt.LimitZmax=nil
-local X,Z,rho,phi=self:_GetDistances(playerData.unit)
-if self:_CheckLimits(X,Z,SpinIt)then
-self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.INITIAL)
-self:_RemoveFlightFromQueue(self.Qspinning,playerData)
-end
-end
-function AIRBOSS:_Waiting(playerData)
-local radius=UTILS.NMToMeters(10)
-local zone=ZONE_RADIUS:New("Carrier 10 NM Zone",self.carrier:GetVec2(),radius)
-local inzone=playerData.unit:IsInZone(zone)
-local Twaiting=timer.getAbsTime()-playerData.time
-if inzone and Twaiting>3*60 and not playerData.warning then
-local text=string.format("You are supposed to wait outside the 10 NM zone.")
-self:MessageToPlayer(playerData,text,"AIRBOSS")
-playerData.warning=true
-end
-if inzone==false and playerData.warning==true then
-playerData.warning=nil
-end
-end
-function AIRBOSS:_Holding(playerData)
-local unit=playerData.unit
-local stack=playerData.flag
-if stack<=0 then
-local text=string.format("ERROR: player %s in step %s is holding but has stack=%s (<=0)",playerData.name,playerData.step,tostring(stack))
-self:E(self.lid..text)
-end
-local patternalt=self:_GetMarshalAltitude(stack,playerData.case)
-local playeralt=unit:GetAltitude()
-local zoneHolding=self:_GetZoneHolding(playerData.case,stack)
-if zoneHolding==nil then
-self:E(self.lid.."ERROR: zoneHolding is nil!")
-self:E({playerData=playerData})
-return
-end
-local inholdingzone=unit:IsInZone(zoneHolding)
-local altdiff=playeralt-patternalt
-local altgood=UTILS.FeetToMeters(500)
-if playerData.difficulty==AIRBOSS.Difficulty.HARD then
-altgood=UTILS.FeetToMeters(200)
-elseif playerData.difficulty==AIRBOSS.Difficulty.NORMAL then
-altgood=UTILS.FeetToMeters(350)
-elseif playerData.difficulty==AIRBOSS.Difficulty.EASY then
-altgood=UTILS.FeetToMeters(500)
-end
-local altback=altgood*0.5
-local justcollapsed=false
-if self.Tcollapse then
-local dT=timer.getTime()-self.Tcollapse
-if dT<=90 then
-justcollapsed=true
-end
-end
-local goodalt=math.abs(altdiff)altgood then
-if not playerData.warning then
-text=text..string.format("You left your assigned altitude. Descent to angels %d.",angels)
-playerData.warning=true
-end
-elseif altdiff<-altgood then
-if not playerData.warning then
-text=text..string.format("You left your assigned altitude. Climb to angels %d.",angels)
-playerData.warning=true
-end
-end
-end
-if playerData.warning and math.abs(altdiff)<=altback then
-text=text..string.format("Altitude is looking good again.")
-playerData.warning=nil
-end
-elseif playerData.holding==false then
-if inholdingzone then
-text=text..string.format("You are back in the holding zone. Now stay there!")
-playerData.holding=true
-else
-self:T3("Player still outside the holding zone. What are you doing man?!")
-end
-elseif playerData.holding==nil then
-if inholdingzone then
-playerData.holding=true
-text=text..string.format("You arrived at the holding zone.")
-if goodalt then
-text=text..string.format(" Altitude is good.")
-else
-if altdiff<0 then
-text=text..string.format(" But you're too low.")
-else
-text=text..string.format(" But you're too high.")
-end
-text=text..string.format("\nCurrently assigned altitude is %d ft.",UTILS.MetersToFeet(patternalt))
-playerData.warning=true
-end
-else
-self:T3("Waiting for player to arrive in the holding zone.")
-end
-end
-if playerData.showhints then
-self:MessageToPlayer(playerData,text,"MARSHAL")
-end
-end
-function AIRBOSS:_Commencing(playerData,zonecheck)
-if zonecheck then
-local zoneCommence=self:_GetZoneCommence(playerData.case,playerData.flag)
-local inzone=playerData.unit:IsInZone(zoneCommence)
-if not inzone then
-if timer.getAbsTime()-playerData.time>180 then
-self:_MarshalCallClearedForRecovery(playerData.onboard,playerData.case)
-playerData.time=timer.getAbsTime()
-end
-return
-end
-end
-self:_RemoveFlightFromMarshalQueue(playerData)
-self:_InitPlayer(playerData)
-if playerData.difficulty~=AIRBOSS.Difficulty.HARD then
-local text=""
-if playerData.case==1 then
-text=text.."Proceed to initial."
-else
-text=text.."Descent to platform."
-if playerData.difficulty==AIRBOSS.Difficulty.EASY and playerData.showhints then
-text=text.." VSI 4000 ft/min until you reach 5000 ft."
-end
-end
-self:MessageToPlayer(playerData,text,"MARSHAL")
-end
-local nextstep
-if playerData.case==1 then
-nextstep=AIRBOSS.PatternStep.INITIAL
-else
-nextstep=AIRBOSS.PatternStep.PLATFORM
-end
-self:_SetPlayerStep(playerData,nextstep)
-for i,_flight in pairs(playerData.section)do
-local flight=_flight
-self:_Commencing(flight,false)
-end
-end
-function AIRBOSS:_Initial(playerData)
-local inzone=playerData.unit:IsInZone(self:_GetZoneInitial(playerData.case))
-local relheading=self:_GetRelativeHeading(playerData.unit,false)
-local altitude=playerData.unit:GetAltitude()
-if inzone and math.abs(relheading)<60 and altitude<=self.initialmaxalt then
-if playerData.showhints then
-local hint=string.format("Initial")
-if playerData.difficulty==AIRBOSS.Difficulty.EASY and playerData.actype~=AIRBOSS.AircraftCarrier.AV8B then
-if playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B then
-hint=hint.." - Hook down, SAS on, Wing Sweep 68°!"
-else
-hint=hint.." - Hook down!"
-end
-end
-self:MessageToPlayer(playerData,hint,"MARSHAL")
-end
-self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.BREAKENTRY)
-return true
-end
-return false
-end
-function AIRBOSS:_CheckCorridor(playerData)
-local validzone=self:_GetZoneCorridor(playerData.case)
-local invalid=playerData.unit:IsNotInZone(validzone)
-if invalid and(not playerData.warning)then
-self:MessageToPlayer(playerData,"you left the approach corridor!","AIRBOSS")
-playerData.warning=true
-end
-if(not invalid)and playerData.warning then
-self:MessageToPlayer(playerData,"you're back in the approach corridor.","AIRBOSS")
-playerData.warning=false
-end
-end
-function AIRBOSS:_Platform(playerData)
-self:_CheckCorridor(playerData)
-local inzone=playerData.unit:IsInZone(self:_GetZonePlatform(playerData.case))
-if inzone then
-self:_PlayerHint(playerData)
-local nextstep
-if math.abs(self.holdingoffset)>0 and playerData.case>1 then
-nextstep=AIRBOSS.PatternStep.ARCIN
-else
-if playerData.case==2 then
-nextstep=AIRBOSS.PatternStep.INITIAL
-elseif playerData.case==3 then
-nextstep=AIRBOSS.PatternStep.DIRTYUP
-end
-end
-self:_SetPlayerStep(playerData,nextstep)
-end
-end
-function AIRBOSS:_ArcInTurn(playerData)
-self:_CheckCorridor(playerData)
-local inzone=playerData.unit:IsInZone(self:_GetZoneArcIn(playerData.case))
-if inzone then
-self:_PlayerHint(playerData)
-self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.ARCOUT)
-end
-end
-function AIRBOSS:_ArcOutTurn(playerData)
-self:_CheckCorridor(playerData)
-local inzone=playerData.unit:IsInZone(self:_GetZoneArcOut(playerData.case))
-if inzone then
-self:_PlayerHint(playerData)
-local nextstep
-if playerData.case==3 then
-nextstep=AIRBOSS.PatternStep.DIRTYUP
-else
-nextstep=AIRBOSS.PatternStep.INITIAL
-end
-self:_SetPlayerStep(playerData,nextstep)
-end
-end
-function AIRBOSS:_DirtyUp(playerData)
-self:_CheckCorridor(playerData)
-local inzone=playerData.unit:IsInZone(self:_GetZoneDirtyUp(playerData.case))
-if inzone then
-self:_PlayerHint(playerData)
-if playerData.actype==AIRBOSS.AircraftCarrier.HORNET
-or playerData.actype==AIRBOSS.AircraftCarrier.F14A
-or playerData.actype==AIRBOSS.AircraftCarrier.F14B
-or playerData.actype==AIRBOSS.AircraftCarrier.RHINOE
-or playerData.actype==AIRBOSS.AircraftCarrier.RHINOF
-or playerData.actype==AIRBOSS.AircraftCarrier.GROWLER
-then
-local callsay=self:_NewRadioCall(self.MarshalCall.SAYNEEDLES,nil,nil,5,playerData.onboard)
-local callfly=self:_NewRadioCall(self.MarshalCall.FLYNEEDLES,nil,nil,5,playerData.onboard)
-self:RadioTransmission(self.MarshalRadio,callsay,false,55,nil,true)
-self:RadioTransmission(self.MarshalRadio,callfly,false,60,nil,true)
-end
-self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.BULLSEYE)
-end
-end
-function AIRBOSS:_Bullseye(playerData)
-self:_CheckCorridor(playerData)
-local inzone=playerData.unit:IsInZone(self:_GetZoneBullseye(playerData.case))
-local relheading=self:_GetRelativeHeading(playerData.unit,true)
-if inzone and math.abs(relheading)<60 then
-self:_PlayerHint(playerData)
-if playerData.actype==AIRBOSS.AircraftCarrier.AV8B and self.carriertype==AIRBOSS.CarrierType.JCARLOS then
-self:RadioTransmission(self.LSORadio,self.LSOCall.EXPECTSPOT5,nil,nil,nil,true)
-elseif playerData.actype==AIRBOSS.AircraftCarrier.AV8B and self.carriertype==AIRBOSS.CarrierType.CANBERRA then
-self:RadioTransmission(self.LSORadio,self.LSOCall.EXPECTSPOT5,nil,nil,nil,true)
-elseif playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
-self:RadioTransmission(self.LSORadio,self.LSOCall.EXPECTSPOT75,nil,nil,nil,true)
-end
-self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.GROOVE_XX)
-end
-end
-function AIRBOSS:_BolterPattern(playerData)
-local X,Z,rho,phi=self:_GetDistances(playerData.unit)
-local Bolter={}
-Bolter.name="Bolter Pattern"
-Bolter.Xmin=-UTILS.NMToMeters(5)
-Bolter.Xmax=UTILS.NMToMeters(3)
-Bolter.Zmin=-UTILS.NMToMeters(5)
-Bolter.Zmax=UTILS.NMToMeters(1)
-Bolter.LimitXmin=100
-Bolter.LimitXmax=nil
-Bolter.LimitZmin=nil
-Bolter.LimitZmax=nil
-if self:_CheckLimits(X,Z,Bolter)then
-local nextstep
-if playerData.case<3 then
-nextstep=AIRBOSS.PatternStep.ABEAM
-else
-nextstep=AIRBOSS.PatternStep.BULLSEYE
-end
-self:_SetPlayerStep(playerData,nextstep)
-end
-end
-function AIRBOSS:_BreakEntry(playerData)
-local X,Z=self:_GetDistances(playerData.unit)
-if self:_CheckAbort(X,Z,self.BreakEntry)then
-self:_AbortPattern(playerData,X,Z,self.BreakEntry,true)
-return
-end
-if self:_CheckLimits(X,Z,self.BreakEntry)then
-self:_PlayerHint(playerData)
-self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.EARLYBREAK)
-end
-end
-function AIRBOSS:_Break(playerData,part)
-local X,Z=self:_GetDistances(playerData.unit)
-local breakpoint=self.BreakEarly
-if part==AIRBOSS.PatternStep.LATEBREAK then
-breakpoint=self.BreakLate
-end
-if self:_CheckAbort(X,Z,breakpoint)then
-self:_AbortPattern(playerData,X,Z,breakpoint,true)
-return
-end
-local tooclose=false
-if part==AIRBOSS.PatternStep.LATEBREAK then
-local close=0.8
-if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
-close=0.5
-end
-if X<0 and Z90 and self:_CheckLimits(X,Z,self.Wake)then
-self:MessageToPlayer(playerData,"you are already at the wake and have not passed the 90. Turn faster next time!","LSO")
-self:RadioTransmission(self.LSORadio,self.LSOCall.DEPARTANDREENTER,nil,nil,nil,true)
-playerData.wop=true
-self:_AddToDebrief(playerData,"Overshoot at wake - Pattern Waveoff!")
-self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.DEBRIEF)
-end
-end
-function AIRBOSS:_Wake(playerData)
-local X,Z=self:_GetDistances(playerData.unit)
-if self:_CheckAbort(X,Z,self.Wake)then
-self:_AbortPattern(playerData,X,Z,self.Wake,true)
-return
-end
-if self:_CheckLimits(X,Z,self.Wake)then
-self:_PlayerHint(playerData)
-self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.FINAL)
-end
-end
-function AIRBOSS:_GetGrooveData(playerData)
-local X,Z=self:_GetDistances(playerData.unit)
-local stern=self:_GetSternCoord()
-local rho=stern:Get2DDistance(playerData.unit:GetCoordinate())
-local astern=X=RAR and rho<=RIC and not playerData.waveoff then
-local waveoff=self:_CheckWaveOff(glideslopeError,lineupError,AoA,playerData)
-if waveoff then
-self:T3(self.lid..string.format("Waveoff distance rho=%.1f m",rho))
-self:RadioTransmission(self.LSORadio,self.LSOCall.WAVEOFF,nil,nil,nil,true)
-playerData.Tlso=timer.getTime()
-playerData.waveoff=true
-return
-end
-end
-groovedata.Step=playerData.step
-if rho>=RAR and 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
-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))
-gd.LUE=lineupError
-end
-if gd.GSE>0.4 and glideslopeError<-0.3 then
-gd.FlyThrough="\\"
-self:T(self.lid..string.format("Got Fly through DOWN at step %s, d=%.3f: Max GSE=%.3f, lower GSE=%.3f",gs,d,gd.GSE,glideslopeError))
-elseif gd.GSE<-0.3 and glideslopeError>0.4 then
-gd.FlyThrough="/"
-self:E(self.lid..string.format("Got Fly through UP at step %s, d=%.3f: Min GSE=%.3f, lower GSE=%.3f",gs,d,gd.GSE,glideslopeError))
-end
-if math.abs(glideslopeError)>math.abs(gd.GSE)then
-self:T(self.lid..string.format("Got bigger GSE at step %s, d=%.3f: GSE |%.3f|>|%.3f|",gs,d,glideslopeError,gd.GSE))
-gd.GSE=glideslopeError
-end
-local aircraftaoa=self:_GetAircraftAoA(playerData)
-local aoaopt=aircraftaoa.OnSpeed
-if math.abs(AoA-aoaopt)>math.abs(gd.AoA-aoaopt)then
-self:T(self.lid..string.format("Got bigger AoA error at step %s, d=%.3f: AoA %.3f>%.3f.",gs,d,AoA,gd.AoA))
-gd.AoA=AoA
-end
-end
-local deltaT=timer.getTime()-playerData.Tlso
-local _advice=true
-if playerData.TIG0==nil and playerData.difficulty~=AIRBOSS.Difficulty.EASY then
-_advice=false
-end
-if deltaT>=self.LSOdT and _advice then
-self:_LSOadvice(playerData,glideslopeError,lineupError)
-end
-end
-if X>self.carrierparam.totlength+self.carrierparam.sterndist then
-if playerData.waveoff then
-if playerData.landed then
-self:_AddToDebrief(playerData,"You were waved off but landed anyway. Airboss wants to talk to you!")
-else
-self:_AddToDebrief(playerData,"You were waved off.")
-end
-elseif playerData.boltered then
-self:_AddToDebrief(playerData,"You boltered.")
-else
-self:T("Player was not waved off but flew past the carrier without landing ==> Own wave off!")
-self:_AddToDebrief(playerData,"Own waveoff.")
-playerData.owo=true
-end
-self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.DEBRIEF)
-end
-end
-function AIRBOSS:_CheckWaveOff(glideslopeError,lineupError,AoA,playerData)
-local waveoff=false
-local glMax=1.8
-local glMin=-1.2
-local luAbs=3.0
-if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
-glMax=2.6
-glMin=-2.2
-luAbs=4.1
-end
-if glideslopeError>glMax then
-local text=string.format("\n- Waveoff due to glideslope error %.2f > %.1f degrees!",glideslopeError,glMax)
-self:T(self.lid..string.format("%s: %s",playerData.name,text))
-self:_AddToDebrief(playerData,text)
-waveoff=true
-elseif glideslopeErrorluAbs then
-local text=string.format("\n- Waveoff due to line up error |%.1f| > %.1f degrees!",lineupError,luAbs)
-self:T(self.lid..string.format("%s: %s",playerData.name,text))
-self:_AddToDebrief(playerData,text)
-waveoff=true
-end
-if playerData.difficulty==AIRBOSS.Difficulty.HARD and playerData.actype~=AIRBOSS.AircraftCarrier.AV8B then
-local aoaac=self:_GetAircraftAoA(playerData)
-if AoAaoaac.SLOW then
-local text=string.format("\n- Waveoff due to AoA %.1f > %.1f!",AoA,aoaac.SLOW)
-self:T(self.lid..string.format("%s: %s",playerData.name,text))
-self:_AddToDebrief(playerData,text)
-waveoff=true
-end
-end
-return waveoff
-end
-function AIRBOSS:_CheckFoulDeck(playerData)
-local check=false
-if playerData.step==AIRBOSS.PatternStep.GROOVE_IM or playerData.step==AIRBOSS.PatternStep.GROOVE_IC then
-check=true
-end
-if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
-if playerData.step==AIRBOSS.PatternStep.GROOVE_AR or playerData.step==AIRBOSS.PatternStep.GROOVE_AL then
-check=true
-end
-end
-if playerData.wofd==true or check==false then
-return
-end
-local runway=self:_GetZoneRunwayBox()
-if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
-runway=self:_GetZoneLandingSpot()
-end
-local R=250
-self:T(self.lid..string.format("Foul deck check: Scanning Carrier Runway Area. Radius=%.1f m.",R))
-local _,_,_,unitscan=self:GetCoordinate():ScanObjects(R,true,false,false)
-local fouldeck=false
-local foulunit=nil
-for _,_unit in pairs(unitscan)do
-local unit=_unit
-local inzone=unit:IsInZone(runway)
-local isaircraft=unit:IsAir()
-local isairborn=unit:InAir()
-if inzone and isaircraft and not isairborn then
-local text=string.format("Unit %s on landing runway ==> Foul deck!",unit:GetName())
-self:T(self.lid..text)
-MESSAGE:New(text,10):ToAllIf(self.Debug)
-if self.Debug then
-runway:FlareZone(FLARECOLOR.Red,30)
-end
-fouldeck=true
-foulunit=unit
-end
-end
-if playerData and fouldeck then
-local text=string.format("Foul deck waveoff due to aircraft %s!",foulunit:GetName())
-self:T(self.lid..string.format("%s: %s",playerData.name,text))
-self:_AddToDebrief(playerData,text)
-self:RadioTransmission(self.LSORadio,self.LSOCall.FOULDECK,false,1)
-self:RadioTransmission(self.LSORadio,self.LSOCall.WAVEOFF,false,1.2,nil,true)
-if playerData.showhints then
-local text=string.format("overfly landing area and enter bolter pattern.")
-self:MessageToPlayer(playerData,text,"LSO",nil,nil,false,3)
-end
-playerData.wofd=true
-playerData.step=AIRBOSS.PatternStep.DEBRIEF
-playerData.warning=nil
-playerData.valid=false
-if foulunit then
-local foulflight=self:_GetFlightFromGroupInQueue(foulunit:GetGroup(),self.flights)
-if foulflight and not foulflight.ai then
-self:MessageToPlayer(foulflight,"move your ass from my runway. NOW!","AIRBOSS")
-end
-end
-end
-return fouldeck
-end
-function AIRBOSS:_GetSternCoord()
-local hdg=self.carrier:GetHeading()
-local FB=self:GetFinalBearing()
-local case=self.case
-self.sterncoord:UpdateFromCoordinate(self:GetCoordinate())
-if self.carriertype==AIRBOSS.CarrierType.INVINCIBLE or self.carriertype==AIRBOSS.CarrierType.HERMES or self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS or self.carriertype==AIRBOSS.CarrierType.CANBERRA then
-if case==3 then
-self.sterncoord:Translate(self.carrierparam.sterndist,hdg,true,true):Translate(8,FB-90,true,true)
-elseif case==2 or case==1 then
-self.sterncoord:Translate(self.carrierparam.sterndist,hdg,true,true):Translate(8,FB-90,true,true)
-end
-elseif self.carriertype==AIRBOSS.CarrierType.STENNIS then
-self.sterncoord:Translate(self.carrierparam.sterndist,hdg,true,true):Translate(7,FB+90,true,true)
-elseif self.carriertype==AIRBOSS.CarrierType.FORRESTAL then
-self.sterncoord:Translate(self.carrierparam.sterndist,hdg,true,true):Translate(7.5,FB+90,true,true)
-else
-self.sterncoord:Translate(self.carrierparam.sterndist,hdg,true,true):Translate(9.5,FB+90,true,true)
-end
-self.sterncoord:SetAltitude(self.carrierparam.deckheight)
-return self.sterncoord
-end
-function AIRBOSS:_GetWireFromDrawArg()
-local wireArgs={}
-wireArgs[1]=141
-wireArgs[2]=142
-wireArgs[3]=143
-wireArgs[4]=144
-for wire,drawArg in pairs(wireArgs)do
-local value=self.carrier:GetDrawArgumentValue(drawArg)
-if math.abs(value)>0.001 then
-return wire
-end
-end
-return 99
-end
-function AIRBOSS:_GetWire(Lcoord,dc)
-local FB=self:GetFinalBearing()
-local Scoord=self:_GetSternCoord()
-local Ldist=Lcoord:Get2DDistance(Scoord)
-dc=dc or 65
-local d=Ldist-dc
-if self.mpWireCorrection then
-d=d-self.mpWireCorrection
-end
-local w1=self.carrierparam.wire1
-local w2=self.carrierparam.wire2
-local w3=self.carrierparam.wire3
-local w4=self.carrierparam.wire4
-local wire
-if d wire=%d (dc=%.1f)",Ldist,Ldist-dc,wire,dc))
-return wire
-end
-function AIRBOSS:_Trapped(playerData)
-if playerData.unit:InAir()==false then
-local unit=playerData.unit
-local coord=unit:GetCoordinate()
-local v=unit:GetVelocityKMH()-self.carrier:GetVelocityKMH()
-local stern=self:_GetSternCoord()
-local s=stern:Get2DDistance(coord)
-local dcorr=100
-if playerData.actype==AIRBOSS.AircraftCarrier.HORNET
-or playerData.actype==AIRBOSS.AircraftCarrier.RHINOE
-or playerData.actype==AIRBOSS.AircraftCarrier.RHINOF
-or playerData.actype==AIRBOSS.AircraftCarrier.GROWLER then
-dcorr=100
-elseif playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B then
-dcorr=100
-elseif playerData.actype==AIRBOSS.AircraftCarrier.A4EC then
-dcorr=56
-elseif playerData.actype==AIRBOSS.AircraftCarrier.T45C then
-dcorr=56
-end
-local wire=self:_GetWire(coord,dcorr)
-local text=string.format("Player %s _Trapped: v=%.1f km/h, s-dcorr=%.1f m ==> wire=%d (dcorr=%d)",playerData.name,v,s-dcorr,wire,dcorr)
-self:T(self.lid..text)
-if v>5 then
-if wire>4 and v>10 and not playerData.warning then
-self:RadioTransmission(self.LSORadio,self.LSOCall.BOLTER,nil,nil,nil,true)
-playerData.warning=true
-end
-self:ScheduleOnce(0.1,self._Trapped,self,playerData)
-return
-end
-if self.Debug then
-coord:SmokeBlue()
-coord:MarkToAll(text)
-stern:MarkToAll("Stern")
-end
-playerData.wire=wire
-local text=string.format("Trapped %d-wire.",wire)
-if wire==3 then
-text=text.." Well done!"
-elseif wire==2 then
-text=text.." Not bad, maybe you even get the 3rd next time."
-elseif wire==4 then
-text=text.." That was scary. You can do better than this!"
-elseif wire==1 then
-text=text.." Try harder next time!"
-end
-self:MessageToPlayer(playerData,text,"LSO","")
-local hint=string.format("Trapped %d-wire.",wire)
-self:_AddToDebrief(playerData,hint,"Groove: IW")
-else
-local text=string.format("Player %s boltered in trapped function.",playerData.name)
-self:T(self.lid..text)
-MESSAGE:New(text,5,"DEBUG"):ToAllIf(self.debug)
-playerData.boltered=true
-end
-playerData.step=AIRBOSS.PatternStep.DEBRIEF
-playerData.warning=nil
-end
-function AIRBOSS:_GetZoneInitial(case)
-self.zoneInitial=self.zoneInitial or ZONE_POLYGON_BASE:New("Zone CASE I/II Initial")
-local radial=self:GetRadial(2,false,false)
-local cv=self:GetCoordinate()
-local vec2={}
-if case==1 then
-local c1=cv:Translate(UTILS.NMToMeters(0.5),radial-90)
-local c2=cv:Translate(UTILS.NMToMeters(1.3),radial-90):Translate(UTILS.NMToMeters(3),radial)
-local c3=cv:Translate(UTILS.NMToMeters(0.4),radial+90):Translate(UTILS.NMToMeters(3),radial)
-local c4=cv:Translate(UTILS.NMToMeters(1.0),radial)
-local c5=cv
-vec2={c1:GetVec2(),c2:GetVec2(),c3:GetVec2(),c4:GetVec2(),c5:GetVec2()}
-else
-local c1=cv:Translate(UTILS.NMToMeters(0.5),radial-90)
-local c2=c1:Translate(UTILS.NMToMeters(0.5),radial)
-local c3=cv:Translate(UTILS.NMToMeters(1.2),radial-90):Translate(UTILS.NMToMeters(3),radial)
-local c4=cv:Translate(UTILS.NMToMeters(1.2),radial+90):Translate(UTILS.NMToMeters(3),radial)
-local c5=cv:Translate(UTILS.NMToMeters(0.5),radial)
-local c6=cv
-vec2={c1:GetVec2(),c2:GetVec2(),c3:GetVec2(),c4:GetVec2(),c5:GetVec2(),c6:GetVec2()}
-end
-self.zoneInitial:UpdateFromVec2(vec2)
-return self.zoneInitial
-end
-function AIRBOSS:_GetZoneLineup()
-self.zoneLineup=self.zoneLineup or ZONE_POLYGON_BASE:New("Zone Lineup")
-local fbi=self:GetRadial(1,false,false)
-local st=self:_GetOptLandingCoordinate()
-local c1=st
-local c2=st:Translate(UTILS.NMToMeters(0.50),fbi+15)
-local c3=st:Translate(UTILS.NMToMeters(0.50),fbi+self.lue._max-0.05)
-local c4=st:Translate(UTILS.NMToMeters(0.77),fbi+self.lue._max-0.05)
-local c5=c4:Translate(UTILS.NMToMeters(0.25),fbi-90)
-local vec2={c1:GetVec2(),c2:GetVec2(),c3:GetVec2(),c4:GetVec2(),c5:GetVec2()}
-self.zoneLineup:UpdateFromVec2(vec2)
-return self.zoneLineup
-end
-function AIRBOSS:_GetZoneGroove(l,w,b)
-self.zoneGroove=self.zoneGroove or ZONE_POLYGON_BASE:New("Zone Groove")
-l=l or 1.50
-w=w or 0.25
-b=b or 0.10
-local fbi=self:GetRadial(1,false,false)
-local st=self:_GetSternCoord()
-local c1=st:Translate(self.carrierparam.totwidthstarboard,fbi-90)
-local c2=st:Translate(UTILS.NMToMeters(0.10),fbi-90):Translate(UTILS.NMToMeters(0.3),fbi)
-local c3=st:Translate(UTILS.NMToMeters(0.25),fbi-90):Translate(UTILS.NMToMeters(l),fbi)
-local c4=st:Translate(UTILS.NMToMeters(w/2),fbi+90):Translate(UTILS.NMToMeters(l),fbi)
-local c5=st:Translate(UTILS.NMToMeters(b),fbi+90):Translate(UTILS.NMToMeters(0.3),fbi)
-local c6=st:Translate(self.carrierparam.totwidthport,fbi+90)
-local vec2={c1:GetVec2(),c2:GetVec2(),c3:GetVec2(),c4:GetVec2(),c5:GetVec2(),c6:GetVec2()}
-self.zoneGroove:UpdateFromVec2(vec2)
-return self.zoneGroove
-end
-function AIRBOSS:_GetZoneBullseye(case)
-local radius=UTILS.NMToMeters(1)
-local distance=UTILS.NMToMeters(3)
-local radial=self:GetRadial(case,false,false)
-local coord=self:GetCoordinate():Translate(distance,radial)
-local vec2=coord:GetVec2()
-local zone=ZONE_RADIUS:New("Zone Bullseye",vec2,radius)
-return zone
-end
-function AIRBOSS:_GetZoneDirtyUp(case)
-local radius=UTILS.NMToMeters(1)
-local distance=UTILS.NMToMeters(9)
-local radial=self:GetRadial(case,false,false)
-local coord=self:GetCoordinate():Translate(distance,radial)
-local vec2=coord:GetVec2()
-local zone=ZONE_RADIUS:New("Zone Dirty Up",vec2,radius)
-return zone
-end
-function AIRBOSS:_GetZoneArcOut(case)
-local radius=UTILS.NMToMeters(1.25)
-local distance=UTILS.NMToMeters(11.75)
-local radial=self:GetRadial(case,false,false)
-local coord=self:GetCoordinate():Translate(distance,radial)
-local zone=ZONE_RADIUS:New("Zone Arc Out",coord:GetVec2(),radius)
-return zone
-end
-function AIRBOSS:_GetZoneArcIn(case)
-local radius=UTILS.NMToMeters(1.25)
-local radial=self:GetRadial(case,false,true)
-local alpha=math.rad(self.holdingoffset)
-local x=14
-local distance=UTILS.NMToMeters(x)
-local coord=self:GetCoordinate():Translate(distance,radial)
-local zone=ZONE_RADIUS:New("Zone Arc In",coord:GetVec2(),radius)
-return zone
-end
-function AIRBOSS:_GetZonePlatform(case)
-local radius=UTILS.NMToMeters(1)
-local radial=self:GetRadial(case,false,true)
-local alpha=math.rad(self.holdingoffset)
-local distance=UTILS.NMToMeters(19)
-local coord=self:GetCoordinate():Translate(distance,radial)
-local zone=ZONE_RADIUS:New("Zone Platform",coord:GetVec2(),radius)
-return zone
-end
-function AIRBOSS:_GetZoneCorridor(case,l)
-l=l or 31
-local radial=self:GetRadial(case,false,false)
-local offset=self:GetRadial(case,false,true)
-local dx=5
-local w=2
-local w2=w/2
-local d=12
-local cv=self:GetCoordinate()
-local c={}
-c[1]=cv:Translate(-UTILS.NMToMeters(dx),radial)
-if math.abs(self.holdingoffset)>=5 then
-c[2]=c[1]:Translate(UTILS.NMToMeters(w2),radial-90)
-c[3]=c[2]:Translate(UTILS.NMToMeters(d+dx+w2),radial)
-c[4]=cv:Translate(UTILS.NMToMeters(15),offset):Translate(UTILS.NMToMeters(1),offset-90)
-c[5]=cv:Translate(UTILS.NMToMeters(l),offset):Translate(UTILS.NMToMeters(1),offset-90)
-c[6]=cv:Translate(UTILS.NMToMeters(l),offset):Translate(UTILS.NMToMeters(1),offset+90)
-c[7]=cv:Translate(UTILS.NMToMeters(13),offset):Translate(UTILS.NMToMeters(1),offset+90)
-c[8]=cv:Translate(UTILS.NMToMeters(11),radial):Translate(UTILS.NMToMeters(1),radial+90)
-c[9]=c[1]:Translate(UTILS.NMToMeters(w2),radial+90)
-else
-c[2]=c[1]:Translate(UTILS.NMToMeters(w2),radial-90)
-c[3]=c[2]:Translate(UTILS.NMToMeters(dx+l),radial)
-c[4]=c[3]:Translate(UTILS.NMToMeters(w),radial+90)
-c[5]=c[1]:Translate(UTILS.NMToMeters(w2),radial+90)
-end
-local p={}
-for _i,_c in ipairs(c)do
-if self.Debug then
-end
-p[_i]=_c:GetVec2()
-end
-local zone=ZONE_POLYGON_BASE:New("CASE II/III Approach Corridor",p)
-return zone
-end
-function AIRBOSS:_GetZoneCarrierBox()
-self.zoneCarrierbox=self.zoneCarrierbox or ZONE_POLYGON_BASE:New("Carrier Box Zone")
-local S=self:_GetSternCoord()
-local hdg=self:GetHeading(false)
-local p={}
-p[1]=S:Translate(self.carrierparam.totwidthstarboard,hdg+90)
-p[2]=p[1]:Translate(self.carrierparam.totlength,hdg)
-p[3]=p[2]:Translate(self.carrierparam.totwidthstarboard+self.carrierparam.totwidthport,hdg-90)
-p[4]=p[3]:Translate(self.carrierparam.totlength,hdg-180)
-local vec2={}
-for _,coord in ipairs(p)do
-table.insert(vec2,coord:GetVec2())
-end
-self.zoneCarrierbox:UpdateFromVec2(vec2)
-return self.zoneCarrierbox
-end
-function AIRBOSS:_GetZoneRunwayBox()
-self.zoneRunwaybox=self.zoneRunwaybox or ZONE_POLYGON_BASE:New("Landing Runway Zone")
-local S=self:_GetSternCoord()
-local FB=self:GetFinalBearing(false)
-local p={}
-p[1]=S:Translate(self.carrierparam.rwywidth*0.5,FB+90)
-p[2]=p[1]:Translate(self.carrierparam.rwylength,FB)
-p[3]=p[2]:Translate(self.carrierparam.rwywidth,FB-90)
-p[4]=p[3]:Translate(self.carrierparam.rwylength,FB-180)
-local vec2={}
-for _,coord in ipairs(p)do
-table.insert(vec2,coord:GetVec2())
-end
-self.zoneRunwaybox:UpdateFromVec2(vec2)
-return self.zoneRunwaybox
-end
-function AIRBOSS:_GetZoneAbeamLandingSpot()
-local S=self:_GetOptLandingCoordinate()
-local FB=self:GetFinalBearing(false)
-local p={}
-p[1]=S:Translate(15,FB):Translate(15,FB+90)
-p[2]=S:Translate(-45,FB):Translate(15,FB+90)
-p[3]=S:Translate(-45,FB):Translate(15,FB-90)
-p[4]=S:Translate(15,FB):Translate(15,FB-90)
-local vec2={}
-for _,coord in ipairs(p)do
-table.insert(vec2,coord:GetVec2())
-end
-local zone=ZONE_POLYGON_BASE:New("Abeam Landing Spot Zone",vec2)
-return zone
-end
-function AIRBOSS:_GetZoneLandingSpot()
-local S=self:_GetLandingSpotCoordinate()
-local FB=self:GetFinalBearing(false)
-local p={}
-p[1]=S:Translate(10,FB):Translate(10,FB+90)
-p[2]=S:Translate(-10,FB):Translate(10,FB+90)
-p[3]=S:Translate(-10,FB):Translate(10,FB-90)
-p[4]=S:Translate(10,FB):Translate(10,FB-90)
-local vec2={}
-for _,coord in ipairs(p)do
-table.insert(vec2,coord:GetVec2())
-end
-local zone=ZONE_POLYGON_BASE:New("Landing Spot Zone",vec2)
-return zone
-end
-function AIRBOSS:_GetZoneHolding(case,stack)
-local zoneHolding=nil
-if stack<=0 then
-self:E(self.lid.."ERROR: Stack <= 0 in _GetZoneHolding!")
-self:E({case=case,stack=stack})
-return nil
-end
-local patternalt,c1,c2=self:_GetMarshalAltitude(stack,case)
-if case==1 then
-local hdg=self:GetHeading()
-local D=UTILS.NMToMeters(2.5)
-local Post=self:GetCoordinate():Translate(D,hdg+270)
-self.zoneHolding=ZONE_RADIUS:New("CASE I Holding Zone",Post:GetVec2(),self.marshalradius)
-if self.carriertype==AIRBOSS.CarrierType.INVINCIBLE or self.carriertype==AIRBOSS.CarrierType.HERMES or self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS or self.carriertype==AIRBOSS.CarrierType.CANBERRA then
-self.zoneHolding=ZONE_RADIUS:New("CASE I Holding Zone",self.carrier:GetVec2(),UTILS.NMToMeters(5))
-end
-else
-local radial=self:GetRadial(case,false,true)
-local p={}
-p[1]=c2:Translate(UTILS.NMToMeters(1),radial-90):GetVec2()
-p[2]=c1:Translate(UTILS.NMToMeters(1),radial-90):GetVec2()
-p[3]=c1:Translate(UTILS.NMToMeters(7),radial+90):GetVec2()
-p[4]=c2:Translate(UTILS.NMToMeters(7),radial+90):GetVec2()
-self.zoneHolding=self.zoneHolding or ZONE_POLYGON_BASE:New("CASE II/III Holding Zone")
-self.zoneHolding:UpdateFromVec2(p)
-end
-return self.zoneHolding
-end
-function AIRBOSS:_GetZoneCommence(case,stack)
-local zone
-if case==1 then
-local hdg=self:GetHeading()
-local D=UTILS.NMToMeters(4.75)
-local R=UTILS.NMToMeters(1)
-local Three=self:GetCoordinate():Translate(D,hdg+275)
-if self.carriertype==AIRBOSS.CarrierType.INVINCIBLE or self.carriertype==AIRBOSS.CarrierType.HERMES or self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS or self.carriertype==AIRBOSS.CarrierType.CANBERRA then
-local Dx=UTILS.NMToMeters(2.25)
-local Dz=UTILS.NMToMeters(2.25)
-R=UTILS.NMToMeters(1)
-Three=self:GetCoordinate():Translate(Dz,hdg-90):Translate(Dx,hdg-180)
-end
-self.zoneCommence=self.zoneCommence or ZONE_RADIUS:New("CASE I Commence Zone")
-self.zoneCommence:UpdateFromVec2(Three:GetVec2(),R)
-else
-stack=stack or 1
-local l=20+stack
-local offset=self:GetRadial(case,false,true)
-local cv=self:GetCoordinate()
-local c={}
-c[1]=cv:Translate(UTILS.NMToMeters(l),offset):Translate(UTILS.NMToMeters(1),offset-90)
-c[2]=cv:Translate(UTILS.NMToMeters(l+2.5),offset):Translate(UTILS.NMToMeters(1),offset-90)
-c[3]=cv:Translate(UTILS.NMToMeters(l+2.5),offset):Translate(UTILS.NMToMeters(1),offset+90)
-c[4]=cv:Translate(UTILS.NMToMeters(l),offset):Translate(UTILS.NMToMeters(1),offset+90)
-local p={}
-for _i,_c in ipairs(c)do
-p[_i]=_c:GetVec2()
-end
-self.zoneCommence=self.zoneCommence or ZONE_POLYGON_BASE:New("CASE II/III Commence Zone")
-self.zoneCommence:UpdateFromVec2(p)
-end
-return self.zoneCommence
-end
-function AIRBOSS:_AttitudeMonitor(playerData)
-local unit=playerData.unit
-local aoa=unit:GetAoA()
-local yaw=unit:GetYaw()
-local roll=unit:GetRoll()
-local pitch=unit:GetPitch()
-local dist=playerData.unit:GetCoordinate():Get2DDistance(self:GetCoordinate())
-local dx,dz,rho,phi=self:_GetDistances(unit)
-local wind=unit:GetCoordinate():GetWindWithTurbulenceVec3()
-local velo=unit:GetVelocityVec3()
-local vabs=UTILS.VecNorm(velo)
-local rwy=false
-local step=playerData.step
-if playerData.step==AIRBOSS.PatternStep.FINAL or
-playerData.step==AIRBOSS.PatternStep.GROOVE_XX or
-playerData.step==AIRBOSS.PatternStep.GROOVE_IM or
-playerData.step==AIRBOSS.PatternStep.GROOVE_IC or
-playerData.step==AIRBOSS.PatternStep.GROOVE_AR or
-playerData.step==AIRBOSS.PatternStep.GROOVE_AL or
-playerData.step==AIRBOSS.PatternStep.GROOVE_LC or
-playerData.step==AIRBOSS.PatternStep.GROOVE_IW then
-step=self:_GS(step,-1)
-rwy=true
-end
-local relhead=self:_GetRelativeHeading(playerData.unit,rwy)
-local text=string.format("Pattern step: %s",step)
-text=text..string.format("\nAoA=%.1f° = %.1f Units | |V|=%.1f knots",aoa,self:_AoADeg2Units(playerData,aoa),UTILS.MpsToKnots(vabs))
-if self.Debug then
-text=text..string.format("\nVx=%.1f Vy=%.1f Vz=%.1f m/s",velo.x,velo.y,velo.z)
-text=text..string.format("\nWind Vx=%.1f Vy=%.1f Vz=%.1f m/s",wind.x,wind.y,wind.z)
-end
-text=text..string.format("\nPitch=%.1f° | Roll=%.1f° | Yaw=%.1f°",pitch,roll,yaw)
-text=text..string.format("\nClimb Angle=%.1f° | Rate=%d ft/min",unit:GetClimbAngle(),velo.y*196.85)
-local dist=self:_GetOptLandingCoordinate():Get3DDistance(playerData.unit:GetVec3())
-local vplayer=playerData.unit:GetVelocityKMH()
-local vcarrier=self.carrier:GetVelocityKMH()
-local dv=math.abs(vplayer-vcarrier)
-local alt=self:_GetAltCarrier(playerData.unit)
-text=text..string.format("\nDist=%.1f m Alt=%.1f m delta|V|=%.1f km/h",dist,alt,dv)
-if playerData.step==AIRBOSS.PatternStep.FINAL or
-playerData.step==AIRBOSS.PatternStep.GROOVE_XX or
-playerData.step==AIRBOSS.PatternStep.GROOVE_IM or
-playerData.step==AIRBOSS.PatternStep.GROOVE_IC or
-playerData.step==AIRBOSS.PatternStep.GROOVE_AR or
-playerData.step==AIRBOSS.PatternStep.GROOVE_AL or
-playerData.step==AIRBOSS.PatternStep.GROOVE_LC or
-playerData.step==AIRBOSS.PatternStep.GROOVE_IW then
-local lue=self:_Lineup(playerData.unit,true)
-local gle=self:_Glideslope(playerData.unit)
-text=text..string.format("\nGamma=%.1f° | Rho=%.1f°",relhead,phi)
-text=text..string.format("\nLineUp=%.2f° | GlideSlope=%.2f° | AoA=%.1f Units",lue,gle,self:_AoADeg2Units(playerData,aoa))
-local grade,points,analysis=self:_LSOgrade(playerData)
-text=text..string.format("\nTgroove=%.1f sec",self:_GetTimeInGroove(playerData))
-text=text..string.format("\nGrade: %s %.1f PT - %s",grade,points,analysis)
-else
-text=text..string.format("\nR=%.2f NM | X=%d Z=%d m",UTILS.MetersToNM(rho),dx,dz)
-text=text..string.format("\nGamma=%.1f° | Rho=%.1f°",relhead,phi)
-end
-MESSAGE:New(text,1,nil,true):ToClient(playerData.client)
-end
-function AIRBOSS:_Glideslope(unit,optangle)
-if optangle==nil then
-if unit:GetTypeName()==AIRBOSS.AircraftCarrier.AV8B then
-optangle=3.0
-else
-optangle=3.5
-end
-end
-local landingcoord=self:_GetOptLandingCoordinate()
-local x=unit:GetCoordinate():Get2DDistance(landingcoord)
-local h=self:_GetAltCarrier(unit)
-if unit:GetTypeName()==AIRBOSS.AircraftCarrier.AV8B then
-h=unit:GetAltitude()-(UTILS.FeetToMeters(50)+self.carrierparam.deckheight+2)
-end
-local glideslope=math.atan(h/x)
-local gs=math.deg(glideslope)-optangle
-return gs
-end
-function AIRBOSS:_Glideslope2(unit,optangle)
-if optangle==nil then
-if unit:GetTypeName()==AIRBOSS.AircraftCarrier.AV8B then
-optangle=3.0
-else
-optangle=3.5
-end
-end
-local landingcoord=self:_GetOptLandingCoordinate()
-local x=unit:GetCoordinate():Get3DDistance(landingcoord)
-local h=self:_GetAltCarrier(unit)
-if unit:GetTypeName()==AIRBOSS.AircraftCarrier.AV8B then
-h=unit:GetAltitude()-(UTILS.FeetToMeters(50)+self.carrierparam.deckheight+2)
-end
-local glideslope=math.asin(h/x)
-local gs=math.deg(glideslope)-optangle
-self:T3(self.lid..string.format("Glide slope error = %.1f, x=%.1f h=%.1f",gs,x,h))
-return gs
-end
-function AIRBOSS:_Lineup(unit,runway)
-local landingcoord=self:_GetOptLandingCoordinate()
-local A=landingcoord:GetVec3()
-local B=unit:GetVec3()
-local C=UTILS.VecSubstract(A,B)
-C.y=0.0
-local X=self.carrier:GetOrientationX()
-X.y=0.0
-if runway then
-X=UTILS.Rotate2D(X,-self.carrierparam.rwyangle)
-end
-local x=UTILS.VecDot(X,C)
-local Z=self.carrier:GetOrientationZ()
-Z.y=0.0
-if runway then
-Z=UTILS.Rotate2D(Z,-self.carrierparam.rwyangle)
-end
-local z=UTILS.VecDot(Z,C)
-local lineup=math.deg(math.atan2(z,x))
-return lineup
-end
-function AIRBOSS:_GetAltCarrier(unit)
-local h=unit:GetAltitude()-self.carrierparam.deckheight-2
-return h
-end
-function AIRBOSS:_GetOptLandingCoordinate()
-self.landingcoord:UpdateFromCoordinate(self:_GetSternCoord())
-local FB=self:GetFinalBearing(false)
-local case=self.case
-if self.carriertype==AIRBOSS.CarrierType.INVINCIBLE or self.carriertype==AIRBOSS.CarrierType.HERMES or self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS or self.carriertype==AIRBOSS.CarrierType.CANBERRA then
-if case==3 then
-self.landingcoord:UpdateFromCoordinate(self:_GetLandingSpotCoordinate())
-self.landingcoord:SetAltitude(UTILS.FeetToMeters(120))
-elseif case==2 or case==1 then
-self.landingcoord:UpdateFromCoordinate(self:_GetLandingSpotCoordinate()):Translate(35,FB-90,true,true)
-self.landingcoord:SetAltitude(UTILS.FeetToMeters(120))
-end
-else
-if self.carrierparam.wire3 then
-self.landingcoord:Translate(self.carrierparam.wire3,FB,true,true)
-end
-self.landingcoord.y=self.landingcoord.y+2
-end
-return self.landingcoord
-end
-function AIRBOSS:_GetLandingSpotCoordinate()
-self.landingspotcoord:UpdateFromCoordinate(self:_GetSternCoord())
-local hdg=self:GetHeading()
-self.landingspotcoord:Translate(self.carrierparam.landingspot,hdg,true,true):SetAltitude(self.carrierparam.deckheight)
-return self.landingspotcoord
-end
-function AIRBOSS:GetHeading(magnetic)
-self:F3({magnetic=magnetic})
-local hdg=self.carrier:GetHeading()
-if magnetic then
-hdg=hdg-self.magvar
-end
-if hdg<0 then
-hdg=hdg+360
-end
-return hdg
-end
-function AIRBOSS:GetBRC()
-return self:GetHeading(true)
-end
-function AIRBOSS:GetWind(alt,magnetic,coord)
-local cv=coord or self:GetCoordinate()
-local Wdir,Wspeed=cv:GetWind(alt or 18)
-if magnetic then
-Wdir=Wdir-self.magvar
-if Wdir<0 then
-Wdir=Wdir+360
-end
-end
-return Wdir,Wspeed
-end
-function AIRBOSS:GetWindOnDeck(alt)
-local cv=self:GetCoordinate()
-local vc=self.carrier:GetVelocityVec3()
-local xc=self.carrier:GetOrientationX()
-local zc=self.carrier:GetOrientationZ()
-xc=UTILS.Rotate2D(xc,-self.carrierparam.rwyangle)
-zc=UTILS.Rotate2D(zc,-self.carrierparam.rwyangle)
-local vw=cv:GetWindWithTurbulenceVec3(alt or 18)
-local vT=UTILS.VecSubstract(vw,vc)
-local vpa=UTILS.VecDot(vT,xc)
-local vpp=UTILS.VecDot(vT,zc)
-local vabs=UTILS.VecNorm(vT)
-return-vpa,vpp,vabs
-end
-function AIRBOSS:GetHeadingIntoWind_old(magnetic,coord)
-local function adjustDegreesForWindSpeed(windSpeed)
-local degreesAdjustment=0
-if windSpeed>0 and windSpeed<3 then
-degreesAdjustment=30
-elseif windSpeed>=3 and windSpeed<5 then
-degreesAdjustment=20
-elseif windSpeed>=5 and windSpeed<8 then
-degreesAdjustment=8
-elseif windSpeed>=8 and windSpeed<13 then
-degreesAdjustment=4
-elseif windSpeed>=13 then
-degreesAdjustment=0
-end
-return degreesAdjustment
-end
-local windfrom,vwind=self:GetWind(nil,nil,coord)
-local intowind=windfrom-self.carrierparam.rwyangle+adjustDegreesForWindSpeed(vwind)
-if vwind<0.1 then
-intowind=self:GetHeading()
-end
-if magnetic then
-intowind=intowind-self.magvar
-end
-if intowind<0 then
-intowind=intowind+360
-end
-return intowind
-end
-function AIRBOSS:GetHeadingIntoWind(vdeck,magnetic,coord)
-local Offset=self.carrierparam.rwyangle or 0
-local windfrom,vwind=self:GetWind(18,nil,coord)
-local Vmin=4
-local Vmax=UTILS.KmphToKnots(self.carrier:GetSpeedMax())
-if vwind<0.1 then
-local h=self:GetHeading(magnetic)
-return h,math.min(vdeck,Vmax)
-end
-vwind=UTILS.MpsToKnots(vwind)
-local windto=(windfrom+180)%360
-local alpha=math.rad(-Offset)
-local C=math.sqrt(math.cos(alpha)^2/math.sin(alpha)^2+1)
-local vdeckMax=vwind+math.cos(alpha)*Vmax
-local vdeckMin=vwind+math.cos(alpha)*Vmin
-local v=0
-local theta=0
-if vdeck>vdeckMax then
-v=Vmax
-theta=math.asin(v/(vwind*C))-math.asin(-1/C)
-elseif vdeckvwind then
-theta=math.pi/2
-v=math.sqrt(vdeck^2-vwind^2)
-else
-theta=math.asin(vdeck*math.sin(alpha)/vwind)
-v=vdeck*math.cos(alpha)-vwind*math.cos(theta)
-end
-local magvar=magnetic and self.magvar or 0
-local intowind=(540+(windto-magvar+math.deg(theta)))%360
-return intowind,v
-end
-function AIRBOSS:GetBRCintoWind(vdeck)
-return self:GetHeadingIntoWind(vdeck,true)
-end
-function AIRBOSS:GetFinalBearing(magnetic)
-local fb=self:GetHeading(magnetic)
-fb=fb+self.carrierparam.rwyangle
-if fb<0 then
-fb=fb+360
-end
-return fb
-end
-function AIRBOSS:GetRadial(case,magnetic,offset,inverse)
-case=case or self.case
-local radial
-if case==1 then
-radial=self:GetFinalBearing(magnetic)-180
-elseif case==2 then
-radial=self:GetHeading(magnetic)-180
-if offset then
-radial=radial+self.holdingoffset
-end
-elseif case==3 then
-radial=self:GetFinalBearing(magnetic)-180
-if offset then
-radial=radial+self.holdingoffset
-end
-end
-if radial<0 then
-radial=radial+360
-end
-if inverse then
-radial=radial-180
-if radial<0 then
-radial=radial+360
-end
-end
-return radial
-end
-function AIRBOSS:_GetDeltaHeading(hdg1,hdg2)
-local V={}
-V.x=math.cos(math.rad(hdg1))
-V.y=0
-V.z=math.sin(math.rad(hdg1))
-local W={}
-W.x=math.cos(math.rad(hdg2))
-W.y=0
-W.z=math.sin(math.rad(hdg2))
-local alpha=UTILS.VecAngle(V,W)
-return alpha
-end
-function AIRBOSS:_GetRelativeHeading(unit,runway)
-local vC=self.carrier:GetOrientationX()
-if runway then
-vC=UTILS.Rotate2D(vC,-self.carrierparam.rwyangle)
-end
-local vP=unit:GetOrientationX()
-vC.y=0;
-vP.y=0
-local rhdg=UTILS.VecAngle(vC,vP)
-return rhdg
-end
-function AIRBOSS:_GetRelativeVelocity(unit)
-local vC=self.carrier:GetVelocityVec3()
-local vP=unit:GetVelocityVec3()
-vC.y=0;
-vP.y=0
-local v=UTILS.VecSubstract(vP,vC)
-return UTILS.VecNorm(v),v
-end
-function AIRBOSS:_GetDistances(unit)
-local a=self.carrier:GetVec3()
-local b=unit:GetVec3()
-local c={x=b.x-a.x,y=0,z=b.z-a.z}
-local x=self.carrier:GetOrientationX()
-local dx=UTILS.VecDot(x,c)
-local z=self.carrier:GetOrientationZ()
-local dz=UTILS.VecDot(z,c)
-local rho=math.sqrt(dx*dx+dz*dz)
-local phi=math.deg(math.atan2(dz,dx))
-if phi<0 then
-phi=phi+360
-end
-return dx,dz,rho,phi
-end
-function AIRBOSS:_CheckLimits(X,Z,check)
-local nextXmin=check.LimitXmin==nil or(check.LimitXmin and(check.LimitXmin<0 and X<=check.LimitXmin or check.LimitXmin>=0 and X>=check.LimitXmin))
-local nextXmax=check.LimitXmax==nil or(check.LimitXmax and(check.LimitXmax<0 and X>=check.LimitXmax or check.LimitXmax>=0 and X<=check.LimitXmax))
-local nextZmin=check.LimitZmin==nil or(check.LimitZmin and(check.LimitZmin<0 and Z<=check.LimitZmin or check.LimitZmin>=0 and Z>=check.LimitZmin))
-local nextZmax=check.LimitZmax==nil or(check.LimitZmax and(check.LimitZmax<0 and Z>=check.LimitZmax or check.LimitZmax>=0 and Z<=check.LimitZmax))
-local next=nextXmin and nextXmax and nextZmin and nextZmax
-local text=string.format("step=%s: next=%s: X=%d Xmin=%s Xmax=%s | Z=%d Zmin=%s Zmax=%s",check.name,tostring(next),X,tostring(check.LimitXmin),tostring(check.LimitXmax),Z,tostring(check.LimitZmin),tostring(check.LimitZmax))
-self:T3(self.lid..text)
-return next
-end
-function AIRBOSS:_LSOadvice(playerData,glideslopeError,lineupError)
-local advice=0
-if glideslopeError>self.gle.HIGH then
-self:RadioTransmission(self.LSORadio,self.LSOCall.HIGH,true,nil,nil,true)
-advice=advice+self.LSOCall.HIGH.duration
-elseif glideslopeError>self.gle.High then
-self:RadioTransmission(self.LSORadio,self.LSOCall.HIGH,false,nil,nil,true)
-advice=advice+self.LSOCall.HIGH.duration
-elseif glideslopeErrorself.lue.RIGHT then
-self:RadioTransmission(self.LSORadio,self.LSOCall.RIGHTFORLINEUP,true,nil,nil,true)
-advice=advice+self.LSOCall.RIGHTFORLINEUP.duration
-elseif lineupError>self.lue.Right then
-self:RadioTransmission(self.LSORadio,self.LSOCall.RIGHTFORLINEUP,false,nil,nil,true)
-advice=advice+self.LSOCall.RIGHTFORLINEUP.duration
-else
-end
-local AOA=playerData.unit:GetAoA()
-local acaoa=self:_GetAircraftAoA(playerData)
-if playerData.actype~=AIRBOSS.AircraftCarrier.AV8B then
-if AOA>acaoa.SLOW then
-self:RadioTransmission(self.LSORadio,self.LSOCall.SLOW,true,nil,nil,true)
-advice=advice+self.LSOCall.SLOW.duration
-elseif AOA>acaoa.Slow then
-self:RadioTransmission(self.LSORadio,self.LSOCall.SLOW,false,nil,nil,true)
-advice=advice+self.LSOCall.SLOW.duration
-elseif AOA>acaoa.OnSpeedMax then
-elseif AOA=76 then
-grade="SLOW V/STOL Groove"
-else
-grade="LIG"
-end
-if t>=16.4 and t<=16.6 then
-grade="_OK_"
-end
-if playerData.actype==AIRBOSS.AircraftCarrier.AV8B and(t>=60.0 and t<=65.0)then
-grade="_OK_ V/STOL"
-end
-return grade
-end
-function AIRBOSS:_LSOgrade(playerData)
-local function count(base,pattern)
-return select(2,string.gsub(base,pattern,""))
-end
-local GXX,nXX=self:_Flightdata2Text(playerData,AIRBOSS.GroovePos.XX)
-local GIM,nIM=self:_Flightdata2Text(playerData,AIRBOSS.GroovePos.IM)
-local GIC,nIC=self:_Flightdata2Text(playerData,AIRBOSS.GroovePos.IC)
-local GAR,nAR=self:_Flightdata2Text(playerData,AIRBOSS.GroovePos.AR)
-local G=GXX.." "..GIM.." ".." "..GIC.." "..GAR
-local N=nXX+nIM+nIC+nAR
-local Nv=nXX+nIM
-local nL=count(G,'_')/2
-local nS=count(G,'%(')
-local nN=N-nS-nL
-local nNv=Nv-nS-nL
-local Tgroove=playerData.Tgroove
-local TgrooveUnicorn=Tgroove and(Tgroove>=15.0 and Tgroove<=18.99)or false
-local TgrooveVstolUnicorn=Tgroove and(Tgroove>=60.0 and Tgroove<=65.0)and playerData.actype==AIRBOSS.AircraftCarrier.AV8B or false
-local grade
-local points
-if N==0 and(TgrooveUnicorn or TgrooveVstolUnicorn or playerData.case==3)then
-grade="_OK_"
-points=5.0
-G="Unicorn"
-else
-if nL>1 and playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
-grade="--"
-points=2.0
-elseif nNv>=1 and playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
-grade="(OK)"
-points=3.0
-elseif nNv<1 and playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
-grade="OK"
-points=4.0
-elseif nL>0 then
-grade="--"
-points=2.0
-elseif nN>0 then
-grade="(OK)"
-points=3.0
-else
-grade="OK"
-points=4.0
-end
-end
-G=G:gsub("%)%(","")
-G=G:gsub("__","")
-local text="LSO grade:\n"
-text=text..G.."\n"
-text=text.."Grade = "..grade.." points = "..points.."\n"
-text=text.."# of total deviations = "..N.."\n"
-text=text.."# of large deviations _ = "..nL.."\n"
-text=text.."# of normal deviations = "..nN.."\n"
-text=text.."# of small deviations ( = "..nS.."\n"
-self:T2(self.lid..text)
-if playerData.wop then
-if playerData.lig then
-grade="WO"
-points=1.0
-G="LIG"
-else
-grade="WOP"
-points=2.0
-G="n/a"
-end
-elseif playerData.wofd then
-if playerData.landed then
-grade="CUT"
-points=0.0
-else
-grade="WOFD"
-points=-1.0
-end
-G="n/a"
-elseif playerData.owo then
-grade="OWO"
-points=2.0
-if N==0 then
-G="n/a"
-end
-elseif playerData.waveoff then
-if playerData.landed then
-grade="CUT"
-points=0.0
-else
-grade="WO"
-points=1.0
-end
-elseif playerData.boltered then
-grade="-- (BOLTER)"
-points=2.5
-elseif not playerData.hover and playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
-if playerData.landed then
-grade="CUT"
-points=0.0
-end
-end
-return grade,points,G
-end
-function AIRBOSS:_Flightdata2Text(playerData,groovestep)
-local function little(text)
-return string.format("(%s)",text)
-end
-local function underline(text)
-return string.format("_%s_",text)
-end
-local fdata=playerData.groove[groovestep]
-if fdata==nil then
-self:T3(self.lid.."Flight data is nil.")
-return"",0
-end
-local step=fdata.Step
-local AOA=fdata.AoA
-local GSE=fdata.GSE
-local LUE=fdata.LUE
-local ROL=fdata.Roll
-local acaoa=self:_GetAircraftAoA(playerData)
-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
-local O=nil
-if step==AIRBOSS.PatternStep.GROOVE_XX then
-if LUEacaoa.SLOW then
-S=underline("SLO")
-elseif AOA>acaoa.Slow then
-S="SLO"
-elseif AOA>acaoa.OnSpeedMax then
-S=little("SLO")
-elseif AOAself.gle.HIGH then
-A=underline("H")
-elseif GSE>self.gle.High then
-A="H"
-elseif GSE>self.gle._max then
-A=little("H")
-elseif GSEself.lue.RIGHT then
-D=underline("LUL")
-elseif LUE>self.lue.Right then
-D="LUL"
-elseif LUE>self.lue._max then
-D=little("LUL")
-elseif playerData.case<3 then
-if LUEpos.Xmax then
-self:T(string.format("Xmax: X=%d > %d=Xmax",X,pos.Xmax))
-abort=true
-elseif pos.Zmin and Zpos.Zmax then
-self:T(string.format("Zmax: Z=%d > %d=Zmax",Z,pos.Zmax))
-abort=true
-end
-return abort
-end
-function AIRBOSS:_TooFarOutText(X,Z,posData)
-local text="you are too "
-local xtext=nil
-if posData.Xmin and XposData.Xmax then
-if posData.Xmax>=0 then
-xtext="far ahead of "
-else
-xtext="close to "
-end
-end
-local ztext=nil
-if posData.Zmin and ZposData.Zmax then
-if posData.Zmax>=0 then
-ztext="far starboard of "
-else
-ztext="too close to "
-end
-end
-if xtext and ztext then
-text=text..xtext.." and "..ztext
-elseif xtext then
-text=text..xtext
-elseif ztext then
-text=text..ztext
-end
-text=text.."the carrier."
-if xtext==nil and ztext==nil then
-text="you are too far from where you should be!"
-end
-return text
-end
-function AIRBOSS:_AbortPattern(playerData,X,Z,posData,patternwo)
-local text=self:_TooFarOutText(X,Z,posData)
-local dtext=string.format("Abort: X=%d Xmin=%s, Xmax=%s | Z=%d Zmin=%s Zmax=%s",X,tostring(posData.Xmin),tostring(posData.Xmax),Z,tostring(posData.Zmin),tostring(posData.Zmax))
-self:T(self.lid..dtext)
-self:MessageToPlayer(playerData,text,"LSO")
-if patternwo then
-playerData.wop=true
-self:_AddToDebrief(playerData,string.format("Pattern wave off: %s",text))
-self:RadioTransmission(self.LSORadio,self.LSOCall.DEPARTANDREENTER,false,3,nil,nil,true)
-playerData.step=AIRBOSS.PatternStep.DEBRIEF
-playerData.warning=nil
-end
-end
-function AIRBOSS:_PlayerHint(playerData,delay,soundoff)
-if not playerData.showhints then
-return
-end
-local alt,aoa,dist,speed=self:_GetAircraftParameters(playerData)
-local hintAlt,debriefAlt,callAlt=self:_AltitudeCheck(playerData,alt)
-local hintSpeed,debriefSpeed,callSpeed=self:_SpeedCheck(playerData,speed)
-local hintAoA,debriefAoA,callAoA=self:_AoACheck(playerData,aoa)
-local hintDist,debriefDist,callDist=self:_DistanceCheck(playerData,dist)
-local hint=""
-if hintAlt and hintAlt~=""then
-hint=hint.."\n"..hintAlt
-end
-if hintSpeed and hintSpeed~=""then
-hint=hint.."\n"..hintSpeed
-end
-if hintAoA and hintAoA~=""then
-hint=hint.."\n"..hintAoA
-end
-if hintDist and hintDist~=""then
-hint=hint.."\n"..hintDist
-end
-local debrief=""
-if debriefAlt and debriefAlt~=""then
-debrief=debrief.."\n- "..debriefAlt
-end
-if debriefSpeed and debriefSpeed~=""then
-debrief=debrief.."\n- "..debriefSpeed
-end
-if debriefAoA and debriefAoA~=""then
-debrief=debrief.."\n- "..debriefAoA
-end
-if debriefDist and debriefDist~=""then
-debrief=debrief.."\n- "..debriefDist
-end
-if debrief~=""then
-self:_AddToDebrief(playerData,debrief)
-end
-delay=delay or 0
-if not soundoff then
-if callAlt then
-self:Sound2Player(playerData,self.LSORadio,callAlt,false,delay)
-delay=delay+callAlt.duration+0.5
-end
-if callSpeed then
-self:Sound2Player(playerData,self.LSORadio,callSpeed,false,delay)
-delay=delay+callSpeed.duration+0.5
-end
-if callAoA then
-self:Sound2Player(playerData,self.LSORadio,callAoA,false,delay)
-delay=delay+callAoA.duration+0.5
-end
-if callDist then
-self:Sound2Player(playerData,self.LSORadio,callDist,false,delay)
-delay=delay+callDist.duration+0.5
-end
-end
-if playerData.step==AIRBOSS.PatternStep.ARCIN then
-if playerData.difficulty==AIRBOSS.Difficulty.EASY then
-local radial=self:GetRadial(playerData.case,true,false,true)
-local turn="right"
-if self.holdingoffset<0 then
-turn="left"
-end
-hint=hint..string.format("\nTurn %s and select TACAN %03d°.",turn,radial)
-end
-end
-if playerData.step==AIRBOSS.PatternStep.DIRTYUP then
-if playerData.difficulty==AIRBOSS.Difficulty.EASY then
-if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
-hint=hint.."\nFAF! Checks completed. Nozzles 50°."
-else
-hint=hint.."\nDirty up! Hook, gear and flaps down."
-end
-end
-end
-if playerData.step==AIRBOSS.PatternStep.BULLSEYE then
-if playerData.difficulty==AIRBOSS.Difficulty.EASY then
-if playerData.actype==AIRBOSS.AircraftCarrier.HORNET
-or playerData.actype==AIRBOSS.AircraftCarrier.RHINOE
-or playerData.actype==AIRBOSS.AircraftCarrier.RHINOF
-or playerData.actype==AIRBOSS.AircraftCarrier.GROWLER then
-hint=hint..string.format("\nIntercept glideslope and follow the needles.")
-else
-hint=hint..string.format("\nIntercept glideslope.")
-end
-end
-end
-if hint~=""then
-local text=string.format("%s%s",playerData.step,hint)
-self:MessageToPlayer(playerData,hint,"AIRBOSS","")
-end
-end
-function AIRBOSS:_StepHint(playerData,step)
-step=step or playerData.step
-if playerData.difficulty==AIRBOSS.Difficulty.EASY and playerData.showhints then
-local alt,aoa,dist,speed=self:_GetAircraftParameters(playerData,step)
-local hint=""
-if alt then
-hint=hint..string.format("\nAltitude %d ft",UTILS.MetersToFeet(alt))
-end
-if aoa then
-hint=hint..string.format("\nAoA %.1f",self:_AoADeg2Units(playerData,aoa))
-end
-if speed then
-hint=hint..string.format("\nSpeed %d knots",UTILS.MpsToKnots(speed))
-end
-if dist then
-hint=hint..string.format("\nDistance to the boat %.1f NM",UTILS.MetersToNM(dist))
-end
-if step==AIRBOSS.PatternStep.LATEBREAK then
-if playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B then
-hint=hint.."\nWing Sweep 20°, Gear DOWN < 280 KIAS."
-end
-end
-if step==AIRBOSS.PatternStep.ABEAM then
-if playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
-hint=hint.."\nNozzles 50°-60°. Antiskid OFF. Lights OFF."
-elseif playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B then
-hint=hint.."\nSlats/Flaps EXTENDED < 225 KIAS. DLC SELECTED. Auto Throttle IF DESIRED."
-else
-hint=hint.."\nDirty up! Gear DOWN, flaps DOWN. Check hook down."
-end
-end
-if hint~=""then
-local text=string.format("Optimal setup at next step %s:%s",step,hint)
-self:MessageToPlayer(playerData,text,"AIRBOSS","",nil,false,1)
-end
-end
-end
-function AIRBOSS:_AltitudeCheck(playerData,altopt)
-if altopt==nil then
-return nil,nil
-end
-local altitude=playerData.unit:GetAltitude()
-local lowscore,badscore=self:_GetGoodBadScore(playerData)
-local _error=(altitude-altopt)/altopt*100
-local radiocall=nil
-local hint=""
-if _error>badscore then
-radiocall=self:_NewRadioCall(self.LSOCall.HIGH,"Paddles","")
-elseif _error>lowscore then
-radiocall=self:_NewRadioCall(self.LSOCall.HIGH,"Paddles","")
-elseif _error<-badscore then
-radiocall=self:_NewRadioCall(self.LSOCall.LOW,"Paddles","")
-elseif _error<-lowscore then
-radiocall=self:_NewRadioCall(self.LSOCall.LOW,"Paddles","")
-else
-hint=string.format("Good altitude. ")
-end
-if playerData.difficulty==AIRBOSS.Difficulty.EASY then
-hint=hint..string.format("Optimal altitude is %d ft.",UTILS.MetersToFeet(altopt))
-elseif playerData.difficulty==AIRBOSS.Difficulty.NORMAL then
-hint=""
-elseif playerData.difficulty==AIRBOSS.Difficulty.HARD then
-hint=""
-end
-local debrief=string.format("Altitude %d ft = %d%% deviation from %d ft.",UTILS.MetersToFeet(altitude),_error,UTILS.MetersToFeet(altopt))
-return hint,debrief,radiocall
-end
-function AIRBOSS:_AoACheck(playerData,optaoa)
-if optaoa==nil then
-return nil,nil
-end
-local lowscore,badscore=self:_GetGoodBadScore(playerData)
-local aoa=playerData.unit:GetAoA()
-local _error=(aoa-optaoa)/optaoa*100
-local aircraftaoa=self:_GetAircraftAoA(playerData)
-local radiocall=nil
-local hint=""
-if aoa>=aircraftaoa.SLOW then
-radiocall=self:_NewRadioCall(self.LSOCall.SLOW,"Paddles","")
-elseif aoa>=aircraftaoa.Slow then
-radiocall=self:_NewRadioCall(self.LSOCall.SLOW,"Paddles","")
-elseif aoa>=aircraftaoa.OnSpeedMax then
-hint="Your're a little slow. "
-elseif aoa>=aircraftaoa.OnSpeedMin then
-hint="You're on speed. "
-elseif aoa>=aircraftaoa.Fast then
-hint="You're a little fast. "
-elseif aoa>=aircraftaoa.FAST then
-radiocall=self:_NewRadioCall(self.LSOCall.FAST,"Paddles","")
-else
-radiocall=self:_NewRadioCall(self.LSOCall.FAST,"Paddles","")
-end
-if playerData.difficulty==AIRBOSS.Difficulty.EASY then
-hint=hint..string.format("Optimal AoA is %.1f.",self:_AoADeg2Units(playerData,optaoa))
-elseif playerData.difficulty==AIRBOSS.Difficulty.NORMAL then
-hint=""
-elseif playerData.difficulty==AIRBOSS.Difficulty.HARD then
-hint=""
-end
-local debrief=string.format("AoA %.1f = %d%% deviation from %.1f.",self:_AoADeg2Units(playerData,aoa),_error,self:_AoADeg2Units(playerData,optaoa))
-return hint,debrief,radiocall
-end
-function AIRBOSS:_SpeedCheck(playerData,speedopt)
-if speedopt==nil then
-return nil,nil
-end
-local speed=playerData.unit:GetVelocityMPS()
-local lowscore,badscore=self:_GetGoodBadScore(playerData)
-local _error=(speed-speedopt)/speedopt*100
-local radiocall=nil
-local hint=""
-if _error>badscore then
-radiocall=self:_NewRadioCall(self.LSOCall.FAST,"AIRBOSS","")
-elseif _error>lowscore then
-radiocall=self:_NewRadioCall(self.LSOCall.FAST,"AIRBOSS","")
-elseif _error<-badscore then
-radiocall=self:_NewRadioCall(self.LSOCall.SLOW,"AIRBOSS","")
-elseif _error<-lowscore then
-radiocall=self:_NewRadioCall(self.LSOCall.SLOW,"AIRBOSS","")
-else
-hint=string.format("Good speed. ")
-end
-if playerData.difficulty==AIRBOSS.Difficulty.EASY then
-hint=hint..string.format("Optimal speed is %d knots.",UTILS.MpsToKnots(speedopt))
-elseif playerData.difficulty==AIRBOSS.Difficulty.NORMAL then
-hint=""
-elseif playerData.difficulty==AIRBOSS.Difficulty.HARD then
-hint=""
-end
-local debrief=string.format("Speed %d knots = %d%% deviation from %d knots.",UTILS.MpsToKnots(speed),_error,UTILS.MpsToKnots(speedopt))
-return hint,debrief,radiocall
-end
-function AIRBOSS:_DistanceCheck(playerData,optdist)
-if optdist==nil then
-return nil,nil
-end
-local distance=playerData.unit:GetCoordinate():Get2DDistance(self:GetCoordinate())
-local lowscore,badscore=self:_GetGoodBadScore(playerData)
-local _error=(distance-optdist)/optdist*100
-local hint
-if _error>badscore then
-hint=string.format("You're too far from the boat!")
-elseif _error>lowscore then
-hint=string.format("You're slightly too far from the boat.")
-elseif _error<-badscore then
-hint=string.format("You're too close to the boat!")
-elseif _error<-lowscore then
-hint=string.format("You're slightly too far from the boat.")
-else
-hint=string.format("Good distance to the boat.")
-end
-if playerData.difficulty==AIRBOSS.Difficulty.EASY then
-hint=hint..string.format(" Optimal distance is %.1f NM.",UTILS.MetersToNM(optdist))
-elseif playerData.difficulty==AIRBOSS.Difficulty.NORMAL then
-hint=""
-elseif playerData.difficulty==AIRBOSS.Difficulty.HARD then
-hint=""
-end
-local debrief=string.format("Distance %.1f NM = %d%% deviation from %.1f NM.",UTILS.MetersToNM(distance),_error,UTILS.MetersToNM(optdist))
-return hint,debrief,nil
-end
-function AIRBOSS:_AddToDebrief(playerData,hint,step)
-step=step or playerData.step
-table.insert(playerData.debrief,{step=step,hint=hint})
-end
-function AIRBOSS:_Debrief(playerData)
-self:F(self.lid..string.format("Debriefing of player %s.",playerData.name))
-playerData.debriefschedulerID=nil
-playerData.attitudemonitor=false
-local grade,points,analysis=self:_LSOgrade(playerData)
-if points and points>=0 then
-table.insert(playerData.points,points)
-end
-local Points=0
-if playerData.landed and not playerData.unit:InAir()then
-for _,_points in pairs(playerData.points)do
-Points=Points+_points
-end
-Points=Points/#playerData.points
-playerData.points={}
-else
-Points=points
-end
-local mygrade={}
-mygrade.grade=grade
-mygrade.points=points
-mygrade.details=analysis
-mygrade.wire=playerData.wire
-mygrade.Tgroove=playerData.Tgroove
-if playerData.landed and not playerData.unit:InAir()then
-mygrade.finalscore=Points
-end
-mygrade.case=playerData.case
-local windondeck=self:GetWindOnDeck()
-mygrade.wind=UTILS.Round(UTILS.MpsToKnots(windondeck),1)
-mygrade.modex=playerData.onboard
-mygrade.airframe=playerData.actype
-mygrade.carriertype=self.carriertype
-mygrade.carriername=self.alias
-mygrade.carrierrwy=self.carrierparam.rwyangle
-mygrade.theatre=self.theatre
-mygrade.mitime=UTILS.SecondsToClock(timer.getAbsTime(),true)
-mygrade.midate=UTILS.GetDCSMissionDate()
-mygrade.osdate="n/a"
-if os then
-mygrade.osdate=os.date()
-end
-playerData.grade=mygrade
-if playerData.trapon and self.trapsheet then
-self:_SaveTrapSheet(playerData,mygrade)
-end
-table.insert(self.playerscores[playerData.name],mygrade)
-self:LSOGrade(playerData,mygrade)
-local text=string.format("%s %.1f PT - %s",grade,Points,analysis)
-if Points==-1 then
-text=string.format("%s n/a PT - Foul deck",grade,Points,analysis)
-end
-if not(playerData.wop or playerData.wofd)then
-if playerData.wire and playerData.wire<=4 then
-text=text..string.format(" %d-wire",playerData.wire)
-end
-if playerData.Tgroove and playerData.Tgroove<=360 and playerData.case<3 then
-text=text..string.format("\nTime in the groove %.1f seconds: %s",playerData.Tgroove,self:_EvalGrooveTime(playerData))
-end
-end
-playerData.lastdebrief=UTILS.DeepCopy(playerData.debrief)
-if playerData.difficulty==AIRBOSS.Difficulty.EASY then
-text=text..string.format("\nYour detailed debriefing can be found via the F10 radio menu.")
-end
-self:MessageToPlayer(playerData,text,"LSO","",30,true)
-playerData.step=AIRBOSS.PatternStep.UNDEFINED
-if playerData.wop then
-if playerData.unit:IsAlive()then
-local heading,distance
-if playerData.case==1 or playerData.case==2 then
-playerData.step=AIRBOSS.PatternStep.INITIAL
-local initial=self:GetCoordinate():Translate(UTILS.NMToMeters(3.5),self:GetRadial(2,false,false,false))
-heading=playerData.unit:GetCoordinate():HeadingTo(initial)
-distance=playerData.unit:GetCoordinate():Get2DDistance(initial)
-elseif playerData.case==3 then
-playerData.step=AIRBOSS.PatternStep.BULLSEYE
-local zone=self:_GetZoneBullseye(playerData.case)
-heading=playerData.unit:GetCoordinate():HeadingTo(zone:GetCoordinate())
-distance=playerData.unit:GetCoordinate():Get2DDistance(zone:GetCoordinate())
-end
-local text=string.format("fly heading %03d° for %d NM to re-enter the pattern.",heading,UTILS.MetersToNM(distance))
-self:MessageToPlayer(playerData,text,"LSO",nil,nil,false,5)
-else
-self:E(self.lid..string.format("ERROR: Player unit not alive!"))
-end
-elseif playerData.wofd then
-if playerData.unit:InAir()then
-playerData.step=AIRBOSS.PatternStep.BOLTER
-else
-self:Sound2Player(playerData,self.LSORadio,self.LSOCall.WELCOMEABOARD)
-local text=string.format("deck was fouled but you landed anyway. Airboss wants to talk to you!")
-self:MessageToPlayer(playerData,text,"LSO",nil,nil,false,3)
-end
-elseif playerData.owo then
-if playerData.unit:InAir()then
-playerData.step=AIRBOSS.PatternStep.BOLTER
-else
-self:E(self.lid.."ERROR: player landed when OWO was issues. This should not happen. Please report!")
-self:Sound2Player(playerData,self.LSORadio,self.LSOCall.WELCOMEABOARD)
-end
-elseif playerData.waveoff then
-if playerData.unit:InAir()then
-playerData.step=AIRBOSS.PatternStep.BOLTER
-else
-self:Sound2Player(playerData,self.LSORadio,self.LSOCall.WELCOMEABOARD)
-local text=string.format("you were waved off but landed anyway. Airboss wants to talk to you!")
-self:MessageToPlayer(playerData,text,"LSO",nil,nil,false,3)
-end
-elseif playerData.boltered then
-if playerData.unit:InAir()then
-playerData.step=AIRBOSS.PatternStep.BOLTER
-end
-elseif playerData.landed then
-if not playerData.unit:InAir()then
-self:Sound2Player(playerData,self.LSORadio,self.LSOCall.WELCOMEABOARD)
-end
-else
-self:MessageToPlayer(playerData,"Undefined state after landing! Please report.","ERROR",nil,20)
-playerData.step=AIRBOSS.PatternStep.UNDEFINED
-end
-if playerData.landed and not playerData.unit:InAir()then
-self:_RecoveredElement(playerData.unit)
-self:_CheckSectionRecovered(playerData)
-end
-playerData.passes=playerData.passes+1
-self:_StepHint(playerData)
-self:_InitPlayer(playerData,playerData.step)
-MESSAGE:New(string.format("Player step %s.",playerData.step),5,"DEBUG"):ToAllIf(self.Debug)
-if self.autosave and mygrade.finalscore then
-self:Save(self.autosavepath,self.autosavefile)
-end
-end
-function AIRBOSS:_CheckCollisionCoord(coordto,coordfrom)
-local dx=100
-local d=0
-if coordfrom then
-d=0
-else
-d=250
-coordfrom=self:GetCoordinate():Translate(d,self:GetHeading())
-end
-local dmax=coordfrom:Get2DDistance(coordto)
-local direction=coordfrom:HeadingTo(coordto)
-local clear=true
-while d<=dmax do
-local cp=coordfrom:Translate(d,direction)
-if not cp:IsSurfaceTypeWater()then
-if self.Debug then
-local st=cp:GetSurfaceType()
-cp:MarkToAll(string.format("Collision check surface type %d",st))
-end
-clear=false
-break
-end
-d=d+dx
-end
-local text=""
-if clear then
-text=string.format("Path into direction %03d° is clear for the next %.1f NM.",direction,UTILS.MetersToNM(d))
-else
-text=string.format("Detected obstacle at distance %.1f NM into direction %03d°.",UTILS.MetersToNM(d),direction)
-end
-self:T2(self.lid..text)
-return not clear,d
-end
-function AIRBOSS:_CheckFreePathToNextWP(fromcoord)
-fromcoord=fromcoord or self:GetCoordinate():Translate(250,self:GetHeading())
-local Nnextwp=math.min(self.currentwp+1,#self.waypoints)
-local nextwp=self.waypoints[Nnextwp]
-local collision=self:_CheckCollisionCoord(nextwp,fromcoord)
-return collision
-end
-function AIRBOSS:_Pathfinder()
-local hdg=self:GetHeading()
-local cv=self:GetCoordinate()
-local directions={-20,20,-30,30,-40,40,-50,50,-60,60,-70,70,-80,80,-90,90,-100,100}
-for _,_direction in pairs(directions)do
-local direction=hdg+_direction
-local _,dfree=self:_CheckCollisionCoord(cv:Translate(UTILS.NMToMeters(20),direction),cv)
-local distance=500
-while distance<=dfree do
-local fromcoord=cv:Translate(distance,direction)
-local collision=self:_CheckFreePathToNextWP(fromcoord)
-self:T2(self.lid..string.format("Pathfinder d=%.1f m, direction=%03d°, collision=%s",distance,direction,tostring(collision)))
-if not collision then
-self:CarrierDetour(fromcoord)
-return
-end
-distance=distance+500
-end
-end
-end
-function AIRBOSS:CarrierResumeRoute(gotocoord)
-AIRBOSS._ResumeRoute(self.carrier:GetGroup(),self,gotocoord)
-return self
-end
-function AIRBOSS:CarrierDetour(coord,speed,uturn,uspeed,tcoord)
-local pos0=self:GetCoordinate()
-local vel0=self.carrier:GetVelocityKNOTS()
-speed=speed or math.max(vel0,5)
-local speedkmh=math.max(UTILS.KnotsToKmph(speed),UTILS.KnotsToKmph(2))
-local cspeedkmh=math.max(self.carrier:GetVelocityKMH(),UTILS.KnotsToKmph(10))
-local uspeedkmh=UTILS.KnotsToKmph(uspeed or speed)
-local wp={}
-table.insert(wp,pos0:WaypointGround(cspeedkmh))
-if tcoord then
-table.insert(wp,tcoord:WaypointGround(cspeedkmh))
-end
-table.insert(wp,coord:WaypointGround(speedkmh))
-if uturn then
-table.insert(wp,pos0:WaypointGround(uspeedkmh))
-end
-local group=self.carrier:GetGroup()
-local TaskResumeRoute=group:TaskFunction("AIRBOSS._ResumeRoute",self)
-group:SetTaskWaypoint(wp[#wp],TaskResumeRoute)
-if self.Debug then
-if tcoord then
-tcoord:MarkToAll(string.format("Detour Turn Help WP. Speed %.1f knots",UTILS.KmphToKnots(cspeedkmh)))
-end
-coord:MarkToAll(string.format("Detour Waypoint. Speed %.1f knots",UTILS.KmphToKnots(speedkmh)))
-if uturn then
-pos0:MarkToAll(string.format("Detour U-turn WP. Speed %.1f knots",UTILS.KmphToKnots(uspeedkmh)))
-end
-end
-self.detour=true
-self.carrier:Route(wp)
-end
-function AIRBOSS:CarrierTurnIntoWind(time,vdeck,uturn)
-local _,vwind=self:GetWind()
-local vdeck=UTILS.MpsToKnots(vdeck)
-local hiw,speedknots=self:GetHeadingIntoWind(vdeck)
-local vtot=UTILS.KnotsToMps(speedknots)
-local dist=vtot*time
-local distNM=UTILS.MetersToNM(dist)
-local hdg=self:GetHeading()
-local deltaH=self:_GetDeltaHeading(hdg,hiw)
-self:I(self.lid..string.format("Carrier steaming into the wind (%.1f kts). Heading=%03d-->%03d (Delta=%.1f), Speed=%.1f knots, Distance=%.1f NM, Time=%d sec",
-UTILS.MpsToKnots(vwind),hdg,hiw,deltaH,speedknots,distNM,speedknots,time))
-local Cv=self:GetCoordinate()
-local Ctiw=nil
-local Csoo=nil
-if deltaH<45 then
-Csoo=Cv:Translate(750,hdg):Translate(750,hiw)
-local hsw=self:GetHeadingIntoWind(vdeck,false,Csoo)
-Ctiw=Csoo:Translate(dist,hsw)
-elseif deltaH<90 then
-Csoo=Cv:Translate(900,hdg):Translate(900,hiw)
-local hsw=self:GetHeadingIntoWind(vdeck,false,Csoo)
-Ctiw=Csoo:Translate(dist,hsw)
-elseif deltaH<135 then
-Csoo=Cv:Translate(1100,hdg-90):Translate(1000,hiw)
-local hsw=self:GetHeadingIntoWind(vdeck,false,Csoo)
-Ctiw=Csoo:Translate(dist,hsw)
-else
-Csoo=Cv:Translate(1200,hdg-90):Translate(1000,hiw)
-local hsw=self:GetHeadingIntoWind(vdeck,false,Csoo)
-Ctiw=Csoo:Translate(dist,hsw)
-end
-self.Creturnto=self:GetCoordinate()
-local nextwp=self:_GetNextWaypoint()
-local vdownwind=UTILS.MpsToKnots(nextwp:GetVelocity())
-if vdownwind<1 then
-vdownwind=10
-end
-self:CarrierDetour(Ctiw,speedknots,uturn,vdownwind,Csoo)
-self.turnintowind=true
-return self
-end
-function AIRBOSS:_GetNextWaypoint()
-local Nextwp=nil
-if self.currentwp==#self.waypoints then
-Nextwp=1
-else
-Nextwp=self.currentwp+1
-end
-local text=string.format("Current WP=%d/%d, next WP=%d",self.currentwp,#self.waypoints,Nextwp)
-self:T2(self.lid..text)
-local nextwp=self.waypoints[Nextwp]
-return nextwp,Nextwp
-end
-function AIRBOSS:_InitWaypoints()
-local Waypoints=self.carrier:GetGroup():GetTemplateRoutePoints()
-self.waypoints={}
-for i,point in ipairs(Waypoints)do
-local coord=COORDINATE:New(point.x,point.alt,point.y)
-coord:SetVelocity(point.speed)
-table.insert(self.waypoints,coord)
-if self.Debug then
-coord:MarkToAll(string.format("Carrier Waypoint %d, Speed=%.1f knots",i,UTILS.MpsToKnots(point.speed)))
-end
-end
-return self
-end
-function AIRBOSS:_PatrolRoute(n)
-local nextWP,N=self:_GetNextWaypoint()
-n=n or N
-local CarrierGroup=self.carrier:GetGroup()
-local Waypoints={}
-local wp=self:GetCoordinate():WaypointGround(CarrierGroup:GetVelocityKMH())
-table.insert(Waypoints,wp)
-for i=n,#self.waypoints do
-local coord=self.waypoints[i]
-local wp=coord:WaypointGround(UTILS.MpsToKmph(coord.Velocity))
-local TaskPassingWP=CarrierGroup:TaskFunction("AIRBOSS._PassingWaypoint",self,i,#self.waypoints)
-CarrierGroup:SetTaskWaypoint(wp,TaskPassingWP)
-table.insert(Waypoints,wp)
-end
-CarrierGroup:Route(Waypoints)
-return self
-end
-function AIRBOSS:_GetETAatNextWP()
-local cwp=self.currentwp
-local tnow=timer.getAbsTime()
-local p=self:GetCoordinate()
-local v=self.carrier:GetVelocityMPS()
-local nextWP=self:_GetNextWaypoint()
-local s=p:Get2DDistance(nextWP)
-local t=s/v
-local eta=t+tnow
-return eta
-end
-function AIRBOSS:_CheckCarrierTurning()
-local vNew=self.carrier:GetOrientationX()
-local vLast=self.Corientlast
-vNew.y=0;
-vLast.y=0
-local deltaLast=math.deg(math.acos(UTILS.VecDot(vNew,vLast)/UTILS.VecNorm(vNew)/UTILS.VecNorm(vLast)))
-self.Corientlast=vNew
-local turning=math.abs(deltaLast)>=1
-if self.turning and not turning then
-local FB=self:GetFinalBearing(true)
-self:_MarshalCallNewFinalBearing(FB)
-end
-if turning and not self.turning then
-local hdg
-if self.turnintowind then
-local vdeck=self.recoverywindow and self.recoverywindow.SPEED or 20
-hdg=self:GetHeadingIntoWind(vdeck,false)
-else
-hdg=self:GetCoordinate():HeadingTo(self:_GetNextWaypoint())
-end
-hdg=hdg-self.magvar
-if hdg<0 then
-hdg=360+hdg
-end
-self:_MarshalCallCarrierTurnTo(hdg)
-end
-self.turning=turning
-end
-function AIRBOSS:_CheckPatternUpdate()
-local dTPupdate=10*60
-local Dupdate=UTILS.NMToMeters(2.5)
-local Hupdate=5
-local dt=timer.getTime()-self.Tpupdate
-if dt=Hupdate then
-self:T(self.lid..string.format("Carrier heading changed by %d°.",deltaHeading))
-Hchange=true
-end
-local pos=self:GetCoordinate()
-local dist=pos:Get2DDistance(self.Cposition)
-local Dchange=false
-if dist>=Dupdate then
-self:T(self.lid..string.format("Carrier position changed by %.1f NM.",UTILS.MetersToNM(dist)))
-Dchange=true
-end
-if Hchange or Dchange then
-for _,_flight in pairs(self.Qmarshal)do
-local flight=_flight
-if flight.ai then
-self:_MarshalAI(flight,flight.flag)
-end
-end
-self.Corientation=vNew
-self.Cposition=pos
-self.Tpupdate=timer.getTime()
-end
-end
-function AIRBOSS._PassingWaypoint(group,airboss,i,final)
-local text=string.format("Group %s passing waypoint %d of %d.",group:GetName(),i,final)
-if airboss.Debug and false then
-local pos=group:GetCoordinate()
-pos:SmokeRed()
-local MarkerID=pos:MarkToAll(string.format("Group %s reached waypoint %d",group:GetName(),i))
-end
-MESSAGE:New(text,10):ToAllIf(airboss.Debug)
-airboss:T(airboss.lid..text)
-airboss.currentwp=i
-airboss:PassingWaypoint(i)
-if i==final and final>1 and airboss.adinfinitum then
-airboss:_PatrolRoute()
-end
-end
-function AIRBOSS._ResumeRoute(group,airboss,gotocoord)
-local nextwp,Nextwp=airboss:_GetNextWaypoint()
-local speedkmh=nextwp.Velocity*3.6
-if speedkmh<1 then
-speedkmh=UTILS.KnotsToKmph(10)
-end
-local waypoints={}
-local c0=group:GetCoordinate()
-local wp0=c0:WaypointGround(speedkmh)
-table.insert(waypoints,wp0)
-if gotocoord then
-local headingto=c0:HeadingTo(gotocoord)
-local hdg1=airboss:GetHeading()
-local hdg2=c0:HeadingTo(gotocoord)
-local delta=airboss:_GetDeltaHeading(hdg1,hdg2)
-if delta>90 then
-local turnradius=UTILS.NMToMeters(3)
-local gotocoordh=c0:Translate(turnradius,hdg1+45)
-local wp=gotocoordh:WaypointGround(speedkmh)
-table.insert(waypoints,wp)
-gotocoordh=c0:Translate(turnradius,hdg1+90)
-wp=gotocoordh:WaypointGround(speedkmh)
-table.insert(waypoints,wp)
-end
-local wp1=gotocoord:WaypointGround(speedkmh)
-table.insert(waypoints,wp1)
-end
-local text=string.format("Carrier is resuming route. Next waypoint %d, Speed=%.1f knots.",Nextwp,UTILS.KmphToKnots(speedkmh))
-MESSAGE:New(text,10):ToAllIf(airboss.Debug)
-airboss:I(airboss.lid..text)
-for i=Nextwp,#airboss.waypoints do
-local coord=airboss.waypoints[i]
-local speed=coord.Velocity*3.6
-if speed<1 then
-speed=UTILS.KnotsToKmph(10)
-end
-local wp=coord:WaypointGround(speed)
-local TaskPassingWP=group:TaskFunction("AIRBOSS._PassingWaypoint",airboss,i,#airboss.waypoints)
-group:SetTaskWaypoint(wp,TaskPassingWP)
-table.insert(waypoints,wp)
-end
-airboss.turnintowind=false
-airboss.detour=false
-group:Route(waypoints)
-end
-function AIRBOSS._ReachedHoldingZone(group,airboss,flight)
-local text=string.format("Flight %s reached holding zone.",group:GetName())
-MESSAGE:New(text,10):ToAllIf(airboss.Debug)
-airboss:T(airboss.lid..text)
-if airboss.Debug then
-group:GetCoordinate():MarkToAll(text)
-end
-if flight then
-flight.holding=true
-flight.time=timer.getAbsTime()
-end
-end
-function AIRBOSS._TaskFunctionMarshalAI(group,airboss,flight)
-local text=string.format("Flight %s is send to marshal.",group:GetName())
-MESSAGE:New(text,10):ToAllIf(airboss.Debug)
-airboss:T(airboss.lid..text)
-local stack=airboss:_GetFreeStack(flight.ai)
-if stack then
-airboss:_MarshalAI(flight,stack)
-else
-if not airboss:_InQueue(airboss.Qwaiting,flight.group)then
-airboss:_WaitAI(flight)
-end
-end
-if flight.refueling==true then
-airboss:I(airboss.lid..string.format("Flight group %s finished refueling task.",flight.groupname))
-end
-flight.refueling=false
-end
-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
-nickname="Hawkeye"
-elseif actype==AIRBOSS.AircraftCarrier.F14A_AI or actype==AIRBOSS.AircraftCarrier.F14A or actype==AIRBOSS.AircraftCarrier.F14B then
-nickname="Tomcat"
-elseif actype==AIRBOSS.AircraftCarrier.FA18C or actype==AIRBOSS.AircraftCarrier.HORNET then
-nickname="Hornet"
-elseif actype==AIRBOSS.AircraftCarrier.RHINOE or actype==AIRBOSS.AircraftCarrier.RHINOF then
-nickname="Rhino"
-elseif actype==AIRBOSS.AircraftCarrier.GROWLER then
-nickname="Growler"
-elseif actype==AIRBOSS.AircraftCarrier.S3B or actype==AIRBOSS.AircraftCarrier.S3BTANKER then
-nickname="Viking"
-end
-return nickname
-end
-function AIRBOSS:_GetOnboardNumberPlayer(group)
-return self:_GetOnboardNumbers(group,true)
-end
-function AIRBOSS:_GetOnboardNumbers(group,playeronly)
-local groupname=group:GetName()
-local text=string.format("Onboard numbers of group %s:",groupname)
-local units=group:GetTemplate().units
-local numbers={}
-for _,unit in pairs(units)do
-local n=tostring(unit.onboard_num)
-local name=unit.name
-local skill=unit.skill or"Unknown"
-text=text..string.format("\n- unit %s: onboard #=%s skill=%s",name,n,tostring(skill))
-if playeronly and skill=="Client"or skill=="Player"then
-return n
-end
-numbers[name]=n
-end
-self:T2(self.lid..text)
-return numbers
-end
-function AIRBOSS:_GetTowerFrequency()
-self.TowerFreq=0
-local striketemplate=self.carrier:GetGroup():GetTemplate()
-for _,unit in pairs(striketemplate.units)do
-if self.carrier:GetName()==unit.name then
-self.TowerFreq=unit.frequency/1000000
-return
-end
-end
-end
-function AIRBOSS:_GetGoodBadScore(playerData)
-local lowscore
-local badscore
-if playerData.difficulty==AIRBOSS.Difficulty.EASY then
-lowscore=10
-badscore=20
-elseif playerData.difficulty==AIRBOSS.Difficulty.NORMAL then
-lowscore=5
-badscore=10
-elseif playerData.difficulty==AIRBOSS.Difficulty.HARD then
-lowscore=2.5
-badscore=5
-end
-return lowscore,badscore
-end
-function AIRBOSS:_IsCarrierAircraft(unit)
-local aircrafttype=unit:GetTypeName()
-if aircrafttype==AIRBOSS.AircraftCarrier.AV8B then
-if self.carriertype==AIRBOSS.CarrierType.INVINCIBLE or self.carriertype==AIRBOSS.CarrierType.HERMES or self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS or self.carriertype==AIRBOSS.CarrierType.CANBERRA then
-return true
-else
-return false
-end
-end
-if self.carriertype==AIRBOSS.CarrierType.INVINCIBLE or self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS or self.carriertype==AIRBOSS.CarrierType.CANBERRA then
-if aircrafttype~=AIRBOSS.AircraftCarrier.AV8B then
-return false
-end
-end
-for _,actype in pairs(AIRBOSS.AircraftCarrier)do
-if actype==aircrafttype then
-return true
-end
-end
-return false
-end
-function AIRBOSS:_IsHumanUnit(unit)
-local playerunit=self:_GetPlayerUnitAndName(unit:GetName())
-if playerunit then
-return true
-else
-return false
-end
-end
-function AIRBOSS:_IsHuman(group)
-local units=group:GetUnits()
-for _,_unit in pairs(units)do
-local human=self:_IsHumanUnit(_unit)
-if human then
-return true
-end
-end
-return false
-end
-function AIRBOSS:_GetFuelState(unit)
-local fuel=unit:GetFuel()
-local maxfuel=self:_GetUnitMasses(unit)
-local fuelstate=fuel*maxfuel
-self:T2(self.lid..string.format("Unit %s fuel state = %.1f kg = %.1f lbs",unit:GetName(),fuelstate,UTILS.kg2lbs(fuelstate)))
-return UTILS.kg2lbs(fuelstate)
-end
-function AIRBOSS:_GetAngels(alt)
-if alt then
-local angels=UTILS.Round(UTILS.MetersToFeet(alt)/1000,0)
-return angels
-else
-return 0
-end
-end
-function AIRBOSS:_GetUnitMasses(unit)
-local Desc=unit:GetDesc()
-local massfuel=Desc.fuelMassMax or 0
-local massempty=Desc.massEmpty or 0
-local massmax=Desc.massMax or 0
-local masscargo=massmax-massfuel-massempty
-self:T2(self.lid..string.format("Unit %s mass fuel=%.1f kg, empty=%.1f kg, max=%.1f kg, cargo=%.1f kg",unit:GetName(),massfuel,massempty,massmax,masscargo))
-return massfuel,massempty,massmax,masscargo
-end
-function AIRBOSS:_GetPlayerDataUnit(unit)
-if unit:IsAlive()then
-local unitname=unit:GetName()
-local playerunit,playername=self:_GetPlayerUnitAndName(unitname)
-if playerunit and playername then
-return self.players[playername]
-end
-end
-return nil
-end
-function AIRBOSS:_GetPlayerDataGroup(group)
-local units=group:GetUnits()
-for _,unit in pairs(units)do
-local playerdata=self:_GetPlayerDataUnit(unit)
-if playerdata then
-return playerdata
-end
-end
-return nil
-end
-function AIRBOSS:_GetPlayerUnit(_unitName)
-for _,_player in pairs(self.players)do
-local player=_player
-if player.unit and player.unit:GetName()==_unitName then
-self:T(self.lid..string.format("Found player=%s unit=%s in players table.",tostring(player.name),tostring(_unitName)))
-return player.unit,player.name
-end
-end
-return nil,nil
-end
-function AIRBOSS:_GetPlayerUnitAndName(_unitName)
-self:F2(_unitName)
-if _unitName~=nil then
-local u,pn=self:_GetPlayerUnit(_unitName)
-if u and pn then
-return u,pn
-end
-local DCSunit=Unit.getByName(_unitName)
-if DCSunit then
-local playername=DCSunit:getPlayerName()
-local unit=UNIT:Find(DCSunit)
-self:T2({DCSunit=DCSunit,unit=unit,playername=playername})
-if DCSunit and unit and playername then
-self:T(self.lid..string.format("Found DCS unit %s with player %s.",tostring(_unitName),tostring(playername)))
-return unit,playername
-end
-end
-end
-return nil,nil
-end
-function AIRBOSS:GetCoalition()
-return self.carrier:GetCoalition()
-end
-function AIRBOSS:GetCoordinate()
-return self.carrier:GetCoord()
-end
-function AIRBOSS:GetCoord()
-return self.carrier:GetCoord()
-end
-function AIRBOSS:_GetStaticWeather()
-local weather=env.mission.weather
-local clouds=weather.clouds
-local visibility=weather.visibility.distance
-local dust=nil
-if weather.enable_dust==true then
-dust=weather.dust_density
-end
-local fog=nil
-if weather.enable_fog==true then
-fog=weather.fog
-end
-return clouds,visibility,fog,dust
-end
-function AIRBOSS._CheckRadioQueueT(param,time)
-AIRBOSS._CheckRadioQueue(param.airboss,param.radioqueue,param.name)
-return time+0.05
-end
-function AIRBOSS:_CheckRadioQueue(radioqueue,name)
-if#radioqueue==0 then
-if name=="LSO"then
-self:T(self.lid..string.format("Stopping LSO radio queue."))
-self.radiotimer:Stop(self.RQLid)
-self.RQLid=nil
-elseif name=="MARSHAL"then
-self:T(self.lid..string.format("Stopping Marshal radio queue."))
-self.radiotimer:Stop(self.RQMid)
-self.RQMid=nil
-end
-return
-end
-local _time=timer.getAbsTime()
-local playing=false
-local next=nil
-local _remove=nil
-for i,_transmission in ipairs(radioqueue)do
-local transmission=_transmission
-if _time>=transmission.Tplay then
-if transmission.isplaying then
-if _time>=transmission.Tstarted+transmission.call.duration then
-transmission.isplaying=false
-_remove=i
-if transmission.radio.alias=="LSO"then
-self.TQLSO=_time
-elseif transmission.radio.alias=="MARSHAL"then
-self.TQMarshal=_time
-end
-else
-playing=true
-end
-else
-local Tlast=nil
-if transmission.interval then
-if transmission.radio.alias=="LSO"then
-Tlast=self.TQLSO
-elseif transmission.radio.alias=="MARSHAL"then
-Tlast=self.TQMarshal
-end
-end
-if transmission.interval==nil then
-if next==nil then
-next=transmission
-end
-else
-if _time-Tlast>=transmission.interval then
-next=transmission
-else
-end
-end
-if next or Tlast then
-break
-end
-end
-else
-end
-end
-if next~=nil and not playing then
-self:Broadcast(next.radio,next.call,next.loud)
-next.isplaying=true
-next.Tstarted=_time
-end
-if _remove then
-table.remove(radioqueue,_remove)
-end
-return
-end
-function AIRBOSS:RadioTransmission(radio,call,loud,delay,interval,click,pilotcall)
-self:F2({radio=radio,call=call,loud=loud,delay=delay,interval=interval,click=click})
-if radio==nil or call==nil then
-return
-end
-if not self.SRS then
-local transmission={}
-transmission.radio=radio
-transmission.call=call
-transmission.Tplay=timer.getAbsTime()+(delay or 0)
-transmission.interval=interval
-transmission.isplaying=false
-transmission.Tstarted=nil
-transmission.loud=loud and call.loud
-if self:_IsOnboard(call.modexsender)then
-self:_Number2Radio(radio,call.modexsender,delay,0.3,pilotcall)
-end
-if self:_IsOnboard(call.modexreceiver)then
-self:_Number2Radio(radio,call.modexreceiver,delay,0.3,pilotcall)
-end
-local caller=""
-if radio.alias=="LSO"then
-table.insert(self.RQLSO,transmission)
-caller="LSOCall"
-if not self.RQLid then
-self:T(self.lid..string.format("Starting LSO radio queue."))
-self.RQLid=self.radiotimer:Schedule(nil,AIRBOSS._CheckRadioQueue,{self,self.RQLSO,"LSO"},0.02,0.05)
-end
-elseif radio.alias=="MARSHAL"then
-table.insert(self.RQMarshal,transmission)
-caller="MarshalCall"
-if not self.RQMid then
-self:T(self.lid..string.format("Starting Marhal radio queue."))
-self.RQMid=self.radiotimer:Schedule(nil,AIRBOSS._CheckRadioQueue,{self,self.RQMarshal,"MARSHAL"},0.02,0.05)
-end
-end
-if click then
-self:RadioTransmission(radio,self[caller].CLICK,false,delay)
-end
-else
-if call.subtitle~=nil and string.len(call.subtitle)>1 then
-local frequency=self.MarshalRadio.frequency
-local modulation=self.MarshalRadio.modulation
-local voice=nil
-local gender=nil
-local culture=nil
-if radio.alias=="AIRBOSS"then
-frequency=self.AirbossRadio.frequency
-modulation=self.AirbossRadio.modulation
-voice=self.AirbossRadio.voice
-gender=self.AirbossRadio.gender
-culture=self.AirbossRadio.culture
-end
-if radio.alias=="MARSHAL"then
-voice=self.MarshalRadio.voice
-gender=self.MarshalRadio.gender
-culture=self.MarshalRadio.culture
-end
-if radio.alias=="LSO"then
-frequency=self.LSORadio.frequency
-modulation=self.LSORadio.modulation
-voice=self.LSORadio.voice
-gender=self.LSORadio.gender
-culture=self.LSORadio.culture
-end
-if pilotcall then
-voice=self.PilotRadio.voice
-gender=self.PilotRadio.gender
-culture=self.PilotRadio.culture
-radio.alias="PILOT"
-end
-if not radio.alias then
-frequency=self.AirbossRadio.frequency
-modulation=self.AirbossRadio.modulation
-radio.alias="AIRBOSS"
-end
-local volume=nil
-if loud then
-volume=1.0
-end
-local text=call.subtitle
-self:T(self.lid..text)
-local srstext=self:_GetNiceSRSText(text)
-self.SRSQ:NewTransmission(srstext,call.duration,self.SRS,nil,0.1,nil,call.subtitle,call.subduration,frequency,modulation,gender,culture,voice,volume,radio.alias)
-end
-end
-end
-function AIRBOSS:SetSRSPilotVoice(Voice,Gender,Culture)
-self.PilotRadio={}
-self.PilotRadio.alias="PILOT"
-self.PilotRadio.voice=Voice or MSRS.Voices.Microsoft.David
-self.PilotRadio.gender=Gender or"male"
-self.PilotRadio.culture=Culture or"en-US"
-if(not Voice)and self.SRS and self.SRS.google then
-self.PilotRadio.voice=MSRS.Voices.Google.Standard.en_US_Standard_J
-end
-return self
-end
-function AIRBOSS:_NeedsSubtitle(call)
-if call.file==self.MarshalCall.NOISE.file or call.file==self.LSOCall.NOISE.file then
-return true
-else
-return false
-end
-end
-function AIRBOSS:Broadcast(radio,call,loud)
-self:F(call)
-if not self.usersoundradio then
-local sender=self:_GetRadioSender(radio)
-local filename=self:_RadioFilename(call,loud,radio.alias)
-local subtitle=self:_RadioSubtitle(radio,call,loud)
-self:T({filename=filename,subtitle=subtitle})
-if sender then
-self:T(self.lid..string.format("Broadcasting from aircraft %s",sender:GetName()))
-local commandFrequency={
-id="SetFrequency",
-params={
-frequency=radio.frequency*1000000,
-modulation=radio.modulation,
-},
-}
-local commandTransmit={
-id="TransmitMessage",
-params={
-file=filename,
-duration=call.subduration or 5,
-subtitle=subtitle,
-loop=false,
-},
-}
-sender:SetCommand(commandFrequency)
-sender:SetCommand(commandTransmit)
-else
-self:T(self.lid..string.format("Broadcasting from carrier via trigger.action.radioTransmission()."))
-local vec3=self.carrier:GetPositionVec3()
-trigger.action.radioTransmission(filename,vec3,radio.modulation,false,radio.frequency*1000000,100)
-for _,_player in pairs(self.players)do
-local playerData=_player
-if playerData.unit:IsInZone(self.zoneCCA)and playerData.actype~=AIRBOSS.AircraftCarrier.A4EC then
-if playerData.subtitles or self:_NeedsSubtitle(call)then
-if radio.alias=="MARSHAL"or(radio.alias=="LSO"and self:_InQueue(self.Qpattern,playerData.group))then
-self:MessageToPlayer(playerData,subtitle,nil,"",call.subduration or 5)
-end
-end
-end
-end
-end
-end
-for _,_player in pairs(self.players)do
-local playerData=_player
-if self.usersoundradio or playerData.actype==AIRBOSS.AircraftCarrier.A4EC then
-if radio.alias=="MARSHAL"or(radio.alias=="LSO"and self:_InQueue(self.Qpattern,playerData.group))then
-self:Sound2Player(playerData,radio,call,loud)
-end
-end
-end
-end
-function AIRBOSS:Sound2Player(playerData,radio,call,loud,delay)
-if playerData.unit:IsInZone(self.zoneCCA)and call then
-local filename=self:_RadioFilename(call,loud,radio.alias)
-local subtitle=self:_RadioSubtitle(radio,call,loud)
-USERSOUND:New(filename):ToGroup(playerData.group,delay)
-if playerData.subtitles or self:_NeedsSubtitle(call)then
-self:MessageToPlayer(playerData,subtitle,nil,"",call.subduration,false,delay)
-end
-end
-end
-function AIRBOSS:_RadioSubtitle(radio,call,loud)
-if call==nil or call.subtitle==nil or call.subtitle==""then
-return""
-end
-local sender=call.sender or radio.alias
-if call.modexsender then
-sender=call.modexsender
-end
-local receiver=call.modexreceiver or""
-local subtitle=string.format("%s: %s",sender,call.subtitle)
-if receiver and receiver~=""then
-subtitle=string.format("%s: %s, %s",sender,receiver,call.subtitle)
-end
-local lastchar=string.sub(subtitle,-1)
-if loud then
-if lastchar=="."or lastchar=="!"then
-subtitle=string.sub(subtitle,1,-1)
-end
-subtitle=subtitle.."!"
-else
-if lastchar=="!"then
-elseif lastchar=="."then
-else
-subtitle=subtitle.."."
-end
-end
-return subtitle
-end
-function AIRBOSS:_RadioFilename(call,loud,channel)
-local prefix=call.file or""
-local suffix=call.suffix or"ogg"
-local path=self.soundfolder or"l10n/DEFAULT/"
-if string.find(call.file,"LSO-")and channel and(channel=="LSO"or channel=="LSOCall")then
-path=self.soundfolderLSO or path
-end
-if string.find(call.file,"MARSHAL-")and channel and(channel=="MARSHAL"or channel=="MarshalCall")then
-path=self.soundfolderMSH or path
-end
-if loud then
-prefix=prefix.."_Loud"
-end
-local filename=string.format("%s%s.%s",path,prefix,suffix)
-return filename
-end
-function AIRBOSS:_GetNiceSRSText(text)
-text=string.gsub(text,"================================\n","")
-text=string.gsub(text,"||","parallel")
-text=string.gsub(text,"==","perpendicular")
-text=string.gsub(text,"BRC","Base recovery")
-text=string.gsub(text,"%((%a+)%)","Morse %1")
-text=string.gsub(text,"°C","° Celsius")
-text=string.gsub(text,"°"," degrees")
-text=string.gsub(text," FB "," Final bearing ")
-text=string.gsub(text," ops"," operations ")
-text=string.gsub(text," kts"," knots")
-text=string.gsub(text,"TACAN","Tackan")
-text=string.gsub(text,"ICLS","I.C.L.S.")
-text=string.gsub(text,"LSO","L.S.O.")
-text=string.gsub(text,"inHg","inches of Mercury")
-text=string.gsub(text,"QFE","Q.F.E.")
-text=string.gsub(text,"hPa","hecto pascal")
-text=string.gsub(text," NM"," nautical miles")
-text=string.gsub(text," ft"," feet")
-text=string.gsub(text,"A/C","aircraft")
-text=string.gsub(text,"(#[%a%d%p%s]+)\n","")
-text=string.gsub(text,"%.000"," dot zero")
-text=string.gsub(text,"00"," double zero")
-text=string.gsub(text," 0 "," zero ")
-text=string.gsub(text,"\n","; ")
-return text
-end
-function AIRBOSS:MessageToPlayer(playerData,message,sender,receiver,duration,clear,delay)
-self:T({sender,receiver,message})
-if playerData and message and message~=""then
-duration=duration or self.Tmessage
-local text
-if receiver and receiver==""then
-text=string.format("%s",message)
-else
-receiver=receiver or playerData.onboard
-text=string.format("%s, %s",receiver,message)
-end
-self:T(self.lid..text)
-if delay and delay>0 then
-self:ScheduleOnce(delay,self.MessageToPlayer,self,playerData,message,sender,receiver,duration,clear)
-else
-if not self.SRS then
-local wait=0
-if receiver==playerData.onboard then
-if sender and(sender=="LSO"or sender=="MARSHAL"or sender=="AIRBOSS")then
-wait=wait+self:_Number2Sound(playerData,sender,receiver)
-end
-end
-if string.find(text:lower(),"negative")then
-local filename=self:_RadioFilename(self.MarshalCall.NEGATIVE,false,"MARSHAL")
-USERSOUND:New(filename):ToGroup(playerData.group,wait)
-wait=wait+self.MarshalCall.NEGATIVE.duration
-end
-if string.find(text:lower(),"affirm")then
-local filename=self:_RadioFilename(self.MarshalCall.AFFIRMATIVE,false,"MARSHAL")
-USERSOUND:New(filename):ToGroup(playerData.group,wait)
-wait=wait+self.MarshalCall.AFFIRMATIVE.duration
-end
-if string.find(text:lower(),"roger")then
-local filename=self:_RadioFilename(self.MarshalCall.ROGER,false,"MARSHAL")
-USERSOUND:New(filename):ToGroup(playerData.group,wait)
-wait=wait+self.MarshalCall.ROGER.duration
-end
-if wait>0 then
-local filename=self:_RadioFilename(self.MarshalCall.CLICK)
-USERSOUND:New(filename):ToGroup(playerData.group,wait)
-end
-else
-local frequency=self.MarshalRadio.frequency
-local modulation=self.MarshalRadio.modulation
-local voice=self.MarshalRadio.voice
-local gender=self.MarshalRadio.gender
-local culture=self.MarshalRadio.culture
-if not sender then sender="AIRBOSS"end
-if string.find(sender,"AIRBOSS")then
-frequency=self.AirbossRadio.frequency
-modulation=self.AirbossRadio.modulation
-voice=self.AirbossRadio.voice
-gender=self.AirbossRadio.gender
-culture=self.AirbossRadio.culture
-end
-if sender=="LSO"then
-frequency=self.LSORadio.frequency
-modulation=self.LSORadio.modulation
-voice=self.LSORadio.voice
-gender=self.LSORadio.gender
-culture=self.LSORadio.culture
-end
-self:T(self.lid..text)
-self:T({sender,frequency,modulation,voice})
-local srstext=self:_GetNiceSRSText(text)
-self.SRSQ:NewTransmission(srstext,duration,self.SRS,nil,0.1,nil,nil,nil,frequency,modulation,gender,culture,voice,nil,sender)
-end
-if playerData.client then
-MESSAGE:New(text,duration,sender,clear):ToClient(playerData.client)
-end
-end
-end
-end
-function AIRBOSS:MessageToPattern(message,sender,receiver,duration,clear,delay)
-local call=self:_NewRadioCall(self.LSOCall.NOISE,sender or"LSO",message,duration,receiver,sender)
-self:RadioTransmission(self.LSORadio,call,false,delay,nil,true)
-end
-function AIRBOSS:MessageToMarshal(message,sender,receiver,duration,clear,delay)
-local call=self:_NewRadioCall(self.MarshalCall.NOISE,sender or"MARSHAL",message,duration,receiver,sender)
-self:RadioTransmission(self.MarshalRadio,call,false,delay,nil,true)
-end
-function AIRBOSS:_NewRadioCall(call,sender,subtitle,subduration,modexreceiver,modexsender)
-local newcall=UTILS.DeepCopy(call)
-newcall.sender=sender
-newcall.subtitle=subtitle or call.subtitle
-newcall.subduration=subduration or self.Tmessage
-if self:_IsOnboard(modexreceiver)then
-newcall.modexreceiver=modexreceiver
-end
-if self:_IsOnboard(modexsender)then
-newcall.modexsender=modexsender
-end
-return newcall
-end
-function AIRBOSS:_GetRadioSender(radio)
-local sender=nil
-if self.senderac then
-sender=UNIT:FindByName(self.senderac)
-end
-if radio.alias=="MARSHAL"then
-if self.radiorelayMSH then
-sender=UNIT:FindByName(self.radiorelayMSH)
-end
-end
-if radio.alias=="LSO"then
-if self.radiorelayLSO then
-sender=UNIT:FindByName(self.radiorelayLSO)
-end
-end
-if sender and sender:IsAlive()and sender:IsAir()then
-return sender
-end
-return nil
-end
-function AIRBOSS:_IsOnboard(text)
-if text==nil then
-return false
-end
-if text=="99"then
-return true
-end
-for _,_flight in pairs(self.flights)do
-local flight=_flight
-for _,onboard in pairs(flight.onboardnumbers)do
-if text==onboard then
-return true
-end
-end
-end
-return false
-end
-function AIRBOSS:_Number2Sound(playerData,sender,number,delay)
-delay=delay or 0
-local function _split(str)
-local chars={}
-for i=1,#str do
-local c=str:sub(i,i)
-table.insert(chars,c)
-end
-return chars
-end
-local Sender
-if sender=="LSO"then
-Sender="LSOCall"
-elseif sender=="MARSHAL"or sender=="AIRBOSS"then
-Sender="MarshalCall"
-else
-self:E(self.lid..string.format("ERROR: Unknown radio sender %s!",tostring(sender)))
-return
-end
-local numbers=_split(number)
-local wait=0
-for i=1,#numbers do
-local n=numbers[i]
-local N=string.format("N%s",n)
-local call=self[Sender][N]
-local filename=self:_RadioFilename(call,false,Sender)
-USERSOUND:New(filename):ToGroup(playerData.group,delay+wait)
-wait=wait+call.duration
-end
-return wait
-end
-function AIRBOSS:_Number2Radio(radio,number,delay,interval,pilotcall)
-local function _split(str)
-local chars={}
-for i=1,#str do
-local c=str:sub(i,i)
-table.insert(chars,c)
-end
-return chars
-end
-local Sender=""
-if radio.alias=="LSO"then
-Sender="LSOCall"
-elseif radio.alias=="MARSHAL"then
-Sender="MarshalCall"
-else
-self:E(self.lid..string.format("ERROR: Unknown radio alias %s!",tostring(radio.alias)))
-end
-if pilotcall then
-Sender="PilotCall"
-end
-local numbers=_split(number)
-local wait=0
-for i=1,#numbers do
-local n=numbers[i]
-local N=string.format("N%s",n)
-local call=self[Sender][N]
-if interval and i==1 then
-self:RadioTransmission(radio,call,false,delay,interval)
-else
-self:RadioTransmission(radio,call,false,delay)
-end
-wait=wait+call.duration
-end
-return wait
-end
-function AIRBOSS:_MarshallInboundCall(unit,modex)
-local vectorCarrier=self:GetCoordinate():GetDirectionVec3(unit:GetCoordinate())
-local bearing=UTILS.Round(unit:GetCoordinate():GetAngleDegrees(vectorCarrier),0)
-local distance=UTILS.Round(UTILS.MetersToNM(unit:GetCoordinate():Get2DDistance(self:GetCoordinate())),0)
-local angels=UTILS.Round(UTILS.MetersToFeet(unit:GetHeight()/1000),0)
-local state=UTILS.Round(self:_GetFuelState(unit)/1000,1)
-local text=string.format("Marshal, %s, marking mom's %d for %d, angels %d, state %.1f",modex,bearing,distance,angels,state)
-self:T(self.lid..text)
-local FS=UTILS.Split(string.format("%.1f",state),".")
-local inboundcall=self:_NewRadioCall(self.MarshalCall.CLICK,unit.UnitName:upper(),text,self.Tmessage,nil,unit.UnitName:upper())
-self:RadioTransmission(self.MarshalRadio,inboundcall)
-self:RadioTransmission(self.MarshalRadio,self.PilotCall.MARSHAL,nil,nil,nil,nil,true)
-self:_Number2Radio(self.MarshalRadio,modex,nil,nil,true)
-self:RadioTransmission(self.MarshalRadio,self.PilotCall.MARKINGMOMS,nil,nil,nil,nil,true)
-self:_Number2Radio(self.MarshalRadio,tostring(bearing),nil,nil,true)
-self:RadioTransmission(self.MarshalRadio,self.PilotCall.FOR,nil,nil,nil,nil,true)
-self:_Number2Radio(self.MarshalRadio,tostring(distance),nil,nil,true)
-self:RadioTransmission(self.MarshalRadio,self.PilotCall.ANGELS,nil,nil,nil,nil,true)
-self:_Number2Radio(self.MarshalRadio,tostring(angels),nil,nil,true)
-self:RadioTransmission(self.MarshalRadio,self.PilotCall.STATE,nil,nil,nil,nil,true)
-self:_Number2Radio(self.MarshalRadio,FS[1],nil,nil,true)
-self:RadioTransmission(self.MarshalRadio,self.PilotCall.POINT,nil,nil,nil,nil,true)
-self:_Number2Radio(self.MarshalRadio,FS[2],nil,nil,true)
-self:RadioTransmission(self.MarshalRadio,self.MarshalRadio.CLICK,nil,nil,nil,nil,true)
-end
-function AIRBOSS:_CommencingCall(unit,modex)
-local text=string.format("%s, commencing",modex)
-self:T(self.lid..text)
-local commencingCall=self:_NewRadioCall(self.MarshalCall.CLICK,unit.UnitName:upper(),text,self.Tmessage,nil,unit.UnitName:upper())
-self:RadioTransmission(self.MarshalRadio,commencingCall)
-self:_Number2Radio(self.MarshalRadio,modex,nil,nil,true)
-self:RadioTransmission(self.MarshalRadio,self.PilotCall.COMMENCING,nil,nil,nil,nil,true)
-self:RadioTransmission(self.MarshalRadio,self.MarshalRadio.CLICK,nil,nil,nil,nil,true)
-end
-function AIRBOSS:_LSOCallAircraftBall(modex,nickname,fuelstate)
-local text=string.format("%s Ball, %.1f.",nickname,fuelstate)
-self:T(self.lid..text)
-local NICKNAME=nickname:upper()
-local FS=UTILS.Split(string.format("%.1f",fuelstate),".")
-local call=self:_NewRadioCall(self.PilotCall[NICKNAME],modex,text,self.Tmessage,nil,modex)
-self:RadioTransmission(self.LSORadio,call,nil,nil,nil,nil,true)
-self:RadioTransmission(self.LSORadio,self.PilotCall.BALL,nil,nil,nil,nil,true)
-self:_Number2Radio(self.LSORadio,FS[1],nil,nil,true)
-self:RadioTransmission(self.LSORadio,self.PilotCall.POINT,nil,nil,nil,nil,true)
-self:_Number2Radio(self.LSORadio,FS[2],nil,nil,true)
-self:RadioTransmission(self.LSORadio,self.LSOCall.CLICK)
-end
-function AIRBOSS:_MarshalCallGasAtTanker(modex)
-local text=string.format("Bingo fuel! Going for gas at the recovery tanker.")
-self:T(self.lid..text)
-local call=self:_NewRadioCall(self.PilotCall.BINGOFUEL,modex,text,self.Tmessage,nil,modex)
-self:RadioTransmission(self.MarshalRadio,call,nil,nil,nil,nil,true)
-self:RadioTransmission(self.MarshalRadio,self.PilotCall.GASATTANKER,nil,nil,nil,true,true)
-end
-function AIRBOSS:_MarshalCallGasAtDivert(modex,divertname)
-local text=string.format("Bingo fuel! Going for gas at divert field %s.",divertname)
-self:T(self.lid..text)
-local call=self:_NewRadioCall(self.PilotCall.BINGOFUEL,modex,text,self.Tmessage,nil,modex)
-self:RadioTransmission(self.MarshalRadio,call,nil,nil,nil,nil,true)
-self:RadioTransmission(self.MarshalRadio,self.PilotCall.GASATDIVERT,nil,nil,nil,true,true)
-end
-function AIRBOSS:_MarshalCallRecoveryStopped(case)
-local text=string.format("Case %d recovery ops are stopped. Deck is closed.",case)
-self:T(self.lid..text)
-local call=self:_NewRadioCall(self.MarshalCall.CASE,"AIRBOSS",text,self.Tmessage,"99")
-self:RadioTransmission(self.MarshalRadio,call)
-self:_Number2Radio(self.MarshalRadio,tostring(case))
-self:RadioTransmission(self.MarshalRadio,self.MarshalCall.RECOVERYOPSSTOPPED,nil,nil,0.2)
-self:RadioTransmission(self.MarshalRadio,self.MarshalCall.DECKCLOSED,nil,nil,nil,true)
-end
-function AIRBOSS:_MarshalCallRecoveryPausedUntilFurtherNotice()
-local call=self:_NewRadioCall(self.MarshalCall.RECOVERYPAUSEDNOTICE,"AIRBOSS",nil,self.Tmessage,"99")
-self:RadioTransmission(self.MarshalRadio,call,nil,nil,nil,true)
-end
-function AIRBOSS:_MarshalCallRecoveryPausedResumedAt(clock)
-local _clock=UTILS.Split(clock,"+")
-local CT=UTILS.Split(_clock[1],":")
-local text=string.format("aircraft recovery is paused and will be resumed at %s.",clock)
-self:T(self.lid..text)
-local call=self:_NewRadioCall(self.MarshalCall.RECOVERYPAUSEDRESUMED,"AIRBOSS",text,self.Tmessage,"99")
-self:RadioTransmission(self.MarshalRadio,call)
-self:_Number2Radio(self.MarshalRadio,CT[1])
-self:_Number2Radio(self.MarshalRadio,CT[2])
-self:RadioTransmission(self.MarshalRadio,self.MarshalCall.HOURS,nil,nil,nil,true)
-end
-function AIRBOSS:_MarshalCallClearedForRecovery(modex,case)
-local text=string.format("you're cleared for Case %d recovery.",case)
-self:T(self.lid..text)
-local call=self:_NewRadioCall(self.MarshalCall.CLEAREDFORRECOVERY,"MARSHAL",text,self.Tmessage,modex)
-local delay=2
-self:RadioTransmission(self.MarshalRadio,call,nil,delay)
-self:_Number2Radio(self.MarshalRadio,tostring(case),delay)
-self:RadioTransmission(self.MarshalRadio,self.MarshalCall.RECOVERY,nil,delay,nil,true)
-end
-function AIRBOSS:_MarshalCallResumeRecovery()
-local call=self:_NewRadioCall(self.MarshalCall.RESUMERECOVERY,"AIRBOSS",nil,self.Tmessage,"99")
-self:RadioTransmission(self.MarshalRadio,call,nil,nil,nil,true)
-end
-function AIRBOSS:_MarshalCallNewFinalBearing(FB)
-local text=string.format("new final bearing %03d°.",FB)
-self:T(self.lid..text)
-local call=self:_NewRadioCall(self.MarshalCall.NEWFB,"AIRBOSS",text,self.Tmessage,"99")
-self:RadioTransmission(self.MarshalRadio,call)
-self:_Number2Radio(self.MarshalRadio,string.format("%03d",FB),nil,0.2)
-self:RadioTransmission(self.MarshalRadio,self.MarshalCall.DEGREES,nil,nil,nil,true)
-end
-function AIRBOSS:_MarshalCallCarrierTurnTo(hdg)
-local text=string.format("carrier is now starting turn to heading %03d°.",hdg)
-self:T(self.lid..text)
-local call=self:_NewRadioCall(self.MarshalCall.CARRIERTURNTOHEADING,"AIRBOSS",text,self.Tmessage,"99")
-self:RadioTransmission(self.MarshalRadio,call)
-self:_Number2Radio(self.MarshalRadio,string.format("%03d",hdg),nil,0.2)
-self:RadioTransmission(self.MarshalRadio,self.MarshalCall.DEGREES,nil,nil,nil,true)
-end
-function AIRBOSS:_MarshalCallStackFull(modex,nwaiting)
-local text=string.format("Marshal stack is currently full. Hold outside 10 NM zone and wait for further instructions. ")
-if nwaiting==1 then
-text=text..string.format("There is one flight ahead of you.")
-elseif nwaiting>1 then
-text=text..string.format("There are %d flights ahead of you.",nwaiting)
-else
-text=text..string.format("You are next in line.")
-end
-self:T(self.lid..text)
-local call=self:_NewRadioCall(self.MarshalCall.STACKFULL,"AIRBOSS",text,self.Tmessage,modex)
-self:RadioTransmission(self.MarshalRadio,call,nil,nil,nil,true)
-end
-function AIRBOSS:_MarshalCallRecoveryStart(case)
-local radial=self:GetRadial(case,true,true,false)
-local text=string.format("Starting aircraft recovery Case %d ops.",case)
-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)
-local call=self:_NewRadioCall(self.MarshalCall.STARTINGRECOVERY,"AIRBOSS",text,self.Tmessage,"99")
-self:RadioTransmission(self.MarshalRadio,call)
-self:_Number2Radio(self.MarshalRadio,tostring(case),nil,0.1)
-self:RadioTransmission(self.MarshalRadio,self.MarshalCall.OPS)
-if case>1 then
-self:RadioTransmission(self.MarshalRadio,self.MarshalCall.MARSHALRADIAL)
-self:_Number2Radio(self.MarshalRadio,string.format("%03d",radial),nil,0.2)
-self:RadioTransmission(self.MarshalRadio,self.MarshalCall.DEGREES,nil,nil,nil,true)
-end
-end
-function AIRBOSS:_MarshalCallArrived(modex,case,brc,altitude,charlie,qfe)
-self:F({modex=modex,case=case,brc=brc,altitude=altitude,charlie=charlie,qfe=qfe})
-local angels=self:_GetAngels(altitude)
-local QFE=UTILS.Split(string.format("%.2f",qfe),".")
-local clock=UTILS.Split(charlie,"+")
-local CT=UTILS.Split(clock[1],":")
-local text=string.format("Case %d, expected BRC %03d°, hold at angels %d. Expected Charlie Time %s. Altimeter %.2f. Report see me.",case,brc,angels,charlie,qfe)
-self:T(self.lid..text)
-local casecall=self:_NewRadioCall(self.MarshalCall.CASE,"MARSHAL",text,self.Tmessage,modex)
-self:RadioTransmission(self.MarshalRadio,casecall)
-self:_Number2Radio(self.MarshalRadio,tostring(case))
-self:RadioTransmission(self.MarshalRadio,self.MarshalCall.EXPECTED,nil,nil,0.5)
-self:RadioTransmission(self.MarshalRadio,self.MarshalCall.BRC)
-self:_Number2Radio(self.MarshalRadio,string.format("%03d",brc))
-self:RadioTransmission(self.MarshalRadio,self.MarshalCall.DEGREES)
-self:RadioTransmission(self.MarshalRadio,self.MarshalCall.HOLDATANGELS,nil,nil,0.5)
-self:_Number2Radio(self.MarshalRadio,tostring(angels))
-self:RadioTransmission(self.MarshalRadio,self.MarshalCall.EXPECTED,nil,nil,0.5)
-self:RadioTransmission(self.MarshalRadio,self.MarshalCall.CHARLIETIME)
-self:_Number2Radio(self.MarshalRadio,CT[1])
-self:_Number2Radio(self.MarshalRadio,CT[2])
-self:RadioTransmission(self.MarshalRadio,self.MarshalCall.HOURS)
-self:RadioTransmission(self.MarshalRadio,self.MarshalCall.ALTIMETER,nil,nil,0.5)
-self:_Number2Radio(self.MarshalRadio,QFE[1])
-self:RadioTransmission(self.MarshalRadio,self.MarshalCall.POINT)
-self:_Number2Radio(self.MarshalRadio,QFE[2])
-self:RadioTransmission(self.MarshalRadio,self.MarshalCall.REPORTSEEME,nil,nil,0.5,true)
-end
-function AIRBOSS:_AddF10Commands(_unitName)
-self:F(_unitName)
-local _unit,playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit and playername then
-local group=_unit:GetGroup()
-local gid=group:GetID()
-if group and gid then
-if not self.menuadded[gid]then
-self.menuadded[gid]=true
-local _rootPath=nil
-if AIRBOSS.MenuF10Root then
-if self.menusingle then
-_rootPath=AIRBOSS.MenuF10Root
-else
-_rootPath=missionCommands.addSubMenuForGroup(gid,self.alias,AIRBOSS.MenuF10Root)
-end
-else
-if AIRBOSS.MenuF10[gid]==nil then
-AIRBOSS.MenuF10[gid]=missionCommands.addSubMenuForGroup(gid,"Airboss")
-end
-if self.menusingle then
-_rootPath=AIRBOSS.MenuF10[gid]
-else
-_rootPath=missionCommands.addSubMenuForGroup(gid,self.alias,AIRBOSS.MenuF10[gid])
-end
-end
-local _helpPath=missionCommands.addSubMenuForGroup(gid,"Help",_rootPath)
-if self.menumarkzones then
-local _markPath=missionCommands.addSubMenuForGroup(gid,"Mark Zones",_helpPath)
-if self.menusmokezones then
-missionCommands.addCommandForGroup(gid,"Smoke Pattern Zones",_markPath,self._MarkCaseZones,self,_unitName,false)
-end
-missionCommands.addCommandForGroup(gid,"Flare Pattern Zones",_markPath,self._MarkCaseZones,self,_unitName,true)
-if self.menusmokezones then
-missionCommands.addCommandForGroup(gid,"Smoke Marshal Zone",_markPath,self._MarkMarshalZone,self,_unitName,false)
-end
-missionCommands.addCommandForGroup(gid,"Flare Marshal Zone",_markPath,self._MarkMarshalZone,self,_unitName,true)
-end
-local _skillPath=missionCommands.addSubMenuForGroup(gid,"Skill Level",_helpPath)
-missionCommands.addCommandForGroup(gid,"Flight Student",_skillPath,self._SetDifficulty,self,_unitName,AIRBOSS.Difficulty.EASY)
-missionCommands.addCommandForGroup(gid,"Naval Aviator",_skillPath,self._SetDifficulty,self,_unitName,AIRBOSS.Difficulty.NORMAL)
-missionCommands.addCommandForGroup(gid,"TOPGUN Graduate",_skillPath,self._SetDifficulty,self,_unitName,AIRBOSS.Difficulty.HARD)
-missionCommands.addCommandForGroup(gid,"Hints On/Off",_skillPath,self._SetHintsOnOff,self,_unitName)
-missionCommands.addCommandForGroup(gid,"My Status",_helpPath,self._DisplayPlayerStatus,self,_unitName)
-missionCommands.addCommandForGroup(gid,"Attitude Monitor",_helpPath,self._DisplayAttitude,self,_unitName)
-missionCommands.addCommandForGroup(gid,"Radio Check LSO",_helpPath,self._LSORadioCheck,self,_unitName)
-missionCommands.addCommandForGroup(gid,"Radio Check Marshal",_helpPath,self._MarshalRadioCheck,self,_unitName)
-missionCommands.addCommandForGroup(gid,"Subtitles On/Off",_helpPath,self._SubtitlesOnOff,self,_unitName)
-missionCommands.addCommandForGroup(gid,"Trapsheet On/Off",_helpPath,self._TrapsheetOnOff,self,_unitName)
-local _kneeboardPath=missionCommands.addSubMenuForGroup(gid,"Kneeboard",_rootPath)
-local _resultsPath=missionCommands.addSubMenuForGroup(gid,"Results",_kneeboardPath)
-missionCommands.addCommandForGroup(gid,"Greenie Board",_resultsPath,self._DisplayScoreBoard,self,_unitName)
-missionCommands.addCommandForGroup(gid,"My LSO Grades",_resultsPath,self._DisplayPlayerGrades,self,_unitName)
-missionCommands.addCommandForGroup(gid,"Last Debrief",_resultsPath,self._DisplayDebriefing,self,_unitName)
-if self.skipperMenu then
-local _skipperPath=missionCommands.addSubMenuForGroup(gid,"Skipper",_kneeboardPath)
-local _menusetspeed=missionCommands.addSubMenuForGroup(gid,"Set Speed",_skipperPath)
-missionCommands.addCommandForGroup(gid,"10 knots",_menusetspeed,self._SkipperRecoverySpeed,self,_unitName,10)
-missionCommands.addCommandForGroup(gid,"15 knots",_menusetspeed,self._SkipperRecoverySpeed,self,_unitName,15)
-missionCommands.addCommandForGroup(gid,"20 knots",_menusetspeed,self._SkipperRecoverySpeed,self,_unitName,20)
-missionCommands.addCommandForGroup(gid,"25 knots",_menusetspeed,self._SkipperRecoverySpeed,self,_unitName,25)
-missionCommands.addCommandForGroup(gid,"30 knots",_menusetspeed,self._SkipperRecoverySpeed,self,_unitName,30)
-local _menusetrtime=missionCommands.addSubMenuForGroup(gid,"Set Time",_skipperPath)
-missionCommands.addCommandForGroup(gid,"15 min",_menusetrtime,self._SkipperRecoveryTime,self,_unitName,15)
-missionCommands.addCommandForGroup(gid,"30 min",_menusetrtime,self._SkipperRecoveryTime,self,_unitName,30)
-missionCommands.addCommandForGroup(gid,"45 min",_menusetrtime,self._SkipperRecoveryTime,self,_unitName,45)
-missionCommands.addCommandForGroup(gid,"60 min",_menusetrtime,self._SkipperRecoveryTime,self,_unitName,60)
-missionCommands.addCommandForGroup(gid,"90 min",_menusetrtime,self._SkipperRecoveryTime,self,_unitName,90)
-local _menusetrtime=missionCommands.addSubMenuForGroup(gid,"Set Marshal Radial",_skipperPath)
-missionCommands.addCommandForGroup(gid,"+30°",_menusetrtime,self._SkipperRecoveryOffset,self,_unitName,30)
-missionCommands.addCommandForGroup(gid,"+15°",_menusetrtime,self._SkipperRecoveryOffset,self,_unitName,15)
-missionCommands.addCommandForGroup(gid,"0°",_menusetrtime,self._SkipperRecoveryOffset,self,_unitName,0)
-missionCommands.addCommandForGroup(gid,"-15°",_menusetrtime,self._SkipperRecoveryOffset,self,_unitName,-15)
-missionCommands.addCommandForGroup(gid,"-30°",_menusetrtime,self._SkipperRecoveryOffset,self,_unitName,-30)
-missionCommands.addCommandForGroup(gid,"U-turn On/Off",_skipperPath,self._SkipperRecoveryUturn,self,_unitName)
-missionCommands.addCommandForGroup(gid,"Start CASE I",_skipperPath,self._SkipperStartRecovery,self,_unitName,1)
-missionCommands.addCommandForGroup(gid,"Start CASE II",_skipperPath,self._SkipperStartRecovery,self,_unitName,2)
-missionCommands.addCommandForGroup(gid,"Start CASE III",_skipperPath,self._SkipperStartRecovery,self,_unitName,3)
-missionCommands.addCommandForGroup(gid,"Stop Recovery",_skipperPath,self._SkipperStopRecovery,self,_unitName)
-end
-missionCommands.addCommandForGroup(gid,"Carrier Info",_kneeboardPath,self._DisplayCarrierInfo,self,_unitName)
-missionCommands.addCommandForGroup(gid,"Weather Report",_kneeboardPath,self._DisplayCarrierWeather,self,_unitName)
-missionCommands.addCommandForGroup(gid,"Set Section",_kneeboardPath,self._SetSection,self,_unitName)
-missionCommands.addCommandForGroup(gid,"Marshal Queue",_kneeboardPath,self._DisplayQueue,self,_unitName,"Marshal")
-missionCommands.addCommandForGroup(gid,"Pattern Queue",_kneeboardPath,self._DisplayQueue,self,_unitName,"Pattern")
-missionCommands.addCommandForGroup(gid,"Waiting Queue",_kneeboardPath,self._DisplayQueue,self,_unitName,"Waiting")
-missionCommands.addCommandForGroup(gid,"Request Marshal",_rootPath,self._RequestMarshal,self,_unitName)
-missionCommands.addCommandForGroup(gid,"Request Commence",_rootPath,self._RequestCommence,self,_unitName)
-missionCommands.addCommandForGroup(gid,"Request Refueling",_rootPath,self._RequestRefueling,self,_unitName)
-missionCommands.addCommandForGroup(gid,"Spinning",_rootPath,self._RequestSpinning,self,_unitName)
-missionCommands.addCommandForGroup(gid,"Emergency Landing",_rootPath,self._RequestEmergency,self,_unitName)
-missionCommands.addCommandForGroup(gid,"[Reset My Status]",_rootPath,self._ResetPlayerStatus,self,_unitName)
-end
-else
-self:E(self.lid..string.format("ERROR: Could not find group or group ID in AddF10Menu() function. Unit name: %s.",_unitName))
-end
-else
-self:E(self.lid..string.format("ERROR: Player unit does not exist in AddF10Menu() function. Unit name: %s.",_unitName))
-end
-end
-function AIRBOSS:_SkipperStartRecovery(_unitName,case)
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit and _playername then
-local playerData=self.players[_playername]
-if playerData then
-local text=string.format("affirm, Case %d recovery will start in 5 min for %d min. Wind on deck %d knots. U-turn=%s.",case,self.skipperTime,self.skipperSpeed,tostring(self.skipperUturn))
-if case>1 then
-text=text..string.format(" Marshal radial %d°.",self.skipperOffset)
-end
-if self:IsRecovering()then
-text="negative, carrier is already recovering."
-self:MessageToPlayer(playerData,text,"AIRBOSS")
-return
-end
-self:MessageToPlayer(playerData,text,"AIRBOSS")
-local t0=timer.getAbsTime()+5*60
-local t9=t0+self.skipperTime*60
-local C0=UTILS.SecondsToClock(t0)
-local C9=UTILS.SecondsToClock(t9)
-self:AddRecoveryWindow(C0,C9,case,self.skipperOffset,true,self.skipperSpeed,self.skipperUturn)
-end
-end
-end
-function AIRBOSS:_SkipperStopRecovery(_unitName)
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit and _playername then
-local playerData=self.players[_playername]
-if playerData then
-local text="roger, stopping recovery right away."
-if not self:IsRecovering()then
-text="negative, carrier is currently not recovering."
-self:MessageToPlayer(playerData,text,"AIRBOSS")
-return
-end
-self:MessageToPlayer(playerData,text,"AIRBOSS")
-self:RecoveryStop()
-end
-end
-end
-function AIRBOSS:_SkipperRecoveryOffset(_unitName,offset)
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit and _playername then
-local playerData=self.players[_playername]
-if playerData then
-local text=string.format("roger, relative CASE II/III Marshal radial set to %d°.",offset)
-self:MessageToPlayer(playerData,text,"AIRBOSS")
-self.skipperOffset=offset
-end
-end
-end
-function AIRBOSS:_SkipperRecoveryTime(_unitName,time)
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit and _playername then
-local playerData=self.players[_playername]
-if playerData then
-local text=string.format("roger, manual recovery time set to %d min.",time)
-self:MessageToPlayer(playerData,text,"AIRBOSS")
-self.skipperTime=time
-end
-end
-end
-function AIRBOSS:_SkipperRecoverySpeed(_unitName,speed)
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit and _playername then
-local playerData=self.players[_playername]
-if playerData then
-local text=string.format("roger, wind on deck set to %d knots.",speed)
-self:MessageToPlayer(playerData,text,"AIRBOSS")
-self.skipperSpeed=speed
-end
-end
-end
-function AIRBOSS:_SkipperRecoveryUturn(_unitName)
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit and _playername then
-local playerData=self.players[_playername]
-if playerData then
-self.skipperUturn=not self.skipperUturn
-local text=string.format("roger, U-turn is now %s.",tostring(self.skipperUturn))
-self:MessageToPlayer(playerData,text,"AIRBOSS")
-end
-end
-end
-function AIRBOSS:_ResetPlayerStatus(_unitName)
-self:F(_unitName)
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit and _playername then
-local playerData=self.players[_playername]
-if playerData then
-local text="roger, status reset executed! You have been removed from all queues."
-self:MessageToPlayer(playerData,text,"AIRBOSS")
-self:_RemoveFlight(playerData)
-if playerData.debriefschedulerID and self.Scheduler then
-self.Scheduler:Stop(playerData.debriefschedulerID)
-end
-self:_InitPlayer(playerData)
-end
-end
-end
-function AIRBOSS:_RequestMarshal(_unitName)
-self:F(_unitName)
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit and _playername then
-local playerData=self.players[_playername]
-if playerData then
-if self.xtVoiceOvers then
-self:_MarshallInboundCall(_unit,playerData.onboard)
-end
-local inCCA=playerData.unit:IsInZone(self.zoneCCA)
-if inCCA then
-if self:_InQueue(self.Qmarshal,playerData.group)then
-local text=string.format("negative, you are already in the Marshal queue. New marshal request denied!")
-self:MessageToPlayer(playerData,text,"MARSHAL")
-elseif self:_InQueue(self.Qpattern,playerData.group)then
-local text=string.format("negative, you are already in the Pattern queue. Marshal request denied!")
-self:MessageToPlayer(playerData,text,"MARSHAL")
-elseif self:_InQueue(self.Qwaiting,playerData.group)then
-local text=string.format("negative, you are in the Waiting queue with %d flights ahead of you. Marshal request denied!",#self.Qwaiting)
-self:MessageToPlayer(playerData,text,"MARSHAL")
-elseif not _unit:InAir()then
-local text=string.format("negative, you are not airborne. Marshal request denied!")
-self:MessageToPlayer(playerData,text,"MARSHAL")
-elseif playerData.name~=playerData.seclead then
-local text=string.format("negative, your section lead %s needs to request Marshal.",playerData.seclead)
-self:MessageToPlayer(playerData,text,"MARSHAL")
-else
-local freestack=self:_GetFreeStack(playerData.ai)
-if freestack then
-self:_MarshalPlayer(playerData,freestack)
-else
-self:_WaitPlayer(playerData)
-end
-end
-else
-local text=string.format("negative, you are not inside CCA. Marshal request denied!")
-self:MessageToPlayer(playerData,text,"MARSHAL")
-end
-end
-end
-end
-function AIRBOSS:_RequestEmergency(_unitName)
-self:F(_unitName)
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit and _playername then
-local playerData=self.players[_playername]
-if playerData then
-local text=""
-if not self.emergency then
-text="negative, no emergency landings on my carrier. We are currently busy. See how you get along!"
-elseif not _unit:InAir()then
-local zone=self:_GetZoneCarrierBox()
-if playerData.unit:IsInZone(zone)then
-text="roger, you are now technically in the bolter pattern. Your next step after takeoff is abeam!"
-local lead=self:_GetFlightLead(playerData)
-self:_SetPlayerStep(lead,AIRBOSS.PatternStep.BOLTER)
-for _,sec in pairs(lead.section)do
-local sectionmember=sec
-self:_SetPlayerStep(sectionmember,AIRBOSS.PatternStep.BOLTER)
-end
-self:_RemoveFlightFromQueue(self.Qwaiting,lead)
-if self:_InQueue(self.Qmarshal,lead.group)then
-self:_RemoveFlightFromMarshalQueue(lead)
-else
-if not self:_InQueue(self.Qpattern,lead.group)then
-self:_AddFlightToPatternQueue(lead)
-end
-end
-else
-text=string.format("negative, you are not airborne. Request denied!")
-end
-else
-text="affirmative, you can bypass the pattern and are cleared for final approach!"
-local lead=self:_GetFlightLead(playerData)
-self:_SetPlayerStep(lead,AIRBOSS.PatternStep.EMERGENCY)
-for _,sec in pairs(lead.section)do
-local sectionmember=sec
-self:_SetPlayerStep(sectionmember,AIRBOSS.PatternStep.EMERGENCY)
-self:_RemoveFlightFromQueue(self.Qspinning,sectionmember)
-end
-self:_RemoveFlightFromQueue(self.Qwaiting,lead)
-if self:_InQueue(self.Qmarshal,lead.group)then
-self:_RemoveFlightFromMarshalQueue(lead)
-else
-if not self:_InQueue(self.Qpattern,lead.group)then
-self:_AddFlightToPatternQueue(lead)
-end
-end
-end
-self:MessageToPlayer(playerData,text,"AIRBOSS")
-end
-end
-end
-function AIRBOSS:_RequestSpinning(_unitName)
-self:F(_unitName)
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit and _playername then
-local playerData=self.players[_playername]
-if playerData then
-local text=""
-if not self:_InQueue(self.Qpattern,playerData.group)then
-text="negative, you have to be in the pattern to spin it!"
-elseif playerData.step==AIRBOSS.PatternStep.SPINNING then
-text="negative, you are already spinning."
-elseif not(playerData.step==AIRBOSS.PatternStep.BREAKENTRY or
-playerData.step==AIRBOSS.PatternStep.EARLYBREAK or
-playerData.step==AIRBOSS.PatternStep.LATEBREAK)then
-text="negative, you have to be in the right step to spin it!"
-else
-self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.SPINNING)
-table.insert(self.Qspinning,playerData)
-local call=self:_NewRadioCall(self.LSOCall.SPINIT,"AIRBOSS","Spin it!",self.Tmessage,playerData.onboard)
-self:RadioTransmission(self.LSORadio,call,nil,nil,nil,true)
-if playerData.difficulty==AIRBOSS.Difficulty.EASY then
-local text="Climb to 1200 feet and proceed to the initial again."
-self:MessageToPlayer(playerData,text,"AIRBOSS","")
-end
-return
-end
-self:MessageToPlayer(playerData,text,"AIRBOSS")
-end
-end
-end
-function AIRBOSS:_RequestCommence(_unitName)
-self:F(_unitName)
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit and _playername then
-local playerData=self.players[_playername]
-if playerData then
-if self.xtVoiceOvers then
-self:_CommencingCall(_unit,playerData.onboard)
-end
-local text=""
-local cleared=false
-if _unit:IsInZone(self.zoneCCA)then
-local stack=playerData.flag
-local _,npattern=self:_GetQueueInfo(self.Qpattern)
-if self:_InQueue(self.Qpattern,playerData.group)then
-text=string.format("negative, %s, you are already in the Pattern queue.",playerData.name)
-elseif not _unit:InAir()then
-text=string.format("negative, %s, you are not airborne.",playerData.name)
-elseif playerData.seclead~=playerData.name then
-text=string.format("negative, %s, your section leader %s has to request commence!",playerData.name,playerData.seclead)
-elseif stack>1 then
-text=string.format("negative, %s, it's not your turn yet! You are in stack no. %s.",playerData.name,stack)
-elseif npattern>=self.Nmaxpattern then
-text=string.format("negative ghostrider, pattern is full!\nThere are %d aircraft currently in the pattern.",npattern)
-elseif self:IsRecovering()==false and not self.airbossnice then
-if self.recoverywindow then
-local clock=UTILS.SecondsToClock(self.recoverywindow.START)
-text=string.format("negative, carrier is currently not recovery. Next window will open at %s.",clock)
-else
-text=string.format("negative, carrier is not recovering. No future windows planned.")
-end
-elseif not self:_InQueue(self.Qmarshal,playerData.group)and not self.airbossnice then
-text="negative, you have to request Marshal before you can commence."
-else
-text=text.."roger."
-if not self:IsRecovering()then
-text=text.." Carrier is not recovering currently! However, you are cleared anyway as I have a nice day."
-end
-if not self:_InQueue(self.Qmarshal,playerData.group)then
-playerData.case=self.case
-if self.TACANon and playerData.difficulty~=AIRBOSS.Difficulty.HARD then
-local radial=self:GetRadial(playerData.case,true,true,true)
-if playerData.case==1 then
-radial=self:GetBRC()
-end
-text=text..string.format("\nSelect TACAN %03d°, Channel %d%s (%s).\n",radial,self.TACANchannel,self.TACANmode,self.TACANmorse)
-end
-for _,flight in pairs(playerData.section)do
-flight.case=playerData.case
-end
-self:_AddFlightToPatternQueue(playerData)
-end
-cleared=true
-end
-else
-text=string.format("negative, %s, you are not inside the CCA!",playerData.name)
-end
-self:T(self.lid..text)
-self:MessageToPlayer(playerData,text,"MARSHAL")
-if cleared then
-self:_Commencing(playerData,false)
-end
-end
-end
-end
-function AIRBOSS:_RequestRefueling(_unitName)
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit and _playername then
-local playerData=self.players[_playername]
-if playerData then
-local text
-if self.tanker then
-if _unit:IsInZone(self.zoneCCA)then
-if self.tanker:IsRunning()or self.tanker:IsRefueling()then
-local angels=self:_GetAngels(self.tanker.altitude)
-text=string.format("affirmative, proceed to tanker at angels %d.",angels)
-if self.tanker.TACANon then
-text=text..string.format("\nTanker TACAN channel %d%s (%s).",self.tanker.TACANchannel,self.tanker.TACANmode,self.tanker.TACANmorse)
-text=text..string.format("\nRadio frequency %.3f MHz AM.",self.tanker.RadioFreq)
-end
-if self.tanker:IsRefueling()then
-text=text.."\nTanker is currently refueling. You might have to queue up."
-end
-self:_RemoveFlightFromMarshalQueue(playerData,true)
-self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.REFUELING)
-for _,sec in pairs(playerData.section)do
-local sectext="follow your section leader to the tanker."
-self:MessageToPlayer(sec,sectext,"MARSHAL")
-self:_SetPlayerStep(sec,AIRBOSS.PatternStep.REFUELING)
-end
-elseif self.tanker:IsReturning()then
-text="negative, tanker is RTB. Request denied!\nWait for the tanker to be back on station if you can."
-end
-else
-text="negative, you are not inside the CCA yet."
-end
-else
-text="negative, no refueling tanker available."
-end
-self:MessageToPlayer(playerData,text,"MARSHAL")
-end
-end
-end
-function AIRBOSS:_RemoveSectionMember(playerData,sectionmember)
-for i,_flight in pairs(playerData.section)do
-local flight=_flight
-if flight.name==sectionmember.name then
-table.remove(playerData.section,i)
-return true
-end
-end
-return false
-end
-function AIRBOSS:_SetSection(_unitName)
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit and _playername then
-local playerData=self.players[_playername]
-if playerData then
-local mycoord=_unit:GetCoordinate()
-local dmax=100
-local text
-if self.NmaxSection==0 then
-text=string.format("negative, setting sections is disabled in this mission. You stay alone.")
-elseif self:_InQueue(self.Qmarshal,playerData.group)then
-text=string.format("negative, you are already in the Marshal queue. Setting section not possible any more!")
-elseif self:_InQueue(self.Qpattern,playerData.group)then
-text=string.format("negative, you are already in the Pattern queue. Setting section not possible any more!")
-else
-if playerData.seclead~=playerData.name then
-local lead=self.players[playerData.seclead]
-if lead then
-local removed=self:_RemoveSectionMember(lead,playerData)
-if removed then
-self:MessageToPlayer(lead,string.format("Flight %s has been removed from your section.",playerData.name),"AIRBOSS","",5)
-self:MessageToPlayer(playerData,string.format("You have been removed from %s's section.",lead.name),"AIRBOSS","",5)
-end
-end
-end
-local section={}
-for _,_flight in pairs(self.flights)do
-local flight=_flight
-if flight.ai==false and flight.groupname~=playerData.groupname and#flight.section==0 and flight.seclead==flight.name then
-local distance=flight.group:GetCoordinate():Get3DDistance(mycoord)
-if distance0 then
-_playerResults[playerName]=Paverage/n
-end
-end
-end
-local text=string.format("Greenie Board (top ten):")
-local i=1
-for _playerName,_points in UTILS.spairs(_playerResults,function(t,a,b)
-return t[b]=0 then
-text=text..string.format("(%.1f)",grade.points)
-end
-end
-i=i+1
-if i>10 then
-break
-end
-end
-if i==1 then
-text=text.."\nNo results yet."
-end
-local playerData=self.players[_playername]
-if playerData.client then
-MESSAGE:New(text,30,nil,true):ToClient(playerData.client)
-end
-end
-end
-function AIRBOSS:_DisplayPlayerGrades(_unitName)
-self:F(_unitName)
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit and _playername then
-local playerData=self.players[_playername]
-if playerData then
-local text=string.format("Your last 10 grades, %s:",_playername)
-local playerGrades=self.playerscores[_playername]or{}
-local p=0
-local n=0
-local m=0
-for i=#playerGrades,1,-1 do
-local grade=playerGrades[i]
-if grade.points>=0 then
-local points=grade.finalscore or grade.points
-if m<10 then
-text=text..string.format("\n[%d] %s %.1f PT - %s",i,grade.grade,points,grade.details)
-if grade.wire and grade.wire<=4 then
-text=text..string.format(" %d-wire",grade.wire)
-end
-if grade.Tgroove and grade.Tgroove<=360 then
-text=text..string.format(" Tgroove=%.1f s",grade.Tgroove)
-end
-end
-if grade.finalscore then
-p=p+grade.finalscore
-n=n+1
-end
-m=m+1
-end
-end
-if n>0 then
-text=text..string.format("\nAverage points = %.1f",p/n)
-else
-text=text..string.format("\nNo data available.")
-end
-if playerData.client then
-MESSAGE:New(text,30,nil,true):ToClient(playerData.client)
-end
-end
-end
-end
-function AIRBOSS:_DisplayDebriefing(_unitName)
-self:F(_unitName)
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit and _playername then
-local playerData=self.players[_playername]
-if playerData then
-local text=string.format("Debriefing:")
-if#playerData.lastdebrief>0 then
-text=text..string.format("\n================================\n")
-for _,_data in pairs(playerData.lastdebrief)do
-local step=_data.step
-local comment=_data.hint
-text=text..string.format("* %s:",step)
-text=text..string.format("%s\n",comment)
-end
-else
-text=text.." Nothing to show yet."
-end
-self:MessageToPlayer(playerData,text,nil,"",30,true)
-end
-end
-end
-function AIRBOSS:_DisplayQueue(_unitname,qname)
-local unit,playername=self:_GetPlayerUnitAndName(_unitname)
-if unit and playername then
-local playerData=self.players[playername]
-if playerData then
-local queue=nil
-if qname=="Marshal"then
-queue=self.Qmarshal
-elseif qname=="Pattern"then
-queue=self.Qpattern
-elseif qname=="Waiting"then
-queue=self.Qwaiting
-end
-local Nqueue,nqueue=self:_GetQueueInfo(queue,playerData.case)
-local text=string.format("%s Queue:",qname)
-if#queue==0 then
-text=text.." empty"
-else
-local N=0
-if qname=="Marshal"then
-for i,_flight in pairs(queue)do
-local flight=_flight
-local charlie=self:_GetCharlieTime(flight)
-local Charlie=UTILS.SecondsToClock(charlie)
-local stack=flight.flag
-local angels=self:_GetAngels(self:_GetMarshalAltitude(stack,flight.case))
-local _,nunit,nsec=self:_GetFlightUnits(flight,true)
-local nick=self:_GetACNickname(flight.actype)
-N=N+nunit
-text=text..string.format("\n[Stack %d] %s (%s*%d+%d): Case %d, Angels %d, Charlie %s",stack,flight.onboard,nick,nunit,nsec,flight.case,angels,tostring(Charlie))
-end
-elseif qname=="Pattern"or qname=="Waiting"then
-for i,_flight in pairs(queue)do
-local flight=_flight
-local _,nunit,nsec=self:_GetFlightUnits(flight,true)
-local nick=self:_GetACNickname(flight.actype)
-local ptime=UTILS.SecondsToClock(timer.getAbsTime()-flight.time)
-N=N+nunit
-text=text..string.format("\n[%d] %s (%s*%d+%d): Case %d, T=%s",i,flight.onboard,nick,nunit,nsec,flight.case,ptime)
-end
-end
-text=text..string.format("\nTotal AC: %d (airborne %d)",N,nqueue)
-end
-self:MessageToPlayer(playerData,text,nil,"",nil,true)
-end
-end
-end
-function AIRBOSS:_DisplayCarrierInfo(_unitname)
-self:F2(_unitname)
-local unit,playername=self:_GetPlayerUnitAndName(_unitname)
-if unit and playername then
-local playerData=self.players[playername]
-if playerData then
-local coord=self:GetCoordinate()
-local carrierheading=self.carrier:GetHeading()
-local carrierspeed=UTILS.MpsToKnots(self.carrier:GetVelocityMPS())
-local tacan="unknown"
-local icls="unknown"
-if self.TACANon and self.TACANchannel~=nil then
-tacan=string.format("%d%s (%s)",self.TACANchannel,self.TACANmode,self.TACANmorse)
-end
-if self.ICLSon and self.ICLSchannel~=nil then
-icls=string.format("%d (%s)",self.ICLSchannel,self.ICLSmorse)
-end
-local wind=UTILS.MpsToKnots(select(1,self:GetWindOnDeck()))
-local Nmarshal,nmarshal=self:_GetQueueInfo(self.Qmarshal,playerData.case)
-local Npattern,npattern=self:_GetQueueInfo(self.Qpattern)
-local Nspinning,nspinning=self:_GetQueueInfo(self.Qspinning)
-local Nwaiting,nwaiting=self:_GetQueueInfo(self.Qwaiting)
-local Ntotal,ntotal=self:_GetQueueInfo(self.flights)
-local Tabs=timer.getAbsTime()
-local recoverytext="Recovery time windows (max 5):"
-if#self.recoverytimes==0 then
-recoverytext=recoverytext.." none."
-else
-local rw=0
-for _,_recovery in pairs(self.recoverytimes)do
-local recovery=_recovery
-if Tabs=5 then
-break
-end
-end
-end
-end
-local tankertext=nil
-if self.tanker then
-tankertext=string.format("Recovery tanker frequency %.3f MHz\n",self.tanker.RadioFreq)
-if self.tanker.TACANon then
-tankertext=tankertext..string.format("Recovery tanker TACAN %d%s (%s)",self.tanker.TACANchannel,self.tanker.TACANmode,self.tanker.TACANmorse)
-else
-tankertext=tankertext.."Recovery tanker TACAN n/a"
-end
-end
-local state=self:GetState()
-if state=="Idle"then
-state="Deck closed"
-end
-if self.turning then
-state=state.." (currently turning)"
-end
-local text=string.format("%s info:\n",self.alias)
-text=text..string.format("================================\n")
-text=text..string.format("Carrier state: %s\n",state)
-if self.case==1 then
-text=text..string.format("Case %d recovery ops\n",self.case)
-else
-local radial=self:GetRadial(self.case,true,true,false)
-text=text..string.format("Case %d recovery ops\nMarshal radial %03d°\n",self.case,radial)
-end
-text=text..string.format("BRC %03d° - FB %03d°\n",self:GetBRC(),self:GetFinalBearing(true))
-text=text..string.format("Speed %.1f kts - Wind on deck %.1f kts\n",carrierspeed,wind)
-text=text..string.format("Tower frequency %.3f MHz\n",self.TowerFreq)
-text=text..string.format("Marshal radio %.3f MHz\n",self.MarshalFreq)
-text=text..string.format("LSO radio %.3f MHz\n",self.LSOFreq)
-text=text..string.format("TACAN Channel %s\n",tacan)
-text=text..string.format("ICLS Channel %s\n",icls)
-if tankertext then
-text=text..tankertext.."\n"
-end
-text=text..string.format("# A/C total %d (%d)\n",Ntotal,ntotal)
-text=text..string.format("# A/C marshal %d (%d)\n",Nmarshal,nmarshal)
-text=text..string.format("# A/C pattern %d (%d) - spinning %d (%d)\n",Npattern,npattern,Nspinning,nspinning)
-text=text..string.format("# A/C waiting %d (%d)\n",Nwaiting,nwaiting)
-text=text..string.format(recoverytext)
-self:T2(self.lid..text)
-self:MessageToPlayer(playerData,text,nil,"",30,true)
-else
-self:E(self.lid..string.format("ERROR: Could not get player data for player %s.",playername))
-end
-end
-end
-function AIRBOSS:_DisplayCarrierWeather(_unitname)
-self:F2(_unitname)
-local unit,playername=self:_GetPlayerUnitAndName(_unitname)
-if unit and playername then
-local text=""
-local coord=self:GetCoordinate()
-local T=coord:GetTemperature()
-local P=coord:GetPressure()
-local Wd,Ws=self:GetWind(nil,true)
-local Bn,Bd=UTILS.BeaufortScale(Ws)
-local WodPA,WodPP=self:GetWindOnDeck()
-local WodPA=UTILS.MpsToKnots(WodPA)
-local WodPP=UTILS.MpsToKnots(WodPP)
-local WD=string.format('%03d°',Wd)
-local Ts=string.format("%d°C",T)
-local tT=string.format("%d°C",T)
-local tW=string.format("%.1f knots",UTILS.MpsToKnots(Ws))
-local tP=string.format("%.2f inHg",UTILS.hPa2inHg(P))
-text=text..string.format("Weather Report at Carrier %s:\n",self.alias)
-text=text..string.format("================================\n")
-text=text..string.format("Temperature %s\n",tT)
-text=text..string.format("Wind from %s at %s (%s)\n",WD,tW,Bd)
-text=text..string.format("Wind on deck || %.1f kts, == %.1f kts\n",WodPA,WodPP)
-text=text..string.format("QFE %.1f hPa = %s",P,tP)
-if self.staticweather then
-local clouds,visibility,fog,dust=self:_GetStaticWeather()
-text=text..string.format("\nVisibility %.1f NM",UTILS.MetersToNM(visibility))
-text=text..string.format("\nCloud base %d ft",UTILS.MetersToFeet(clouds.base))
-text=text..string.format("\nCloud thickness %d ft",UTILS.MetersToFeet(clouds.thickness))
-text=text..string.format("\nCloud density %d",clouds.density)
-text=text..string.format("\nPrecipitation %d",clouds.iprecptns)
-if fog then
-text=text..string.format("\nFog thickness %d ft",UTILS.MetersToFeet(fog.thickness))
-text=text..string.format("\nFog visibility %d ft",UTILS.MetersToFeet(fog.visibility))
-else
-text=text..string.format("\nNo fog")
-end
-if dust then
-text=text..string.format("\nDust density %d",dust)
-else
-text=text..string.format("\nNo dust")
-end
-end
-self:T2(self.lid..text)
-self:MessageToPlayer(self.players[playername],text,nil,"",30,true)
-else
-self:E(self.lid..string.format("ERROR! Could not find player unit in CarrierWeather! Unit name = %s",_unitname))
-end
-end
-function AIRBOSS:_SetDifficulty(_unitname,difficulty)
-self:T2({difficulty=difficulty,unitname=_unitname})
-local unit,playername=self:_GetPlayerUnitAndName(_unitname)
-if unit and playername then
-local playerData=self.players[playername]
-if playerData then
-playerData.difficulty=difficulty
-local text=string.format("roger, your skill level is now: %s.",difficulty)
-self:MessageToPlayer(playerData,text,nil,playerData.name,5)
-else
-self:E(self.lid..string.format("ERROR: Could not get player data for player %s.",playername))
-end
-if playerData.difficulty==AIRBOSS.Difficulty.HARD then
-playerData.showhints=false
-else
-playerData.showhints=true
-end
-end
-end
-function AIRBOSS:_SetHintsOnOff(_unitname)
-self:F2(_unitname)
-local unit,playername=self:_GetPlayerUnitAndName(_unitname)
-if unit and playername then
-local playerData=self.players[playername]
-if playerData then
-playerData.showhints=not playerData.showhints
-local text=""
-if playerData.showhints==true then
-text=string.format("roger, hints are now ON.")
-else
-text=string.format("affirm, hints are now OFF.")
-end
-self:MessageToPlayer(playerData,text,nil,playerData.name,5)
-end
-end
-end
-function AIRBOSS:_DisplayAttitude(_unitname)
-self:F2(_unitname)
-local unit,playername=self:_GetPlayerUnitAndName(_unitname)
-if unit and playername then
-local playerData=self.players[playername]
-if playerData then
-playerData.attitudemonitor=not playerData.attitudemonitor
-end
-end
-end
-function AIRBOSS:_SubtitlesOnOff(_unitname)
-self:F2(_unitname)
-local unit,playername=self:_GetPlayerUnitAndName(_unitname)
-if unit and playername then
-local playerData=self.players[playername]
-if playerData then
-playerData.subtitles=not playerData.subtitles
-local text=""
-if playerData.subtitles==true then
-text=string.format("roger, subtitiles are now ON.")
-elseif playerData.subtitles==false then
-text=string.format("affirm, subtitiles are now OFF.")
-end
-self:MessageToPlayer(playerData,text,nil,playerData.name,5)
-end
-end
-end
-function AIRBOSS:_TrapsheetOnOff(_unitname)
-self:F2(_unitname)
-local unit,playername=self:_GetPlayerUnitAndName(_unitname)
-if unit and playername then
-local playerData=self.players[playername]
-if playerData then
-local text=""
-if self.trapsheet then
-playerData.trapon=not playerData.trapon
-if playerData.trapon==true then
-text=string.format("roger, your trapsheets are now SAVED.")
-else
-text=string.format("affirm, your trapsheets are NOT SAVED.")
-end
-else
-text="negative, trap sheet data recorder is broken on this carrier."
-end
-self:MessageToPlayer(playerData,text,nil,playerData.name,5)
-end
-end
-end
-function AIRBOSS:_DisplayPlayerStatus(_unitName)
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit and _playername then
-local playerData=self.players[_playername]
-if playerData then
-local steptext=playerData.step
-if playerData.step==AIRBOSS.PatternStep.HOLDING then
-if playerData.holding==nil then
-steptext="Transit to Marshal"
-elseif playerData.holding==false then
-steptext="Marshal (outside zone)"
-elseif playerData.holding==true then
-steptext="Marshal Stack Holding"
-end
-end
-local stack=playerData.flag
-local stacktext=nil
-if stack>0 then
-local stackalt=self:_GetMarshalAltitude(stack)
-local angels=self:_GetAngels(stackalt)
-stacktext=string.format("Marshal Stack %d, Angels %d\n",stack,angels)
-if playerData.step==AIRBOSS.PatternStep.HOLDING and playerData.case>1 then
-local radial=self:GetRadial(playerData.case,true,true,true)
-stacktext=stacktext..string.format("Select TACAN %03d°, %d DME\n",radial,angels+15)
-end
-end
-local fuel=playerData.unit:GetFuel()*100
-local fuelstate=self:_GetFuelState(playerData.unit)
-local _,nunitsGround=self:_GetFlightUnits(playerData,true)
-local _,nunitsAirborne=self:_GetFlightUnits(playerData,false)
-local text=string.format("Status of player %s (%s)\n",playerData.name,playerData.callsign)
-text=text..string.format("================================\n")
-text=text..string.format("Step: %s\n",steptext)
-if stacktext then
-text=text..stacktext
-end
-text=text..string.format("Recovery Case: %d\n",playerData.case)
-text=text..string.format("Skill Level: %s\n",playerData.difficulty)
-text=text..string.format("Modex: %s (%s)\n",playerData.onboard,self:_GetACNickname(playerData.actype))
-text=text..string.format("Fuel State: %.1f lbs/1000 (%.1f %%)\n",fuelstate/1000,fuel)
-text=text..string.format("# units: %d (%d airborne)\n",nunitsGround,nunitsAirborne)
-text=text..string.format("Section Lead: %s (%d/%d)",tostring(playerData.seclead),#playerData.section+1,self.NmaxSection+1)
-for _,_sec in pairs(playerData.section)do
-local sec=_sec
-text=text..string.format("\n- %s",sec.name)
-end
-if playerData.step==AIRBOSS.PatternStep.INITIAL then
-local zoneinitial=self:GetCoordinate():Translate(UTILS.NMToMeters(3.5),self:GetRadial(2,false,false,false))
-local flyhdg=playerData.unit:GetCoordinate():HeadingTo(zoneinitial)
-local flydist=UTILS.MetersToNM(playerData.unit:GetCoordinate():Get2DDistance(zoneinitial))
-local brc=self:GetBRC()
-text=text..string.format("\nTo Initial: Fly heading %03d° for %.1f NM and turn to BRC %03d°",flyhdg,flydist,brc)
-elseif playerData.step==AIRBOSS.PatternStep.PLATFORM then
-local zoneplatform=self:_GetZonePlatform(playerData.case):GetCoordinate()
-local flyhdg=playerData.unit:GetCoordinate():HeadingTo(zoneplatform)
-local flydist=UTILS.MetersToNM(playerData.unit:GetCoordinate():Get2DDistance(zoneplatform))
-local hdg=self:GetRadial(playerData.case,true,true,true)
-text=text..string.format("\nTo Platform: Fly heading %03d° for %.1f NM and turn to %03d°",flyhdg,flydist,hdg)
-end
-self:MessageToPlayer(playerData,text,nil,"",30,true)
-else
-self:E(self.lid..string.format("ERROR: playerData=nil. Unit name=%s, player name=%s",_unitName,_playername))
-end
-else
-self:E(self.lid..string.format("ERROR: could not find player for unit %s",_unitName))
-end
-end
-function AIRBOSS:_MarkMarshalZone(_unitName,flare)
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit and _playername then
-local playerData=self.players[_playername]
-if playerData then
-local stack=playerData.flag
-local case=playerData.case
-local text=""
-if stack>0 then
-local zoneHolding=self:_GetZoneHolding(case,stack)
-local zoneThree=self:_GetZoneCommence(case,stack)
-local patternalt=self:_GetMarshalAltitude(stack,case)
-patternalt=5
-text="roger, marking"
-if flare then
-text=text..string.format("\n* Marshal zone stack %d with WHITE flares.",stack)
-zoneHolding:FlareZone(FLARECOLOR.White,45,nil,patternalt)
-text=text.."\n* Commence zone with RED flares."
-zoneThree:FlareZone(FLARECOLOR.Red,45,nil,patternalt)
-else
-text=text..string.format("\n* Marshal zone stack %d with WHITE smoke.",stack)
-zoneHolding:SmokeZone(SMOKECOLOR.White,45,patternalt)
-text=text.."\n* Commence zone with RED smoke."
-zoneThree:SmokeZone(SMOKECOLOR.Red,45,patternalt)
-end
-else
-text="negative, you are currently not in a Marshal stack. No zones will be marked!"
-end
-self:MessageToPlayer(playerData,text,"MARSHAL",playerData.name)
-end
-end
-end
-function AIRBOSS:_MarkCaseZones(_unitName,flare)
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit and _playername then
-local playerData=self.players[_playername]
-if playerData then
-local case=playerData.case
-local text=string.format("affirm, marking CASE %d zones",case)
-if flare then
-if case==1 or case==2 then
-text=text.."\n* initial with GREEN flares"
-self:_GetZoneInitial(case):FlareZone(FLARECOLOR.Green,45)
-end
-if case==2 or case==3 then
-text=text.."\n* approach corridor with GREEN flares"
-self:_GetZoneCorridor(case):FlareZone(FLARECOLOR.Green,45)
-end
-if case==2 or case==3 then
-text=text.."\n* platform with RED flares"
-self:_GetZonePlatform(case):FlareZone(FLARECOLOR.Red,45)
-end
-if case==3 then
-text=text.."\n* dirty up with YELLOW flares"
-self:_GetZoneDirtyUp(case):FlareZone(FLARECOLOR.Yellow,45)
-end
-if case==2 or case==3 then
-if math.abs(self.holdingoffset)>0 then
-self:_GetZoneArcIn(case):FlareZone(FLARECOLOR.White,45)
-text=text.."\n* arc turn in with WHITE flares"
-self:_GetZoneArcOut(case):FlareZone(FLARECOLOR.White,45)
-text=text.."\n* arc trun out with WHITE flares"
-end
-end
-if case==3 then
-text=text.."\n* bullseye with GREEN flares"
-self:_GetZoneBullseye(case):FlareZone(FLARECOLOR.Green,45)
-end
-if self.carriertype==AIRBOSS.CarrierType.INVINCIBLE or self.carriertype==AIRBOSS.CarrierType.HERMES or self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS or self.carriertype==AIRBOSS.CarrierType.CANBERRA then
-text=text.."\n* abeam landing stop with RED flares"
-local ALSPT=self:_GetZoneAbeamLandingSpot()
-ALSPT:FlareZone(FLARECOLOR.Red,5,nil,UTILS.FeetToMeters(110))
-text=text.."\n* primary landing spot with GREEN flares"
-local LSPT=self:_GetZoneLandingSpot()
-LSPT:FlareZone(FLARECOLOR.Green,5,nil,self.carrierparam.deckheight)
-end
-else
-if case==1 or case==2 then
-text=text.."\n* initial with GREEN smoke"
-self:_GetZoneInitial(case):SmokeZone(SMOKECOLOR.Green,45)
-end
-if case==2 or case==3 then
-text=text.."\n* approach corridor with GREEN smoke"
-self:_GetZoneCorridor(case):SmokeZone(SMOKECOLOR.Green,45)
-end
-if case==2 or case==3 then
-text=text.."\n* platform with RED smoke"
-self:_GetZonePlatform(case):SmokeZone(SMOKECOLOR.Red,45)
-end
-if case==2 or case==3 then
-if math.abs(self.holdingoffset)>0 then
-self:_GetZoneArcIn(case):SmokeZone(SMOKECOLOR.Blue,45)
-text=text.."\n* arc turn in with BLUE smoke"
-self:_GetZoneArcOut(case):SmokeZone(SMOKECOLOR.Blue,45)
-text=text.."\n* arc trun out with BLUE smoke"
-end
-end
-if case==3 then
-text=text.."\n* dirty up with ORANGE smoke"
-self:_GetZoneDirtyUp(case):SmokeZone(SMOKECOLOR.Orange,45)
-end
-if case==3 then
-text=text.."\n* bullseye with GREEN smoke"
-self:_GetZoneBullseye(case):SmokeZone(SMOKECOLOR.Green,45)
-end
-end
-self:MessageToPlayer(playerData,text,"MARSHAL",playerData.name)
-end
-end
-end
-function AIRBOSS:_LSORadioCheck(_unitName)
-self:F(_unitName)
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit and _playername then
-local playerData=self.players[_playername]
-if playerData then
-self:RadioTransmission(self.LSORadio,self.LSOCall.RADIOCHECK,nil,nil,nil,true)
-end
-end
-end
-function AIRBOSS:_MarshalRadioCheck(_unitName)
-self:F(_unitName)
-local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
-if _unit and _playername then
-local playerData=self.players[_playername]
-if playerData then
-self:RadioTransmission(self.MarshalRadio,self.MarshalCall.RADIOCHECK,nil,nil,nil,true)
-end
-end
-end
-function AIRBOSS:_SaveTrapSheet(playerData,grade)
-if playerData.trapsheet==nil or#playerData.trapsheet==0 or not io then
-return
-end
-local function _savefile(filename,data)
-local f=io.open(filename,"wb")
-if f then
-f:write(data)
-f:close()
-else
-self:E(self.lid..string.format("ERROR: could not save trap sheet to file %s.\nFile may contain invalid characters.",tostring(filename)))
-end
-end
-local path=self.trappath
-if lfs then
-path=path or lfs.writedir()
-end
-local filename=nil
-for i=1,9999 do
-if self.trapprefix then
-filename=string.format("%s_%s-%04d.csv",self.trapprefix,playerData.actype,i)
-else
-local name=UTILS.ReplaceIllegalCharacters(playerData.name,"_")
-filename=string.format("AIRBOSS-%s_Trapsheet-%s_%s-%04d.csv",self.alias,name,playerData.actype,i)
-end
-if path~=nil then
-filename=path.."\\"..filename
-end
-local _exists=UTILS.FileExists(filename)
-if not _exists then
-break
-end
-end
-local text=string.format("Saving player %s trapsheet to file %s",playerData.name,filename)
-self:I(self.lid..text)
-local data="#Time,Rho,X,Z,Alt,AoA,GSE,LUE,Vtot,Vy,Gamma,Pitch,Roll,Yaw,Step,Grade,Points,Details\n"
-local g0=playerData.trapsheet[1]
-local T0=g0.Time
-for i=1,#playerData.trapsheet do
-local groove=playerData.trapsheet[i]
-local t=groove.Time-T0
-local a=UTILS.MetersToNM(groove.Rho or 0)
-local b=-groove.X or 0
-local c=groove.Z or 0
-local d=UTILS.MetersToFeet(groove.Alt or 0)
-local e=groove.AoA or 0
-local f=groove.GSE or 0
-local g=-groove.LUE or 0
-local h=UTILS.MpsToKnots(groove.Vel or 0)
-local i=(groove.Vy or 0)*196.85
-local j=groove.Gamma or 0
-local k=groove.Pitch or 0
-local l=groove.Roll or 0
-local m=groove.Yaw or 0
-local n=self:_GS(groove.Step,-1)or"n/a"
-local o=groove.Grade or"n/a"
-local p=groove.GradePoints or 0
-local q=groove.GradeDetail or"n/a"
-data=data..string.format("%.2f,%.3f,%.1f,%.1f,%.1f,%.2f,%.2f,%.2f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%s,%s,%.1f,%s\n",t,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q)
-end
-_savefile(filename,data)
-end
-function AIRBOSS:onbeforeSave(From,Event,To,path,filename)
-if not io then
-self:E(self.lid.."ERROR: io not desanitized. Can't save player grades.")
-return false
-end
-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.")
-end
-return true
-end
-function AIRBOSS:onafterSave(From,Event,To,path,filename)
-local function _savefile(filename,data)
-local f=assert(io.open(filename,"wb"))
-f:write(data)
-f:close()
-end
-if lfs then
-path=path or lfs.writedir()
-end
-filename=filename or string.format("AIRBOSS-%s_LSOgrades.csv",self.alias)
-if path~=nil then
-filename=path.."\\"..filename
-end
-local scores="Name,Pass,Points Final,Points Pass,Grade,Details,Wire,Tgroove,Case,Wind,Modex,Airframe,Carrier Type,Carrier Name,Theatre,Mission Time,Mission Date,OS Date\n"
-local n=0
-for playername,grades in pairs(self.playerscores)do
-for i,_grade in pairs(grades)do
-local grade=_grade
-local wire="n/a"
-if grade.wire and grade.wire<=4 then
-wire=tostring(grade.wire)
-end
-local Tgroove="n/a"
-if grade.Tgroove and grade.Tgroove<=360 and grade.case<3 then
-Tgroove=tostring(UTILS.Round(grade.Tgroove,1))
-end
-local finalscore="n/a"
-if grade.finalscore then
-finalscore=tostring(UTILS.Round(grade.finalscore,1))
-end
-scores=scores..string.format("%s,%d,%s,%.1f,%s,%s,%s,%s,%d,%s,%s,%s,%s,%s,%s,%s,%s,%s\n",playername,i,finalscore,grade.points,grade.grade,grade.details,wire,Tgroove,grade.case,grade.wind,grade.modex,grade.airframe,grade.carriertype,grade.carriername,grade.theatre,grade.mitime,grade.midate,grade.osdate)
-n=n+1
-end
-end
-local text=string.format("Saving %d player LSO grades to file %s",n,filename)
-self:I(self.lid..text)
-_savefile(filename,scores)
-end
-function AIRBOSS:onbeforeLoad(From,Event,To,path,filename)
-local function _fileexists(name)
-local f=io.open(name,"r")
-if f~=nil then
-io.close(f)
-return true
-else
-return false
-end
-end
-if not io then
-self:E(self.lid.."WARNING: io not desanitized. Can't load player grades.")
-return false
-end
-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.")
-end
-if lfs then
-path=path or lfs.writedir()
-end
-filename=filename or string.format("AIRBOSS-%s_LSOgrades.csv",self.alias)
-if path~=nil then
-filename=path.."\\"..filename
-end
-local exists=_fileexists(filename)
-if exists then
-return true
-else
-self:E(self.lid..string.format("WARNING: Player LSO grades file %s does not exist.",filename))
-return false
-end
-end
-function AIRBOSS:onafterLoad(From,Event,To,path,filename)
-local function _loadfile(filename)
-local f=assert(io.open(filename,"rb"))
-local data=f:read("*all")
-f:close()
-return data
-end
-if lfs then
-path=path or lfs.writedir()
-end
-filename=filename or string.format("AIRBOSS-%s_LSOgrades.csv",self.alias)
-if path~=nil then
-filename=path.."\\"..filename
-end
-local text=string.format("Loading player LSO grades from file %s",filename)
-MESSAGE:New(text,10):ToAllIf(self.Debug)
-self:I(self.lid..text)
-local data=_loadfile(filename)
-local playergrades=UTILS.Split(data,"\n")
-table.remove(playergrades,1)
-self.playerscores={}
-local n=0
-for _,gradeline in pairs(playergrades)do
-local gradedata=UTILS.Split(gradeline,",")
-self:T2(gradedata)
-local grade={}
-local playername=gradedata[1]
-if gradedata[3]~=nil and gradedata[3]~="n/a"then
-grade.finalscore=tonumber(gradedata[3])
-end
-grade.points=tonumber(gradedata[4])
-grade.grade=tostring(gradedata[5])
-grade.details=tostring(gradedata[6])
-if gradedata[7]~=nil and gradedata[7]~="n/a"then
-grade.wire=tonumber(gradedata[7])
-end
-if gradedata[8]~=nil and gradedata[8]~="n/a"then
-grade.Tgroove=tonumber(gradedata[8])
-end
-grade.case=tonumber(gradedata[9])
-grade.wind=gradedata[10]or"n/a"
-grade.modex=gradedata[11]or"n/a"
-grade.airframe=gradedata[12]or"n/a"
-grade.carriertype=gradedata[13]or"n/a"
-grade.carriername=gradedata[14]or"n/a"
-grade.theatre=gradedata[15]or"n/a"
-grade.mitime=gradedata[16]or"n/a"
-grade.midate=gradedata[17]or"n/a"
-grade.osdate=gradedata[18]or"n/a"
-self.playerscores[playername]=self.playerscores[playername]or{}
-table.insert(self.playerscores[playername],grade)
-n=n+1
-self:T2({playername,self.playerscores[playername]})
-end
-local text=string.format("Loaded %d player LSO grades from file %s",n,filename)
-self:I(self.lid..text)
-end
-function AIRBOSS:onafterLSOGrade(From,Event,To,playerData,grade)
-if self.funkmanSocket then
-local trapsheet={};trapsheet.X={};trapsheet.Z={};trapsheet.AoA={};trapsheet.Alt={}
-for i=1,#playerData.trapsheet do
-local ts=playerData.trapsheet[i]
-table.insert(trapsheet.X,UTILS.Round(ts.X,1))
-table.insert(trapsheet.Z,UTILS.Round(ts.Z,1))
-table.insert(trapsheet.AoA,UTILS.Round(ts.AoA,2))
-table.insert(trapsheet.Alt,UTILS.Round(ts.Alt,1))
-end
-local result={}
-result.command=SOCKET.DataType.LSOGRADE
-result.name=playerData.name
-result.trapsheet=trapsheet
-result.airframe=grade.airframe
-result.mitime=grade.mitime
-result.midate=grade.midate
-result.wind=grade.wind
-result.carriertype=grade.carriertype
-result.carriername=grade.carriername
-result.carrierrwy=grade.carrierrwy
-result.landingdist=self.carrierparam.landingdist
-result.theatre=grade.theatre
-result.case=playerData.case
-result.Tgroove=grade.Tgroove
-result.wire=grade.wire
-result.grade=grade.grade
-result.points=grade.points
-result.details=grade.details
-self:T(self.lid.."Result onafterLSOGrade")
-self:T(result)
-self.funkmanSocket:SendTable(result)
-end
-end
-AIRWING={
-ClassName="AIRWING",
-verbose=0,
-lid=nil,
-menu=nil,
-payloads={},
-payloadcounter=0,
-pointsCAP={},
-pointsTANKER={},
-pointsAWACS={},
-pointsRecon={},
-markpoints=false,
-capOptionPatrolRaceTrack=false,
-capFormation=nil,
-}
-AIRWING.version="0.9.4"
-function AIRWING:New(warehousename,airwingname)
-local self=BASE:Inherit(self,LEGION:New(warehousename,airwingname))
-if not self then
-BASE:E(string.format("ERROR: Could not find warehouse %s!",warehousename))
-return nil
-end
-self.lid=string.format("AIRWING %s | ",self.alias)
-self.nflightsCAP=0
-self.nflightsAWACS=0
-self.nflightsRecon=0
-self.nflightsTANKERboom=0
-self.nflightsTANKERprobe=0
-self.nflightsRecoveryTanker=0
-self.nflightsRescueHelo=0
-self.markpoints=false
-self:AddTransition("*","FlightOnMission","*")
-return self
-end
-function AIRWING:AddSquadron(Squadron)
-table.insert(self.cohorts,Squadron)
-self:AddAssetToSquadron(Squadron,Squadron.Ngroups)
-if Squadron.attribute==GROUP.Attribute.AIR_AWACS then
-self:NewPayload(Squadron.templategroup,-1,AUFTRAG.Type.AWACS)
-elseif Squadron.attribute==GROUP.Attribute.AIR_TANKER then
-self:NewPayload(Squadron.templategroup,-1,AUFTRAG.Type.TANKER)
-end
-self:NewPayload(Squadron.templategroup,-1,AUFTRAG.Type.RELOCATECOHORT,0)
-Squadron:SetAirwing(self)
-if Squadron:IsStopped()then
-Squadron:Start()
-end
-return self
-end
-function AIRWING:NewPayload(Unit,Npayloads,MissionTypes,Performance)
-Performance=Performance or 50
-if type(Unit)=="string"then
-local name=Unit
-Unit=UNIT:FindByName(name)
-if not Unit then
-Unit=GROUP:FindByName(name)
-end
-end
-if Unit then
-if Unit:IsInstanceOf("GROUP")then
-Unit=Unit:GetUnit(1)
-end
-if MissionTypes and type(MissionTypes)~="table"then
-MissionTypes={MissionTypes}
-end
-local payload={}
-payload.uid=self.payloadcounter
-payload.unitname=Unit:GetName()
-payload.aircrafttype=Unit:GetTypeName()
-payload.pylons=Unit:GetTemplatePayload()
-self:SetPayloadAmount(payload,Npayloads)
-payload.capabilities={}
-for _,missiontype in pairs(MissionTypes)do
-local capability={}
-capability.MissionType=missiontype
-capability.Performance=Performance
-table.insert(payload.capabilities,capability)
-end
-if not AUFTRAG.CheckMissionType(AUFTRAG.Type.ORBIT,MissionTypes)then
-local capability={}
-capability.MissionType=AUFTRAG.Type.ORBIT
-capability.Performance=50
-table.insert(payload.capabilities,capability)
-end
-if not AUFTRAG.CheckMissionType(AUFTRAG.Type.RELOCATECOHORT,MissionTypes)then
-local capability={}
-capability.MissionType=AUFTRAG.Type.RELOCATECOHORT
-capability.Performance=50
-table.insert(payload.capabilities,capability)
-end
-self:T(self.lid..string.format("Adding new payload from unit %s for aircraft type %s: ID=%d, N=%d (unlimited=%s), performance=%d, missions: %s",
-payload.unitname,payload.aircrafttype,payload.uid,payload.navail,tostring(payload.unlimited),Performance,table.concat(MissionTypes,", ")))
-table.insert(self.payloads,payload)
-self.payloadcounter=self.payloadcounter+1
-return payload
-end
-self:E(self.lid.."ERROR: No UNIT found to create PAYLOAD!")
-return nil
-end
-function AIRWING:SetPayloadAmount(Payload,Navailable)
-Navailable=Navailable or 99
-if Payload then
-Payload.unlimited=Navailable<0
-if Payload.unlimited then
-Payload.navail=1
-else
-Payload.navail=Navailable
-end
-end
-return self
-end
-function AIRWING:IncreasePayloadAmount(Payload,N)
-N=N or 1
-if Payload and Payload.navail>=0 then
-Payload.navail=Payload.navail+N
-Payload.navail=math.max(Payload.navail,0)
-end
-return self
-end
-function AIRWING:GetPayloadAmount(Payload)
-return Payload.navail
-end
-function AIRWING:GetPayloadCapabilities(Payload)
-return Payload.capabilities
-end
-function AIRWING:AddPayloadCapability(Payload,MissionTypes,Performance)
-if MissionTypes and type(MissionTypes)~="table"then
-MissionTypes={MissionTypes}
-end
-Payload.capabilities=Payload.capabilities or{}
-for _,missiontype in pairs(MissionTypes)do
-local capability={}
-capability.MissionType=missiontype
-capability.Performance=Performance
-table.insert(Payload.capabilities,capability)
-end
-return self
-end
-function AIRWING:FetchPayloadFromStock(UnitType,MissionType,Payloads)
-if not self.payloads or#self.payloads==0 then
-self:T(self.lid.."WARNING: No payloads in stock!")
-return nil
-end
-if self.verbose>=4 then
-self:I(self.lid..string.format("Looking for payload for unit type=%s and mission type=%s",UnitType,MissionType))
-for i,_payload in pairs(self.payloads)do
-local payload=_payload
-local performance=self:GetPayloadPeformance(payload,MissionType)
-self:I(self.lid..string.format("[%d] Payload type=%s navail=%d unlimited=%s",i,payload.aircrafttype,payload.navail,tostring(payload.unlimited)))
-end
-end
-local function sortpayloads(a,b)
-local pA=a
-local pB=b
-if a and b then
-local performanceA=self:GetPayloadPeformance(a,MissionType)
-local performanceB=self:GetPayloadPeformance(b,MissionType)
-return(performanceA>performanceB)or(performanceA==performanceB and a.unlimited==true and b.unlimited~=true)or(performanceA==performanceB and a.unlimited==true and b.unlimited==true and a.navail>b.navail)
-elseif not a then
-self:I(self.lid..string.format("FF ERROR in sortpayloads: a is nil"))
-return false
-elseif not b then
-self:I(self.lid..string.format("FF ERROR in sortpayloads: b is nil"))
-return true
-else
-self:I(self.lid..string.format("FF ERROR in sortpayloads: a and b are nil"))
-return false
-end
-end
-local function _checkPayloads(payload)
-if Payloads then
-for _,Payload in pairs(Payloads)do
-if Payload.uid==payload.uid then
-return true
-end
-end
-else
-return nil
-end
-return false
-end
-local payloads={}
-for _,_payload in pairs(self.payloads)do
-local payload=_payload
-local specialpayload=_checkPayloads(payload)
-local compatible=AUFTRAG.CheckMissionCapability(MissionType,payload.capabilities)
-local goforit=specialpayload or(specialpayload==nil and compatible)
-if payload.aircrafttype==UnitType and payload.navail>0 and goforit then
-table.insert(payloads,payload)
-end
-end
-if self.verbose>=4 then
-self:I(self.lid..string.format("Sorted payloads for mission type %s and aircraft type=%s:",MissionType,UnitType))
-for _,_payload in ipairs(self.payloads)do
-local payload=_payload
-if payload.aircrafttype==UnitType and AUFTRAG.CheckMissionCapability(MissionType,payload.capabilities)then
-local performace=self:GetPayloadPeformance(payload,MissionType)
-self:I(self.lid..string.format("- %s payload for %s: avail=%d performace=%d",MissionType,payload.aircrafttype,payload.navail,performace))
-end
-end
-end
-if#payloads==0 then
-self:T(self.lid..string.format("WARNING: Could not find a payload for airframe %s mission type %s!",UnitType,MissionType))
-return nil
-elseif#payloads==1 then
-local payload=payloads[1]
-if not payload.unlimited then
-payload.navail=payload.navail-1
-end
-return payload
-else
-table.sort(payloads,sortpayloads)
-local payload=payloads[1]
-if not payload.unlimited then
-payload.navail=payload.navail-1
-end
-return payload
-end
-end
-function AIRWING:ReturnPayloadFromAsset(asset)
-local payload=asset.payload
-if payload then
-if not payload.unlimited then
-payload.navail=payload.navail+1
-end
-asset.payload=nil
-else
-self:E(self.lid.."ERROR: asset had no payload attached!")
-end
-end
-function AIRWING:AddAssetToSquadron(Squadron,Nassets)
-if Squadron then
-local Group=GROUP:FindByName(Squadron.templatename)
-if Group then
-local text=string.format("Adding asset %s to squadron %s",Group:GetName(),Squadron.name)
-self:T(self.lid..text)
-self:AddAsset(Group,Nassets,nil,nil,nil,nil,Squadron.skill,Squadron.livery,Squadron.name)
-else
-self:E(self.lid.."ERROR: Group does not exist!")
-end
-else
-self:E(self.lid.."ERROR: Squadron does not exit!")
-end
-return self
-end
-function AIRWING:GetSquadron(SquadronName)
-local squad=self:_GetCohort(SquadronName)
-return squad
-end
-function AIRWING:GetSquadronOfAsset(Asset)
-return self:GetSquadron(Asset.squadname)
-end
-function AIRWING:RemoveAssetFromSquadron(Asset)
-local squad=self:GetSquadronOfAsset(Asset)
-if squad then
-squad:DelAsset(Asset)
-end
-end
-function AIRWING:SetNumberCAP(n)
-self.nflightsCAP=n or 1
-return self
-end
-function AIRWING:SetCAPFormation(Formation)
-self.capFormation=Formation
-return self
-end
-function AIRWING:SetCapCloseRaceTrack(OnOff)
-self.capOptionPatrolRaceTrack=OnOff
-return self
-end
-function AIRWING:SetNumberTankerBoom(Nboom)
-self.nflightsTANKERboom=Nboom or 1
-return self
-end
-function AIRWING:ShowPatrolPointMarkers(onoff)
-if onoff then
-self.markpoints=true
-else
-self.markpoints=false
-end
-return self
-end
-function AIRWING:SetNumberTankerProbe(Nprobe)
-self.nflightsTANKERprobe=Nprobe or 1
-return self
-end
-function AIRWING:SetNumberAWACS(n)
-self.nflightsAWACS=n or 1
-return self
-end
-function AIRWING:SetNumberRecon(n)
-self.nflightsRecon=n or 1
-return self
-end
-function AIRWING:SetNumberRescuehelo(n)
-self.nflightsRescueHelo=n or 1
-return self
-end
-function AIRWING:_PatrolPointMarkerText(point)
-local text=string.format("%s Occupied=%d, \nheading=%03d, leg=%d NM, alt=%d ft, speed=%d kts",
-point.type,point.noccupied,point.heading,point.leg,point.altitude,point.speed)
-return text
-end
-function AIRWING:UpdatePatrolPointMarker(point)
-if self.markpoints then
-local text=string.format("%s Occupied=%d\nheading=%03d, leg=%d NM, alt=%d ft, speed=%d kts",
-point.type,point.noccupied,point.heading,point.leg,point.altitude,point.speed)
-point.marker:UpdateText(text,1)
-end
-end
-function AIRWING:NewPatrolPoint(Type,Coordinate,Altitude,Speed,Heading,LegLength,RefuelSystem)
-if Coordinate and Coordinate:IsInstanceOf("ZONE_BASE")then
-Coordinate=Coordinate:GetCoordinate()
-end
-local patrolpoint={}
-patrolpoint.type=Type or"Unknown"
-patrolpoint.coord=Coordinate or self:GetCoordinate():Translate(UTILS.NMToMeters(math.random(10,15)),math.random(360))
-patrolpoint.heading=Heading or math.random(360)
-patrolpoint.leg=LegLength or 15
-patrolpoint.altitude=Altitude or math.random(10,20)*1000
-patrolpoint.speed=Speed or 350
-patrolpoint.noccupied=0
-patrolpoint.refuelsystem=RefuelSystem
-if self.markpoints then
-patrolpoint.marker=MARKER:New(Coordinate,"New Patrol Point"):ToAll()
-AIRWING.UpdatePatrolPointMarker(patrolpoint)
-end
-return patrolpoint
-end
-function AIRWING:AddPatrolPointCAP(Coordinate,Altitude,Speed,Heading,LegLength)
-local patrolpoint=self:NewPatrolPoint("CAP",Coordinate,Altitude,Speed,Heading,LegLength)
-table.insert(self.pointsCAP,patrolpoint)
-return self
-end
-function AIRWING:AddPatrolPointRecon(Coordinate,Altitude,Speed,Heading,LegLength)
-local patrolpoint=self:NewPatrolPoint("RECON",Coordinate,Altitude,Speed,Heading,LegLength)
-table.insert(self.pointsRecon,patrolpoint)
-return self
-end
-function AIRWING:AddPatrolPointTANKER(Coordinate,Altitude,Speed,Heading,LegLength,RefuelSystem)
-local patrolpoint=self:NewPatrolPoint("Tanker",Coordinate,Altitude,Speed,Heading,LegLength,RefuelSystem)
-table.insert(self.pointsTANKER,patrolpoint)
-return self
-end
-function AIRWING:AddPatrolPointAWACS(Coordinate,Altitude,Speed,Heading,LegLength)
-local patrolpoint=self:NewPatrolPoint("AWACS",Coordinate,Altitude,Speed,Heading,LegLength)
-table.insert(self.pointsAWACS,patrolpoint)
-return self
-end
-function AIRWING:SetAirboss(airboss)
-self.airboss=airboss
-return self
-end
-function AIRWING: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
-elseif TakeoffType:lower()=="air"then
-self.takeoffType=COORDINATE.WaypointType.TurningPoint
-else
-self.takeoffType=COORDINATE.WaypointType.TakeOffParking
-end
-return self
-end
-function AIRWING:SetTakeoffCold()
-self:SetTakeoffType("Cold")
-return self
-end
-function AIRWING:SetTakeoffHot()
-self:SetTakeoffType("Hot")
-return self
-end
-function AIRWING:SetTakeoffAir()
-self:SetTakeoffType("Air")
-return self
-end
-function AIRWING:SetDespawnAfterLanding(Switch)
-if Switch then
-self.despawnAfterLanding=Switch
-else
-self.despawnAfterLanding=true
-end
-return self
-end
-function AIRWING:SetDespawnAfterHolding(Switch)
-if Switch then
-self.despawnAfterHolding=Switch
-else
-self.despawnAfterHolding=true
-end
-return self
-end
-function AIRWING:onafterStart(From,Event,To)
-self:GetParent(self,AIRWING).onafterStart(self,From,Event,To)
-self:I(self.lid..string.format("Starting AIRWING v%s",AIRWING.version))
-end
-function AIRWING:onafterStatus(From,Event,To)
-self:GetParent(self).onafterStatus(self,From,Event,To)
-local fsmstate=self:GetState()
-self:CheckCAP()
-self:CheckTANKER()
-self:CheckAWACS()
-self:CheckRescuhelo()
-self:CheckRECON()
-self:CheckTransportQueue()
-self:CheckMissionQueue()
-if self.verbose>=1 then
-local Nmissions=self:CountMissionsInQueue()
-local Npayloads=self:CountPayloadsInStock(AUFTRAG.Type)
-local Npq,Np,Nq=self:CountAssetsOnMission()
-local assets=string.format("%d (OnMission: Total=%d, Active=%d, Queued=%d)",self:CountAssets(),Npq,Np,Nq)
-local text=string.format("%s: Missions=%d, Payloads=%d (%d), Squads=%d, Assets=%s",fsmstate,Nmissions,Npayloads,#self.payloads,#self.cohorts,assets)
-self:I(self.lid..text)
-end
-if self.verbose>=2 then
-local text=string.format("Missions Total=%d:",#self.missionqueue)
-for i,_mission in pairs(self.missionqueue)do
-local mission=_mission
-local prio=string.format("%d/%s",mission.prio,tostring(mission.importance));if mission.urgent then prio=prio.." (!)"end
-local assets=string.format("%d/%d",mission:CountOpsGroups(),mission:GetNumberOfRequiredAssets())
-local target=string.format("%d/%d Damage=%.1f",mission:CountMissionTargets(),mission:GetTargetInitialNumber(),mission:GetTargetDamage())
-local mystatus=mission:GetLegionStatus(self)
-text=text..string.format("\n[%d] %s %s: Status=%s [%s], Prio=%s, Assets=%s, Targets=%s",i,mission.name,mission.type,mystatus,mission.status,prio,assets,target)
-end
-self:I(self.lid..text)
-end
-if self.verbose>=3 then
-local text="Squadrons:"
-for i,_squadron in pairs(self.cohorts)do
-local squadron=_squadron
-local callsign=squadron.callsignName and UTILS.GetCallsignName(squadron.callsignName)or"N/A"
-local modex=squadron.modex and squadron.modex or-1
-local skill=squadron.skill and tostring(squadron.skill)or"N/A"
-text=text..string.format("\n* %s %s: %s*%d/%d, Callsign=%s, Modex=%d, Skill=%s",squadron.name,squadron:GetState(),squadron.aircrafttype,squadron:CountAssets(true),#squadron.assets,callsign,modex,skill)
-end
-self:I(self.lid..text)
-end
-end
-function AIRWING:_GetPatrolData(PatrolPoints,RefuelSystem)
-local function sort(a,b)
-return a.noccupied0 then
-table.sort(PatrolPoints,sort)
-for _,_patrolpoint in pairs(PatrolPoints)do
-local patrolpoint=_patrolpoint
-if(RefuelSystem and patrolpoint.refuelsystem and RefuelSystem==patrolpoint.refuelsystem)or RefuelSystem==nil or patrolpoint.refuelsystem==nil then
-return patrolpoint
-end
-end
-end
-return self:NewPatrolPoint()
-end
-function AIRWING:CheckCAP()
-local Ncap=0
-for _,_mission in pairs(self.missionqueue)do
-local mission=_mission
-if mission:IsNotOver()and(mission.type==AUFTRAG.Type.GCICAP or mission.type==AUFTRAG.Type.PATROLRACETRACK)and mission.patroldata then
-Ncap=Ncap+1
-end
-end
-for i=1,self.nflightsCAP-Ncap do
-local patrol=self:_GetPatrolData(self.pointsCAP)
-local altitude=patrol.altitude+1000*patrol.noccupied
-local missionCAP=nil
-if self.capOptionPatrolRaceTrack then
-missionCAP=AUFTRAG:NewPATROL_RACETRACK(patrol.coord,altitude,patrol.speed,patrol.heading,patrol.leg,self.capFormation)
-else
-missionCAP=AUFTRAG:NewGCICAP(patrol.coord,altitude,patrol.speed,patrol.heading,patrol.leg)
-end
-missionCAP.patroldata=patrol
-patrol.noccupied=patrol.noccupied+1
-if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol)end
-self:AddMission(missionCAP)
-end
-return self
-end
-function AIRWING:CheckRECON()
-local Ncap=0
-for _,_mission in pairs(self.missionqueue)do
-local mission=_mission
-if mission:IsNotOver()and mission.type==AUFTRAG.Type.RECON and mission.patroldata then
-Ncap=Ncap+1
-end
-end
-for i=1,self.nflightsRecon-Ncap do
-local patrol=self:_GetPatrolData(self.pointsRecon)
-local altitude=patrol.altitude
-local ZoneSet=SET_ZONE:New()
-local Zone=ZONE_RADIUS:New(self.alias.." Recon "..math.random(1,10000),patrol.coord:GetVec2(),UTILS.NMToMeters(patrol.leg/2))
-ZoneSet:AddZone(Zone)
-if self.Debug then
-Zone:DrawZone(self.coalition,{0,0,1},Alpha,FillColor,FillAlpha,2,true)
-end
-local missionRECON=AUFTRAG:NewRECON(ZoneSet,patrol.speed,patrol.altitude,true)
-missionRECON.patroldata=patrol
-missionRECON.categories={AUFTRAG.Category.AIRCRAFT}
-patrol.noccupied=patrol.noccupied+1
-if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol)end
-self:AddMission(missionRECON)
-end
-return self
-end
-function AIRWING:CheckTANKER()
-local Nboom=0
-local Nprob=0
-for _,_mission in pairs(self.missionqueue)do
-local mission=_mission
-if mission:IsNotOver()and mission.type==AUFTRAG.Type.TANKER and mission.patroldata then
-if mission.refuelSystem==Unit.RefuelingSystem.BOOM_AND_RECEPTACLE then
-Nboom=Nboom+1
-elseif mission.refuelSystem==Unit.RefuelingSystem.PROBE_AND_DROGUE then
-Nprob=Nprob+1
-end
-end
-end
-for i=1,self.nflightsTANKERboom-Nboom do
-local patrol=self:_GetPatrolData(self.pointsTANKER)
-local altitude=patrol.altitude+1000*patrol.noccupied
-local mission=AUFTRAG:NewTANKER(patrol.coord,altitude,patrol.speed,patrol.heading,patrol.leg,Unit.RefuelingSystem.BOOM_AND_RECEPTACLE)
-mission.patroldata=patrol
-patrol.noccupied=patrol.noccupied+1
-if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol)end
-self:AddMission(mission)
-end
-for i=1,self.nflightsTANKERprobe-Nprob do
-local patrol=self:_GetPatrolData(self.pointsTANKER)
-local altitude=patrol.altitude+1000*patrol.noccupied
-local mission=AUFTRAG:NewTANKER(patrol.coord,altitude,patrol.speed,patrol.heading,patrol.leg,Unit.RefuelingSystem.PROBE_AND_DROGUE)
-mission.patroldata=patrol
-patrol.noccupied=patrol.noccupied+1
-if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol)end
-self:AddMission(mission)
-end
-return self
-end
-function AIRWING:CheckAWACS()
-local N=0
-for _,_mission in pairs(self.missionqueue)do
-local mission=_mission
-if mission:IsNotOver()and mission.type==AUFTRAG.Type.AWACS and mission.patroldata then
-N=N+1
-end
-end
-for i=1,self.nflightsAWACS-N do
-local patrol=self:_GetPatrolData(self.pointsAWACS)
-local altitude=patrol.altitude+1000*patrol.noccupied
-local mission=AUFTRAG:NewAWACS(patrol.coord,altitude,patrol.speed,patrol.heading,patrol.leg)
-mission.patroldata=patrol
-patrol.noccupied=patrol.noccupied+1
-if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol)end
-self:AddMission(mission)
-end
-return self
-end
-function AIRWING:CheckRescuhelo()
-local N=self:CountMissionsInQueue({AUFTRAG.Type.RESCUEHELO})
-local name=self.airbase:GetName()
-local carrier=UNIT:FindByName(name)
-for i=1,self.nflightsRescueHelo-N do
-local mission=AUFTRAG:NewRESCUEHELO(carrier)
-self:AddMission(mission)
-end
-return self
-end
-function AIRWING:GetTankerForFlight(flightgroup)
-local tankers=self:GetAssetsOnMission(AUFTRAG.Type.TANKER)
-if#tankers>0 then
-local tankeropt={}
-for _,_tanker in pairs(tankers)do
-local tanker=_tanker
-if flightgroup.refueltype and flightgroup.refueltype==tanker.flightgroup.tankertype then
-local tankercoord=tanker.flightgroup.group:GetCoordinate()
-local assetcoord=flightgroup.group:GetCoordinate()
-local dist=assetcoord:Get2DDistance(tankercoord)
-if dist>5 then
-table.insert(tankeropt,{tanker=tanker,dist=dist})
-end
-end
-end
-table.sort(tankeropt,function(a,b)return a.dist0 then
-return tankeropt[1].tanker
-else
-return nil
-end
-end
-return nil
-end
-function AIRWING:SetUsingOpsAwacs(ConnectecdAwacs)
-self:I(self.lid.."Added AWACS Object: "..ConnectecdAwacs:GetName()or"unknown")
-self.UseConnectedOpsAwacs=true
-self.ConnectedOpsAwacs=ConnectecdAwacs
-return self
-end
-function AIRWING:RemoveUsingOpsAwacs()
-self:I(self.lid.."Reomve AWACS Object: "..self.ConnectedOpsAwacs:GetName()or"unknown")
-self.UseConnectedOpsAwacs=false
-return self
-end
-function AIRWING:onafterFlightOnMission(From,Event,To,FlightGroup,Mission)
-self:T(self.lid..string.format("Group %s on %s mission %s",FlightGroup:GetName(),Mission:GetType(),Mission:GetName()))
-if self.UseConnectedOpsAwacs and self.ConnectedOpsAwacs then
-self.ConnectedOpsAwacs:__FlightOnMission(2,FlightGroup,Mission)
-end
-end
-function AIRWING:CountPayloadsInStock(MissionTypes,UnitTypes,Payloads)
-if MissionTypes then
-if type(MissionTypes)=="string"then
-MissionTypes={MissionTypes}
-end
-end
-if UnitTypes then
-if type(UnitTypes)=="string"then
-UnitTypes={UnitTypes}
-end
-end
-local function _checkUnitTypes(payload)
-if UnitTypes then
-for _,unittype in pairs(UnitTypes)do
-if unittype==payload.aircrafttype then
-return true
-end
-end
-else
-return true
-end
-return false
-end
-local function _checkPayloads(payload)
-if Payloads then
-for _,Payload in pairs(Payloads)do
-if Payload.uid==payload.uid then
-return true
-end
-end
-else
-return nil
-end
-return false
-end
-local n=0
-for _,_payload in pairs(self.payloads)do
-local payload=_payload
-for _,MissionType in pairs(MissionTypes)do
-local specialpayload=_checkPayloads(payload)
-local compatible=AUFTRAG.CheckMissionCapability(MissionType,payload.capabilities)
-local goforit=specialpayload or(specialpayload==nil and compatible)
-if goforit and _checkUnitTypes(payload)then
-if payload.unlimited then
-return 999
-else
-n=n+payload.navail
-end
-end
-end
-end
-return n
-end
-function AIRWING:GetPayloadPeformance(Payload,MissionType)
-if Payload then
-for _,Capability in pairs(Payload.capabilities)do
-local capability=Capability
-if capability.MissionType==MissionType then
-return capability.Performance
-end
-end
-else
-self:E(self.lid.."ERROR: Payload is nil!")
-end
-return-1
-end
-function AIRWING:GetPayloadMissionTypes(Payload)
-local missiontypes={}
-for _,Capability in pairs(Payload.capabilities)do
-local capability=Capability
-table.insert(missiontypes,capability.MissionType)
-end
-return missiontypes
-end
-ARMYGROUP={
-ClassName="ARMYGROUP",
-formationPerma=nil,
-engage={},
-}
-ARMYGROUP.version="1.0.1"
-function ARMYGROUP:New(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
-local self=BASE:Inherit(self,OPSGROUP:New(group))
-self.lid=string.format("ARMYGROUP %s | ",self.groupname)
-self:SetDefaultROE()
-self:SetDefaultAlarmstate()
-self:SetDefaultEPLRS(self.isEPLRS)
-self:SetDefaultEmission()
-self:SetDetection()
-self:SetPatrolAdInfinitum(false)
-self:SetRetreatZones()
-self:AddTransition("*","FullStop","Holding")
-self:AddTransition("*","Cruise","Cruising")
-self:AddTransition("*","RTZ","Returning")
-self:AddTransition("Holding","Returned","Returned")
-self:AddTransition("Returning","Returned","Returned")
-self:AddTransition("*","Detour","OnDetour")
-self:AddTransition("OnDetour","DetourReached","Cruising")
-self:AddTransition("*","Retreat","Retreating")
-self:AddTransition("Retreating","Retreated","Retreated")
-self:AddTransition("*","Suppressed","*")
-self:AddTransition("*","Unsuppressed","*")
-self:AddTransition("Cruising","EngageTarget","Engaging")
-self:AddTransition("Holding","EngageTarget","Engaging")
-self:AddTransition("OnDetour","EngageTarget","Engaging")
-self:AddTransition("Engaging","Disengage","Cruising")
-self:AddTransition("*","Rearm","Rearm")
-self:AddTransition("Rearm","Rearming","Rearming")
-self:AddTransition("*","Rearmed","Cruising")
-self:_InitWaypoints()
-self:_InitGroup()
-self:HandleEvent(EVENTS.Birth,self.OnEventBirth)
-self:HandleEvent(EVENTS.Dead,self.OnEventDead)
-self:HandleEvent(EVENTS.RemoveUnit,self.OnEventRemoveUnit)
-self:HandleEvent(EVENTS.Hit,self.OnEventHit)
-self.timerStatus=TIMER:New(self.Status,self):Start(1,30)
-self.timerQueueUpdate=TIMER:New(self._QueueUpdate,self):Start(2,5)
-self.timerCheckZone=TIMER:New(self._CheckInZones,self):Start(2,30)
-_DATABASE:AddOpsGroup(self)
-return self
-end
-function ARMYGROUP:SetPatrolAdInfinitum(switch)
-if switch==false then
-self.adinfinitum=false
-else
-self.adinfinitum=true
-end
-return self
-end
-function ARMYGROUP:GetClosestRoad()
-local coord=self:GetCoordinate():GetClosestPointToRoad()
-return coord
-end
-function ARMYGROUP:GetClosestRoadDist()
-local road=self:GetClosestRoad()
-if road then
-local dist=road:Get2DDistance(self:GetCoordinate())
-return dist
-end
-return math.huge
-end
-function ARMYGROUP:AddTaskFireAtPoint(Coordinate,Clock,Radius,Nshots,WeaponType,Prio)
-Coordinate=self:_CoordinateFromObject(Coordinate)
-local DCStask=CONTROLLABLE.TaskFireAtPoint(nil,Coordinate:GetVec2(),Radius,Nshots,WeaponType)
-local task=self:AddTask(DCStask,Clock,nil,Prio)
-return task
-end
-function ARMYGROUP:AddTaskBarrage(Clock,Heading,Alpha,Altitude,Radius,Nshots,WeaponType,Prio)
-Heading=Heading or 0
-Alpha=Alpha or 60
-Altitude=Altitude or 100
-local distance=Altitude/math.tan(math.rad(Alpha))
-local a=self:GetVec2()
-local vec2=UTILS.Vec2Translate(a,distance,Heading)
-local DCStask=CONTROLLABLE.TaskFireAtPoint(nil,vec2,Radius,Nshots,WeaponType,Altitude)
-local task=self:AddTask(DCStask,Clock,nil,Prio)
-return task
-end
-function ARMYGROUP:AddTaskWaypointFireAtPoint(Coordinate,Waypoint,Radius,Nshots,WeaponType,Prio)
-Coordinate=self:_CoordinateFromObject(Coordinate)
-Waypoint=Waypoint or self:GetWaypointNext()
-local DCStask=CONTROLLABLE.TaskFireAtPoint(nil,Coordinate:GetVec2(),Radius,Nshots,WeaponType)
-local task=self:AddTaskWaypoint(DCStask,Waypoint,nil,Prio)
-return task
-end
-function ARMYGROUP:AddTaskAttackGroup(TargetGroup,WeaponExpend,WeaponType,Clock,Prio)
-local DCStask=CONTROLLABLE.TaskAttackGroup(nil,TargetGroup,WeaponType,WeaponExpend,AttackQty,Direction,Altitude,AttackQtyLimit,GroupAttack)
-local task=self:AddTask(DCStask,Clock,nil,Prio)
-return task
-end
-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
-function ARMYGROUP:SetRetreatZones(RetreatZoneSet)
-self.retreatZones=RetreatZoneSet or SET_ZONE:New()
-return self
-end
-function ARMYGROUP:AddRetreatZone(RetreatZone)
-self.retreatZones:AddZone(RetreatZone)
-return self
-end
-function ARMYGROUP:SetSuppressionOn(Tave,Tmin,Tmax)
-self.suppressionOn=true
-self.TsuppressMin=Tmin or 1
-self.TsuppressMin=math.max(self.TsuppressMin,1)
-self.TsuppressMax=Tmax or 15
-self.TsuppressMax=math.max(self.TsuppressMax,self.TsuppressMin)
-self.TsuppressAve=Tave or 10
-self.TsuppressAve=math.max(self.TsuppressMin)
-self.TsuppressAve=math.min(self.TsuppressMax)
-self:T(self.lid..string.format("Set ave suppression time to %d seconds.",self.TsuppressAve))
-self:T(self.lid..string.format("Set min suppression time to %d seconds.",self.TsuppressMin))
-self:T(self.lid..string.format("Set max suppression time to %d seconds.",self.TsuppressMax))
-return self
-end
-function ARMYGROUP:SetSuppressionOff()
-self.suppressionOn=false
-end
-function ARMYGROUP:IsHolding()
-return self:Is("Holding")
-end
-function ARMYGROUP:IsCruising()
-return self:Is("Cruising")
-end
-function ARMYGROUP:IsOnDetour()
-return self:Is("OnDetour")
-end
-function ARMYGROUP:IsCombatReady()
-local combatready=true
-if self:IsRearming()or self:IsRetreating()or self:IsOutOfAmmo()or self:IsEngaging()or self:IsDead()or self:IsStopped()or self:IsInUtero()then
-combatready=false
-end
-if self:IsPickingup()or self:IsLoading()or self:IsTransporting()or self:IsLoaded()or self:IsCargo()or self:IsCarrier()then
-combatready=false
-end
-return combatready
-end
-function ARMYGROUP:Status()
-local fsmstate=self:GetState()
-local alive=self:IsAlive()
-if alive then
-self:_UpdatePosition()
-self:_CheckDetectedUnits()
-self:_CheckAmmoStatus()
-self:_CheckDamage()
-self:_CheckStuck()
-if self:IsEngaging()then
-self:_UpdateEngageTarget()
-end
-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
-if self:_CountPausedMissions()>0 then
-self:UnpauseMission()
-else
-self:Cruise()
-end
-end
-end
-end
-local mission=self:GetMissionCurrent()
-if mission and mission.updateDCSTask then
-if mission.type==AUFTRAG.Type.CAPTUREZONE then
-local Task=mission:GetGroupWaypointTask(self)
-if mission:GetGroupStatus(self)==AUFTRAG.GroupStatus.EXECUTING or mission:GetGroupStatus(self)==AUFTRAG.GroupStatus.STARTED then
-self:_UpdateTask(Task,mission)
-end
-end
-end
-else
-self:_CheckDamage()
-end
-if alive~=nil then
-if self.verbose>=1 then
-local nelem=self:CountElements()
-local Nelem=#self.elements
-local nTaskTot,nTaskSched,nTaskWP=self:CountRemainingTasks()
-local nMissions=self:CountRemainingMissison()
-local roe=self:GetROE()or-1
-local als=self:GetAlarmstate()or-1
-local wpidxCurr=self.currentwp
-local wpuidCurr=self:GetWaypointUIDFromIndex(wpidxCurr)or 0
-local wpidxNext=self:GetWaypointIndexNext()or 0
-local wpuidNext=self:GetWaypointUIDFromIndex(wpidxNext)or 0
-local wpN=#self.waypoints or 0
-local wpF=tostring(self.passedfinalwp)
-local speed=UTILS.MpsToKnots(self.velocity or 0)
-local speedEx=UTILS.MpsToKnots(self:GetExpectedSpeed())
-local alt=self.position and self.position.y or 0
-local hdg=self.heading or 0
-local formation=self.option.Formation or"unknown"
-local life=self.life or 0
-local ammo=self:GetAmmoTot().Total
-local ndetected=self.detectionOn and tostring(self.detectedunits:Count())or"Off"
-local cargo=0
-for _,_element in pairs(self.elements)do
-local element=_element
-cargo=cargo+element.weightCargo
-end
-local text=string.format("%s [%d/%d]: ROE/AS=%d/%d | T/M=%d/%d | Wp=%d[%d]-->%d[%d]/%d [%s] | Life=%.1f | v=%.1f (%d) [%s] | Hdg=%03d | Ammo=%d | Detect=%s | Cargo=%.1f",
-fsmstate,nelem,Nelem,roe,als,nTaskTot,nMissions,wpidxCurr,wpuidCurr,wpidxNext,wpuidNext,wpN,wpF,life,speed,speedEx,formation,hdg,ammo,ndetected,cargo)
-self:I(self.lid..text)
-end
-else
-if self.verbose>=1 then
-local text=string.format("State %s: Alive=%s",fsmstate,tostring(self:IsAlive()))
-self:I(self.lid..text)
-end
-end
-if self.verbose>=2 then
-local text="Elements:"
-for i,_element in pairs(self.elements)do
-local element=_element
-local name=element.name
-local status=element.status
-local unit=element.unit
-local life,life0=self:GetLifePoints(element)
-local life0=element.life0
-local ammo=self:GetAmmoElement(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:T(self.lid..text)
-end
-if self:IsCruising()and self.detectionOn and self.engagedetectedOn then
-local targetgroup,targetdist=self:_GetDetectedTarget()
-if targetgroup then
-self:T(self.lid..string.format("Engaging target group %s at distance %d meters",targetgroup:GetName(),targetdist))
-self:EngageTarget(targetgroup)
-end
-end
-self:_CheckCargoTransport()
-self:_PrintTaskAndMissionStatus()
-end
-function ARMYGROUP:onafterElementSpawned(From,Event,To,Element)
-self:T(self.lid..string.format("Element spawned %s",Element.name))
-self:_UpdateStatus(Element,OPSGROUP.ElementStatus.SPAWNED)
-end
-function ARMYGROUP:onafterSpawned(From,Event,To)
-self:T(self.lid..string.format("Group spawned!"))
-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("Has EPLRS = %s\n",tostring(self.isEPLRS))
-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
-self:_UpdatePosition()
-self.isDead=false
-self.isDestroyed=false
-if self.isAI then
-self:SwitchROE(self.option.ROE)
-self:SwitchAlarmstate(self.option.Alarm)
-self:SwitchEmission(self.option.Emission)
-self:SwitchEPLRS(self.option.EPLRS)
-self:SwitchInvisible(self.option.Invisible)
-self:SwitchImmortal(self.option.Immortal)
-self:_SwitchTACAN()
-if self.radioDefault then
-self:SwitchRadio(self.radioDefault.Freq,self.radioDefault.Modu)
-else
-self:SetDefaultRadio(self.radio.Freq,self.radio.Modu,true)
-end
-if not self.option.Formation then
-end
-local Nwp=#self.waypoints
-if Nwp>1 and self.isMobile then
-self:T(self.lid..string.format("Got %d waypoints on spawn ==> Cruise in -1.0 sec!",Nwp))
-self:__Cruise(-1,nil,self.option.Formation)
-else
-self:T(self.lid.."No waypoints on spawn ==> Full Stop!")
-self:FullStop()
-end
-end
-end
-function ARMYGROUP:onbeforeUpdateRoute(From,Event,To,n,N,Speed,Formation)
-local allowed=true
-local trepeat=nil
-if self:IsWaiting()then
-self:T(self.lid.."Update route denied. Group is WAITING!")
-return false
-elseif self:IsInUtero()then
-self:T(self.lid.."Update route denied. Group is INUTERO!")
-return false
-elseif self:IsDead()then
-self:T(self.lid.."Update route denied. Group is DEAD!")
-return false
-elseif self:IsStopped()then
-self:T(self.lid.."Update route denied. Group is STOPPED!")
-return false
-elseif self:IsHolding()then
-self:T(self.lid.."Update route denied. Group is holding position!")
-return false
-elseif self:IsEngaging()then
-self:T(self.lid.."Update route allowed. Group is engaging!")
-return true
-end
-if self.taskcurrent>0 then
-local task=self:GetTaskByID(self.taskcurrent)
-if task then
-if task.dcstask.id==AUFTRAG.SpecialTask.PATROLZONE then
-self:T2(self.lid.."Allowing update route for Task: PatrolZone")
-elseif task.dcstask.id==AUFTRAG.SpecialTask.RECON then
-self:T2(self.lid.."Allowing update route for Task: ReconMission")
-elseif task.dcstask.id==AUFTRAG.SpecialTask.RELOCATECOHORT then
-self:T2(self.lid.."Allowing update route for Task: Relocate Cohort")
-elseif task.dcstask.id==AUFTRAG.SpecialTask.REARMING then
-self:T2(self.lid.."Allowing update route for Task: Rearming")
-else
-local taskname=task and task.description or"No description"
-self:T(self.lid..string.format("WARNING: Update route denied because taskcurrent=%d>0! Task description = %s",self.taskcurrent,tostring(taskname)))
-allowed=false
-end
-else
-self:T(self.lid..string.format("WARNING: before update route taskcurrent=%d (>0!) but no task?!",self.taskcurrent))
-allowed=false
-end
-end
-if not self.isAI then
-allowed=false
-end
-self:T2(self.lid..string.format("Onbefore Updateroute in state %s: allowed=%s (repeat in %s)",self:GetState(),tostring(allowed),tostring(trepeat)))
-if trepeat then
-self:__UpdateRoute(trepeat,n)
-end
-return allowed
-end
-function ARMYGROUP:onafterUpdateRoute(From,Event,To,n,N,Speed,Formation)
-n=n or self:GetWaypointIndexNext(self.adinfinitum)
-N=N or#self.waypoints
-N=math.min(N,#self.waypoints)
-local text=string.format("Update route state=%s: n=%s, N=%s, Speed=%s, Formation=%s",self:GetState(),tostring(n),tostring(N),tostring(Speed),tostring(Formation))
-self:T(self.lid..text)
-local waypoints={}
-local wp=self.waypoints[n]
-local coordinate=self:GetCoordinate()
-local coordRoad=coordinate:GetClosestPointToRoad()
-local roaddist=coordinate:Get2DDistance(coordRoad)
-local formation0=wp.action
-if formation0==ENUMS.Formation.Vehicle.OnRoad then
-if roaddist>10 then
-formation0=ENUMS.Formation.Vehicle.OffRoad
-else
-formation0=ENUMS.Formation.Vehicle.OnRoad
-end
-end
-local current=coordinate:WaypointGround(UTILS.MpsToKmph(self.speedWp),formation0)
-table.insert(waypoints,1,current)
-if N-n>0 then
-for j=n,N do
-local i=j-1
-if i==0 then
-i=self.currentwp
-end
-local wp=UTILS.DeepCopy(self.waypoints[j])
-local wp0=self.waypoints[i]
-if false and self.attribute==GROUP.Attribute.GROUND_APC then
-local text=string.format("FF Update: i=%d, wp[i]=%s, wp[i-1]=%s",i,wp.action,wp0.action)
-env.info(text)
-end
-if Speed then
-wp.speed=UTILS.KnotsToMps(tonumber(Speed))
-else
-if wp.speed<0.1 then
-wp.speed=UTILS.KmphToMps(self.speedCruise)
-end
-end
-if self.formationPerma then
-wp.action=self.formationPerma
-elseif Formation then
-wp.action=Formation
-end
-if wp.action==ENUMS.Formation.Vehicle.OnRoad and wp0.roaddist>=0 then
-local wproad=wp0.roadcoord:WaypointGround(UTILS.MpsToKmph(wp.speed),ENUMS.Formation.Vehicle.OnRoad)
-table.insert(waypoints,wproad)
-end
-if wp.action==ENUMS.Formation.Vehicle.OnRoad and wp.roaddist>=0 then
-wp.action=ENUMS.Formation.Vehicle.OffRoad
-local wproad=wp.roadcoord:WaypointGround(UTILS.MpsToKmph(wp.speed),ENUMS.Formation.Vehicle.OnRoad)
-table.insert(waypoints,wproad)
-end
-table.insert(waypoints,wp)
-end
-else
-local wp=UTILS.DeepCopy(self.waypoints[n])
-if wp.speed<0.1 then
-wp.speed=UTILS.KmphToMps(self.speedCruise)
-end
-local formation=wp.action
-if self.formationPerma then
-formation=self.formationPerma
-elseif Formation then
-formation=Formation
-end
-if formation==ENUMS.Formation.Vehicle.OnRoad then
-if roaddist>10 then
-local wproad=coordRoad:WaypointGround(UTILS.MpsToKmph(wp.speed),ENUMS.Formation.Vehicle.OnRoad)
-table.insert(waypoints,wproad)
-end
-if wp.roaddist>10 then
-local wproad=wp.roadcoord:WaypointGround(UTILS.MpsToKmph(wp.speed),ENUMS.Formation.Vehicle.OnRoad)
-table.insert(waypoints,wproad)
-end
-end
-if wp.action==ENUMS.Formation.Vehicle.OnRoad and wp.roaddist>10 then
-wp.action=ENUMS.Formation.Vehicle.OffRoad
-end
-table.insert(waypoints,wp)
-end
-local wp=waypoints[1]
-self.option.Formation=wp.action
-self.speedWp=wp.speed
-if self.verbose>=10 then
-for i,_wp in pairs(waypoints)do
-local wp=_wp
-local text=string.format("WP #%d UID=%d Formation=%s: Speed=%d m/s, Alt=%d m, Type=%s",i,wp.uid and wp.uid or-1,wp.action,wp.speed,wp.alt,wp.type)
-local coord=COORDINATE:NewFromWaypoint(wp):MarkToAll(text)
-self:I(text)
-end
-end
-if self:IsEngaging()or not self.passedfinalwp then
-self:T(self.lid..string.format("Updateing route: WP %d-->%d (%d/%d), Speed=%.1f knots, Formation=%s",
-self.currentwp,n,#waypoints,#self.waypoints,UTILS.MpsToKnots(self.speedWp),tostring(self.option.Formation)))
-self:Route(waypoints)
-else
-self:T(self.lid..string.format("WARNING: Passed final WP when UpdateRoute() ==> Full Stop!"))
-self:FullStop()
-end
-end
-function ARMYGROUP:onafterGotoWaypoint(From,Event,To,UID,Speed,Formation)
-local n=self:GetWaypointIndex(UID)
-if n then
-Speed=Speed or self:GetSpeedToWaypoint(n)
-self:__UpdateRoute(-0.01,n,nil,Speed,Formation)
-end
-end
-function ARMYGROUP:onafterDetour(From,Event,To,Coordinate,Speed,Formation,ResumeRoute)
-for _,_wp in pairs(self.waypoints)do
-local wp=_wp
-if wp.detour then
-self:RemoveWaypointByID(wp.uid)
-end
-end
-Speed=Speed or self:GetSpeedCruise()
-local uid=self:GetWaypointCurrentUID()
-local wp=self:AddWaypoint(Coordinate,Speed,uid,Formation,true)
-if ResumeRoute then
-wp.detour=1
-else
-wp.detour=0
-end
-end
-function ARMYGROUP:onafterOutOfAmmo(From,Event,To)
-self:T(self.lid..string.format("Group is out of ammo at t=%.3f",timer.getTime()))
-local task=self:GetTaskCurrent()
-if task then
-if task.dcstask.id=="FireAtPoint"or task.dcstask.id==AUFTRAG.SpecialTask.BARRAGE then
-self:T(self.lid..string.format("Cancelling current %s task because out of ammo!",task.dcstask.id))
-self:TaskCancel(task)
-end
-end
-if self.rearmOnOutOfAmmo then
-local truck,dist=self:FindNearestAmmoSupply(30)
-if truck then
-self:T(self.lid..string.format("Found Ammo Truck %s [%s]",truck:GetName(),truck:GetTypeName()))
-local Coordinate=truck:GetCoordinate()
-self:__Rearm(-1,Coordinate)
-return
-end
-end
-if self.retreatOnOutOfAmmo then
-self:T(self.lid.."Retreat on out of ammo")
-self:__Retreat(-1)
-return
-end
-if self.rtzOnOutOfAmmo and not self:IsMissionTypeInQueue(AUFTRAG.Type.REARMING)then
-self:T(self.lid.."RTZ on out of ammo")
-self:__RTZ(-1)
-end
-end
-function ARMYGROUP:onbeforeRearm(From,Event,To,Coordinate,Formation)
-local dt=nil
-local allowed=true
-if self:IsOnMission()then
-local mission=self:GetMissionCurrent()
-if mission and mission.type~=AUFTRAG.Type.REARMING then
-self:T(self.lid.."Rearm command but have current mission ==> Pausing mission!")
-self:PauseMission()
-dt=-0.1
-allowed=false
-else
-self:T(self.lid.."Rearm command and current mission is REARMING ==> Transition ALLOWED!")
-end
-end
-if self:IsEngaging()then
-self:T(self.lid.."Rearm command but currently engaging ==> Disengage!")
-self:Disengage()
-dt=-0.1
-allowed=false
-end
-if allowed and not Coordinate then
-local truck=self:FindNearestAmmoSupply()
-if truck and truck:IsAlive()then
-self:__Rearm(-0.1,truck:GetCoordinate(),Formation)
-end
-return false
-end
-if dt then
-self:T(self.lid..string.format("Trying Rearm again in %.2f sec",dt))
-self:__Rearm(dt,Coordinate,Formation)
-allowed=false
-end
-return allowed
-end
-function ARMYGROUP:onafterRearm(From,Event,To,Coordinate,Formation)
-self:T(self.lid..string.format("Group send to rearm"))
-local uid=self:GetWaypointCurrentUID()
-local wp=self:AddWaypoint(Coordinate,nil,uid,Formation,true)
-wp.detour=0
-end
-function ARMYGROUP:onafterRearmed(From,Event,To)
-self:T(self.lid.."Group rearmed")
-local mission=self:GetMissionCurrent()
-if mission and mission.type==AUFTRAG.Type.REARMING then
-self:MissionDone(mission)
-else
-self:_CheckGroupDone(1)
-end
-end
-function ARMYGROUP:onbeforeRTZ(From,Event,To,Zone,Formation)
-self:T2(self.lid.."onbeforeRTZ")
-local zone=Zone or self.homezone
-if zone then
-if(not self.isMobile)and(not self:IsInZone(zone))then
-self:Teleport(zone:GetCoordinate(),0,true)
-self:__RTZ(-1,Zone,Formation)
-return false
-end
-else
-return false
-end
-return true
-end
-function ARMYGROUP:onafterRTZ(From,Event,To,Zone,Formation)
-self:T2(self.lid.."onafterRTZ")
-local zone=Zone or self.homezone
-self:CancelAllMissions()
-if zone then
-if self:IsInZone(zone)then
-self:Returned()
-else
-self:T(self.lid..string.format("RTZ to Zone %s",zone:GetName()))
-local Coordinate=zone:GetRandomCoordinate()
-local uid=self:GetWaypointCurrentUID()
-local wp=self:AddWaypoint(Coordinate,nil,uid,Formation,true)
-wp.detour=0
-end
-else
-self:T(self.lid.."ERROR: No RTZ zone given!")
-end
-end
-function ARMYGROUP:onafterReturned(From,Event,To)
-self:T(self.lid..string.format("Group returned"))
-if self.legion then
-self:T(self.lid..string.format("Adding group back to warehouse stock"))
-self.legion:__AddAsset(10,self.group,1)
-end
-end
-function ARMYGROUP:onafterRearming(From,Event,To)
-local pos=self:GetCoordinate()
-local wp=pos:WaypointGround(0)
-self:Route({wp})
-end
-function ARMYGROUP:onbeforeRetreat(From,Event,To,Zone,Formation)
-if not Zone then
-local a=self:GetVec2()
-local distmin=math.huge
-local zonemin=nil
-for _,_zone in pairs(self.retreatZones:GetSet())do
-local zone=_zone
-local b=zone:GetVec2()
-local dist=UTILS.VecDist2D(a,b)
-if dist Pausing mission!")
-self:PauseMission()
-dt=-0.1
-allowed=false
-end
-if dt then
-self:T(self.lid..string.format("Trying Engage again in %.2f sec",dt))
-self:__EngageTarget(dt,Target)
-allowed=false
-end
-return allowed
-end
-function ARMYGROUP:onafterEngageTarget(From,Event,To,Target,Speed,Formation)
-self:T(self.lid.."Engaging Target")
-if Target:IsInstanceOf("TARGET")then
-self.engage.Target=Target
-else
-self.engage.Target=TARGET:New(Target)
-end
-self.engage.Coordinate=UTILS.DeepCopy(self.engage.Target:GetCoordinate())
-local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate,0.95)
-self.engage.roe=self:GetROE()
-self.engage.alarmstate=self:GetAlarmstate()
-self:SwitchAlarmstate(ENUMS.AlarmState.Auto)
-self:SwitchROE(ENUMS.ROE.OpenFire)
-local uid=self:GetWaypointCurrentUID()
-self.engage.Formation=Formation or ENUMS.Formation.Vehicle.Vee
-self.engage.Speed=Speed
-self.engage.Waypoint=self:AddWaypoint(intercoord,self.engage.Speed,uid,self.engage.Formation,true)
-self.engage.Waypoint.detour=1
-end
-function ARMYGROUP:_UpdateEngageTarget()
-if self.engage.Target and self.engage.Target:IsAlive()then
-local vec3=self.engage.Target:GetVec3()
-if vec3 then
-local dist=UTILS.VecDist3D(vec3,self.engage.Coordinate:GetVec3())
-local los=self:HasLoS(vec3)
-if dist>100 or los==false then
-self.engage.Coordinate:UpdateFromVec3(vec3)
-local uid=self:GetWaypointCurrentUID()
-self:RemoveWaypointByID(self.engage.Waypoint.uid)
-local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate,0.9)
-self.engage.Waypoint=self:AddWaypoint(intercoord,self.engage.Speed,uid,self.engage.Formation,true)
-self.engage.Waypoint.detour=0
-end
-else
-self:Disengage()
-end
-else
-self:Disengage()
-end
-end
-function ARMYGROUP:onafterDisengage(From,Event,To)
-self:T(self.lid.."Disengage Target")
-self:SwitchROE(self.engage.roe)
-self:SwitchAlarmstate(self.engage.alarmstate)
-local task=self:GetTaskCurrent()
-if task and task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK then
-self:T(self.lid.."Disengage with current task GROUNDATTACK ==> Task Done!")
-self:TaskDone(task)
-end
-if self.engage.Waypoint then
-self:RemoveWaypointByID(self.engage.Waypoint.uid)
-end
-self:_CheckGroupDone(1)
-end
-function ARMYGROUP:onafterDetourReached(From,Event,To)
-self:T(self.lid.."Group reached detour coordinate")
-end
-function ARMYGROUP:onafterFullStop(From,Event,To)
-self:T(self.lid..string.format("Full stop!"))
-local pos=self:GetCoordinate()
-local wp=pos:WaypointGround(0)
-self:Route({wp})
-end
-function ARMYGROUP:onafterCruise(From,Event,To,Speed,Formation)
-self.Twaiting=nil
-self.dTwait=nil
-self:T(self.lid.."Cruise ==> Update route in 0.01 sec")
-self:__UpdateRoute(-0.01,nil,nil,Speed,Formation)
-end
-function ARMYGROUP:onafterHit(From,Event,To,Enemy)
-self:T(self.lid..string.format("ArmyGroup hit by %s",Enemy and Enemy:GetName()or"unknown"))
-if self.suppressionOn then
-env.info(self.lid.."FF suppress")
-self:_Suppress()
-end
-end
-function ARMYGROUP:AddWaypoint(Coordinate,Speed,AfterWaypointWithID,Formation,Updateroute)
-self:T(self.lid..string.format("AddWaypoint Formation = %s",tostring(Formation)))
-local coordinate=self:_CoordinateFromObject(Coordinate)
-local wpnumber=self:GetWaypointIndexAfterID(AfterWaypointWithID)
-Speed=Speed or self:GetSpeedCruise()
-if not Formation then
-if self.formationPerma then
-Formation=self.formationPerma
-elseif self.optionDefault.Formation then
-Formation=self.optionDefault.Formation
-elseif self.option.Formation then
-Formation=self.option.Formation
-else
-Formation=ENUMS.Formation.Vehicle.OnRoad
-end
-self:T2(self.lid..string.format("Formation set to = %s",tostring(Formation)))
-end
-local wp=coordinate:WaypointGround(UTILS.KnotsToKmph(Speed),Formation)
-local waypoint=self:_CreateWaypoint(wp)
-self:_AddWaypoint(waypoint,wpnumber)
-waypoint.roadcoord=coordinate:GetClosestPointToRoad(false)
-if waypoint.roadcoord then
-waypoint.roaddist=coordinate:Get2DDistance(waypoint.roadcoord)
-else
-waypoint.roaddist=1000*1000
-end
-self:T(self.lid..string.format("Adding waypoint UID=%d (index=%d), Speed=%.1f knots, Dist2Road=%d m, Action=%s",waypoint.uid,wpnumber,Speed,waypoint.roaddist,waypoint.action))
-if Updateroute==nil or Updateroute==true then
-self:__UpdateRoute(-0.01)
-end
-return waypoint
-end
-function ARMYGROUP:_InitGroup(Template)
-if self.groupinitialized then
-self:T(self.lid.."WARNING: Group was already initialized! Will NOT do it again!")
-return
-end
-local template=Template or self:_GetTemplate()
-self.isAI=true
-self.isLateActivated=template.lateActivation
-self.isUncontrolled=false
-self.speedMax=self.group:GetSpeedMax()
-if self.speedMax>3.6 then
-self.isMobile=true
-else
-self.isMobile=false
-end
-self.speedCruise=self.speedMax*0.7
-self.ammo=self:GetAmmoTot()
-self.radio.On=false
-self.radio.Freq=133
-self.radio.Modu=radio.modulation.AM
-self:SetDefaultRadio(self.radio.Freq,self.radio.Modu,self.radio.On)
-self.option.Formation=template.route.points[1].action
-self.optionDefault.Formation=ENUMS.Formation.Vehicle.OnRoad
-self:SetDefaultTACAN(nil,nil,nil,nil,true)
-self.tacan=UTILS.DeepCopy(self.tacanDefault)
-local units=self.group:GetUnits()
-local dcsgroup=Group.getByName(self.groupname)
-local size0=dcsgroup:getInitialSize()
-if#units~=size0 then
-self:T(self.lid..string.format("ERROR: Got #units=%d but group consists of %d units!",#units,size0))
-end
-for _,unit in pairs(units)do
-local unitname=unit:GetName()
-self:_AddElementByName(unitname)
-end
-self.groupinitialized=true
-return self
-end
-function ARMYGROUP:SwitchFormation(Formation,Permanently,NoRouteUpdate)
-if self:IsAlive()or self:IsInUtero()then
-Formation=Formation or(self.optionDefault.Formation or"Off road")
-Permanently=Permanently or false
-if Permanently then
-self.formationPerma=Formation
-else
-self.formationPerma=nil
-end
-self.option.Formation=Formation or"Off road"
-if self:IsInUtero()then
-self:T(self.lid..string.format("Will switch formation to %s (permanently=%s) when group is spawned",tostring(self.option.Formation),tostring(Permanently)))
-else
-if NoRouteUpdate then
-else
-self:__UpdateRoute(-1,nil,nil,Formation)
-end
-self:T(self.lid..string.format("Switching formation to %s (permanently=%s)",tostring(self.option.Formation),tostring(Permanently)))
-end
-end
-return self
-end
-function ARMYGROUP:FindNearestAmmoSupply(Radius)
-Radius=UTILS.NMToMeters(Radius or 30)
-local coord=self:GetCoordinate()
-local myCoalition=self:GetCoalition()
-local units=coord:ScanUnits(Radius)
-local dmin=math.huge
-local truck=nil
-for _,_unit in pairs(units.Set)do
-local unit=_unit
-if unit:IsAlive()and unit:GetCoalition()==myCoalition and unit:IsAmmoSupply()and unit:GetVelocityKMH()<1 then
-local d=coord:Get2DDistance(unit:GetCoord())
-if dself.TsuppressionOver then
-self.TsuppressionOver=Tnow+Tsuppress
-else
-renew=false
-end
-end
-if renew then
-self:__Unsuppressed(self.TsuppressionOver-Tnow)
-end
-self:T(self.lid..string.format("Suppressed for %d sec",Tsuppress))
-end
-function ARMYGROUP:onbeforeUnsuppressed(From,Event,To)
-local Tnow=timer.getTime()
-self:T(self.lid..string.format("onbeforeRecovered: Time now: %d - Time over: %d",Tnow,self.TsuppressionOver))
-if Tnow>=self.TsuppressionOver then
-return true
-else
-return false
-end
-end
-function ARMYGROUP:onafterUnsuppressed(From,Event,To)
-local text=string.format("Group %s has recovered!",self:GetName())
-MESSAGE:New(text,10):ToAll()
-self:T(self.lid..text)
-self:SwitchROE(self.suppressionROE)
-if true then
-self.group:FlareGreen()
-end
-end
-ATIS={
-ClassName="ATIS",
-lid=nil,
-theatre=nil,
-airbasename=nil,
-airbase=nil,
-frequency=nil,
-modulation=nil,
-power=nil,
-radioqueue=nil,
-soundpath=nil,
-relayunitname=nil,
-towerfrequency=nil,
-activerunway=nil,
-subduration=nil,
-metric=nil,
-PmmHg=nil,
-qnhonly=false,
-TDegF=nil,
-zuludiff=nil,
-zulutimeonly=false,
-magvar=nil,
-ils={},
-ndbinner={},
-ndbouter={},
-vor=nil,
-tacan=nil,
-rsbn=nil,
-prmg={},
-rwylength=nil,
-elevation=nil,
-runwaymag={},
-runwaym2t=nil,
-windtrue=nil,
-altimeterQNH=nil,
-usemarker=nil,
-markerid=nil,
-relHumidity=nil,
-ReportmBar=false,
-TransmitOnlyWithPlayers=false,
-ATISforFARPs=false,
-locale="en",
-}
-ATIS.Alphabet={
-[1]="Alfa",
-[2]="Bravo",
-[3]="Charlie",
-[4]="Delta",
-[5]="Echo",
-[6]="Delta",
-[7]="Echo",
-[8]="Foxtrot",
-[9]="Golf",
-[10]="Hotel",
-[11]="India",
-[12]="Juliett",
-[13]="Kilo",
-[14]="Lima",
-[15]="Mike",
-[16]="November",
-[17]="Oscar",
-[18]="Papa",
-[19]="Quebec",
-[20]="Romeo",
-[21]="Sierra",
-[22]="Tango",
-[23]="Uniform",
-[24]="Victor",
-[25]="Whiskey",
-[26]="Xray",
-[27]="Yankee",
-[28]="Zulu",
-}
-ATIS.RunwayM2T={
-Caucasus=0,
-Nevada=12,
-Normandy=-10,
-PersianGulf=2,
-TheChannel=-10,
-Syria=5,
-MarianaIslands=2,
-Falklands=12,
-SinaiMap=5,
-}
-ATIS.ICAOPhraseology={
-Caucasus=true,
-Nevada=false,
-Normandy=true,
-PersianGulf=true,
-TheChannel=true,
-Syria=true,
-MarianaIslands=true,
-Falklands=true,
-SinaiMap=true,
-}
-ATIS.Sound={
-ActiveRunway={filename="ActiveRunway.ogg",duration=0.99},
-AdviceOnInitial={filename="AdviceOnInitial.ogg",duration=3.00},
-Airport={filename="Airport.ogg",duration=0.66},
-Altimeter={filename="Altimeter.ogg",duration=0.68},
-At={filename="At.ogg",duration=0.41},
-CloudBase={filename="CloudBase.ogg",duration=0.82},
-CloudCeiling={filename="CloudCeiling.ogg",duration=0.61},
-CloudsBroken={filename="CloudsBroken.ogg",duration=1.07},
-CloudsFew={filename="CloudsFew.ogg",duration=0.99},
-CloudsNo={filename="CloudsNo.ogg",duration=1.01},
-CloudsNotAvailable={filename="CloudsNotAvailable.ogg",duration=2.35},
-CloudsOvercast={filename="CloudsOvercast.ogg",duration=0.83},
-CloudsScattered={filename="CloudsScattered.ogg",duration=1.18},
-Decimal={filename="Decimal.ogg",duration=0.54},
-DegreesCelsius={filename="DegreesCelsius.ogg",duration=1.27},
-DegreesFahrenheit={filename="DegreesFahrenheit.ogg",duration=1.23},
-DewPoint={filename="DewPoint.ogg",duration=0.65},
-Dust={filename="Dust.ogg",duration=0.54},
-Elevation={filename="Elevation.ogg",duration=0.78},
-EndOfInformation={filename="EndOfInformation.ogg",duration=1.15},
-Feet={filename="Feet.ogg",duration=0.45},
-Fog={filename="Fog.ogg",duration=0.47},
-Gusting={filename="Gusting.ogg",duration=0.55},
-HectoPascal={filename="HectoPascal.ogg",duration=1.15},
-Hundred={filename="Hundred.ogg",duration=0.47},
-InchesOfMercury={filename="InchesOfMercury.ogg",duration=1.16},
-Information={filename="Information.ogg",duration=0.85},
-Kilometers={filename="Kilometers.ogg",duration=0.78},
-Knots={filename="Knots.ogg",duration=0.59},
-Left={filename="Left.ogg",duration=0.54},
-MegaHertz={filename="MegaHertz.ogg",duration=0.87},
-Meters={filename="Meters.ogg",duration=0.59},
-MetersPerSecond={filename="MetersPerSecond.ogg",duration=1.14},
-Miles={filename="Miles.ogg",duration=0.60},
-MillimetersOfMercury={filename="MillimetersOfMercury.ogg",duration=1.53},
-Minus={filename="Minus.ogg",duration=0.64},
-N0={filename="N-0.ogg",duration=0.55},
-N1={filename="N-1.ogg",duration=0.41},
-N2={filename="N-2.ogg",duration=0.37},
-N3={filename="N-3.ogg",duration=0.41},
-N4={filename="N-4.ogg",duration=0.37},
-N5={filename="N-5.ogg",duration=0.43},
-N6={filename="N-6.ogg",duration=0.55},
-N7={filename="N-7.ogg",duration=0.43},
-N8={filename="N-8.ogg",duration=0.38},
-N9={filename="N-9.ogg",duration=0.55},
-NauticalMiles={filename="NauticalMiles.ogg",duration=1.04},
-None={filename="None.ogg",duration=0.43},
-QFE={filename="QFE.ogg",duration=0.63},
-QNH={filename="QNH.ogg",duration=0.71},
-Rain={filename="Rain.ogg",duration=0.41},
-Right={filename="Right.ogg",duration=0.44},
-Snow={filename="Snow.ogg",duration=0.48},
-SnowStorm={filename="SnowStorm.ogg",duration=0.82},
-StatuteMiles={filename="StatuteMiles.ogg",duration=1.15},
-SunriseAt={filename="SunriseAt.ogg",duration=0.92},
-SunsetAt={filename="SunsetAt.ogg",duration=0.95},
-Temperature={filename="Temperature.ogg",duration=0.64},
-Thousand={filename="Thousand.ogg",duration=0.55},
-ThunderStorm={filename="ThunderStorm.ogg",duration=0.81},
-TimeLocal={filename="TimeLocal.ogg",duration=0.90},
-TimeZulu={filename="TimeZulu.ogg",duration=0.86},
-TowerFrequency={filename="TowerFrequency.ogg",duration=1.19},
-Visibilty={filename="Visibility.ogg",duration=0.79},
-WeatherPhenomena={filename="WeatherPhenomena.ogg",duration=1.07},
-WindFrom={filename="WindFrom.ogg",duration=0.60},
-ILSFrequency={filename="ILSFrequency.ogg",duration=1.30},
-InnerNDBFrequency={filename="InnerNDBFrequency.ogg",duration=1.56},
-OuterNDBFrequency={filename="OuterNDBFrequency.ogg",duration=1.59},
-RunwayLength={filename="RunwayLength.ogg",duration=0.91},
-VORFrequency={filename="VORFrequency.ogg",duration=1.38},
-TACANChannel={filename="TACANChannel.ogg",duration=0.88},
-PRMGChannel={filename="PRMGChannel.ogg",duration=1.18},
-RSBNChannel={filename="RSBNChannel.ogg",duration=1.14},
-Zulu={filename="Zulu.ogg",duration=0.62},
-}
-ATIS.Messages={
-EN=
-{
-HOURS="hours",
-TIME="hours",
-NOCLOUDINFO="Cloud coverage information not available",
-OVERCAST="Overcast",
-BROKEN="Broken clouds",
-SCATTERED="Scattered clouds",
-FEWCLOUDS="Few clouds",
-NOCLOUDS="No clouds",
-AIRPORT="Airport",
-INFORMATION="Information",
-SUNRISEAT="Sunrise at %s local time",
-SUNSETAT="Sunset at %s local time",
-WINDFROMMS="Wind from %s at %s m/s",
-WINDFROMKNOTS="Wind from %s at %s knots",
-GUSTING="gusting",
-VISIKM="Visibility %s km",
-VISISM="Visibility %s SM",
-RAIN="rain",
-TSTORM="thunderstorm",
-SNOW="snow",
-SSTROM="snowstorm",
-FOG="fog",
-DUST="dust",
-PHENOMENA="Weather phenomena",
-CLOUDBASEM="Cloud base %s, ceiling %s meters",
-CLOUDBASEFT="Cloud base %s, ceiling %s feet",
-TEMPERATURE="Temperature",
-DEWPOINT="Dew point",
-ALTIMETER="Altimeter",
-ACTIVERUN="Active runway departure",
-ACTIVELANDING="Active runway arrival",
-LEFT="Left",
-RIGHT="Right",
-RWYLENGTH="Runway length",
-METERS="meters",
-FEET="feet",
-ELEVATION="Elevation",
-TOWERFREQ="Tower frequency",
-ILSFREQ="ILS frequency",
-OUTERNDB="Outer NDB frequency",
-INNERNDB="Inner NDB frequency",
-VORFREQ="VOR frequency",
-VORFREQTTS="V O R frequency",
-TACANCH="TACAN channel %dX Ray",
-RSBNCH="RSBN channel",
-PRMGCH="PRMG channel",
-ADVISE="Advise on initial contact, you have information",
-STATUTE="statute miles",
-DEGREES="degrees Celsius",
-FAHRENHEIT="degrees Fahrenheit",
-INCHHG="inches of Mercury",
-MMHG="millimeters of Mercury",
-HECTO="hectopascals",
-METERSPER="meters per second",
-TACAN="tackan",
-FARP="farp",
-DELIMITER="point",
-},
-DE=
-{
-HOURS="Uhr",
-TIME="Zeit",
-NOCLOUDINFO="Informationen über Wolken nicht verfuegbar",
-OVERCAST="Geschlossene Wolkendecke",
-BROKEN="Stark bewoelkt",
-SCATTERED="Bewoelkt",
-FEWCLOUDS="Leicht bewoelkt",
-NOCLOUDS="Klar",
-AIRPORT="Flughafen",
-INFORMATION="Information",
-SUNRISEAT="Sonnenaufgang um %s lokaler Zeit",
-SUNSETAT="Sonnenuntergang um %s lokaler Zeit",
-WINDFROMMS="Wind aus %s mit %s m/s",
-WINDFROMKNOTS="Wind aus %s mit %s Knoten",
-GUSTING="boeig",
-VISIKM="Sichtweite %s km",
-VISISM="Sichtweite %s Meilen",
-RAIN="Regen",
-TSTORM="Gewitter",
-SNOW="Schnee",
-SSTROM="Schneesturm",
-FOG="Nebel",
-DUST="Staub",
-PHENOMENA="Wetter Phaenomene",
-CLOUDBASEM="Wolkendecke von %s bis %s Meter",
-CLOUDBASEFT="Wolkendecke von %s bis %s Fuß",
-TEMPERATURE="Temperatur",
-DEWPOINT="Taupunkt",
-ALTIMETER="Hoehenmesser",
-ACTIVERUN="Aktive Startbahn",
-ACTIVELANDING="Aktive Landebahn",
-LEFT="Links",
-RIGHT="Rechts",
-RWYLENGTH="Startbahn",
-METERS="Meter",
-FEET="Fuß",
-ELEVATION="Hoehe",
-TOWERFREQ="Kontrollturm Frequenz",
-ILSFREQ="ILS Frequenz",
-OUTERNDB="Aeussere NDB Frequenz",
-INNERNDB="Innere NDB Frequenz",
-VORFREQ="VOR Frequenz",
-VORFREQTTS="V O R Frequenz",
-TACANCH="TACAN Kanal %d Xaver",
-RSBNCH="RSBN Kanal",
-PRMGCH="PRMG Kanal",
-ADVISE="Hinweis bei Erstkontakt, Sie haben Informationen",
-STATUTE="englische Meilen",
-DEGREES="Grad Celsius",
-FAHRENHEIT="Grad Fahrenheit",
-INCHHG="Inches H G",
-MMHG="Millimeter H G",
-HECTO="Hektopascal",
-METERSPER="Meter pro Sekunde",
-TACAN="Tackan",
-FARP="Farp",
-DELIMITER="Komma",
-},
-ES=
-{
-HOURS="horas",
-TIME="horas",
-NOCLOUDINFO="Información sobre capa de nubes no disponible",
-OVERCAST="Nublado",
-BROKEN="Nubes rotas",
-SCATTERED="Nubes dispersas",
-FEWCLOUDS="Ligeramente nublado",
-NOCLOUDS="Despejado",
-AIRPORT="Aeropuerto",
-INFORMATION="Informacion",
-SUNRISEAT="Amanecer a las %s hora local",
-SUNSETAT="Puesta de sol a las %s hora local",
-WINDFROMMS="Viento procedente de %s con %s m/s",
-WINDFROMKNOTS="Viento de %s con %s nudos",
-GUSTING="ráfagas",
-VISIKM="Visibilidad %s km",
-VISISM="Visibilidad %s millas",
-RAIN="Lluvia",
-TSTORM="Tormenta",
-SNOW="Nieve",
-SSTROM="Tormenta de nieve",
-FOG="Niebla",
-DUST="Polvo",
-PHENOMENA="Fenómenos meteorológicos",
-CLOUDBASEM="Capa de nubes de %s a %s metros",
-CLOUDBASEFT="Capa de nubes de %s a %s pies",
-TEMPERATURE="Temperatura",
-DEWPOINT="Punto de rocio",
-ALTIMETER="Altímetro",
-ACTIVERUN="Pista activa",
-ACTIVELANDING="Pista de aterrizaje activa",
-LEFT="Izquierda",
-RIGHT="Derecha",
-RWYLENGTH="Longitud de pista",
-METERS="Metro",
-FEET="Pie",
-ELEVATION="Elevación",
-TOWERFREQ="Frecuencias de la torre de control",
-ILSFREQ="Fecuencia ILS",
-OUTERNDB="Frecuencia NDB externa",
-INNERNDB="Frecuencia NDB interior",
-VORFREQ="Frecuencia VOR",
-VORFREQTTS="Frecuencia V O R",
-TACANCH="Canal TACAN %d Xaver",
-RSBNCH="Canal RSBN",
-PRMGCH="Canal PRMG",
-ADVISE="Avise en el contacto inicial a torre de que tiene la informacion",
-STATUTE="Millas inglesas",
-DEGREES="Grados Celsius",
-FAHRENHEIT="Grados Fahrenheit",
-INCHHG="Pulgadas de mercurio",
-MMHG="Milímeteros de Mercurio",
-HECTO="Hectopascales",
-METERSPER="Metros por segundo",
-TACAN="Tacan",
-FARP="Farp",
-DELIMITER="Punto",
-},
-}
-ATIS.locale="en"
-_ATIS={}
-ATIS.version="0.10.4"
-function ATIS:New(AirbaseName,Frequency,Modulation)
-local self=BASE:Inherit(self,FSM:New())
-self.airbasename=AirbaseName
-self.airbase=AIRBASE:FindByName(AirbaseName)
-if self.airbase==nil then
-self:E("ERROR: Airbase %s for ATIS could not be found!",tostring(AirbaseName))
-return nil
-end
-self.frequency=Frequency or 143.00
-self.modulation=Modulation or 0
-self.theatre=env.mission.theatre
-self.lid=string.format("ATIS %s | ",self.airbasename)
-_ATIS[#_ATIS+1]=self
-self:SetSoundfilesPath()
-self:SetSubtitleDuration()
-self:SetMagneticDeclination()
-self:SetRunwayCorrectionMagnetic2True()
-self:SetRadioPower()
-self:SetAltimeterQNH(true)
-self:SetMapMarks(false)
-self:SetRelativeHumidity()
-self:SetQueueUpdateTime()
-self:SetReportmBar(false)
-self:_InitLocalization()
-self:SetStartState("Stopped")
-self:AddTransition("Stopped","Start","Running")
-self:AddTransition("*","Status","*")
-self:AddTransition("*","Broadcast","*")
-self:AddTransition("*","CheckQueue","*")
-self:AddTransition("*","Report","*")
-self:AddTransition("*","Stop","Stopped")
-return self
-end
-function ATIS:_InitLocalization()
-self:T(self.lid.."_InitLocalization")
-self.gettext=TEXTANDSOUND:New("AWACS","en")
-self.locale="en"
-for locale,table in pairs(self.Messages)do
-local Locale=string.lower(tostring(locale))
-self:T("**** Adding locale: "..Locale)
-for ID,Text in pairs(table)do
-self:T(string.format('Adding ID %s',tostring(ID)))
-self.gettext:AddEntry(Locale,tostring(ID),Text)
-end
-end
-return self
-end
-function ATIS:SetLocale(locale)
-self.locale=string.lower(locale)
-return self
-end
-function ATIS:SetSoundfilesPath(path)
-self.soundpath=tostring(path or"ATIS Soundfiles/")
-self:T(self.lid..string.format("Setting sound files path to %s",self.soundpath))
-return self
-end
-function ATIS:SetRadioRelayUnitName(unitname)
-self.relayunitname=unitname
-self:T(self.lid..string.format("Setting radio relay unit to %s",self.relayunitname))
-return self
-end
-function ATIS:SetTowerFrequencies(freqs)
-if type(freqs)=="table"then
-else
-freqs={freqs}
-end
-self.towerfrequency=freqs
-return self
-end
-function ATIS:SetTransmitOnlyWithPlayers(Switch)
-self.TransmitOnlyWithPlayers=Switch
-if self.msrsQ then
-self.msrsQ:SetTransmitOnlyWithPlayers(Switch)
-end
-return self
-end
-function ATIS:SetActiveRunway(runway)
-self.activerunway=tostring(runway)
-local prefer=nil
-if string.find(string.lower(runway),"l")then
-prefer=true
-elseif string.find(string.lower(runway),"r")then
-prefer=false
-end
-self.airbase:SetActiveRunway(runway,prefer)
-return self
-end
-function ATIS:SetActiveRunwayLanding(runway,preferleft)
-self.airbase:SetActiveRunwayLanding(runway,preferleft)
-return self
-end
-function ATIS:SetActiveRunwayTakeoff(runway,preferleft)
-self.airbase:SetActiveRunwayTakeoff(runway,preferleft)
-return self
-end
-function ATIS:SetRunwayLength()
-self.rwylength=true
-return self
-end
-function ATIS:SetRunwayLength()
-self.rwylength=true
-return self
-end
-function ATIS:SetElevation()
-self.elevation=true
-return self
-end
-function ATIS:SetRadioPower(power)
-self.power=power or 100
-return self
-end
-function ATIS:SetMapMarks(switch)
-if switch==nil or switch==true then
-self.usemarker=true
-else
-self.usemarker=false
-end
-return self
-end
-function ATIS:GetSRSText()
-return self.SRSText
-end
-function ATIS:SetRunwayHeadingsMagnetic(headings)
-if type(headings)=="table"then
-else
-headings={headings}
-end
-for _,heading in pairs(headings)do
-if type(heading)=="number"then
-heading=string.format("%02d",heading)
-end
-self:T(self.lid..string.format("Adding user specified magnetic runway heading %s",heading))
-table.insert(self.runwaymag,heading)
-local h=self:GetRunwayWithoutLR(heading)
-local head2=tonumber(h)-18
-if head2<0 then
-head2=head2+36
-end
-head2=string.format("%02d",head2)
-local left=self:GetRunwayLR(heading)
-if left==true then
-head2=head2.."L"
-elseif left==false then
-head2=head2.."R"
-end
-self:T(self.lid..string.format("Adding user specified magnetic runway heading %s (inverse)",head2))
-table.insert(self.runwaymag,head2)
-end
-return self
-end
-function ATIS:SetSubtitleDuration(duration)
-self.subduration=tonumber(duration or 10)
-return self
-end
-function ATIS:SetMetricUnits()
-self.metric=true
-return self
-end
-function ATIS:SetImperialUnits()
-self.metric=false
-return self
-end
-function ATIS:SetPressureMillimetersMercury()
-self.PmmHg=true
-return self
-end
-function ATIS:SetTemperatureFahrenheit()
-self.TDegF=true
-return self
-end
-function ATIS:SetRelativeHumidity(Humidity)
-self.relHumidity=Humidity or 50
-return self
-end
-function ATIS:SetAltimeterQNH(switch)
-if switch==true or switch==nil then
-self.altimeterQNH=true
-else
-self.altimeterQNH=false
-end
-return self
-end
-function ATIS:SetReportmBar(switch)
-if switch==true or switch==nil then
-self.ReportmBar=true
-else
-self.ReportmBar=false
-end
-return self
-end
-function ATIS:SetAdditionalInformation(text)
-self.AdditionalInformation=text
-return self
-end
-function ATIS:ReportQNHOnly()
-self.qnhonly=true
-return self
-end
-function ATIS:SetMagneticDeclination(magvar)
-self.magvar=magvar or UTILS.GetMagneticDeclination()
-return self
-end
-function ATIS:SetRunwayCorrectionMagnetic2True(correction)
-self.runwaym2t=correction or ATIS.RunwayM2T[UTILS.GetDCSMap()]
-return self
-end
-function ATIS:SetReportWindTrue()
-self.windtrue=true
-return self
-end
-function ATIS:SetZuluTimeDifference(delta)
-self.zuludiff=delta
-return self
-end
-function ATIS:ReportZuluTimeOnly()
-self.zulutimeonly=true
-return self
-end
-function ATIS:AddILS(frequency,runway)
-local ils={}
-ils.frequency=tonumber(frequency)
-ils.runway=runway and tostring(runway)or nil
-table.insert(self.ils,ils)
-return self
-end
-function ATIS:SetVOR(frequency)
-self.vor=frequency
-return self
-end
-function ATIS:AddNDBouter(frequency,runway)
-local ndb={}
-ndb.frequency=tonumber(frequency)
-ndb.runway=runway and tostring(runway)or nil
-table.insert(self.ndbouter,ndb)
-return self
-end
-function ATIS:AddNDBinner(frequency,runway)
-local ndb={}
-ndb.frequency=tonumber(frequency)
-ndb.runway=runway and tostring(runway)or nil
-table.insert(self.ndbinner,ndb)
-return self
-end
-function ATIS:SetTACAN(channel)
-self.tacan=channel
-return self
-end
-function ATIS:SetRSBN(channel)
-self.rsbn=channel
-return self
-end
-function ATIS:AddPRMG(channel,runway)
-local ndb={}
-ndb.frequency=tonumber(channel)
-ndb.runway=runway and tostring(runway)or nil
-table.insert(self.prmg,ndb)
-return self
-end
-function ATIS:MarkRunways(markall)
-local airbases=AIRBASE.GetAllAirbases()
-for _,_airbase in pairs(airbases)do
-local airbase=_airbase
-if(not markall and airbase:GetName()==self.airbasename)or markall==true then
-airbase:GetRunwayData(self.runwaym2t,true)
-end
-end
-end
-function ATIS:SetSRS(PathToSRS,Gender,Culture,Voice,Port,GoogleKey)
-if PathToSRS or MSRS.path then
-self.useSRS=true
-self.msrs=MSRS:New(PathToSRS,self.frequency,self.modulation)
-self.msrs:SetGender(Gender)
-self.msrs:SetCulture(Culture)
-self.msrs:SetVoice(Voice)
-self.msrs:SetPort(Port)
-self.msrs:SetCoalition(self:GetCoalition())
-self.msrs:SetLabel("ATIS")
-self.msrs:SetGoogle(GoogleKey)
-self.msrs:SetCoordinate(self.airbase:GetCoordinate())
-self.msrsQ=MSRSQUEUE:New("ATIS")
-self.msrsQ:SetTransmitOnlyWithPlayers(self.TransmitOnlyWithPlayers)
-if self.dTQueueCheck<=10 then
-self:SetQueueUpdateTime(90)
-end
-else
-self:E(self.lid..string.format("ERROR: No SRS path specified!"))
-end
-return self
-end
-function ATIS:SetQueueUpdateTime(TimeInterval)
-self.dTQueueCheck=TimeInterval or 5
-end
-function ATIS:GetCoalition()
-local coal=self.airbase and self.airbase:GetCoalition()or nil
-return coal
-end
-function ATIS:onafterStart(From,Event,To)
-self:T({From,Event,To})
-self:T("Airbase category is "..self.airbase:GetAirbaseCategory())
-if self.airbase:GetAirbaseCategory()==Airbase.Category.SHIP then
-self:E(self.lid..string.format("ERROR: Cannot start ATIS for airbase %s! Only AIRDROMES are supported but NOT SHIPS.",self.airbasename))
-return
-end
-if self.airbase:GetAirbaseCategory()==Airbase.Category.HELIPAD then
-self:E(self.lid..string.format("EXPERIMENTAL: Starting ATIS for Helipad %s! SRS must be ON",self.airbasename))
-self.ATISforFARPs=true
-self.useSRS=true
-end
-if type(self.frequency)=="table"then
-local frequency=table.concat(self.frequency,"/")
-local modulation=self.modulation
-if type(self.modulation)=="table"then
-modulation=table.concat(self.modulation,"/")
-end
-self:I(self.lid..string.format("Starting ATIS v%s for airbase %s on %s MHz Modulation=%s",ATIS.version,self.airbasename,frequency,modulation))
-else
-self:I(self.lid..string.format("Starting ATIS v%s for airbase %s on %.3f MHz Modulation=%d",ATIS.version,self.airbasename,self.frequency,self.modulation))
-end
-if not self.useSRS then
-self.radioqueue=RADIOQUEUE:New(self.frequency,self.modulation,string.format("ATIS %s",self.airbasename))
-self.radioqueue:SetSenderCoordinate(self.airbase:GetCoordinate())
-self.radioqueue:SetSenderUnitName(self.relayunitname)
-self.radioqueue:SetRadioPower(self.power)
-self.radioqueue:SetDigit(0,ATIS.Sound.N0.filename,ATIS.Sound.N0.duration,self.soundpath)
-self.radioqueue:SetDigit(1,ATIS.Sound.N1.filename,ATIS.Sound.N1.duration,self.soundpath)
-self.radioqueue:SetDigit(2,ATIS.Sound.N2.filename,ATIS.Sound.N2.duration,self.soundpath)
-self.radioqueue:SetDigit(3,ATIS.Sound.N3.filename,ATIS.Sound.N3.duration,self.soundpath)
-self.radioqueue:SetDigit(4,ATIS.Sound.N4.filename,ATIS.Sound.N4.duration,self.soundpath)
-self.radioqueue:SetDigit(5,ATIS.Sound.N5.filename,ATIS.Sound.N5.duration,self.soundpath)
-self.radioqueue:SetDigit(6,ATIS.Sound.N6.filename,ATIS.Sound.N6.duration,self.soundpath)
-self.radioqueue:SetDigit(7,ATIS.Sound.N7.filename,ATIS.Sound.N7.duration,self.soundpath)
-self.radioqueue:SetDigit(8,ATIS.Sound.N8.filename,ATIS.Sound.N8.duration,self.soundpath)
-self.radioqueue:SetDigit(9,ATIS.Sound.N9.filename,ATIS.Sound.N9.duration,self.soundpath)
-self.radioqueue:Start(1,0.1)
-end
-self:HandleEvent(EVENTS.BaseCaptured)
-self:__Status(-2)
-self:__CheckQueue(-3)
-end
-function ATIS:onafterStatus(From,Event,To)
-self:T({From,Event,To})
-local fsmstate=self:GetState()
-local relayunitstatus="N/A"
-if self.relayunitname then
-local ru=UNIT:FindByName(self.relayunitname)
-if ru then
-relayunitstatus=tostring(ru:IsAlive())
-end
-end
-local text=""
-if type(self.frequency)=="table"then
-local frequency=table.concat(self.frequency,"/")
-local modulation=self.modulation
-if type(self.modulation)=="table"then
-modulation=table.concat(self.modulation,"/")
-end
-text=string.format("State %s: Freq=%s MHz %s",fsmstate,frequency,modulation)
-else
-text=string.format("State %s: Freq=%.3f MHz %s",fsmstate,self.frequency,UTILS.GetModulationName(self.modulation))
-end
-if self.useSRS then
-text=text..string.format(", SRS path=%s (%s), gender=%s, culture=%s, voice=%s",tostring(self.msrs.path),tostring(self.msrs.port),tostring(self.msrs.gender),tostring(self.msrs.culture),tostring(self.msrs.voice))
-else
-text=text..string.format(", Relay unit=%s (alive=%s)",tostring(self.relayunitname),relayunitstatus)
-end
-self:T(self.lid..text)
-if not self:Is("Stopped")then
-self:__Status(60)
-end
-end
-function ATIS:onafterCheckQueue(From,Event,To)
-self:T({From,Event,To})
-if not self:Is("Stopped")then
-if self.useSRS then
-self:Broadcast()
-else
-if#self.radioqueue.queue==0 then
-self:T(self.lid..string.format("Radio queue empty. Repeating message."))
-self:Broadcast()
-else
-self:T2(self.lid..string.format("Radio queue %d transmissions queued.",#self.radioqueue.queue))
-end
-end
-self:__CheckQueue(math.abs(self.dTQueueCheck))
-end
-end
-function ATIS:onafterBroadcast(From,Event,To)
-self:T({From,Event,To})
-local coord=self.airbase:GetCoordinate()
-local height=coord:GetLandHeight()
-local qfe=coord:GetPressure(height)
-local qnh=coord:GetPressure(0)
-if self.altimeterQNH then
-local L=-0.0065
-local R=8.31446
-local g=9.80665
-local M=0.0289644
-local T0=coord:GetTemperature(0)+273.15
-local TS=288.15
-local q=qnh*100
-local P=q*(1+L*height/T0)^(-g*M/(R*L))
-local Q=P/(1+L*height/TS)^(-g*M/(R*L))
-local A=(T0/L)*((P/q)^(((-R*L)/(g*M)))-1)
-self:T2(self.lid..string.format("height=%.1f, A=%.1f, T0=%.1f, QFE=%.1f, QNH=%.1f, P=%.1f, Q=%.1f hPa = %.2f",height,A,T0-273.15,qfe,qnh,P/100,Q/100,UTILS.hPa2inHg(Q/100)))
-qnh=Q/100
-end
-local mBarqnh=qnh
-local mBarqfe=qfe
-if self.PmmHg then
-qfe=UTILS.hPa2mmHg(qfe)
-qnh=UTILS.hPa2mmHg(qnh)
-else
-if not self.metric then
-qfe=UTILS.hPa2inHg(qfe)
-qnh=UTILS.hPa2inHg(qnh)
-end
-end
-local QFE=UTILS.Split(string.format("%.2f",qfe),".")
-local QNH=UTILS.Split(string.format("%.2f",qnh),".")
-if self.PmmHg then
-QFE=UTILS.Split(string.format("%.1f",qfe),".")
-QNH=UTILS.Split(string.format("%.1f",qnh),".")
-else
-if self.metric then
-QFE=UTILS.Split(string.format("%.1f",qfe),".")
-QNH=UTILS.Split(string.format("%.1f",qnh),".")
-end
-end
-local windFrom,windSpeed=coord:GetWind(height+10)
-local magvar=self.magvar
-if self.windtrue then
-magvar=0
-end
-windFrom=windFrom-magvar
-if windFrom<0 then
-windFrom=windFrom+360
-end
-local WINDFROM=string.format("%03d",windFrom)
-local WINDSPEED=string.format("%d",UTILS.MpsToKnots(windSpeed))
-if WINDFROM=="000"then
-WINDFROM="360"
-end
-if self.metric then
-WINDSPEED=string.format("%d",windSpeed)
-end
-local runwayLanding,rwyLandingLeft
-local runwayTakeoff,rwyTakeoffLeft
-if self.airbase:GetAirbaseCategory()==Airbase.Category.HELIPAD then
-runwayLanding,rwyLandingLeft="PAD 01",false
-runwayTakeoff,rwyTakeoffLeft="PAD 02",false
-else
-runwayLanding,rwyLandingLeft=self:GetActiveRunway()
-runwayTakeoff,rwyTakeoffLeft=self:GetActiveRunway(true)
-end
-local time=timer.getAbsTime()
-if self.zuludiff then
-time=time-self.zuludiff*60*60
-else
-time=time-UTILS.GMTToLocalTimeDifference()*60*60
-end
-if time<0 then
-time=24*60*60+time
-end
-local clock=UTILS.SecondsToClock(time)
-local zulu=UTILS.Split(clock,":")
-local ZULU=string.format("%s%s",zulu[1],zulu[2])
-local hours=self.gettext:GetEntry("TIME",self.locale)
-if self.useSRS then
-ZULU=string.format("%s %s",hours,zulu[1])
-end
-local NATO=ATIS.Alphabet[tonumber(zulu[1])+1]
-self:T3(string.format("clock=%s",tostring(clock)))
-self:T3(string.format("zulu1=%s",tostring(zulu[1])))
-self:T3(string.format("zulu2=%s",tostring(zulu[2])))
-self:T3(string.format("ZULU =%s",tostring(ZULU)))
-self:T3(string.format("NATO =%s",tostring(NATO)))
-local hours=self.gettext:GetEntry("HOURS",self.locale)
-local sunrise=coord:GetSunrise()
-sunrise=UTILS.Split(sunrise,":")
-local SUNRISE=string.format("%s%s",sunrise[1],sunrise[2])
-if self.useSRS then
-SUNRISE=string.format("%s %s %s",sunrise[1],sunrise[2],hours)
-end
-local sunset=coord:GetSunset()
-sunset=UTILS.Split(sunset,":")
-local SUNSET=string.format("%s%s",sunset[1],sunset[2])
-if self.useSRS then
-SUNSET=string.format("%s %s %s",sunset[1],sunset[2],hours)
-end
-local temperature=coord:GetTemperature(height+5)
-local dewpoint=temperature-(100-self.relHumidity)/5
-if self.TDegF then
-temperature=UTILS.CelsiusToFahrenheit(temperature)
-dewpoint=UTILS.CelsiusToFahrenheit(dewpoint)
-end
-local TEMPERATURE=string.format("%d",math.abs(temperature))
-local DEWPOINT=string.format("%d",math.abs(dewpoint))
-local clouds,visibility,turbulence,fog,dust,static=self:GetMissionWeather()
-if fog and fog.thicknessUTILS.FeetToMeters(1500)then
-dust=nil
-end
-local visibilitymin=visibility
-if fog then
-if fog.visibility10 then
-reportedviz=10
-end
-VISIBILITY=string.format("%d",reportedviz)
-else
-local reportedviz=UTILS.Round(UTILS.MetersToSM(visibilitymin))
-if reportedviz>10 then
-reportedviz=10
-end
-VISIBILITY=string.format("%d",reportedviz)
-end
-local cloudbase=clouds.base
-local cloudceil=clouds.base+clouds.thickness
-local clouddens=clouds.density
-local cloudspreset=clouds.preset or"Nothing"
-local precepitation=0
-if cloudspreset:find("Preset10")then
-clouddens=4
-elseif cloudspreset:find("Preset11")then
-clouddens=4
-elseif cloudspreset:find("Preset12")then
-clouddens=4
-elseif cloudspreset:find("Preset13")then
-clouddens=7
-elseif cloudspreset:find("Preset14")then
-clouddens=7
-elseif cloudspreset:find("Preset15")then
-clouddens=7
-elseif cloudspreset:find("Preset16")then
-clouddens=7
-elseif cloudspreset:find("Preset17")then
-clouddens=7
-elseif cloudspreset:find("Preset18")then
-clouddens=7
-elseif cloudspreset:find("Preset19")then
-clouddens=7
-elseif cloudspreset:find("Preset20")then
-clouddens=7
-elseif cloudspreset:find("Preset21")then
-clouddens=9
-elseif cloudspreset:find("Preset22")then
-clouddens=9
-elseif cloudspreset:find("Preset23")then
-clouddens=9
-elseif cloudspreset:find("Preset24")then
-clouddens=9
-elseif cloudspreset:find("Preset25")then
-clouddens=9
-elseif cloudspreset:find("Preset26")then
-clouddens=9
-elseif cloudspreset:find("Preset27")then
-clouddens=9
-elseif cloudspreset:find("Preset1")then
-clouddens=1
-elseif cloudspreset:find("Preset2")then
-clouddens=1
-elseif cloudspreset:find("Preset3")then
-clouddens=4
-elseif cloudspreset:find("Preset4")then
-clouddens=4
-elseif cloudspreset:find("Preset5")then
-clouddens=4
-elseif cloudspreset:find("Preset6")then
-clouddens=4
-elseif cloudspreset:find("Preset7")then
-clouddens=4
-elseif cloudspreset:find("Preset8")then
-clouddens=4
-elseif cloudspreset:find("Preset9")then
-clouddens=4
-elseif cloudspreset:find("RainyPreset")then
-clouddens=9
-if temperature>5 then
-precepitation=1
-else
-precepitation=3
-end
-elseif cloudspreset:find("RainyPreset1")then
-clouddens=9
-if temperature>5 then
-precepitation=1
-else
-precepitation=3
-end
-elseif cloudspreset:find("RainyPreset2")then
-clouddens=9
-if temperature>5 then
-precepitation=1
-else
-precepitation=3
-end
-elseif cloudspreset:find("RainyPreset3")then
-clouddens=9
-if temperature>5 then
-precepitation=1
-else
-precepitation=3
-end
-end
-local CLOUDBASE=string.format("%d",UTILS.MetersToFeet(cloudbase))
-local CLOUDCEIL=string.format("%d",UTILS.MetersToFeet(cloudceil))
-if self.metric then
-CLOUDBASE=string.format("%d",cloudbase)
-CLOUDCEIL=string.format("%d",cloudceil)
-end
-local CLOUDBASE1000,CLOUDBASE0100=self:_GetThousandsAndHundreds(UTILS.MetersToFeet(cloudbase))
-local CLOUDCEIL1000,CLOUDCEIL0100=self:_GetThousandsAndHundreds(UTILS.MetersToFeet(cloudceil))
-if self.metric then
-CLOUDBASE1000,CLOUDBASE0100=self:_GetThousandsAndHundreds(cloudbase)
-CLOUDCEIL1000,CLOUDCEIL0100=self:_GetThousandsAndHundreds(cloudceil)
-end
-local CloudCover={}
-CloudCover=ATIS.Sound.CloudsNotAvailable
-local CLOUDSsub=self.gettext:GetEntry("NOCLOUDINFO",self.locale)
-if static then
-if clouddens>=9 then
-CloudCover=ATIS.Sound.CloudsOvercast
-CLOUDSsub=self.gettext:GetEntry("OVERCAST",self.locale)
-elseif clouddens>=7 then
-CloudCover=ATIS.Sound.CloudsBroken
-CLOUDSsub=self.gettext:GetEntry("BROKEN",self.locale)
-elseif clouddens>=4 then
-CloudCover=ATIS.Sound.CloudsScattered
-CLOUDSsub=self.gettext:GetEntry("SCATTERED",self.locale)
-elseif clouddens>=1 then
-CloudCover=ATIS.Sound.CloudsFew
-CLOUDSsub=self.gettext:GetEntry("FEWCLOUDS",self.locale)
-else
-CLOUDBASE=nil
-CLOUDCEIL=nil
-CloudCover=ATIS.Sound.CloudsNo
-CLOUDSsub=self.gettext:GetEntry("NOCLOUDS",self.locale)
-end
-end
-local subtitle=""
-subtitle=string.format("%s",self.airbasename)
-if(not self.ATISforFARPs)and self.airbasename:find("AFB")==nil and self.airbasename:find("Airport")==nil
-and self.airbasename:find("Airstrip")==nil and self.airbasename:find("airfield")==nil and self.airbasename:find("AB")==nil
-and self.airbasename:find("Field")==nil
-then
-subtitle=subtitle.." "..self.gettext:GetEntry("AIRPORT",self.locale)
-end
-if not self.useSRS then
-self.radioqueue:NewTransmission(string.format("%s/%s.ogg",self.theatre,self.airbasename),3.0,self.soundpath,nil,nil,subtitle,self.subduration)
-end
-local alltext=subtitle
-local information=self.gettext:GetEntry("INFORMATION",self.locale)
-subtitle=string.format("%s %s",information,NATO)
-local _INFORMATION=subtitle
-if not self.useSRS then
-self:Transmission(ATIS.Sound.Information,0.5,subtitle)
-self.radioqueue:NewTransmission(string.format("NATO Alphabet/%s.ogg",NATO),0.75,self.soundpath)
-end
-alltext=alltext..";\n"..subtitle
-subtitle=string.format("%s Zulu",ZULU)
-if not self.useSRS then
-self.radioqueue:Number2Transmission(ZULU,nil,0.5)
-self:Transmission(ATIS.Sound.Zulu,0.2,subtitle)
-end
-alltext=alltext..";\n"..subtitle
-if not self.zulutimeonly then
-local sunrise=self.gettext:GetEntry("SUNRISEAT",self.locale)
-subtitle=string.format(sunrise,SUNRISE)
-if not self.useSRS then
-self:Transmission(ATIS.Sound.SunriseAt,0.5,subtitle)
-self.radioqueue:Number2Transmission(SUNRISE,nil,0.2)
-self:Transmission(ATIS.Sound.TimeLocal,0.2)
-end
-alltext=alltext..";\n"..subtitle
-local sunset=self.gettext:GetEntry("SUNSETAT",self.locale)
-subtitle=string.format(sunset,SUNSET)
-if not self.useSRS then
-self:Transmission(ATIS.Sound.SunsetAt,0.5,subtitle)
-self.radioqueue:Number2Transmission(SUNSET,nil,0.5)
-self:Transmission(ATIS.Sound.TimeLocal,0.2)
-end
-alltext=alltext..";\n"..subtitle
-end
-if self.useSRS then
-WINDFROM=string.gsub(WINDFROM,".","%1 ")
-end
-if self.metric then
-local windfrom=self.gettext:GetEntry("WINDFROMMS",self.locale)
-subtitle=string.format(windfrom,WINDFROM,WINDSPEED)
-else
-local windfrom=self.gettext:GetEntry("WINDFROMKNOTS",self.locale)
-subtitle=string.format(windfrom,WINDFROM,WINDSPEED)
-end
-if turbulence>0 then
-subtitle=subtitle..", "..self.gettext:GetEntry("GUSTING",self.locale)
-end
-local _WIND=subtitle
-if not self.useSRS then
-self:Transmission(ATIS.Sound.WindFrom,1.0,subtitle)
-self.radioqueue:Number2Transmission(WINDFROM)
-self:Transmission(ATIS.Sound.At,0.2)
-self.radioqueue:Number2Transmission(WINDSPEED)
-if self.metric then
-self:Transmission(ATIS.Sound.MetersPerSecond,0.2)
-else
-self:Transmission(ATIS.Sound.Knots,0.2)
-end
-if turbulence>0 then
-self:Transmission(ATIS.Sound.Gusting,0.2)
-end
-end
-alltext=alltext..";\n"..subtitle
-if self.metric then
-local visi=self.gettext:GetEntry("VISIKM",self.locale)
-subtitle=string.format(visi,VISIBILITY)
-else
-local visi=self.gettext:GetEntry("VISISM",self.locale)
-subtitle=string.format(visi,VISIBILITY)
-end
-if not self.useSRS then
-self:Transmission(ATIS.Sound.Visibilty,1.0,subtitle)
-self.radioqueue:Number2Transmission(VISIBILITY)
-if self.metric then
-self:Transmission(ATIS.Sound.Kilometers,0.2)
-else
-self:Transmission(ATIS.Sound.StatuteMiles,0.2)
-end
-end
-alltext=alltext..";\n"..subtitle
-subtitle=""
-local wp=false
-local wpsub=""
-if precepitation==1 then
-wp=true
-wpsub=wpsub.." "..self.gettext:GetEntry("RAIN",self.locale)
-elseif precepitation==2 then
-if wp then
-wpsub=wpsub..","
-end
-wpsub=wpsub.." "..self.gettext:GetEntry("TSTORM",self.locale)
-wp=true
-elseif precepitation==3 then
-wpsub=wpsub.." "..self.gettext:GetEntry("SNOW",self.locale)
-wp=true
-elseif precepitation==4 then
-wpsub=wpsub.." "..self.gettext:GetEntry("SSTROM",self.locale)
-wp=true
-end
-if fog then
-if wp then
-wpsub=wpsub..","
-end
-wpsub=wpsub.." "..self.gettext:GetEntry("FOG",self.locale)
-wp=true
-end
-if dust then
-if wp then
-wpsub=wpsub..","
-end
-wpsub=wpsub.." "..self.gettext:GetEntry("DUST",self.locale)
-wp=true
-end
-if wp then
-local phenos=self.gettext:GetEntry("PHENOMENA",self.locale)
-subtitle=string.format("%s: %s",phenos,wpsub)
-if not self.useSRS then
-self:Transmission(ATIS.Sound.WeatherPhenomena,1.0,subtitle)
-if precepitation==1 then
-self:Transmission(ATIS.Sound.Rain,0.5)
-elseif precepitation==2 then
-self:Transmission(ATIS.Sound.ThunderStorm,0.5)
-elseif precepitation==3 then
-self:Transmission(ATIS.Sound.Snow,0.5)
-elseif precepitation==4 then
-self:Transmission(ATIS.Sound.SnowStorm,0.5)
-end
-if fog then
-self:Transmission(ATIS.Sound.Fog,0.5)
-end
-if dust then
-self:Transmission(ATIS.Sound.Dust,0.5)
-end
-end
-alltext=alltext..";\n"..subtitle
-end
-if not self.useSRS then
-self:Transmission(CloudCover,1.0,CLOUDSsub)
-end
-if CLOUDBASE and static then
-local cbase=tostring(tonumber(CLOUDBASE1000)*1000+tonumber(CLOUDBASE0100)*100)
-local cceil=tostring(tonumber(CLOUDCEIL1000)*1000+tonumber(CLOUDCEIL0100)*100)
-if self.metric then
-local cloudbase=self.gettext:GetEntry("CLOUDBASEM",self.locale)
-subtitle=string.format(cloudbase,cbase,cceil)
-else
-local cloudbase=self.gettext:GetEntry("CLOUDBASEFT",self.locale)
-subtitle=string.format(cloudbase,cbase,cceil)
-end
-if not self.useSRS then
-self:Transmission(ATIS.Sound.CloudBase,1.0,subtitle)
-if tonumber(CLOUDBASE1000)>0 then
-self.radioqueue:Number2Transmission(CLOUDBASE1000)
-self:Transmission(ATIS.Sound.Thousand,0.1)
-end
-if tonumber(CLOUDBASE0100)>0 then
-self.radioqueue:Number2Transmission(CLOUDBASE0100)
-self:Transmission(ATIS.Sound.Hundred,0.1)
-end
-self:Transmission(ATIS.Sound.CloudCeiling,0.5)
-if tonumber(CLOUDCEIL1000)>0 then
-self.radioqueue:Number2Transmission(CLOUDCEIL1000)
-self:Transmission(ATIS.Sound.Thousand,0.1)
-end
-if tonumber(CLOUDCEIL0100)>0 then
-self.radioqueue:Number2Transmission(CLOUDCEIL0100)
-self:Transmission(ATIS.Sound.Hundred,0.1)
-end
-if self.metric then
-self:Transmission(ATIS.Sound.Meters,0.1)
-else
-self:Transmission(ATIS.Sound.Feet,0.1)
-end
-end
-end
-alltext=alltext..";\n"..subtitle
-subtitle=""
-local temptext=self.gettext:GetEntry("TEMPERATURE",self.locale)
-if self.TDegF then
-if temperature<0 then
-subtitle=string.format("%s -%s °F",temptext,TEMPERATURE)
-else
-subtitle=string.format("%s %s °F",temptext,TEMPERATURE)
-end
-else
-if temperature<0 then
-subtitle=string.format("%s -%s °C",temptext,TEMPERATURE)
-else
-subtitle=string.format("%s %s °C",temptext,TEMPERATURE)
-end
-end
-local _TEMPERATURE=subtitle
-if not self.useSRS then
-self:Transmission(ATIS.Sound.Temperature,1.0,subtitle)
-if temperature<0 then
-self:Transmission(ATIS.Sound.Minus,0.2)
-end
-self.radioqueue:Number2Transmission(TEMPERATURE)
-if self.TDegF then
-self:Transmission(ATIS.Sound.DegreesFahrenheit,0.2)
-else
-self:Transmission(ATIS.Sound.DegreesCelsius,0.2)
-end
-end
-alltext=alltext..";\n"..subtitle
-local dewtext=self.gettext:GetEntry("DEWPOINT",self.locale)
-if self.TDegF then
-if dewpoint<0 then
-subtitle=string.format("%s -%s °F",dewtext,DEWPOINT)
-else
-subtitle=string.format("%s %s °F",dewtext,DEWPOINT)
-end
-else
-if dewpoint<0 then
-subtitle=string.format("%s -%s °C",dewtext,DEWPOINT)
-else
-subtitle=string.format("%s %s °C",dewtext,DEWPOINT)
-end
-end
-local _DEWPOINT=subtitle
-if not self.useSRS then
-self:Transmission(ATIS.Sound.DewPoint,1.0,subtitle)
-if dewpoint<0 then
-self:Transmission(ATIS.Sound.Minus,0.2)
-end
-self.radioqueue:Number2Transmission(DEWPOINT)
-if self.TDegF then
-self:Transmission(ATIS.Sound.DegreesFahrenheit,0.2)
-else
-self:Transmission(ATIS.Sound.DegreesCelsius,0.2)
-end
-end
-alltext=alltext..";\n"..subtitle
-local altim=self.gettext:GetEntry("ALTIMETER",self.locale)
-if self.PmmHg then
-if self.qnhonly then
-subtitle=string.format("%s %s.%s mmHg",altim,QNH[1],QNH[2])
-else
-subtitle=string.format("%s: QNH %s.%s, QFE %s.%s mmHg",altim,QNH[1],QNH[2],QFE[1],QFE[2])
-end
-else
-if self.metric then
-if self.qnhonly then
-subtitle=string.format("%s %s.%s hPa",altim,QNH[1],QNH[2])
-else
-subtitle=string.format("%s: QNH %s.%s, QFE %s.%s hPa",altim,QNH[1],QNH[2],QFE[1],QFE[2])
-end
-else
-if self.qnhonly then
-subtitle=string.format("%s %s.%s inHg",altim,QNH[1],QNH[2])
-else
-subtitle=string.format("%s: QNH %s.%s, QFE %s.%s inHg",altim,QNH[1],QNH[2],QFE[1],QFE[2])
-end
-end
-end
-if self.ReportmBar and not self.metric then
-if self.qnhonly then
-subtitle=string.format("%s;\n%s %d hPa",subtitle,altim,mBarqnh)
-else
-subtitle=string.format("%s;\n%s: QNH %d, QFE %d hPa",subtitle,altim,mBarqnh,mBarqfe)
-end
-end
-local _ALTIMETER=subtitle
-if not self.useSRS then
-self:Transmission(ATIS.Sound.Altimeter,1.0,subtitle)
-if not self.qnhonly then
-self:Transmission(ATIS.Sound.QNH,0.5)
-end
-self.radioqueue:Number2Transmission(QNH[1])
-if ATIS.ICAOPhraseology[UTILS.GetDCSMap()]then
-self:Transmission(ATIS.Sound.Decimal,0.2)
-end
-self.radioqueue:Number2Transmission(QNH[2])
-if not self.qnhonly then
-self:Transmission(ATIS.Sound.QFE,0.75)
-self.radioqueue:Number2Transmission(QFE[1])
-if ATIS.ICAOPhraseology[UTILS.GetDCSMap()]then
-self:Transmission(ATIS.Sound.Decimal,0.2)
-end
-self.radioqueue:Number2Transmission(QFE[2])
-end
-if self.PmmHg then
-self:Transmission(ATIS.Sound.MillimetersOfMercury,0.1)
-else
-if self.metric then
-self:Transmission(ATIS.Sound.HectoPascal,0.1)
-else
-self:Transmission(ATIS.Sound.InchesOfMercury,0.1)
-end
-end
-end
-alltext=alltext..";\n"..subtitle
-local _RUNACT
-if not self.ATISforFARPs then
-local subtitle=""
-if runwayLanding then
-local actrun=self.gettext:GetEntry("ACTIVELANDING",self.locale)
-subtitle=string.format("%s %s",actrun,runwayLanding)
-if rwyLandingLeft==true then
-subtitle=subtitle.." "..self.gettext:GetEntry("LEFT",self.locale)
-elseif rwyLandingLeft==false then
-subtitle=subtitle.." "..self.gettext:GetEntry("RIGHT",self.locale)
-end
-alltext=alltext..";\n"..subtitle
-end
-if runwayTakeoff then
-local actrun=self.gettext:GetEntry("ACTIVERUN",self.locale)
-subtitle=string.format("%s %s",actrun,runwayTakeoff)
-if rwyTakeoffLeft==true then
-subtitle=subtitle.." "..self.gettext:GetEntry("LEFT",self.locale)
-elseif rwyTakeoffLeft==false then
-subtitle=subtitle.." "..self.gettext:GetEntry("RIGHT",self.locale)
-end
-end
-_RUNACT=subtitle
-if not self.useSRS then
-self:Transmission(ATIS.Sound.ActiveRunway,1.0,subtitle)
-self.radioqueue:Number2Transmission(runwayLanding)
-if rwyLandingLeft==true then
-self:Transmission(ATIS.Sound.Left,0.2)
-elseif rwyLandingLeft==false then
-self:Transmission(ATIS.Sound.Right,0.2)
-end
-end
-alltext=alltext..";\n"..subtitle
-if self.rwylength then
-local runact=self.airbase:GetActiveRunway(self.runwaym2t)
-local length=runact.length
-if not self.metric then
-length=UTILS.MetersToFeet(length)
-end
-local L1000,L0100=self:_GetThousandsAndHundreds(length)
-local rwyl=self.gettext:GetEntry("RWYLENGTH",self.locale)
-local meters=self.gettext:GetEntry("METERS",self.locale)
-local feet=self.gettext:GetEntry("FEET",self.locale)
-local subtitle=string.format("%s %d",rwyl,length)
-if self.metric then
-subtitle=subtitle.." "..meters
-else
-subtitle=subtitle.." "..feet
-end
-if not self.useSRS then
-self:Transmission(ATIS.Sound.RunwayLength,1.0,subtitle)
-if tonumber(L1000)>0 then
-self.radioqueue:Number2Transmission(L1000)
-self:Transmission(ATIS.Sound.Thousand,0.1)
-end
-if tonumber(L0100)>0 then
-self.radioqueue:Number2Transmission(L0100)
-self:Transmission(ATIS.Sound.Hundred,0.1)
-end
-if self.metric then
-self:Transmission(ATIS.Sound.Meters,0.1)
-else
-self:Transmission(ATIS.Sound.Feet,0.1)
-end
-end
-alltext=alltext..";\n"..subtitle
-end
-end
-if self.elevation then
-local elev=self.gettext:GetEntry("ELEVATION",self.locale)
-local meters=self.gettext:GetEntry("METERS",self.locale)
-local feet=self.gettext:GetEntry("FEET",self.locale)
-local elevation=self.airbase:GetHeight()
-if not self.metric then
-elevation=UTILS.MetersToFeet(elevation)
-end
-local L1000,L0100=self:_GetThousandsAndHundreds(elevation)
-local subtitle=string.format("%s %d",elev,elevation)
-if self.metric then
-subtitle=subtitle.." "..meters
-else
-subtitle=subtitle.." "..feet
-end
-if not self.useSRS then
-self:Transmission(ATIS.Sound.Elevation,1.0,subtitle)
-if tonumber(L1000)>0 then
-self.radioqueue:Number2Transmission(L1000)
-self:Transmission(ATIS.Sound.Thousand,0.1)
-end
-if tonumber(L0100)>0 then
-self.radioqueue:Number2Transmission(L0100)
-self:Transmission(ATIS.Sound.Hundred,0.1)
-end
-if self.metric then
-self:Transmission(ATIS.Sound.Meters,0.1)
-else
-self:Transmission(ATIS.Sound.Feet,0.1)
-end
-end
-alltext=alltext..";\n"..subtitle
-end
-if self.towerfrequency then
-local freqs=""
-for i,freq in pairs(self.towerfrequency)do
-freqs=freqs..string.format("%.3f MHz",freq)
-if i<#self.towerfrequency then
-freqs=freqs..", "
-end
-end
-local twrfrq=self.gettext:GetEntry("TOWERFREQ",self.locale)
-subtitle=string.format("%s %s",twrfrq,freqs)
-if not self.useSRS then
-self:Transmission(ATIS.Sound.TowerFrequency,1.0,subtitle)
-for _,freq in pairs(self.towerfrequency)do
-local f=string.format("%.3f",freq)
-f=UTILS.Split(f,".")
-self.radioqueue:Number2Transmission(f[1],nil,0.5)
-if tonumber(f[2])>0 then
-self:Transmission(ATIS.Sound.Decimal,0.2)
-self.radioqueue:Number2Transmission(f[2])
-end
-self:Transmission(ATIS.Sound.MegaHertz,0.2)
-end
-end
-alltext=alltext..";\n"..subtitle
-end
-local ils=self:GetNavPoint(self.ils,runwayLanding,rwyLandingLeft)
-if ils then
-local ilstxt=self.gettext:GetEntry("ILSFREQ",self.locale)
-subtitle=string.format("%s %.2f MHz",ilstxt,ils.frequency)
-if not self.useSRS then
-self:Transmission(ATIS.Sound.ILSFrequency,1.0,subtitle)
-local f=string.format("%.2f",ils.frequency)
-f=UTILS.Split(f,".")
-self.radioqueue:Number2Transmission(f[1],nil,0.5)
-if tonumber(f[2])>0 then
-self:Transmission(ATIS.Sound.Decimal,0.2)
-self.radioqueue:Number2Transmission(f[2])
-end
-self:Transmission(ATIS.Sound.MegaHertz,0.2)
-end
-alltext=alltext..";\n"..subtitle
-end
-local ndb=self:GetNavPoint(self.ndbouter,runwayLanding,rwyLandingLeft)
-if ndb then
-local ndbtxt=self.gettext:GetEntry("OUTERNDB",self.locale)
-subtitle=string.format("%s %.2f MHz",ndbtxt,ndb.frequency)
-if not self.useSRS then
-self:Transmission(ATIS.Sound.OuterNDBFrequency,1.0,subtitle)
-local f=string.format("%.2f",ndb.frequency)
-f=UTILS.Split(f,".")
-self.radioqueue:Number2Transmission(f[1],nil,0.5)
-if tonumber(f[2])>0 then
-self:Transmission(ATIS.Sound.Decimal,0.2)
-self.radioqueue:Number2Transmission(f[2])
-end
-self:Transmission(ATIS.Sound.MegaHertz,0.2)
-end
-alltext=alltext..";\n"..subtitle
-end
-local ndb=self:GetNavPoint(self.ndbinner,runwayLanding,rwyLandingLeft)
-if ndb then
-local ndbtxt=self.gettext:GetEntry("INNERNDB",self.locale)
-subtitle=string.format("%s %.2f MHz",ndbtxt,ndb.frequency)
-if not self.useSRS then
-self:Transmission(ATIS.Sound.InnerNDBFrequency,1.0,subtitle)
-local f=string.format("%.2f",ndb.frequency)
-f=UTILS.Split(f,".")
-self.radioqueue:Number2Transmission(f[1],nil,0.5)
-if tonumber(f[2])>0 then
-self:Transmission(ATIS.Sound.Decimal,0.2)
-self.radioqueue:Number2Transmission(f[2])
-end
-self:Transmission(ATIS.Sound.MegaHertz,0.2)
-end
-alltext=alltext..";\n"..subtitle
-end
-if self.vor then
-local vortxt=self.gettext:GetEntry("VORFREQ",self.locale)
-local vorttstxt=self.gettext:GetEntry("VORFREQTTS",self.locale)
-subtitle=string.format("%s %.2f MHz",vortxt,self.vor)
-if self.useSRS then
-subtitle=string.format("%s %.2f MHz",vorttstxt,self.vor)
-end
-if not self.useSRS then
-self:Transmission(ATIS.Sound.VORFrequency,1.0,subtitle)
-local f=string.format("%.2f",self.vor)
-f=UTILS.Split(f,".")
-self.radioqueue:Number2Transmission(f[1],nil,0.5)
-if tonumber(f[2])>0 then
-self:Transmission(ATIS.Sound.Decimal,0.2)
-self.radioqueue:Number2Transmission(f[2])
-end
-self:Transmission(ATIS.Sound.MegaHertz,0.2)
-end
-alltext=alltext..";\n"..subtitle
-end
-if self.tacan then
-local tactxt=self.gettext:GetEntry("TACANCH",self.locale)
-subtitle=string.format(tactxt,self.tacan)
-if not self.useSRS then
-self:Transmission(ATIS.Sound.TACANChannel,1.0,subtitle)
-self.radioqueue:Number2Transmission(tostring(self.tacan),nil,0.2)
-self.radioqueue:NewTransmission("NATO Alphabet/Xray.ogg",0.75,self.soundpath,nil,0.2)
-end
-alltext=alltext..";\n"..subtitle
-end
-if self.rsbn then
-local rsbntxt=self.gettext:GetEntry("RSBNCH",self.locale)
-subtitle=string.format("%s %d",rsbntxt,self.rsbn)
-if not self.useSRS then
-self:Transmission(ATIS.Sound.RSBNChannel,1.0,subtitle)
-self.radioqueue:Number2Transmission(tostring(self.rsbn),nil,0.2)
-end
-alltext=alltext..";\n"..subtitle
-end
-local ndb=self:GetNavPoint(self.prmg,runwayLanding,rwyLandingLeft)
-if ndb then
-local prmtxt=self.gettext:GetEntry("PRMGCH",self.locale)
-subtitle=string.format("%s %d",prmtxt,ndb.frequency)
-if not self.useSRS then
-self:Transmission(ATIS.Sound.PRMGChannel,1.0,subtitle)
-self.radioqueue:Number2Transmission(tostring(ndb.frequency),nil,0.5)
-end
-alltext=alltext..";\n"..subtitle
-end
-if self.useSRS and self.AdditionalInformation then
-alltext=alltext..";\n"..self.AdditionalInformation
-end
-local advtxt=self.gettext:GetEntry("ADVISE",self.locale)
-subtitle=string.format("%s %s",advtxt,NATO)
-if not self.useSRS then
-self:Transmission(ATIS.Sound.AdviceOnInitial,0.5,subtitle)
-self.radioqueue:NewTransmission(string.format("NATO Alphabet/%s.ogg",NATO),0.75,self.soundpath)
-end
-alltext=alltext..";\n"..subtitle
-self:Report(alltext)
-if self.usemarker then
-self:UpdateMarker(_INFORMATION,_RUNACT,_WIND,_ALTIMETER,_TEMPERATURE)
-end
-end
-function ATIS:onafterReport(From,Event,To,Text)
-self:T({From,Event,To})
-self:T(self.lid..string.format("Report:\n%s",Text))
-if self.useSRS and self.msrs then
-local text=string.gsub(Text,"[\r\n]","")
-local statute=self.gettext:GetEntry("STATUTE",self.locale)
-local degc=self.gettext:GetEntry("DEGREES",self.locale)
-local degf=self.gettext:GetEntry("FAHRENHEIT",self.locale)
-local inhg=self.gettext:GetEntry("INCHHG",self.locale)
-local mmhg=self.gettext:GetEntry("MMHG",self.locale)
-local hpa=self.gettext:GetEntry("HECTO",self.locale)
-local emes=self.gettext:GetEntry("METERSPER",self.locale)
-local tacan=self.gettext:GetEntry("TACAN",self.locale)
-local farp=self.gettext:GetEntry("FARP",self.locale)
-local text=string.gsub(text,"SM",statute)
-text=string.gsub(text,"°C",degc)
-text=string.gsub(text,"°F",degf)
-text=string.gsub(text,"inHg",inhg)
-text=string.gsub(text,"mmHg",mmhg)
-text=string.gsub(text,"hPa",hpa)
-text=string.gsub(text,"m/s",emes)
-text=string.gsub(text,"TACAN",tacan)
-text=string.gsub(text,"FARP",farp)
-local delimiter=self.gettext:GetEntry("DELIMITER",self.locale)
-if string.lower(self.locale)~="en"then
-text=string.gsub(text,"(%d+)(%.)(%d+)","%1 "..delimiter.." %3")
-end
-local text=string.gsub(text,";"," . ")
-self:T("SRS TTS: "..text)
-local duration=STTS.getSpeechTime(text,0.95)
-self.msrsQ:NewTransmission(text,duration,self.msrs,nil,2)
-self.SRSText=text
-end
-end
-function ATIS:OnEventBaseCaptured(EventData)
-if EventData and EventData.Place then
-local airbase=EventData.Place
-if EventData.PlaceName==self.airbasename then
-local NewCoalitionAirbase=airbase:GetCoalition()
-if self.useSRS and self.msrs and self.msrs.coalition~=NewCoalitionAirbase then
-self.msrs:SetCoalition(NewCoalitionAirbase)
-end
-end
-end
-end
-function ATIS:UpdateMarker(information,runact,wind,altimeter,temperature)
-if self.markerid then
-self.airbase:GetCoordinate():RemoveMark(self.markerid)
-end
-local text=""
-if type(self.frequency)=="table"then
-local frequency=table.concat(self.frequency,"/")
-local modulation=self.modulation
-if type(modulation)=="table"then
-modulation=table.concat(self.modulation,"/")
-end
-text=string.format("ATIS on %s %s, %s:\n",tostring(frequency),tostring(modulation),tostring(information))
-else
-text=string.format("ATIS on %.3f %s, %s:\n",self.frequency,UTILS.GetModulationName(self.modulation),tostring(information))
-end
-text=text..string.format("%s\n",tostring(runact))
-text=text..string.format("%s\n",tostring(wind))
-text=text..string.format("%s\n",tostring(altimeter))
-text=text..string.format("%s",tostring(temperature))
-self.markerid=self.airbase:GetCoordinate():MarkToAll(text,true)
-return self.markerid
-end
-function ATIS:GetActiveRunway(Takeoff)
-local runway=nil
-if Takeoff then
-runway=self.airbase:GetActiveRunwayTakeoff()
-else
-runway=self.airbase:GetActiveRunwayLanding()
-end
-if runway then
-return runway.name,runway.isLeft
-else
-return nil,nil
-end
-end
-function ATIS:GetMagneticRunway(windfrom)
-local diffmin=nil
-local runway=nil
-for _,heading in pairs(self.runwaymag)do
-local hdg=self:GetRunwayWithoutLR(heading)
-local diff=UTILS.HdgDiff(windfrom,tonumber(hdg)*10)
-if diffmin==nil or diff360 then heading=90 end
-mission.TrackPoint2=Coordinate:Translate(leg,heading,true)
-mission.TrackSpeed=UTILS.IasToTas(UTILS.KnotsToKmph(Speed or 300),mission.TrackAltitude)
-mission.missionSpeed=UTILS.KnotsToKmph(Speed or 300)
-mission.missionAltitude=mission.TrackAltitude*0.9
-mission.missionTask=ENUMS.MissionTask.CAP
-mission.optionROE=ENUMS.ROE.ReturnFire
-mission.optionROT=ENUMS.ROT.PassiveDefense
-mission.categories={AUFTRAG.Category.AIRCRAFT}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewORBIT(Coordinate,Altitude,Speed,Heading,Leg)
-local mission=AUFTRAG:New(AUFTRAG.Type.ORBIT)
-mission:_TargetFromObject(Coordinate)
-if Altitude then
-mission.orbitAltitude=UTILS.FeetToMeters(Altitude)
-else
-mission.orbitAltitude=Coordinate.y
-end
-mission.orbitSpeed=UTILS.IasToTas(UTILS.KnotsToMps(Speed or 350),mission.orbitAltitude)
-mission.missionSpeed=UTILS.KnotsToKmph(Speed or 350)
-if Leg then
-mission.orbitLeg=UTILS.NMToMeters(Leg)
-if Heading and Heading<0 then
-mission.orbitHeadingRel=true
-Heading=-Heading
-end
-mission.orbitHeading=Heading
-end
-mission.missionAltitude=mission.orbitAltitude*0.9
-mission.missionFraction=0.9
-mission.optionROE=ENUMS.ROE.ReturnFire
-mission.optionROT=ENUMS.ROT.PassiveDefense
-mission.categories={AUFTRAG.Category.AIRCRAFT}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewORBIT_CIRCLE(Coordinate,Altitude,Speed)
-local mission=AUFTRAG:NewORBIT(Coordinate,Altitude,Speed)
-return mission
-end
-function AUFTRAG:NewORBIT_RACETRACK(Coordinate,Altitude,Speed,Heading,Leg)
-Heading=Heading or math.random(360)
-Leg=Leg or 10
-local mission=AUFTRAG:NewORBIT(Coordinate,Altitude,Speed,Heading,Leg)
-return mission
-end
-function AUFTRAG:NewORBIT_GROUP(Group,Altitude,Speed,Leg,Heading,OffsetVec2,Distance)
-Altitude=Altitude or 6000
-local mission=AUFTRAG:NewORBIT(Group,Altitude,Speed,Heading,Leg)
-mission.updateDCSTask=true
-if OffsetVec2 then
-if OffsetVec2.x then
-OffsetVec2.x=UTILS.NMToMeters(OffsetVec2.x)
-end
-if OffsetVec2.y then
-OffsetVec2.y=UTILS.NMToMeters(OffsetVec2.y)
-end
-if OffsetVec2.r then
-OffsetVec2.r=UTILS.NMToMeters(OffsetVec2.r)
-end
-end
-mission.orbitOffsetVec2=OffsetVec2
-mission.orbitDeltaR=UTILS.NMToMeters(Distance or 5)
-mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewGCICAP(Coordinate,Altitude,Speed,Heading,Leg)
-local mission=AUFTRAG:NewORBIT_RACETRACK(Coordinate,Altitude,Speed,Heading,Leg)
-mission.type=AUFTRAG.Type.GCICAP
-mission:_SetLogID()
-mission.missionTask=ENUMS.MissionTask.INTERCEPT
-mission.optionROT=ENUMS.ROT.PassiveDefense
-mission.categories={AUFTRAG.Category.AIRCRAFT}
-return mission
-end
-function AUFTRAG:NewTANKER(Coordinate,Altitude,Speed,Heading,Leg,RefuelSystem)
-local mission=AUFTRAG:NewORBIT_RACETRACK(Coordinate,Altitude,Speed,Heading,Leg)
-mission.type=AUFTRAG.Type.TANKER
-mission:_SetLogID()
-mission.refuelSystem=RefuelSystem
-mission.missionTask=ENUMS.MissionTask.REFUELING
-mission.optionROE=ENUMS.ROE.WeaponHold
-mission.optionROT=ENUMS.ROT.PassiveDefense
-mission.categories={AUFTRAG.Category.AIRCRAFT}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewAWACS(Coordinate,Altitude,Speed,Heading,Leg)
-local mission=AUFTRAG:NewORBIT_RACETRACK(Coordinate,Altitude,Speed,Heading,Leg)
-mission.type=AUFTRAG.Type.AWACS
-mission:_SetLogID()
-mission.missionTask=ENUMS.MissionTask.AWACS
-mission.optionROE=ENUMS.ROE.WeaponHold
-mission.optionROT=ENUMS.ROT.PassiveDefense
-mission.categories={AUFTRAG.Category.AIRCRAFT}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewINTERCEPT(Target)
-local mission=AUFTRAG:New(AUFTRAG.Type.INTERCEPT)
-mission:_TargetFromObject(Target)
-mission.missionTask=ENUMS.MissionTask.INTERCEPT
-mission.missionFraction=0.1
-mission.optionROE=ENUMS.ROE.OpenFire
-mission.optionROT=ENUMS.ROT.EvadeFire
-mission.categories={AUFTRAG.Category.AIRCRAFT}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewCAP(ZoneCAP,Altitude,Speed,Coordinate,Heading,Leg,TargetTypes)
-TargetTypes=UTILS.EnsureTable(TargetTypes,true)
-Altitude=Altitude or 10000
-local mission=AUFTRAG:NewORBIT(Coordinate or ZoneCAP:GetCoordinate(),Altitude,Speed or 350,Heading,Leg)
-mission.type=AUFTRAG.Type.CAP
-mission:_SetLogID()
-mission.engageZone=ZoneCAP
-mission.engageTargetTypes=TargetTypes or{"Air"}
-mission.missionTask=ENUMS.MissionTask.CAP
-mission.optionROE=ENUMS.ROE.OpenFire
-mission.optionROT=ENUMS.ROT.EvadeFire
-mission.missionSpeed=UTILS.KnotsToKmph(UTILS.KnotsToAltKIAS(Speed or 350,Altitude))
-mission.categories={AUFTRAG.Category.AIRCRAFT}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewCAPGROUP(Grp,Altitude,Speed,RelHeading,Leg,OffsetDist,OffsetAngle,UpdateDistance,TargetTypes,EngageRange)
-TargetTypes=UTILS.EnsureTable(TargetTypes,true)
-local OffsetVec2={r=OffsetDist or 6,phi=OffsetAngle or 180}
-Leg=Leg or 14
-local Heading=nil
-if RelHeading then
-Heading=-math.abs(RelHeading)
-end
-local mission=AUFTRAG:NewORBIT_GROUP(Grp,Altitude,Speed,Leg,Heading,OffsetVec2,UpdateDistance)
-mission.type=AUFTRAG.Type.CAP
-mission:_SetLogID()
-local engage=EngageRange or 32
-local zoneCAPGroup=ZONE_GROUP:New("CAPGroup",Grp,UTILS.NMToMeters(engage))
-mission.engageZone=zoneCAPGroup
-mission.engageTargetTypes=TargetTypes or{"Air"}
-mission.missionTask=ENUMS.MissionTask.CAP
-mission.optionROE=ENUMS.ROE.OpenFire
-mission.optionROT=ENUMS.ROT.EvadeFire
-mission.categories={AUFTRAG.Category.AIRCRAFT}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewCAS(ZoneCAS,Altitude,Speed,Coordinate,Heading,Leg,TargetTypes)
-TargetTypes=UTILS.EnsureTable(TargetTypes,true)
-local mission=AUFTRAG:NewORBIT(Coordinate or ZoneCAS:GetCoordinate(),Altitude or 10000,Speed,Heading,Leg)
-mission.type=AUFTRAG.Type.CAS
-mission:_SetLogID()
-mission.engageZone=ZoneCAS
-mission.engageTargetTypes=TargetTypes or{"Helicopters","Ground Units","Light armed ships"}
-mission.missionTask=ENUMS.MissionTask.CAS
-mission.optionROE=ENUMS.ROE.OpenFire
-mission.optionROT=ENUMS.ROT.EvadeFire
-mission.categories={AUFTRAG.Category.AIRCRAFT}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewCASENHANCED(CasZone,Altitude,Speed,RangeMax,NoEngageZoneSet,TargetTypes)
-local mission=AUFTRAG:New(AUFTRAG.Type.CASENHANCED)
-if type(CasZone)=="string"then
-CasZone=ZONE:New(CasZone)
-end
-mission:_TargetFromObject(CasZone)
-mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.CASENHANCED)
-mission:SetEngageDetected(RangeMax,TargetTypes or{"Helicopters","Ground Units","Light armed ships"},CasZone,NoEngageZoneSet)
-mission.optionROE=ENUMS.ROE.OpenFire
-mission.optionROT=ENUMS.ROT.EvadeFire
-mission.missionFraction=0.5
-mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed)or nil
-mission.missionAltitude=Altitude and UTILS.FeetToMeters(Altitude)or nil
-mission.dTevaluate=15
-mission.categories={AUFTRAG.Category.AIRCRAFT}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewFAC(FacZone,Speed,Altitude,Frequency,Modulation)
-local mission=AUFTRAG:New(AUFTRAG.Type.FAC)
-if type(FacZone)=="string"then
-FacZone=ZONE:FindByName(FacZone)
-end
-mission:_TargetFromObject(FacZone)
-mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.FAC)
-mission.facFreq=Frequency or 133
-mission.facModu=Modulation or radio.modulation.AM
-mission.optionROE=ENUMS.ROE.ReturnFire
-mission.optionROT=ENUMS.ROT.EvadeFire
-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.categories={AUFTRAG.Category.AIRCRAFT,AUFTRAG.Category.GROUND}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewFACA(Target,Designation,DataLink,Frequency,Modulation)
-local mission=AUFTRAG:New(AUFTRAG.Type.FACA)
-mission:_TargetFromObject(Target)
-mission.facDesignation=Designation
-mission.facDatalink=true
-mission.facFreq=Frequency or 133
-mission.facModu=Modulation or radio.modulation.AM
-mission.missionTask=ENUMS.MissionTask.AFAC
-mission.missionAltitude=nil
-mission.missionFraction=0.5
-mission.optionROE=ENUMS.ROE.ReturnFire
-mission.optionROT=ENUMS.ROT.PassiveDefense
-mission.categories={AUFTRAG.Category.AIRCRAFT}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewBAI(Target,Altitude)
-local mission=AUFTRAG:New(AUFTRAG.Type.BAI)
-mission:_TargetFromObject(Target)
-mission.engageWeaponType=ENUMS.WeaponFlag.Auto
-mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL
-mission.engageAltitude=UTILS.FeetToMeters(Altitude or 5000)
-mission.missionTask=ENUMS.MissionTask.GROUNDATTACK
-mission.missionAltitude=mission.engageAltitude
-mission.missionFraction=0.75
-mission.optionROE=ENUMS.ROE.OpenFire
-mission.optionROT=ENUMS.ROT.PassiveDefense
-mission.categories={AUFTRAG.Category.AIRCRAFT}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewSEAD(Target,Altitude)
-local mission=AUFTRAG:New(AUFTRAG.Type.SEAD)
-mission:_TargetFromObject(Target)
-mission.engageWeaponType=ENUMS.WeaponFlag.Auto
-mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL
-mission.engageAltitude=UTILS.FeetToMeters(Altitude or 25000)
-mission.missionTask=ENUMS.MissionTask.SEAD
-mission.missionAltitude=mission.engageAltitude
-mission.missionFraction=0.2
-mission.optionROE=ENUMS.ROE.OpenFire
-mission.optionROT=ENUMS.ROT.EvadeFire
-mission.categories={AUFTRAG.Category.AIRCRAFT}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewSTRIKE(Target,Altitude)
-local mission=AUFTRAG:New(AUFTRAG.Type.STRIKE)
-mission:_TargetFromObject(Target)
-mission.engageWeaponType=ENUMS.WeaponFlag.Auto
-mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL
-mission.engageAltitude=UTILS.FeetToMeters(Altitude or 2000)
-mission.missionTask=ENUMS.MissionTask.GROUNDATTACK
-mission.missionAltitude=mission.engageAltitude
-mission.missionFraction=0.75
-mission.optionROE=ENUMS.ROE.OpenFire
-mission.optionROT=ENUMS.ROT.PassiveDefense
-mission.categories={AUFTRAG.Category.AIRCRAFT}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewBOMBING(Target,Altitude)
-local mission=AUFTRAG:New(AUFTRAG.Type.BOMBING)
-mission:_TargetFromObject(Target)
-mission.engageWeaponType=ENUMS.WeaponFlag.Auto
-mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL
-mission.engageAltitude=UTILS.FeetToMeters(Altitude or 25000)
-mission.missionTask=ENUMS.MissionTask.GROUNDATTACK
-mission.missionAltitude=mission.engageAltitude*0.8
-mission.missionFraction=0.5
-mission.optionROE=ENUMS.ROE.OpenFire
-mission.optionROT=ENUMS.ROT.NoReaction
-mission.dTevaluate=5*60
-mission.categories={AUFTRAG.Category.AIRCRAFT}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewBOMBRUNWAY(Airdrome,Altitude)
-if type(Airdrome)=="string"then
-Airdrome=AIRBASE:FindByName(Airdrome)
-end
-local mission=AUFTRAG:New(AUFTRAG.Type.BOMBRUNWAY)
-mission:_TargetFromObject(Airdrome)
-mission.engageWeaponType=ENUMS.WeaponFlag.Auto
-mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL
-mission.engageAltitude=UTILS.FeetToMeters(Altitude or 25000)
-mission.missionTask=ENUMS.MissionTask.RUNWAYATTACK
-mission.missionAltitude=mission.engageAltitude*0.8
-mission.missionFraction=0.75
-mission.optionROE=ENUMS.ROE.OpenFire
-mission.optionROT=ENUMS.ROT.PassiveDefense
-mission.dTevaluate=5*60
-mission.categories={AUFTRAG.Category.AIRCRAFT}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewBOMBCARPET(Target,Altitude,CarpetLength)
-local mission=AUFTRAG:New(AUFTRAG.Type.BOMBCARPET)
-mission:_TargetFromObject(Target)
-mission.engageWeaponType=ENUMS.WeaponFlag.Auto
-mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL
-mission.engageAltitude=UTILS.FeetToMeters(Altitude or 25000)
-mission.engageCarpetLength=CarpetLength or 500
-mission.engageAsGroup=false
-mission.engageDirection=nil
-mission.missionTask=ENUMS.MissionTask.GROUNDATTACK
-mission.missionAltitude=mission.engageAltitude*0.8
-mission.missionFraction=0.5
-mission.optionROE=ENUMS.ROE.OpenFire
-mission.optionROT=ENUMS.ROT.NoReaction
-mission.dTevaluate=5*60
-mission.categories={AUFTRAG.Category.AIRCRAFT}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewGROUNDESCORT(EscortGroup,OrbitDistance,TargetTypes)
-local mission=AUFTRAG:New(AUFTRAG.Type.GROUNDESCORT)
-if type(EscortGroup)=="string"then
-mission.escortGroupName=EscortGroup
-mission:_TargetFromObject()
-else
-mission:_TargetFromObject(EscortGroup)
-end
-mission.orbitDistance=OrbitDistance and UTILS.NMToMeters(OrbitDistance)or UTILS.NMToMeters(1.5)
-mission.engageTargetTypes=TargetTypes or{"Ground vehicles"}
-mission.missionTask=ENUMS.MissionTask.GROUNDESCORT
-mission.missionFraction=0.1
-mission.missionAltitude=100
-mission.optionROE=ENUMS.ROE.OpenFire
-mission.optionROT=ENUMS.ROT.EvadeFire
-mission.categories={AUFTRAG.Category.HELICOPTER}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewESCORT(EscortGroup,OffsetVector,EngageMaxDistance,TargetTypes)
-local mission=AUFTRAG:New(AUFTRAG.Type.ESCORT)
-if type(EscortGroup)=="string"then
-mission.escortGroupName=EscortGroup
-mission:_TargetFromObject()
-else
-mission:_TargetFromObject(EscortGroup)
-end
-mission.escortVec3=OffsetVector or{x=-100,y=0,z=200}
-mission.engageMaxDistance=EngageMaxDistance and UTILS.NMToMeters(EngageMaxDistance)or UTILS.NMToMeters(32)
-mission.engageTargetTypes=TargetTypes or{"Air"}
-mission.missionTask=ENUMS.MissionTask.ESCORT
-mission.missionFraction=0.1
-mission.missionAltitude=1000
-mission.optionROE=ENUMS.ROE.OpenFire
-mission.optionROT=ENUMS.ROT.PassiveDefense
-mission.categories={AUFTRAG.Category.AIRCRAFT}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewRESCUEHELO(Carrier)
-local mission=AUFTRAG:New(AUFTRAG.Type.RESCUEHELO)
-mission:_TargetFromObject(Carrier)
-mission.missionTask=ENUMS.MissionTask.NOTHING
-mission.missionFraction=0.5
-mission.optionROE=ENUMS.ROE.WeaponHold
-mission.optionROT=ENUMS.ROT.NoReaction
-mission.categories={AUFTRAG.Category.HELICOPTER}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewRECOVERYTANKER(Carrier,Altitude,Speed,Leg,RelHeading,OffsetDist,OffsetAngle,UpdateDistance)
-local OffsetVec2={r=OffsetDist or 6,phi=OffsetAngle or 180}
-Leg=Leg or 14
-Speed=Speed or 250
-local Heading=nil
-if RelHeading then
-Heading=-math.abs(RelHeading)
-end
-local mission=AUFTRAG:NewORBIT_GROUP(Carrier,Altitude,Speed,Leg,Heading,OffsetVec2,UpdateDistance)
-mission.type=AUFTRAG.Type.RECOVERYTANKER
-mission.missionTask=ENUMS.MissionTask.REFUELING
-mission.missionFraction=0.9
-mission.optionROE=ENUMS.ROE.WeaponHold
-mission.optionROT=ENUMS.ROT.NoReaction
-mission.categories={AUFTRAG.Category.AIRPLANE}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewTROOPTRANSPORT(TransportGroupSet,DropoffCoordinate,PickupCoordinate,PickupRadius)
-local mission=AUFTRAG:New(AUFTRAG.Type.TROOPTRANSPORT)
-if TransportGroupSet:IsInstanceOf("GROUP")then
-mission.transportGroupSet=SET_GROUP:New()
-mission.transportGroupSet:AddGroup(TransportGroupSet)
-elseif TransportGroupSet:IsInstanceOf("SET_GROUP")then
-mission.transportGroupSet=TransportGroupSet
-else
-mission:E(mission.lid.."ERROR: TransportGroupSet must be a GROUP or SET_GROUP object!")
-return nil
-end
-mission:_TargetFromObject(mission.transportGroupSet)
-mission.transportPickup=PickupCoordinate or mission:GetTargetCoordinate()
-mission.transportDropoff=DropoffCoordinate
-mission.transportPickupRadius=PickupRadius or 100
-mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.TROOPTRANSPORT)
-mission.optionROE=ENUMS.ROE.ReturnFire
-mission.optionROT=ENUMS.ROT.PassiveDefense
-mission.categories={AUFTRAG.Category.HELICOPTER,AUFTRAG.Category.GROUND}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewCARGOTRANSPORT(StaticCargo,DropZone)
-local mission=AUFTRAG:New(AUFTRAG.Type.CARGOTRANSPORT)
-mission:_TargetFromObject(StaticCargo)
-mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.CARGOTRANSPORT)
-mission.optionROE=ENUMS.ROE.ReturnFire
-mission.optionROT=ENUMS.ROT.PassiveDefense
-mission.categories={AUFTRAG.Category.HELICOPTER}
-mission.DCStask=mission:GetDCSMissionTask()
-mission.DCStask.params.groupId=StaticCargo:GetID()
-mission.DCStask.params.zoneId=DropZone.ZoneID
-mission.DCStask.params.zone=DropZone
-mission.DCStask.params.cargo=StaticCargo
-return mission
-end
-function AUFTRAG:NewARTY(Target,Nshots,Radius,Altitude)
-local mission=AUFTRAG:New(AUFTRAG.Type.ARTY)
-mission:_TargetFromObject(Target)
-mission.artyShots=Nshots or nil
-mission.artyRadius=Radius or 100
-mission.artyAltitude=Altitude
-mission.engageWeaponType=ENUMS.WeaponFlag.Auto
-mission.optionROE=ENUMS.ROE.OpenFire
-mission.optionAlarm=0
-mission.missionFraction=0.0
-mission.dTevaluate=8*60
-mission.categories={AUFTRAG.Category.GROUND,AUFTRAG.Category.NAVAL}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewBARRAGE(Zone,Heading,Angle,Radius,Altitude,Nshots)
-local mission=AUFTRAG:New(AUFTRAG.Type.BARRAGE)
-mission:_TargetFromObject(Zone)
-mission.artyShots=Nshots
-mission.artyRadius=Radius or 100
-mission.artyAltitude=Altitude
-mission.artyHeading=Heading
-mission.artyAngle=Angle
-mission.engageWeaponType=ENUMS.WeaponFlag.Auto
-mission.optionROE=ENUMS.ROE.OpenFire
-mission.optionAlarm=0
-mission.missionFraction=0.0
-mission.dTevaluate=10
-mission.categories={AUFTRAG.Category.GROUND,AUFTRAG.Category.NAVAL}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewPATROLZONE(Zone,Speed,Altitude,Formation)
-local mission=AUFTRAG:New(AUFTRAG.Type.PATROLZONE)
-if type(Zone)=="string"then
-Zone=ZONE:New(Zone)
-end
-mission:_TargetFromObject(Zone)
-mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.PATROLZONE)
-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.categories={AUFTRAG.Category.ALL}
-mission.DCStask=mission:GetDCSMissionTask()
-mission.DCStask.params.formation=Formation or"Off Road"
-return mission
-end
-function AUFTRAG:NewCAPTUREZONE(OpsZone,Coalition,Speed,Altitude,Formation)
-local mission=AUFTRAG:New(AUFTRAG.Type.CAPTUREZONE)
-mission:_TargetFromObject(OpsZone)
-mission.coalition=Coalition
-mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.CAPTUREZONE)
-mission.optionROE=ENUMS.ROE.ReturnFire
-mission.optionROT=ENUMS.ROT.PassiveDefense
-mission.optionAlarm=ENUMS.AlarmState.Auto
-mission.missionFraction=0.1
-mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed)or nil
-mission.missionAltitude=Altitude and UTILS.FeetToMeters(Altitude)or nil
-mission.categories={AUFTRAG.Category.ALL}
-mission.DCStask=mission:GetDCSMissionTask()
-mission.updateDCSTask=true
-local params={}
-params.formation=Formation or"Off Road"
-params.zone=mission:GetObjective()
-params.altitude=mission.missionAltitude
-params.speed=mission.missionSpeed
-mission.DCStask.params=params
-return mission
-end
-function AUFTRAG:NewARMORATTACK(Target,Speed,Formation)
-local mission=AUFTRAG:NewGROUNDATTACK(Target,Speed,Formation)
-mission.type=AUFTRAG.Type.ARMORATTACK
-return mission
-end
-function AUFTRAG:NewGROUNDATTACK(Target,Speed,Formation)
-local mission=AUFTRAG:New(AUFTRAG.Type.GROUNDATTACK)
-mission:_TargetFromObject(Target)
-mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.GROUNDATTACK)
-mission.optionROE=ENUMS.ROE.OpenFire
-mission.optionAlarm=ENUMS.AlarmState.Auto
-mission.optionFormation="On Road"
-mission.missionFraction=0.70
-mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed)or nil
-mission.categories={AUFTRAG.Category.GROUND}
-mission.DCStask=mission:GetDCSMissionTask()
-mission.DCStask.params.speed=Speed
-mission.DCStask.params.formation=Formation or ENUMS.Formation.Vehicle.Vee
-return mission
-end
-function AUFTRAG:NewRECON(ZoneSet,Speed,Altitude,Adinfinitum,Randomly,Formation)
-local mission=AUFTRAG:New(AUFTRAG.Type.RECON)
-mission:_TargetFromObject(ZoneSet)
-if ZoneSet:IsInstanceOf("SET_ZONE")then
-mission.missionZoneSet=ZoneSet
-elseif ZoneSet:IsInstanceOf("ZONE_BASE")then
-mission.missionZoneSet=SET_ZONE:New()
-mission.missionZoneSet:AddZone(ZoneSet)
-end
-mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.RECON)
-mission.optionROE=ENUMS.ROE.WeaponHold
-mission.optionROT=ENUMS.ROT.PassiveDefense
-mission.optionAlarm=ENUMS.AlarmState.Auto
-mission.missionFraction=0.5
-mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed)or nil
-mission.missionAltitude=Altitude and UTILS.FeetToMeters(Altitude)or UTILS.FeetToMeters(2000)
-mission.categories={AUFTRAG.Category.ALL}
-mission.DCStask=mission:GetDCSMissionTask()
-mission.DCStask.params.adinfinitum=Adinfinitum
-mission.DCStask.params.randomly=Randomly
-mission.DCStask.params.formation=Formation
-return mission
-end
-function AUFTRAG:NewAMMOSUPPLY(Zone)
-local mission=AUFTRAG:New(AUFTRAG.Type.AMMOSUPPLY)
-mission:_TargetFromObject(Zone)
-mission.optionROE=ENUMS.ROE.WeaponHold
-mission.optionAlarm=ENUMS.AlarmState.Auto
-mission.missionFraction=1.0
-mission.missionWaypointRadius=0
-mission.categories={AUFTRAG.Category.GROUND}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewFUELSUPPLY(Zone)
-local mission=AUFTRAG:New(AUFTRAG.Type.FUELSUPPLY)
-mission:_TargetFromObject(Zone)
-mission.optionROE=ENUMS.ROE.WeaponHold
-mission.optionAlarm=ENUMS.AlarmState.Auto
-mission.missionFraction=1.0
-mission.categories={AUFTRAG.Category.GROUND}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewREARMING(Zone)
-local mission=AUFTRAG:New(AUFTRAG.Type.REARMING)
-mission:_TargetFromObject(Zone)
-mission.optionROE=ENUMS.ROE.WeaponHold
-mission.optionAlarm=ENUMS.AlarmState.Auto
-mission.missionFraction=1.0
-mission.missionWaypointRadius=0
-mission.categories={AUFTRAG.Category.GROUND}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewALERT5(MissionType)
-local mission=AUFTRAG:New(AUFTRAG.Type.ALERT5)
-mission.missionTask=self:GetMissionTaskforMissionType(MissionType)
-mission.optionROE=ENUMS.ROE.WeaponHold
-mission.optionROT=ENUMS.ROT.NoReaction
-mission.alert5MissionType=MissionType
-mission.missionFraction=1.0
-mission.categories={AUFTRAG.Category.AIRCRAFT}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewONGUARD(Coordinate)
-local mission=AUFTRAG:New(AUFTRAG.Type.ONGUARD)
-mission:_TargetFromObject(Coordinate)
-mission.optionROE=ENUMS.ROE.OpenFire
-mission.optionAlarm=ENUMS.AlarmState.Auto
-mission.missionFraction=1.0
-mission.categories={AUFTRAG.Category.GROUND,AUFTRAG.Category.NAVAL}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewAIRDEFENSE(Zone)
-local mission=AUFTRAG:New(AUFTRAG.Type.AIRDEFENSE)
-mission:_TargetFromObject(Zone)
-mission.optionROE=ENUMS.ROE.OpenFire
-mission.optionAlarm=ENUMS.AlarmState.Auto
-mission.missionFraction=1.0
-mission.categories={AUFTRAG.Category.GROUND,AUFTRAG.Category.NAVAL}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewEWR(Zone)
-local mission=AUFTRAG:New(AUFTRAG.Type.EWR)
-mission:_TargetFromObject(Zone)
-mission.optionROE=ENUMS.ROE.WeaponHold
-mission.optionAlarm=ENUMS.AlarmState.Auto
-mission.missionFraction=1.0
-mission.categories={AUFTRAG.Category.GROUND}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:_NewRELOCATECOHORT(Legion,Cohort)
-local mission=AUFTRAG:New(AUFTRAG.Type.RELOCATECOHORT)
-mission:_TargetFromObject(Legion.spawnzone)
-mission.optionROE=ENUMS.ROE.ReturnFire
-mission.optionAlarm=ENUMS.AlarmState.Auto
-mission.missionFraction=0.0
-mission.categories={AUFTRAG.Category.ALL}
-mission.DCStask=mission:GetDCSMissionTask()
-if Cohort.isGround then
-mission.optionFormation=ENUMS.Formation.Vehicle.OnRoad
-end
-mission.DCStask.params.legion=Legion
-mission.DCStask.params.cohort=Cohort
-return mission
-end
-function AUFTRAG:NewNOTHING(RelaxZone)
-local mission=AUFTRAG:New(AUFTRAG.Type.NOTHING)
-mission:_TargetFromObject(RelaxZone)
-mission.optionROE=ENUMS.ROE.WeaponHold
-mission.optionAlarm=ENUMS.AlarmState.Auto
-mission.missionFraction=1.0
-mission.categories={AUFTRAG.Category.GROUND,AUFTRAG.Category.NAVAL}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewARMOREDGUARD(Coordinate,Formation)
-local mission=AUFTRAG:New(AUFTRAG.Type.ARMOREDGUARD)
-mission:_TargetFromObject(Coordinate)
-mission.optionROE=ENUMS.ROE.OpenFire
-mission.optionAlarm=ENUMS.AlarmState.Auto
-mission.optionFormation=Formation or"On Road"
-mission.missionFraction=1.0
-mission.categories={AUFTRAG.Category.GROUND}
-mission.DCStask=mission:GetDCSMissionTask()
-return mission
-end
-function AUFTRAG:NewFromTarget(Target,MissionType)
-local mission=nil
-if MissionType==AUFTRAG.Type.ANTISHIP then
-mission=self:NewANTISHIP(Target,Altitude)
-elseif MissionType==AUFTRAG.Type.ARTY then
-mission=self:NewARTY(Target,Nshots,Radius)
-elseif MissionType==AUFTRAG.Type.BAI then
-mission=self:NewBAI(Target,Altitude)
-elseif MissionType==AUFTRAG.Type.BOMBCARPET then
-mission=self:NewBOMBCARPET(Target,Altitude,CarpetLength)
-elseif MissionType==AUFTRAG.Type.BOMBING then
-mission=self:NewBOMBING(Target,Altitude)
-elseif MissionType==AUFTRAG.Type.BOMBRUNWAY then
-mission=self:NewBOMBRUNWAY(Target,Altitude)
-elseif MissionType==AUFTRAG.Type.CAS then
-mission=self:NewCAS(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000),Altitude,Speed,Target:GetAverageCoordinate(),Heading,Leg,TargetTypes)
-elseif MissionType==AUFTRAG.Type.CASENHANCED then
-mission=self:NewCASENHANCED(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000),Altitude,Speed,RangeMax,NoEngageZoneSet,TargetTypes)
-elseif MissionType==AUFTRAG.Type.INTERCEPT then
-mission=self:NewINTERCEPT(Target)
-elseif MissionType==AUFTRAG.Type.SEAD then
-mission=self:NewSEAD(Target,Altitude)
-elseif MissionType==AUFTRAG.Type.STRIKE then
-mission=self:NewSTRIKE(Target,Altitude)
-elseif MissionType==AUFTRAG.Type.ARMORATTACK then
-mission=self:NewARMORATTACK(Target,Speed)
-elseif MissionType==AUFTRAG.Type.GROUNDATTACK then
-mission=self:NewGROUNDATTACK(Target,Speed,Formation)
-else
-return nil
-end
-return mission
-end
-function AUFTRAG:_DetermineAuftragType(Target)
-local group=nil
-local airbase=nil
-local scenery=nil
-local coordinate=nil
-local auftrag=nil
-if Target:IsInstanceOf("GROUP")then
-group=Target
-elseif Target:IsInstanceOf("UNIT")then
-group=Target:GetGroup()
-elseif Target:IsInstanceOf("AIRBASE")then
-airbase=Target
-elseif Target:IsInstanceOf("SCENERY")then
-scenery=Target
-end
-if group then
-local category=group:GetCategory()
-local attribute=group:GetAttribute()
-if category==Group.Category.AIRPLANE or category==Group.Category.HELICOPTER then
-auftrag=AUFTRAG.Type.INTERCEPT
-elseif category==Group.Category.GROUND or category==Group.Category.TRAIN then
-if attribute==GROUP.Attribute.GROUND_SAM then
-auftrag=AUFTRAG.Type.SEAD
-elseif attribute==GROUP.Attribute.GROUND_AAA then
-auftrag=AUFTRAG.Type.BAI
-elseif attribute==GROUP.Attribute.GROUND_ARTILLERY then
-auftrag=AUFTRAG.Type.BAI
-elseif attribute==GROUP.Attribute.GROUND_INFANTRY then
-auftrag=AUFTRAG.Type.CAS
-elseif attribute==GROUP.Attribute.GROUND_TANK then
-auftrag=AUFTRAG.Type.BAI
-else
-auftrag=AUFTRAG.Type.BAI
-end
-elseif category==Group.Category.SHIP then
-auftrag=AUFTRAG.Type.ANTISHIP
-else
-self:T(self.lid.."ERROR: Unknown Group category!")
-end
-elseif airbase then
-auftrag=AUFTRAG.Type.BOMBRUNWAY
-elseif scenery then
-auftrag=AUFTRAG.Type.STRIKE
-elseif coordinate then
-auftrag=AUFTRAG.Type.BOMBING
-end
-return auftrag
-end
-function AUFTRAG:NewAUTO(EngageGroup)
-local mission=nil
-local Target=EngageGroup
-local auftrag=self:_DetermineAuftragType(EngageGroup)
-if auftrag==AUFTRAG.Type.ANTISHIP then
-mission=AUFTRAG:NewANTISHIP(Target)
-elseif auftrag==AUFTRAG.Type.ARTY then
-mission=AUFTRAG:NewARTY(Target)
-elseif auftrag==AUFTRAG.Type.AWACS then
-mission=AUFTRAG:NewAWACS(Coordinate,Altitude,Speed,Heading,Leg)
-elseif auftrag==AUFTRAG.Type.BAI then
-mission=AUFTRAG:NewBAI(Target,Altitude)
-elseif auftrag==AUFTRAG.Type.BOMBING then
-mission=AUFTRAG:NewBOMBING(Target,Altitude)
-elseif auftrag==AUFTRAG.Type.BOMBRUNWAY then
-mission=AUFTRAG:NewBOMBRUNWAY(Airdrome,Altitude)
-elseif auftrag==AUFTRAG.Type.BOMBCARPET then
-mission=AUFTRAG:NewBOMBCARPET(Target,Altitude,CarpetLength)
-elseif auftrag==AUFTRAG.Type.CAP then
-mission=AUFTRAG:NewCAP(ZoneCAP,Altitude,Speed,Coordinate,Heading,Leg,TargetTypes)
-elseif auftrag==AUFTRAG.Type.CAS then
-mission=AUFTRAG:NewCAS(ZoneCAS,Altitude,Speed,Coordinate,Heading,Leg,TargetTypes)
-elseif auftrag==AUFTRAG.Type.ESCORT then
-mission=AUFTRAG:NewESCORT(EscortGroup,OffsetVector,EngageMaxDistance,TargetTypes)
-elseif auftrag==AUFTRAG.Type.FACA then
-mission=AUFTRAG:NewFACA(Target,Designation,DataLink,Frequency,Modulation)
-elseif auftrag==AUFTRAG.Type.FERRY then
-elseif auftrag==AUFTRAG.Type.GCICAP then
-mission=AUFTRAG:NewGCICAP(Coordinate,Altitude,Speed,Heading,Leg)
-elseif auftrag==AUFTRAG.Type.INTERCEPT then
-mission=AUFTRAG:NewINTERCEPT(Target)
-elseif auftrag==AUFTRAG.Type.ORBIT then
-mission=AUFTRAG:NewORBIT(Coordinate,Altitude,Speed,Heading,Leg)
-elseif auftrag==AUFTRAG.Type.RECON then
-mission=AUFTRAG:NewRECON(ZoneSet,Speed,Altitude,Adinfinitum,Randomly,Formation)
-elseif auftrag==AUFTRAG.Type.RESCUEHELO then
-mission=AUFTRAG:NewRESCUEHELO(Carrier)
-elseif auftrag==AUFTRAG.Type.SEAD then
-mission=AUFTRAG:NewSEAD(Target,Altitude)
-elseif auftrag==AUFTRAG.Type.STRIKE then
-mission=AUFTRAG:NewSTRIKE(Target,Altitude)
-elseif auftrag==AUFTRAG.Type.TANKER then
-mission=AUFTRAG:NewTANKER(Coordinate,Altitude,Speed,Heading,Leg,RefuelSystem)
-elseif auftrag==AUFTRAG.Type.TROOPTRANSPORT then
-mission=AUFTRAG:NewTROOPTRANSPORT(TransportGroupSet,DropoffCoordinate,PickupCoordinate)
-elseif auftrag==AUFTRAG.Type.PATROLRACETRACK then
-mission=AUFTRAG:NewPATROL_RACETRACK(Coordinate,Altitude,Speed,Heading,Leg,Formation)
-else
-end
-if mission then
-mission:SetPriority(10,true)
-end
-return mission
-end
-function AUFTRAG:SetTime(ClockStart,ClockStop)
-local Tnow=timer.getAbsTime()
-local Tstart=Tnow+5
-if ClockStart and type(ClockStart)=="number"then
-Tstart=Tnow+ClockStart
-elseif ClockStart and type(ClockStart)=="string"then
-Tstart=UTILS.ClockToSeconds(ClockStart)
-end
-local Tstop=nil
-if ClockStop and type(ClockStop)=="number"then
-Tstop=Tnow+ClockStop
-elseif ClockStop and type(ClockStop)=="string"then
-Tstop=UTILS.ClockToSeconds(ClockStop)
-end
-self.Tstart=Tstart
-self.Tstop=Tstop
-if Tstop then
-self.duration=self.Tstop-self.Tstart
-end
-return self
-end
-function AUFTRAG:SetDuration(Duration)
-self.durationExe=Duration
-return self
-end
-function AUFTRAG:SetTeleport(Switch)
-if Switch==nil then
-Switch=true
-end
-self.teleport=Switch
-return self
-end
-function AUFTRAG:SetReturnToLegion(Switch)
-self.legionReturn=Switch
-self:T(self.lid..string.format("Setting ReturnToLetion=%s",tostring(self.legionReturn)))
-return self
-end
-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
-function AUFTRAG:SetPriority(Prio,Urgent,Importance)
-self.prio=Prio or 50
-self.urgent=Urgent
-self.importance=Importance
-return self
-end
-function AUFTRAG:SetRepeat(Nrepeat)
-self.Nrepeat=Nrepeat or 0
-return self
-end
-function AUFTRAG:SetRepeatOnFailure(Nrepeat)
-self.NrepeatFailure=Nrepeat or 0
-return self
-end
-function AUFTRAG:SetRepeatOnSuccess(Nrepeat)
-self.NrepeatSuccess=Nrepeat or 0
-return self
-end
-function AUFTRAG:SetReinforce(Nreinforce)
-self.reinforce=Nreinforce
-return self
-end
-function AUFTRAG:SetRequiredAssets(NassetsMin,NassetsMax)
-self.NassetsMin=NassetsMin or 1
-self.NassetsMax=NassetsMax or self.NassetsMin
-if self.NassetsMax0 then
-local N=self:CountOpsGroups()
-if N Nmin=%d",self.NassetsMin,N,self.reinforce,Nmin))
-end
-end
-end
-return Nmin,Nmax
-end
-function AUFTRAG:SetAssetsStayAlive(Switch)
-if Switch==nil then
-Switch=true
-end
-self.assetStayAlive=Switch
-return self
-end
-function AUFTRAG:SetRequiredEscorts(NescortMin,NescortMax,MissionType,TargetTypes,EngageRange)
-self.NescortMin=NescortMin or 1
-self.NescortMax=NescortMax or self.NescortMin
-if self.NescortMaxself.Tstop then
-return false
-end
-local startme=self:EvalConditionsAll(self.conditionStart)
-if not startme then
-return false
-end
-return true
-end
-function AUFTRAG:IsReadyToCancel()
-local Tnow=timer.getAbsTime()
-if self.Tstop and Tnow>=self.Tstop then
-return true
-end
-local failure=self:EvalConditionsAny(self.conditionFailure)
-if failure then
-self.failurecondition=true
-return true
-end
-local success=self:EvalConditionsAny(self.conditionSuccess)
-if success then
-self.successcondition=true
-return true
-end
-return false
-end
-function AUFTRAG:IsReadyToPush()
-local Tnow=timer.getAbsTime()
-if self.Tpush and Tnow<=self.Tpush then
-return false
-end
-local push=self:EvalConditionsAll(self.conditionPush)
-return push
-end
-function AUFTRAG:EvalConditionsAll(Conditions)
-for _,_condition in pairs(Conditions or{})do
-local condition=_condition
-local istrue=condition.func(unpack(condition.arg))
-if not istrue then
-return false
-end
-end
-return true
-end
-function AUFTRAG:EvalConditionsAny(Conditions)
-for _,_condition in pairs(Conditions or{})do
-local condition=_condition
-local istrue=condition.func(unpack(condition.arg))
-if istrue then
-return true
-end
-end
-return false
-end
-function AUFTRAG:onafterStatus(From,Event,To)
-local Tnow=timer.getAbsTime()
-if self.escortGroupName then
-local group=GROUP:FindByName(self.escortGroupName)
-if group and group:IsAlive()then
-self:T(self.lid..string.format("ESCORT group %s is now alive. Updating DCS task and adding group to TARGET",tostring(self.escortGroupName)))
-self.engageTarget:AddObject(group)
-self.DCStask=self:GetDCSMissionTask()
-self.escortGroupName=nil
-end
-end
-local Ntargets=self:CountMissionTargets()
-local Ntargets0=self:GetTargetInitialNumber()
-local Ngroups=self:CountOpsGroups()
-local Nassigned=self.Nassigned and self.Nassigned-self.Ndead or 0
-local conditionDone=false
-if self.conditionFailureSet then
-conditionDone=self:EvalConditionsAny(self.conditionFailure)
-end
-if self.conditionSuccessSet and not conditionDone then
-conditionDone=self:EvalConditionsAny(self.conditionSuccess)
-end
-if self:IsNotOver()then
-if self:CheckGroupsDone()then
-self:Done()
-elseif(self.Tstop and Tnow>self.Tstop+10)then
-self:Cancel()
-elseif conditionDone then
-self:Cancel()
-elseif self.durationExe and self.Texecuting and Tnow-self.Texecuting>self.durationExe then
-local Nrepeat=self.Nrepeat
-local NrepeatS=self.NrepeatSuccess
-local NrepeatF=self.NrepeatFailure
-self:Cancel()
-self.Nrepeat=Nrepeat
-self.NrepeatSuccess=NrepeatS
-self.NrepeatFailure=NrepeatF
-elseif(Ntargets0>0 and Ntargets==0)then
-self:T(self.lid.."No targets left cancelling mission!")
-self:Cancel()
-elseif self:IsExecuting()and self:_IsNotReinforcing()then
-if Ngroups==0 then
-self:Done()
-else
-local done=true
-for groupname,data in pairs(self.groupdata or{})do
-local groupdata=data
-local opsgroup=groupdata.opsgroup
-if opsgroup:IsAlive()then
-done=false
-end
-end
-if done then
-self:Done()
-end
-end
-end
-end
-local fsmstate=self:GetState()
-if fsmstate~=self.status then
-self:T(self.lid..string.format("ERROR: FSM state %s != %s mission status!",fsmstate,self.status))
-end
-if self.verbose>=1 then
-local Cstart=UTILS.SecondsToClock(self.Tstart,true)
-local Cstop=self.Tstop and UTILS.SecondsToClock(self.Tstop,true)or"INF"
-local targetname=self:GetTargetName()or"unknown"
-local Nlegions=#self.legions
-local commander=self.commander and self.statusCommander or"N/A"
-local chief=self.chief and self.statusChief or"N/A"
-self:T(self.lid..string.format("Status %s: Target=%s, T=%s-%s, assets=%d, groups=%d, targets=%d, legions=%d, commander=%s, chief=%s",
-self.status,targetname,Cstart,Cstop,#self.assets,Ngroups,Ntargets,Nlegions,commander,chief))
-end
-if self.verbose>=2 then
-local text="Group data:"
-for groupname,_groupdata in pairs(self.groupdata)do
-local groupdata=_groupdata
-text=text..string.format("\n- %s: status mission=%s opsgroup=%s",groupname,groupdata.status,groupdata.opsgroup and groupdata.opsgroup:GetState()or"N/A")
-end
-self:I(self.lid..text)
-end
-if self.verbose>=3 then
-local text=string.format("Assets [N=%d,Nassigned=%s, Ndead=%s]:",self.Nassets or 0,self.Nassigned or 0,self.Ndead or 0)
-for i,_asset in pairs(self.assets or{})do
-local asset=_asset
-text=text..string.format("\n[%d] %s: spawned=%s, requested=%s, reserved=%s",i,asset.spawngroupname,tostring(asset.spawned),tostring(asset.requested),tostring(asset.reserved))
-end
-self:I(self.lid..text)
-end
-local ready2evaluate=self.Tover and Tnow-self.Tover>=self.dTevaluate or false
-if self:IsOver()and ready2evaluate then
-self:Evaluate()
-else
-self:__Status(-30)
-end
-if self.markerOn then
-self:UpdateMarker()
-end
-end
-function AUFTRAG:Evaluate()
-local failed=false
-local targetdamage=self:GetTargetDamage()
-local owndamage=self.Ncasualties/self.Nelements*100
-local Ntargets=self:CountMissionTargets()
-local Ntargets0=self:GetTargetInitialNumber()
-local Life=self:GetTargetLife()
-local Life0=self:GetTargetInitialLife()
-if Ntargets0>0 then
-if self.type==AUFTRAG.Type.TROOPTRANSPORT or self.type==AUFTRAG.Type.ESCORT then
-if Ntargets0 then
-failed=true
-end
-end
-else
-if self.Nelements==self.Ncasualties then
-failed=true
-end
-end
-local successCondition=self:EvalConditionsAny(self.conditionSuccess)
-local failureCondition=self:EvalConditionsAny(self.conditionFailure)
-if failureCondition then
-failed=true
-elseif successCondition then
-failed=false
-end
-if self.verbose>0 then
-local text=string.format("Evaluating mission:\n")
-text=text..string.format("Own casualties = %d/%d\n",self.Ncasualties,self.Nelements)
-text=text..string.format("Own losses = %.1f %%\n",owndamage)
-text=text..string.format("Killed units = %d\n",self.Nkills)
-text=text..string.format("--------------------------\n")
-text=text..string.format("Targets left = %d/%d\n",Ntargets,Ntargets0)
-text=text..string.format("Targets life = %.1f/%.1f\n",Life,Life0)
-text=text..string.format("Enemy losses = %.1f %%\n",targetdamage)
-text=text..string.format("--------------------------\n")
-text=text..string.format("Success Cond = %s\n",tostring(successCondition))
-text=text..string.format("Failure Cond = %s\n",tostring(failureCondition))
-text=text..string.format("--------------------------\n")
-text=text..string.format("Final Success = %s\n",tostring(not failed))
-text=text..string.format("=========================")
-self:I(self.lid..text)
-end
-if failed then
-self:I(self.lid..string.format("Mission %d [%s] failed!",self.auftragsnummer,self.type))
-if self.chief then
-self.chief.Nfailure=self.chief.Nfailure+1
-end
-self:Failed()
-else
-self:I(self.lid..string.format("Mission %d [%s] success!",self.auftragsnummer,self.type))
-if self.chief then
-self.chief.Nsuccess=self.chief.Nsuccess+1
-end
-self:Success()
-end
-return self
-end
-function AUFTRAG:GetOpsGroups()
-local opsgroups={}
-for _,_groupdata in pairs(self.groupdata or{})do
-local groupdata=_groupdata
-table.insert(opsgroups,groupdata.opsgroup)
-end
-return opsgroups
-end
-function AUFTRAG:GetAssetDataByName(AssetName)
-return self.groupdata[tostring(AssetName)]
-end
-function AUFTRAG:GetGroupData(opsgroup)
-if opsgroup and self.groupdata then
-return self.groupdata[opsgroup.groupname]
-end
-return nil
-end
-function AUFTRAG:SetGroupStatus(opsgroup,status)
-local oldstatus=self:GetGroupStatus(opsgroup)
-self:T(self.lid..string.format("Setting OPSGROUP %s to status %s-->%s",opsgroup and opsgroup.groupname or"nil",tostring(oldstatus),tostring(status)))
-if oldstatus==AUFTRAG.GroupStatus.CANCELLED and status==AUFTRAG.GroupStatus.DONE then
-else
-local groupdata=self:GetGroupData(opsgroup)
-if groupdata then
-groupdata.status=status
-else
-self:T(self.lid.."WARNING: Could not SET flight data for flight group. Setting status to DONE")
-end
-end
-local isNotOver=self:IsNotOver()
-local groupsDone=self:CheckGroupsDone()
-self:T2(self.lid..string.format("Setting OPSGROUP %s status to %s. IsNotOver=%s CheckGroupsDone=%s",opsgroup.groupname,self:GetGroupStatus(opsgroup),tostring(self:IsNotOver()),tostring(groupsDone)))
-if isNotOver and groupsDone then
-self:T3(self.lid.."All assigned OPSGROUPs done ==> mission DONE!")
-self:Done()
-else
-self:T3(self.lid.."Mission NOT DONE yet!")
-end
-return self
-end
-function AUFTRAG:GetGroupStatus(opsgroup)
-self:T3(self.lid..string.format("Trying to get Flight status for flight group %s",opsgroup and opsgroup.groupname or"nil"))
-local groupdata=self:GetGroupData(opsgroup)
-if groupdata then
-return groupdata.status
-else
-self:T(self.lid..string.format("WARNING: Could not GET groupdata for opsgroup %s. Returning status DONE.",opsgroup and opsgroup.groupname or"nil"))
-return AUFTRAG.GroupStatus.DONE
-end
-end
-function AUFTRAG:AddLegion(Legion)
-self:T(self.lid..string.format("Adding legion %s",Legion.alias))
-table.insert(self.legions,Legion)
-return self
-end
-function AUFTRAG:RemoveLegion(Legion)
-for i=#self.legions,1,-1 do
-local legion=self.legions[i]
-if legion.alias==Legion.alias then
-self:T(self.lid..string.format("Removing legion %s",Legion.alias))
-table.remove(self.legions,i)
-self.statusLegion[Legion.alias]=nil
-return self
-end
-end
-self:T(self.lid..string.format("ERROR: Legion %s not found and could not be removed!",Legion.alias))
-return self
-end
-function AUFTRAG:SetLegionStatus(Legion,Status)
-local status=self:GetLegionStatus(Legion)
-self:T(self.lid..string.format("Setting LEGION %s to status %s-->%s",Legion.alias,tostring(status),tostring(Status)))
-self.statusLegion[Legion.alias]=Status
-return self
-end
-function AUFTRAG:GetLegionStatus(Legion)
-local status=self.statusLegion[Legion.alias]or"unknown"
-return status
-end
-function AUFTRAG:SetGroupWaypointCoordinate(opsgroup,coordinate)
-local groupdata=self:GetGroupData(opsgroup)
-if groupdata then
-groupdata.waypointcoordinate=coordinate
-end
-return self
-end
-function AUFTRAG:GetGroupWaypointCoordinate(opsgroup)
-local groupdata=self:GetGroupData(opsgroup)
-if groupdata then
-return groupdata.waypointcoordinate
-end
-end
-function AUFTRAG:SetGroupWaypointTask(opsgroup,task)
-self:T2(self.lid..string.format("Setting waypoint task %s",task and task.description or"WTF"))
-local groupdata=self:GetGroupData(opsgroup)
-if groupdata then
-groupdata.waypointtask=task
-end
-end
-function AUFTRAG:GetGroupWaypointTask(opsgroup)
-local groupdata=self:GetGroupData(opsgroup)
-if groupdata then
-return groupdata.waypointtask
-end
-end
-function AUFTRAG:SetGroupWaypointIndex(opsgroup,waypointindex)
-self:T2(self.lid..string.format("Setting Mission waypoint UID=%d",waypointindex))
-local groupdata=self:GetGroupData(opsgroup)
-if groupdata then
-groupdata.waypointindex=waypointindex
-end
-return self
-end
-function AUFTRAG:GetGroupWaypointIndex(opsgroup)
-local groupdata=self:GetGroupData(opsgroup)
-if groupdata then
-return groupdata.waypointindex
-end
-end
-function AUFTRAG:SetGroupEgressWaypointUID(opsgroup,waypointindex)
-self:T2(self.lid..string.format("Setting Egress waypoint UID=%d",waypointindex))
-local groupdata=self:GetGroupData(opsgroup)
-if groupdata then
-groupdata.waypointEgressUID=waypointindex
-end
-return self
-end
-function AUFTRAG:GetGroupEgressWaypointUID(opsgroup)
-local groupdata=self:GetGroupData(opsgroup)
-if groupdata then
-return groupdata.waypointEgressUID
-end
-end
-function AUFTRAG:CheckGroupsDone()
-for groupname,data in pairs(self.groupdata)do
-local groupdata=data
-if groupdata then
-if not(groupdata.status==AUFTRAG.GroupStatus.DONE or groupdata.status==AUFTRAG.GroupStatus.CANCELLED)then
-self:T2(self.lid..string.format("CheckGroupsDone: OPSGROUP %s is not DONE or CANCELLED but in state %s. Mission NOT DONE!",groupdata.opsgroup.groupname,groupdata.status:upper()))
-return false
-end
-end
-end
-for _,_legion in pairs(self.legions)do
-local legion=_legion
-local status=self:GetLegionStatus(legion)
-if not status==AUFTRAG.Status.CANCELLED then
-self:T2(self.lid..string.format("CheckGroupsDone: LEGION %s is not CANCELLED but in state %s. Mission NOT DONE!",legion.alias,status))
-return false
-end
-end
-if self.commander then
-if not self.statusCommander==AUFTRAG.Status.CANCELLED then
-self:T2(self.lid..string.format("CheckGroupsDone: COMMANDER is not CANCELLED but in state %s. Mission NOT DONE!",self.statusCommander))
-return false
-end
-end
-if self.chief then
-if not self.statusChief==AUFTRAG.Status.CANCELLED then
-self:T2(self.lid..string.format("CheckGroupsDone: CHIEF is not CANCELLED but in state %s. Mission NOT DONE!",self.statusChief))
-return false
-end
-end
-if self:IsPlanned()or self:IsQueued()or self:IsRequested()then
-self:T2(self.lid..string.format("CheckGroupsDone: Mission is still in state %s [FSM=%s] (PLANNED or QUEUED or REQUESTED). Mission NOT DONE!",self.status,self:GetState()))
-return false
-end
-if self:IsExecuting()and self:_IsReinforcing()then
-self:T2(self.lid..string.format("CheckGroupsDone: Mission is still in state %s [FSM=%s] and reinfoce=%d. Mission NOT DONE!",self.status,self:GetState(),self.reinforce))
-return false
-end
-if self:IsStarted()and self:CountOpsGroups()==0 then
-self:T(self.lid..string.format("CheckGroupsDone: Mission is STARTED state %s [FSM=%s] but count of alive OPSGROUP is zero. Mission DONE!",self.status,self:GetState()))
-return true
-end
-return true
-end
-function AUFTRAG:OnEventUnitLost(EventData)
-if EventData and EventData.IniGroup and EventData.IniUnit then
-local unit=EventData.IniUnit
-local group=EventData.IniGroup
-local unitname=EventData.IniUnitName
-for _,_groupdata in pairs(self.groupdata)do
-local groupdata=_groupdata
-if groupdata and groupdata.opsgroup and groupdata.opsgroup.groupname==EventData.IniGroupName then
-self:T(self.lid..string.format("UNIT LOST event for opsgroup %s unit %s",groupdata.opsgroup.groupname,EventData.IniUnitName))
-end
-end
-end
-end
-function AUFTRAG:onafterPlanned(From,Event,To)
-self.status=AUFTRAG.Status.PLANNED
-self:T(self.lid..string.format("New mission status=%s",self.status))
-end
-function AUFTRAG:onafterQueued(From,Event,To,Airwing)
-self.status=AUFTRAG.Status.QUEUED
-self:T(self.lid..string.format("New mission status=%s",self.status))
-end
-function AUFTRAG:onafterRequested(From,Event,To)
-self.status=AUFTRAG.Status.REQUESTED
-self:T(self.lid..string.format("New mission status=%s",self.status))
-end
-function AUFTRAG:onafterAssign(From,Event,To)
-self.status=AUFTRAG.Status.ASSIGNED
-self:T(self.lid..string.format("New mission status=%s",self.status))
-end
-function AUFTRAG:onafterScheduled(From,Event,To)
-self.status=AUFTRAG.Status.SCHEDULED
-self:T(self.lid..string.format("New mission status=%s",self.status))
-end
-function AUFTRAG:onafterStarted(From,Event,To)
-self.status=AUFTRAG.Status.STARTED
-self.Tstarted=timer.getAbsTime()
-self:T(self.lid..string.format("New mission status=%s",self.status))
-end
-function AUFTRAG:onafterExecuting(From,Event,To)
-self.status=AUFTRAG.Status.EXECUTING
-self.Texecuting=timer.getAbsTime()
-self:T(self.lid..string.format("New mission status=%s",self.status))
-end
-function AUFTRAG:onafterElementDestroyed(From,Event,To,OpsGroup,Element)
-self.Ncasualties=self.Ncasualties+1
-end
-function AUFTRAG:onafterGroupDead(From,Event,To,OpsGroup)
-local asset=self:GetAssetByName(OpsGroup.groupname)
-if asset then
-self:AssetDead(asset)
-end
-self.Ndead=self.Ndead+1
-end
-function AUFTRAG:onafterAssetDead(From,Event,To,Asset)
-local N=self:CountOpsGroups()
-local notreinforcing=self:_IsNotReinforcing()
-self:T(self.lid..string.format("Asset %s dead! Number of ops groups remaining %d (reinforcing=%s)",tostring(Asset.spawngroupname),N,tostring(not notreinforcing)))
-if N==0 and notreinforcing then
-if self:IsNotOver()then
-self:Cancel()
-else
-end
-end
-self:DelAsset(Asset)
-end
-function AUFTRAG:onafterCancel(From,Event,To)
-local Ngroups=self:CountOpsGroups()
-self:T(self.lid..string.format("CANCELLING mission in status %s. Will wait for %d groups to report mission DONE before evaluation",self.status,Ngroups))
-self.Tover=timer.getAbsTime()
-self.Nrepeat=self.repeated
-self.NrepeatFailure=self.repeatedFailure
-self.NrepeatSuccess=self.repeatedSuccess
-self.dTevaluate=0
-if self.chief then
-self:T(self.lid..string.format("CHIEF will cancel the mission. Will wait for mission DONE before evaluation!"))
-self.chief:MissionCancel(self)
-elseif self.commander then
-self:T(self.lid..string.format("COMMANDER will cancel the mission. Will wait for mission DONE before evaluation!"))
-self.commander:MissionCancel(self)
-elseif self.legions and#self.legions>0 then
-for _,_legion in pairs(self.legions or{})do
-local legion=_legion
-self:T(self.lid..string.format("LEGION %s will cancel the mission. Will wait for mission DONE before evaluation!",legion.alias))
-legion:MissionCancel(self)
-end
-else
-self:T(self.lid..string.format("No legion, commander or chief. Attached groups will cancel the mission on their own. Will wait for mission DONE before evaluation!"))
-for _,_groupdata in pairs(self.groupdata or{})do
-local groupdata=_groupdata
-groupdata.opsgroup:MissionCancel(self)
-end
-end
-if self:IsPlanned()or self:IsQueued()or self:IsRequested()or Ngroups==0 then
-self:T(self.lid..string.format("Cancelled mission was in %s stage with %d groups assigned and alive. Call it done!",self.status,Ngroups))
-self:Done()
-end
-end
-function AUFTRAG:onafterDone(From,Event,To)
-self.status=AUFTRAG.Status.DONE
-self:T(self.lid..string.format("New mission status=%s",self.status))
-self.Tover=timer.getAbsTime()
-self.Texecuting=nil
-self.statusChief=AUFTRAG.Status.DONE
-self.statusCommander=AUFTRAG.Status.DONE
-for _,_legion in pairs(self.legions)do
-local Legion=_legion
-self:SetLegionStatus(Legion,AUFTRAG.Status.DONE)
-if self.type==AUFTRAG.Type.RELOCATECOHORT then
-local requestid=self.requestID[Legion.alias]
-if requestid then
-self:T(self.lid.."Removing request from pending queue")
-Legion:_DeleteQueueItemByID(requestid,Legion.pending)
-local Cohort=self.DCStask.params.cohort
-Legion:DelCohort(Cohort)
-else
-self:E(self.lid.."WARNING: Could NOT remove relocation request from from pending queue (all assets were spawned?)")
-end
-end
-end
-if self.type==AUFTRAG.Type.RELOCATECOHORT then
-local cohort=self.DCStask.params.cohort
-cohort:Relocated()
-end
-end
-function AUFTRAG:onafterSuccess(From,Event,To)
-self.status=AUFTRAG.Status.SUCCESS
-self:T(self.lid..string.format("New mission status=%s",self.status))
-self.statusChief=self.status
-self.statusCommander=self.status
-for _,_legion in pairs(self.legions)do
-local Legion=_legion
-self:SetLegionStatus(Legion,self.status)
-end
-local repeatme=self.repeatedSuccess Repeat mission!",self.repeated+1,N))
-self:Repeat()
-else
-self:T(self.lid..string.format("Mission SUCCESS! Number of max repeats %d reached ==> Stopping mission!",self.repeated+1))
-self:Stop()
-end
-end
-function AUFTRAG:onafterFailed(From,Event,To)
-self.status=AUFTRAG.Status.FAILED
-self:T(self.lid..string.format("New mission status=%s",self.status))
-self.statusChief=self.status
-self.statusCommander=self.status
-for _,_legion in pairs(self.legions)do
-local Legion=_legion
-self:SetLegionStatus(Legion,self.status)
-end
-local repeatme=self.repeatedFailure Repeat mission!",self.repeated+1,N))
-self:Repeat()
-else
-self:T(self.lid..string.format("Mission FAILED! Number of max repeats %d reached ==> Stopping mission!",self.repeated+1))
-self:Stop()
-end
-end
-function AUFTRAG:onbeforeRepeat(From,Event,To)
-if not(self.chief or self.commander or#self.legions>0)then
-self:E(self.lid.."ERROR: Mission can only be repeated by a CHIEF, COMMANDER or LEGION! Stopping AUFTRAG")
-self:Stop()
-return false
-end
-return true
-end
-function AUFTRAG:onafterRepeat(From,Event,To)
-self.status=AUFTRAG.Status.PLANNED
-self:T(self.lid..string.format("New mission status=%s (on Repeat)",self.status))
-self.statusChief=self.status
-self.statusCommander=self.status
-for _,_legion in pairs(self.legions)do
-local Legion=_legion
-self:SetLegionStatus(Legion,self.status)
-end
-self.repeated=self.repeated+1
-if self.chief then
-self.statusChief=AUFTRAG.Status.PLANNED
-if self.commander then
-self.statusCommander=AUFTRAG.Status.PLANNED
-end
-for _,_legion in pairs(self.legions)do
-local legion=_legion
-legion:RemoveMission(self)
-end
-elseif self.commander then
-self.statusCommander=AUFTRAG.Status.PLANNED
-for _,_legion in pairs(self.legions)do
-local legion=_legion
-legion:RemoveMission(self)
-self:SetLegionStatus(legion,AUFTRAG.Status.PLANNED)
-end
-elseif#self.legions>0 then
-for _,_legion in pairs(self.legions)do
-local legion=_legion
-legion:RemoveMission(self)
-self:SetLegionStatus(legion,AUFTRAG.Status.PLANNED)
-legion:AddMission(self)
-end
-else
-self:E(self.lid.."ERROR: Mission can only be repeated by a CHIEF, COMMANDER or LEGION! Stopping AUFTRAG")
-self:Stop()
-return
-end
-self.assets={}
-for groupname,_groupdata in pairs(self.groupdata)do
-local groupdata=_groupdata
-local opsgroup=groupdata.opsgroup
-if opsgroup then
-self:DelOpsGroup(opsgroup)
-end
-end
-self.groupdata={}
-self.Ncasualties=0
-self.Nelements=0
-self.Ngroups=0
-self.Nassigned=nil
-self.Ndead=0
-self.DCStask=self:GetDCSMissionTask()
-self:__Status(-30)
-end
-function AUFTRAG:onafterStop(From,Event,To)
-self:T(self.lid..string.format("STOPPED mission in status=%s. Removing missions from queues. Stopping CallScheduler!",self.status))
-if self.chief then
-self.chief:RemoveMission(self)
-end
-if self.commander then
-self.commander:RemoveMission(self)
-end
-if#self.legions>0 then
-for _,_legion in pairs(self.legions)do
-local legion=_legion
-legion:RemoveMission(self)
-end
-end
-for _,_groupdata in pairs(self.groupdata)do
-local groupdata=_groupdata
-groupdata.opsgroup:RemoveMission(self)
-end
-self.assets={}
-self.groupdata={}
-self.CallScheduler:Clear()
-end
-function AUFTRAG:_TargetFromObject(Object)
-if not self.engageTarget then
-if Object and Object:IsInstanceOf("TARGET")then
-self.engageTarget=Object
-else
-self.engageTarget=TARGET:New(Object)
-end
-else
-end
-return self
-end
-function AUFTRAG:CountMissionTargets()
-local N=0
-local Coalitions=self.coalition and UTILS.GetCoalitionEnemy(self.coalition,true)or nil
-if self.engageTarget then
-N=self.engageTarget:CountTargets(Coalitions)
-end
-return N
-end
-function AUFTRAG:GetTargetInitialNumber()
-local target=self:GetTargetData()
-if target then
-return target.N0
-else
-return 0
-end
-end
-function AUFTRAG:GetTargetInitialLife()
-local target=self:GetTargetData()
-if target then
-return target.life0
-else
-return 0
-end
-end
-function AUFTRAG:GetTargetDamage()
-local target=self:GetTargetData()
-if target then
-return target:GetDamage()
-else
-return 0
-end
-end
-function AUFTRAG:GetTargetLife()
-local target=self:GetTargetData()
-if target then
-return target:GetLife()
-else
-return 0
-end
-end
-function AUFTRAG:GetTargetData()
-return self.engageTarget
-end
-function AUFTRAG:GetObjective(RefCoordinate,Coalitions)
-local objective=self:GetTargetData():GetObject(RefCoordinate,Coalitions)
-return objective
-end
-function AUFTRAG:GetTargetType()
-local target=self.engageTarget
-if target then
-local to=target:GetObjective()
-if to then
-return to.Type
-else
-return"Unknown"
-end
-else
-return"Unknown"
-end
-end
-function AUFTRAG:GetTargetVec2()
-local coord=self:GetTargetCoordinate()
-if coord then
-local vec2=coord:GetVec2()
-return vec2
-end
-return nil
-end
-function AUFTRAG:GetTargetCoordinate()
-if self.transportPickup then
-return self.transportPickup
-elseif self.missionZoneSet and self.type==AUFTRAG.Type.RECON then
-return self.missionZoneSet:GetAverageCoordinate()
-elseif self.engageTarget then
-local coord=self.engageTarget:GetCoordinate()
-return coord
-elseif self.type==AUFTRAG.Type.ALERT5 then
-return nil
-else
-self:T(self.lid.."ERROR: Cannot get target coordinate!")
-end
-return nil
-end
-function AUFTRAG:GetTargetHeading()
-if self.engageTarget then
-local heading=self.engageTarget:GetHeading()
-return heading
-end
-return nil
-end
-function AUFTRAG:GetTargetName()
-if self.engageTarget then
-local name=self.engageTarget:GetName()
-return name
-end
-return"N/A"
-end
-function AUFTRAG:GetTargetDistance(FromCoord)
-local TargetCoord=self:GetTargetCoordinate()
-if TargetCoord and FromCoord then
-return TargetCoord:Get2DDistance(FromCoord)
-else
-self:T(self.lid.."ERROR: TargetCoord or FromCoord does not exist in AUFTRAG:GetTargetDistance() function! Returning 0")
-end
-return 0
-end
-function AUFTRAG:AddAsset(Asset)
-self:T(self.lid..string.format("Adding asset \"%s\" to mission",tostring(Asset.spawngroupname)))
-self.assets=self.assets or{}
-local asset=self:GetAssetByName(Asset.spawngroupname)
-if not asset then
-table.insert(self.assets,Asset)
-self.Nassigned=self.Nassigned or 0
-self.Nassigned=self.Nassigned+1
-end
-return self
-end
-function AUFTRAG:_AddAssets(Assets)
-for _,asset in pairs(Assets)do
-self:AddAsset(asset)
-end
-return self
-end
-function AUFTRAG:DelAsset(Asset)
-for i,_asset in pairs(self.assets or{})do
-local asset=_asset
-if asset.uid==Asset.uid then
-self:T(self.lid..string.format("Removing asset \"%s\" from mission",tostring(Asset.spawngroupname)))
-table.remove(self.assets,i)
-return self
-end
-end
-return self
-end
-function AUFTRAG:GetAssetByName(Name)
-for i,_asset in pairs(self.assets or{})do
-local asset=_asset
-if asset.spawngroupname==Name then
-return asset
-end
-end
-return nil
-end
-function AUFTRAG:CountOpsGroups()
-local N=0
-for _,_groupdata in pairs(self.groupdata)do
-local groupdata=_groupdata
-if groupdata and groupdata.opsgroup and groupdata.opsgroup:IsAlive()and not groupdata.opsgroup:IsDead()then
-N=N+1
-end
-end
-return N
-end
-function AUFTRAG:CountOpsGroupsInStatus(Status)
-local N=0
-for _,_groupdata in pairs(self.groupdata)do
-local groupdata=_groupdata
-if groupdata and groupdata.status==Status then
-N=N+1
-end
-end
-return N
-end
-function AUFTRAG:GetMissionTypesText(MissionTypes)
-local text=""
-for _,missiontype in pairs(MissionTypes)do
-text=text..string.format("%s, ",missiontype)
-end
-return text
-end
-function AUFTRAG:SetMissionWaypointCoord(Coordinate)
-if Coordinate:IsInstanceOf("ZONE_BASE")then
-Coordinate=Coordinate:GetCoordinate()
-end
-self.missionWaypointCoord=Coordinate
-return self
-end
-function AUFTRAG:SetMissionWaypointRandomization(Radius)
-self.missionWaypointRadius=Radius
-return self
-end
-function AUFTRAG:SetMissionEgressCoord(Coordinate,Altitude)
-if Coordinate:IsInstanceOf("ZONE_BASE")then
-Coordinate=Coordinate:GetCoordinate()
-end
-self.missionEgressCoord=Coordinate
-if Altitude then
-self.missionEgressCoord.y=UTILS.FeetToMeters(Altitude)
-end
-end
-function AUFTRAG:GetMissionEgressCoord()
-return self.missionEgressCoord
-end
-function AUFTRAG:_GetMissionWaypointCoordSet()
-if self.missionWaypointCoord then
-local coord=self.missionWaypointCoord
-if self.missionAltitude then
-coord.y=self.missionAltitude
-end
-return coord
-end
-end
-function AUFTRAG:GetMissionWaypointCoord(group,randomradius,surfacetypes)
-if self.missionWaypointCoord then
-local coord=self.missionWaypointCoord
-if self.missionAltitude then
-coord.y=self.missionAltitude
-end
-return coord
-end
-local waypointcoord=COORDINATE:New(0,0,0)
-local coord=group:GetCoordinate()
-if coord then
-waypointcoord=coord:GetIntermediateCoordinate(self:GetTargetCoordinate(),self.missionFraction)
-else
-self:E(self.lid..string.format("ERROR: Cannot get coordinate of group %s (alive=%s)!",tostring(group:GetName()),tostring(group:IsAlive())))
-end
-local alt=waypointcoord.y
-if randomradius then
-waypointcoord=ZONE_RADIUS:New("Temp",waypointcoord:GetVec2(),randomradius):GetRandomCoordinate(nil,nil,surfacetypes):SetAltitude(alt,false)
-end
-if self.missionAltitude then
-waypointcoord:SetAltitude(self.missionAltitude,true)
-end
-return waypointcoord
-end
-function AUFTRAG:_SetLogID()
-self.lid=string.format("Auftrag #%d %s | ",self.auftragsnummer,tostring(self.type))
-return self
-end
-function AUFTRAG:_GetRequestID(Legion)
-local requestid=nil
-local name=nil
-if type(Legion)=="string"then
-name=Legion
-else
-name=Legion.alias
-end
-if name then
-requestid=self.requestID[name]
-end
-return nil
-end
-function AUFTRAG:_GetRequest(Legion)
-local request=nil
-local requestID=self:_GetRequestID(Legion)
-if requestID then
-request=Legion:GetRequestByID(requestID)
-end
-return request
-end
-function AUFTRAG:_SetRequestID(Legion,RequestID)
-local requestid=nil
-local name=nil
-if type(Legion)=="string"then
-name=Legion
-else
-name=Legion.alias
-end
-if name then
-if self.requestID[name]then
-self:I(self.lid..string.format("WARNING: Mission already has a request ID=%d!",self.requestID[name]))
-end
-self.requestID[name]=RequestID
-end
-return self
-end
-function AUFTRAG:_IsNotReinforcing()
-local Nassigned=self.Nassigned and self.Nassigned-self.Ndead or 0
-local notreinforcing=((not self.reinforce)or(self.reinforce==0 and Nassigned<=0))
-return notreinforcing
-end
-function AUFTRAG:_IsReinforcing()
-local reinforcing=not self:_IsNotReinforcing()
-return reinforcing
-end
-function AUFTRAG:UpdateMarker()
-local text=string.format("%s %s: %s",self.name,self.type:upper(),self.status:upper())
-text=text..string.format("\n%s",self:GetTargetName())
-text=text..string.format("\nTargets %d/%d, Life Points=%d/%d",self:CountMissionTargets(),self:GetTargetInitialNumber(),self:GetTargetLife(),self:GetTargetInitialLife())
-text=text..string.format("\nOpsGroups %d/%d",self:CountOpsGroups(),self:GetNumberOfRequiredAssets())
-if not self.marker then
-local targetcoord=self:GetTargetCoordinate()
-if targetcoord then
-if self.markerCoaliton and self.markerCoaliton>=0 then
-self.marker=MARKER:New(targetcoord,text):ReadOnly():ToCoalition(self.markerCoaliton)
-else
-self.marker=MARKER:New(targetcoord,text):ReadOnly():ToAll()
-end
-end
-else
-if self.marker:GetText()~=text then
-self.marker:UpdateText(text)
-end
-end
-return self
-end
-function AUFTRAG:GetDCSMissionTask()
-local DCStasks={}
-if self.type==AUFTRAG.Type.ANTISHIP then
-local DCStask=CONTROLLABLE.EnRouteTaskAntiShip(nil)
-table.insert(self.enrouteTasks,DCStask)
-self:_GetDCSAttackTask(self.engageTarget,DCStasks)
-elseif self.type==AUFTRAG.Type.AWACS then
-local DCStask=CONTROLLABLE.EnRouteTaskAWACS(nil)
-table.insert(self.enrouteTasks,DCStask)
-elseif self.type==AUFTRAG.Type.BAI then
-self:_GetDCSAttackTask(self.engageTarget,DCStasks)
-elseif self.type==AUFTRAG.Type.BOMBING then
-local DCStask=CONTROLLABLE.TaskBombing(nil,self:GetTargetVec2(),self.engageAsGroup,self.engageWeaponExpend,self.engageQuantity,self.engageDirection,self.engageAltitude,self.engageWeaponType,Divebomb)
-table.insert(DCStasks,DCStask)
-elseif self.type==AUFTRAG.Type.BOMBRUNWAY then
-local DCStask=CONTROLLABLE.TaskBombingRunway(nil,self.engageTarget:GetObject(),self.engageWeaponType,self.engageWeaponExpend,self.engageQuantity,self.engageDirection,self.engageAsGroup)
-table.insert(DCStasks,DCStask)
-elseif self.type==AUFTRAG.Type.BOMBCARPET then
-local DCStask=CONTROLLABLE.TaskCarpetBombing(nil,self:GetTargetVec2(),self.engageAsGroup,self.engageWeaponExpend,self.engageQuantity,self.engageDirection,self.engageAltitude,self.engageWeaponType,self.engageCarpetLength)
-table.insert(DCStasks,DCStask)
-elseif self.type==AUFTRAG.Type.CAP then
-local DCStask=CONTROLLABLE.EnRouteTaskEngageTargetsInZone(nil,self.engageZone:GetVec2(),self.engageZone:GetRadius(),self.engageTargetTypes,Priority)
-table.insert(self.enrouteTasks,DCStask)
-elseif self.type==AUFTRAG.Type.CAS then
-local DCStask=CONTROLLABLE.EnRouteTaskEngageTargetsInZone(nil,self.engageZone:GetVec2(),self.engageZone:GetRadius(),self.engageTargetTypes,Priority)
-table.insert(self.enrouteTasks,DCStask)
-elseif self.type==AUFTRAG.Type.ESCORT then
-local DCStask=CONTROLLABLE.TaskEscort(nil,self.engageTarget:GetObject(),self.escortVec3,nil,self.engageMaxDistance,self.engageTargetTypes)
-table.insert(DCStasks,DCStask)
-elseif self.type==AUFTRAG.Type.GROUNDESCORT then
-local DCSTask=CONTROLLABLE.TaskGroundEscort(nil,self.engageTarget:GetObject(),nil,self.orbitDistance,self.engageTargetTypes)
-table.insert(DCStasks,DCSTask)
-elseif self.type==AUFTRAG.Type.FACA then
-local DCStask=CONTROLLABLE.TaskFAC_AttackGroup(nil,self.engageTarget:GetObject(),self.engageWeaponType,self.facDesignation,self.facDatalink,self.facFreq,self.facModu,CallsignName,CallsignNumber)
-table.insert(DCStasks,DCStask)
-elseif self.type==AUFTRAG.Type.FAC then
-local DCStask={}
-DCStask.id=AUFTRAG.SpecialTask.PATROLZONE
-local param={}
-param.zone=self:GetObjective()
-param.altitude=self.missionAltitude
-param.speed=self.missionSpeed
-DCStask.params=param
-table.insert(DCStasks,DCStask)
-local DCSenroute=CONTROLLABLE.EnRouteTaskFAC(self,self.facFreq,self.facModu)
-table.insert(self.enrouteTasks,DCSenroute)
-elseif self.type==AUFTRAG.Type.FERRY then
-local DCStask={}
-DCStask.id=AUFTRAG.SpecialTask.FERRY
-local param={}
-DCStask.params=param
-table.insert(DCStasks,DCStask)
-elseif self.type==AUFTRAG.Type.RELOCATECOHORT then
-local DCStask={}
-DCStask.id=AUFTRAG.SpecialTask.RELOCATECOHORT
-local param={}
-DCStask.params=param
-table.insert(DCStasks,DCStask)
-elseif self.type==AUFTRAG.Type.INTERCEPT then
-self:_GetDCSAttackTask(self.engageTarget,DCStasks)
-elseif self.type==AUFTRAG.Type.ORBIT then
-elseif self.type==AUFTRAG.Type.GCICAP then
-elseif self.type==AUFTRAG.Type.RECON then
-local DCStask={}
-DCStask.id=AUFTRAG.SpecialTask.RECON
-local param={}
-param.target=self.engageTarget
-param.altitude=self.missionAltitude
-param.speed=self.missionSpeed
-param.lastindex=nil
-DCStask.params=param
-table.insert(DCStasks,DCStask)
-elseif self.type==AUFTRAG.Type.SEAD then
-self:_GetDCSAttackTask(self.engageTarget,DCStasks)
-elseif self.type==AUFTRAG.Type.STRIKE then
-local DCStask=CONTROLLABLE.TaskAttackMapObject(nil,self:GetTargetVec2(),self.engageAsGroup,self.engageWeaponExpend,self.engageQuantity,self.engageDirection,self.engageAltitude,self.engageWeaponType)
-table.insert(DCStasks,DCStask)
-elseif self.type==AUFTRAG.Type.TANKER or self.type==AUFTRAG.Type.RECOVERYTANKER then
-local DCStask=CONTROLLABLE.EnRouteTaskTanker(nil)
-table.insert(self.enrouteTasks,DCStask)
-elseif self.type==AUFTRAG.Type.TROOPTRANSPORT then
-local TaskEmbark=CONTROLLABLE.TaskEmbarking(TaskControllable,self.transportPickup,self.transportGroupSet,self.transportWaitForCargo)
-local TaskDisEmbark=CONTROLLABLE.TaskDisembarking(TaskControllable,self.transportDropoff,self.transportGroupSet)
-table.insert(DCStasks,TaskEmbark)
-table.insert(DCStasks,TaskDisEmbark)
-elseif self.type==AUFTRAG.Type.OPSTRANSPORT then
-local DCStask={}
-DCStask.id="OpsTransport"
-local param={}
-DCStask.params=param
-table.insert(DCStasks,DCStask)
-elseif self.type==AUFTRAG.Type.CARGOTRANSPORT then
-local TaskCargoTransportation={
-id="CargoTransportation",
-params={}
-}
-table.insert(DCStasks,TaskCargoTransportation)
-elseif self.type==AUFTRAG.Type.RESCUEHELO then
-local DCStask={}
-DCStask.id=AUFTRAG.SpecialTask.FORMATION
-local param={}
-param.unitname=self:GetTargetName()
-param.offsetX=200
-param.offsetZ=240
-param.altitude=70
-param.dtFollow=1.0
-DCStask.params=param
-table.insert(DCStasks,DCStask)
-elseif self.type==AUFTRAG.Type.ARTY then
-if self.artyShots==1 or self.artyRadius<10 or true then
-local DCStask=CONTROLLABLE.TaskFireAtPoint(nil,self:GetTargetVec2(),self.artyRadius,self.artyShots,self.engageWeaponType,self.artyAltitude)
-table.insert(DCStasks,DCStask)
-else
-local Vec2=self:GetTargetVec2()
-local zone=ZONE_RADIUS:New("temp",Vec2,self.artyRadius)
-for i=1,self.artyShots do
-local vec2=zone:GetRandomVec2()
-local DCStask=CONTROLLABLE.TaskFireAtPoint(nil,vec2,0,1,self.engageWeaponType,self.artyAltitude)
-table.insert(DCStasks,DCStask)
-end
-end
-elseif self.type==AUFTRAG.Type.BARRAGE then
-local DCStask={}
-DCStask.id=AUFTRAG.SpecialTask.BARRAGE
-local param={}
-param.zone=self:GetObjective()
-param.altitude=self.artyAltitude
-param.radius=self.artyRadius
-param.heading=self.artyHeading
-param.angle=self.artyAngle
-param.shots=self.artyShots
-param.weaponTypoe=self.engageWeaponType
-DCStask.params=param
-table.insert(DCStasks,DCStask)
-elseif self.type==AUFTRAG.Type.PATROLZONE then
-local DCStask={}
-DCStask.id=AUFTRAG.SpecialTask.PATROLZONE
-local param={}
-param.zone=self:GetObjective()
-param.altitude=self.missionAltitude
-param.speed=self.missionSpeed
-DCStask.params=param
-table.insert(DCStasks,DCStask)
-elseif self.type==AUFTRAG.Type.CAPTUREZONE then
-local DCStask={}
-DCStask.id=AUFTRAG.SpecialTask.CAPTUREZONE
-local param={}
-DCStask.params=param
-table.insert(DCStasks,DCStask)
-elseif self.type==AUFTRAG.Type.CASENHANCED then
-local DCStask={}
-DCStask.id=AUFTRAG.SpecialTask.PATROLZONE
-local param={}
-param.zone=self:GetObjective()
-param.altitude=self.missionAltitude
-param.speed=self.missionSpeed
-DCStask.params=param
-table.insert(DCStasks,DCStask)
-elseif self.type==AUFTRAG.Type.GROUNDATTACK then
-local DCStask={}
-DCStask.id=AUFTRAG.SpecialTask.GROUNDATTACK
-local param={}
-param.target=self:GetTargetData()
-param.action="Wedge"
-param.speed=self.missionSpeed
-DCStask.params=param
-table.insert(DCStasks,DCStask)
-elseif self.type==AUFTRAG.Type.AMMOSUPPLY then
-local DCStask={}
-DCStask.id=AUFTRAG.SpecialTask.AMMOSUPPLY
-local param={}
-param.zone=self:GetObjective()
-DCStask.params=param
-table.insert(DCStasks,DCStask)
-elseif self.type==AUFTRAG.Type.FUELSUPPLY then
-local DCStask={}
-DCStask.id=AUFTRAG.SpecialTask.FUELSUPPLY
-local param={}
-param.zone=self:GetObjective()
-DCStask.params=param
-table.insert(DCStasks,DCStask)
-elseif self.type==AUFTRAG.Type.REARMING then
-local DCStask={}
-DCStask.id=AUFTRAG.SpecialTask.REARMING
-local param={}
-param.zone=self:GetObjective()
-DCStask.params=param
-table.insert(DCStasks,DCStask)
-elseif self.type==AUFTRAG.Type.ALERT5 then
-local DCStask={}
-DCStask.id=AUFTRAG.SpecialTask.ALERT5
-local param={}
-DCStask.params=param
-table.insert(DCStasks,DCStask)
-elseif self.type==AUFTRAG.Type.NOTHING then
-local DCStask={}
-DCStask.id=AUFTRAG.SpecialTask.NOTHING
-local param={}
-DCStask.params=param
-table.insert(DCStasks,DCStask)
-elseif self.type==AUFTRAG.Type.PATROLRACETRACK then
-local DCStask={}
-DCStask.id=AUFTRAG.SpecialTask.PATROLRACETRACK
-local param={}
-param.TrackAltitude=self.TrackAltitude
-param.TrackSpeed=self.TrackSpeed
-param.TrackPoint1=self.TrackPoint1
-param.TrackPoint2=self.TrackPoint2
-param.missionSpeed=self.missionSpeed
-param.missionAltitude=self.missionAltitude
-param.TrackFormation=self.TrackFormation
-DCStask.params=param
-table.insert(DCStasks,DCStask)
-elseif self.type==AUFTRAG.Type.HOVER then
-local DCStask={}
-DCStask.id=AUFTRAG.SpecialTask.HOVER
-local param={}
-param.hoverAltitude=self.hoverAltitude
-param.hoverTime=self.hoverTime
-param.missionSpeed=self.missionSpeed
-param.missionAltitude=self.missionAltitude
-DCStask.params=param
-table.insert(DCStasks,DCStask)
-elseif self.type==AUFTRAG.Type.ONGUARD or self.type==AUFTRAG.Type.ARMOREDGUARD then
-local DCStask={}
-DCStask.id=self.type==AUFTRAG.Type.ONGUARD and AUFTRAG.SpecialTask.ONGUARD or AUFTRAG.SpecialTask.ARMOREDGUARD
-local param={}
-param.coordinate=self:GetObjective()
-DCStask.params=param
-table.insert(DCStasks,DCStask)
-elseif self.type==AUFTRAG.Type.AIRDEFENSE then
-local DCStask={}
-DCStask.id=AUFTRAG.SpecialTask.AIRDEFENSE
-local param={}
-param.zone=self:GetObjective()
-DCStask.params=param
-table.insert(DCStasks,DCStask)
-elseif self.type==AUFTRAG.Type.EWR then
-local DCStask={}
-DCStask.id=AUFTRAG.SpecialTask.EWR
-local param={}
-param.zone=self:GetObjective()
-DCStask.params=param
-table.insert(DCStasks,DCStask)
-local Enroutetask=CONTROLLABLE.EnRouteTaskEWR()
-table.insert(self.enrouteTasks,Enroutetask)
-else
-self:T(self.lid..string.format("ERROR: Unknown mission task!"))
-return nil
-end
-if self.type==AUFTRAG.Type.ORBIT or
-self.type==AUFTRAG.Type.CAP or
-self.type==AUFTRAG.Type.CAS or
-self.type==AUFTRAG.Type.GCICAP or
-self.type==AUFTRAG.Type.AWACS or
-self.type==AUFTRAG.Type.TANKER or
-self.type==AUFTRAG.Type.RECOVERYTANKER then
-self.orbitVec2=self:GetTargetVec2()
-if self.orbitVec2 then
-self.targetHeading=self:GetTargetHeading()
-local OffsetVec2=nil
-if(self.orbitOffsetVec2~=nil)then
-OffsetVec2=UTILS.DeepCopy(self.orbitOffsetVec2)
-end
-if OffsetVec2 then
-if self.orbitOffsetVec2.r then
-local r=self.orbitOffsetVec2.r
-local phi=(self.orbitOffsetVec2.phi or 0)+self.targetHeading
-OffsetVec2.x=r*math.cos(math.rad(phi))
-OffsetVec2.y=r*math.sin(math.rad(phi))
-else
-OffsetVec2.x=self.orbitOffsetVec2.x
-OffsetVec2.y=self.orbitOffsetVec2.y
-end
-end
-local orbitVec2=OffsetVec2 and UTILS.Vec2Add(self.orbitVec2,OffsetVec2)or self.orbitVec2
-local orbitRaceTrack=nil
-if self.orbitLeg then
-local heading=0
-if self.orbitHeading then
-if self.orbitHeadingRel then
-heading=self.targetHeading+self.orbitHeading
-else
-heading=self.orbitHeading
-end
-else
-heading=self.targetHeading or 0
-end
-orbitRaceTrack=UTILS.Vec2Translate(orbitVec2,self.orbitLeg,heading)
-end
-local orbitRaceTrackCoord=nil
-if orbitRaceTrack then
-orbitRaceTrackCoord=COORDINATE:NewFromVec2(orbitRaceTrack)
-end
-local DCStask=CONTROLLABLE.TaskOrbit(nil,COORDINATE:NewFromVec2(orbitVec2),self.orbitAltitude,self.orbitSpeed,orbitRaceTrackCoord)
-table.insert(DCStasks,DCStask)
-end
-end
-self:T3({missiontask=DCStasks})
-if#DCStasks==1 then
-return DCStasks[1]
-else
-return CONTROLLABLE.TaskCombo(nil,DCStasks)
-end
-end
-function AUFTRAG:_GetDCSAttackTask(Target,DCStasks)
-DCStasks=DCStasks or{}
-for _,_target in pairs(Target.targets)do
-local target=_target
-if target.Type==TARGET.ObjectType.GROUP then
-local DCStask=CONTROLLABLE.TaskAttackGroup(nil,target.Object,self.engageWeaponType,self.engageWeaponExpend,self.engageQuantity,self.engageDirection,self.engageAltitude,self.engageAsGroup)
-table.insert(DCStasks,DCStask)
-elseif target.Type==TARGET.ObjectType.UNIT or target.Type==TARGET.ObjectType.STATIC then
-local DCStask=CONTROLLABLE.TaskAttackUnit(nil,target.Object,self.engageAsGroup,self.WeaponExpend,self.engageQuantity,self.engageDirection,self.engageAltitude,self.engageWeaponType)
-table.insert(DCStasks,DCStask)
-end
-end
-return DCStasks
-end
-function AUFTRAG:GetMissionTaskforMissionType(MissionType)
-local mtask=ENUMS.MissionTask.NOTHING
-if MissionType==AUFTRAG.Type.ANTISHIP then
-mtask=ENUMS.MissionTask.ANTISHIPSTRIKE
-elseif MissionType==AUFTRAG.Type.AWACS then
-mtask=ENUMS.MissionTask.AWACS
-elseif MissionType==AUFTRAG.Type.BAI then
-mtask=ENUMS.MissionTask.GROUNDATTACK
-elseif MissionType==AUFTRAG.Type.BOMBCARPET then
-mtask=ENUMS.MissionTask.GROUNDATTACK
-elseif MissionType==AUFTRAG.Type.BOMBING then
-mtask=ENUMS.MissionTask.GROUNDATTACK
-elseif MissionType==AUFTRAG.Type.BOMBRUNWAY then
-mtask=ENUMS.MissionTask.RUNWAYATTACK
-elseif MissionType==AUFTRAG.Type.CAP then
-mtask=ENUMS.MissionTask.CAP
-elseif MissionType==AUFTRAG.Type.GCICAP then
-mtask=ENUMS.MissionTask.CAP
-elseif MissionType==AUFTRAG.Type.CAS then
-mtask=ENUMS.MissionTask.CAS
-elseif MissionType==AUFTRAG.Type.PATROLZONE then
-mtask=ENUMS.MissionTask.CAS
-elseif MissionType==AUFTRAG.Type.CASENHANCED then
-mtask=ENUMS.MissionTask.CAS
-elseif MissionType==AUFTRAG.Type.ESCORT then
-mtask=ENUMS.MissionTask.ESCORT
-elseif MissionType==AUFTRAG.Type.FACA then
-mtask=ENUMS.MissionTask.AFAC
-elseif MissionType==AUFTRAG.Type.FAC then
-mtask=ENUMS.MissionTask.AFAC
-elseif MissionType==AUFTRAG.Type.FERRY then
-mtask=ENUMS.MissionTask.NOTHING
-elseif MissionType==AUFTRAG.Type.GROUNDESCORT then
-mtask=ENUMS.MissionTask.GROUNDESCORT
-elseif MissionType==AUFTRAG.Type.INTERCEPT then
-mtask=ENUMS.MissionTask.INTERCEPT
-elseif MissionType==AUFTRAG.Type.RECON then
-mtask=ENUMS.MissionTask.RECONNAISSANCE
-elseif MissionType==AUFTRAG.Type.SEAD then
-mtask=ENUMS.MissionTask.SEAD
-elseif MissionType==AUFTRAG.Type.STRIKE then
-mtask=ENUMS.MissionTask.GROUNDATTACK
-elseif MissionType==AUFTRAG.Type.TANKER then
-mtask=ENUMS.MissionTask.REFUELING
-elseif MissionType==AUFTRAG.Type.TROOPTRANSPORT then
-mtask=ENUMS.MissionTask.TRANSPORT
-elseif MissionType==AUFTRAG.Type.CARGOTRANSPORT then
-mtask=ENUMS.MissionTask.TRANSPORT
-elseif MissionType==AUFTRAG.Type.ARMORATTACK then
-mtask=ENUMS.MissionTask.NOTHING
-elseif MissionType==AUFTRAG.Type.HOVER then
-mtask=ENUMS.MissionTask.NOTHING
-elseif MissionType==AUFTRAG.Type.PATROLRACETRACK then
-mtask=ENUMS.MissionTask.CAP
-end
-return mtask
-end
-function AUFTRAG.CheckMissionType(MissionType,PossibleTypes)
-if type(PossibleTypes)=="string"then
-PossibleTypes={PossibleTypes}
-end
-for _,canmission in pairs(PossibleTypes)do
-if canmission==MissionType then
-return true
-end
-end
-return false
-end
-function AUFTRAG.CheckMissionCapability(MissionTypes,Capabilities,All)
-if type(MissionTypes)~="table"then
-MissionTypes={MissionTypes}
-end
-for _,cap in pairs(Capabilities)do
-local capability=cap
-for _,MissionType in pairs(MissionTypes)do
-if All==true then
-if capability.MissionType~=MissionType then
-return false
-end
-else
-if capability.MissionType==MissionType then
-return true
-end
-end
-end
-end
-if All==true then
-return true
-else
-return false
-end
-end
-function AUFTRAG.CheckMissionCapabilityAny(MissionTypes,Capabilities)
-local res=AUFTRAG.CheckMissionCapability(MissionTypes,Capabilities,false)
-return res
-end
-function AUFTRAG.CheckMissionCapabilityAll(MissionTypes,Capabilities)
-local res=AUFTRAG.CheckMissionCapability(MissionTypes,Capabilities,true)
-return res
-end
-do
-AWACS={
-ClassName="AWACS",
-version="0.2.59",
-lid="",
-coalition=coalition.side.BLUE,
-coalitiontxt="blue",
-OpsZone=nil,
-StationZone=nil,
-AirWing=nil,
-Frequency=271,
-Modulation=radio.modulation.AM,
-Airbase=nil,
-AwacsAngels=25,
-OrbitZone=nil,
-CallSign=CALLSIGN.AWACS.Magic,
-CallSignNo=1,
-debug=false,
-verbose=false,
-ManagedGrps={},
-ManagedGrpID=0,
-ManagedTaskID=0,
-AnchorStacks={},
-CAPIdleAI={},
-CAPIdleHuman={},
-TaskedCAPAI={},
-TaskedCAPHuman={},
-OpenTasks={},
-ManagedTasks={},
-PictureAO={},
-PictureEWR={},
-Contacts={},
-Countactcounter=0,
-ContactsAO={},
-RadioQueue={},
-PrioRadioQueue={},
-TacticalQueue={},
-AwacsTimeOnStation=4,
-AwacsTimeStamp=0,
-EscortsTimeOnStation=4,
-EscortsTimeStamp=0,
-CAPTimeOnStation=4,
-AwacsROE="",
-AwacsROT="",
-MenuStrict=true,
-MaxAIonCAP=3,
-AIonCAP=0,
-AICAPMissions={},
-ShiftChangeAwacsFlag=false,
-ShiftChangeEscortsFlag=false,
-ShiftChangeAwacsRequested=false,
-ShiftChangeEscortsRequested=false,
-CAPAirwings={},
-MonitoringData={},
-MonitoringOn=false,
-FlightGroups={},
-AwacsMission=nil,
-AwacsInZone=false,
-AwacsReady=false,
-CatchAllMissions={},
-CatchAllFGs={},
-PictureInterval=300,
-ReassignTime=120,
-PictureTimeStamp=0,
-BorderZone=nil,
-RejectZone=nil,
-maxassigndistance=100,
-PlayerGuidance=true,
-ModernEra=true,
-callsignshort=true,
-keepnumber=true,
-callsignTranslations=nil,
-TacDistance=45,
-MeldDistance=35,
-ThreatDistance=25,
-AOName="Rock",
-AOCoordinate=nil,
-clientmenus=nil,
-RadarBlur=15,
-ReassignmentPause=180,
-NoGroupTags=false,
-SuppressScreenOutput=false,
-NoMissileCalls=true,
-GoogleTTSPadding=1,
-WindowsTTSPadding=2.5,
-PlayerCapAssignment=true,
-AllowMarkers=false,
-PlayerStationName=nil,
-GCI=false,
-GCIGroup=nil,
-locale="en",
-IncludeHelicopters=false,
-TacticalMenu=false,
-TacticalFrequencies={},
-TacticalSubscribers={},
-TacticalBaseFreq=130,
-TacticalIncrFreq=0.5,
-TacticalModulation=radio.modulation.AM,
-TacticalInterval=120,
-}
-AWACS.CallSignClear={
-[1]="Overlord",
-[2]="Magic",
-[3]="Wizard",
-[4]="Focus",
-[5]="Darkstar",
-}
-AWACS.AnchorNames={
-[1]="One",
-[2]="Two",
-[3]="Three",
-[4]="Four",
-[5]="Five",
-[6]="Six",
-[7]="Seven",
-[8]="Eight",
-[9]="Nine",
-[10]="Ten",
-}
-AWACS.IFF=
-{
-SPADES="Spades",
-NEUTRAL="Neutral",
-FRIENDLY="Friendly",
-ENEMY="Hostile",
-BOGEY="Bogey",
-}
-AWACS.Phonetic=
-{
-[1]='Alpha',
-[2]='Bravo',
-[3]='Charlie',
-[4]='Delta',
-[5]='Echo',
-[6]='Foxtrot',
-[7]='Golf',
-[8]='Hotel',
-[9]='India',
-[10]='Juliett',
-[11]='Kilo',
-[12]='Lima',
-[13]='Mike',
-[14]='November',
-[15]='Oscar',
-[16]='Papa',
-[17]='Quebec',
-[18]='Romeo',
-[19]='Sierra',
-[20]='Tango',
-[21]='Uniform',
-[22]='Victor',
-[23]='Whiskey',
-[24]='Xray',
-[25]='Yankee',
-[26]='Zulu',
-}
-AWACS.Shipsize=
-{
-[1]="Singleton",
-[2]="Two-Ship",
-[3]="Heavy",
-[4]="Gorilla",
-}
-AWACS.ROE={
-POLICE="Police",
-VID="Visual ID",
-IFF="IFF",
-BVR="Beyond Visual Range",
-}
-AWACS.ROT={
-BYPASSESCAPE="Bypass and Escape",
-EVADE="Evade Fire",
-PASSIVE="Passive Defense",
-RETURNFIRE="Return Fire",
-OPENFIRE="Open Fire",
-}
-AWACS.THREATLEVEL={
-GREEN=3,
-AMBER=7,
-RED=10,
-}
-AWACS.CapVoices={
-[1]="de-DE-Wavenet-A",
-[2]="de-DE-Wavenet-B",
-[3]="fr-FR-Wavenet-A",
-[4]="fr-FR-Wavenet-B",
-[5]="en-GB-Wavenet-A",
-[6]="en-GB-Wavenet-B",
-[7]="en-GB-Wavenet-D",
-[8]="en-AU-Wavenet-B",
-[9]="en-US-Wavenet-J",
-[10]="en-US-Wavenet-H",
-}
-AWACS.Messages={
-EN=
-{
-DEFEND="%s, %s! %s! %s! Defend!",
-VECTORTO="%s, %s. Vector%s %s",
-VECTORTOTTS="%s, %s, Vector%s %s",
-ANGELS=". Angels ",
-ZERO="zero",
-VANISHED="%s, %s Group. Vanished.",
-VANISHEDTTS="%s, %s group vanished.",
-SHIFTCHANGE="%s shift change for %s control.",
-GROUPCAP="Group",
-GROUP="group",
-MILES="miles",
-THOUSAND="thousand",
-BOGEY="Bogey",
-ALLSTATIONS="All Stations",
-PICCLEAN="%s. %s. Picture Clean.",
-PICTURE="Picture",
-ONE="One",
-GROUPMULTI="groups",
-NOTCHECKEDIN="%s. %s. Negative. You are not checked in.",
-CLEAN="%s. %s. Clean.",
-DOPE="%s. %s. Bogey Dope. ",
-VIDPOS="%s. %s. Copy, target identified as %s.",
-VIDNEG="%s. %s. Negative, get closer to target.",
-FFNEUTRAL="Neutral",
-FFFRIEND="Friendly",
-FFHOSTILE="Hostile",
-FFSPADES="Spades",
-FFCLEAN="Clean",
-COPY="%s. %s. Copy.",
-TARGETEDBY="Targeted by %s.",
-STATUS="Status",
-ALREADYCHECKEDIN="%s. %s. Negative. You are already checked in.",
-ALPHACHECK="Alpha Check",
-CHECKINAI="%s. %s. Checking in as fragged. Expected playtime %d hours. Request Alpha Check %s.",
-SAFEFLIGHT="%s. %s. Copy. Have a safe flight home.",
-VERYLOW="very low",
-AIONSTATION="%s. %s. On station over anchor %d at angels %d. Ready for tasking.",
-POPUP="Pop-up",
-NEWGROUP="New group",
-HIGH=" High.",
-VERYFAST=" Very fast.",
-FAST=" Fast.",
-THREAT="Threat",
-MERGED="Merged",
-SCREENVID="Intercept and VID %s group.",
-SCREENINTER="Intercept %s group.",
-ENGAGETAG="Targeted by %s.",
-REQCOMMIT="%s. %s group. %s. %s, request commit.",
-AICOMMIT="%s. %s group. %s. %s, commit.",
-COMMIT="Commit",
-SUNRISE="%s. All stations, SUNRISE SUNRISE SUNRISE, %s.",
-AWONSTATION="%s on station for %s control.",
-STATIONAT="%s. %s. Station at %s at angels %d.",
-STATIONATLONG="%s. %s. Station at %s at angels %d doing %d knots.",
-STATIONSCREEN="%s. %s.\nStation at %s\nAngels %d\nSpeed %d knots\nCoord %s\nROE %s.",
-STATIONTASK="Station at %s\nAngels %d\nSpeed %d knots\nCoord %s\nROE %s",
-VECTORSTATION=" to Station",
-TEXTOPTIONS1="Lost friendly flight",
-TEXTOPTIONS2="Vanished friendly flight",
-TEXTOPTIONS3="Faded friendly contact",
-TEXTOPTIONS4="Lost contact with",
-},
-}
-AWACS.TaskDescription={
-ANCHOR="Anchor",
-REANCHOR="Re-Anchor",
-VID="VID",
-IFF="IFF",
-INTERCEPT="Intercept",
-SWEEP="Sweep",
-RTB="RTB",
-}
-AWACS.TaskStatus={
-IDLE="Idle",
-UNASSIGNED="Unassigned",
-REQUESTED="Requested",
-ASSIGNED="Assigned",
-EXECUTING="Executing",
-SUCCESS="Success",
-FAILED="Failed",
-DEAD="Dead",
-}
-function AWACS:New(Name,AirWing,Coalition,AirbaseName,AwacsOrbit,OpsZone,StationZone,Frequency,Modulation)
-local self=BASE:Inherit(self,FSM:New())
-if Coalition and type(Coalition)=="string"then
-if Coalition=="blue"then
-self.coalition=coalition.side.BLUE
-self.coalitiontxt=Coalition
-elseif Coalition=="red"then
-self.coalition=coalition.side.RED
-self.coalitiontxt=Coalition
-elseif Coalition=="neutral"then
-self.coalition=coalition.side.NEUTRAL
-self.coalitiontxt=Coalition
-else
-self:E("ERROR: Unknown coalition in AWACS!")
-end
-else
-self.coalition=Coalition
-self.coalitiontxt=string.lower(UTILS.GetCoalitionName(self.coalition))
-end
-self.Name=Name
-self.AirWing=AirWing
-AirWing:SetUsingOpsAwacs(self)
-self.CAPAirwings=FIFO:New()
-self.CAPAirwings:Push(AirWing,1)
-self.AwacsFG=nil
-self.RadarBlur=15
-if type(OpsZone)=="string"then
-self.OpsZone=ZONE:New(OpsZone)
-elseif type(OpsZone)=="table"and OpsZone.ClassName and string.find(OpsZone.ClassName,"ZONE")then
-self.OpsZone=OpsZone
-else
-self:E("AWACS - Invalid Zone passed!")
-return
-end
-self.AOCoordinate=COORDINATE:NewFromVec3(coalition.getMainRefPoint(self.coalition))
-self.AOName=self.OpsZone:GetName()
-self.UseBullsAO=true
-self.ControlZoneRadius=100
-self.StationZone=ZONE:New(StationZone)
-self.StationZoneName=StationZone
-self.Frequency=Frequency or 271
-self.Modulation=Modulation or radio.modulation.AM
-self.MultiFrequency={self.Frequency}
-self.MultiModulation={self.Modulation}
-self.Airbase=AIRBASE:FindByName(AirbaseName)
-self.AwacsAngels=25
-if AwacsOrbit then
-self.OrbitZone=ZONE:New(AwacsOrbit)
-end
-self.BorderZone=nil
-self.CallSign=CALLSIGN.AWACS.Magic
-self.CallSignNo=1
-self.NoHelos=true
-self.AIRequested=0
-self.AIonCAP=0
-self.AICAPMissions=FIFO:New()
-self.FlightGroups=FIFO:New()
-self.Countactcounter=0
-self.PictureInterval=300
-self.PictureTimeStamp=0
-self.ReassignTime=120
-self.intelstarted=false
-self.sunrisedone=false
-local speed=250
-self.SpeedBase=speed
-self.Speed=speed
-self.Heading=0
-self.Leg=50
-self.invisible=false
-self.immortal=false
-self.callsigntxt="AWACS"
-self.AwacsTimeOnStation=4
-self.AwacsTimeStamp=0
-self.EscortsTimeOnStation=4
-self.EscortsTimeStamp=0
-self.ShiftChangeTime=0.25
-self.ShiftChangeAwacsFlag=false
-self.ShiftChangeEscortsFlag=false
-self.CapSpeedBase=270
-self.CAPTimeOnStation=4
-self.MaxAIonCAP=4
-self.AICAPCAllName=CALLSIGN.Aircraft.Colt
-self.AICAPCAllNumber=0
-self.CAPGender="male"
-self.CAPCulture="en-US"
-self.CAPVoice=nil
-self.AwacsMission=nil
-self.AwacsInZone=false
-self.AwacsReady=false
-self.AwacsROE=AWACS.ROE.IFF
-self.AwacsROT=AWACS.ROT.BYPASSESCAPE
-self.HasEscorts=false
-self.EscortTemplate=""
-self.EscortMission={}
-self.EscortMissionReplacement={}
-self.PathToSRS="C:\\Program Files\\DCS-SimpleRadio-Standalone"
-self.Gender="female"
-self.Culture="en-GB"
-self.Voice=nil
-self.Port=5002
-self.Volume=1.0
-self.RadioQueue=FIFO:New()
-self.PrioRadioQueue=FIFO:New()
-self.TacticalQueue=FIFO:New()
-self.maxspeakentries=3
-self.GoogleTTSPadding=1
-self.WindowsTTSPadding=2.5
-self.clientset=SET_CLIENT:New():FilterActive(true):FilterCoalitions(self.coalitiontxt):FilterCategories("plane"):FilterStart()
-self.PlayerGuidance=true
-self.ModernEra=true
-self.NoGroupTags=false
-self.SuppressScreenOutput=false
-self.ReassignmentPause=180
-self.callsignshort=true
-self.DeclareRadius=5
-self.MenuStrict=true
-self.maxassigndistance=100
-self.NoMissileCalls=true
-self.PlayerCapAssignment=true
-self.ManagedGrps={}
-self.ManagedGrpID=0
-self.callsignTranslations=nil
-self.AnchorStacks=FIFO:New()
-self.AnchorBaseAngels=22
-self.AnchorStackDistance=2
-self.AnchorMaxStacks=4
-self.AnchorMaxAnchors=2
-self.AnchorMaxZones=6
-self.AnchorCurrZones=1
-self.AnchorTurn=-(360/self.AnchorMaxZones)
-self:_CreateAnchorStack()
-self.ManagedTasks=FIFO:New()
-local MonitoringData={}
-MonitoringData.AICAPCurrent=0
-MonitoringData.AICAPMax=self.MaxAIonCAP
-MonitoringData.Airwings=1
-MonitoringData.PlayersCheckedin=0
-MonitoringData.Players=0
-MonitoringData.AwacsShiftChange=false
-MonitoringData.AwacsStateFG="unknown"
-MonitoringData.AwacsStateMission="unknown"
-MonitoringData.EscortsShiftChange=false
-MonitoringData.EscortsStateFG={}
-MonitoringData.EscortsStateMission={}
-self.MonitoringOn=false
-self.MonitoringData=MonitoringData
-self.CatchAllMissions={}
-self.CatchAllFGs={}
-self.PictureAO=FIFO:New()
-self.PictureEWR=FIFO:New()
-self.Contacts=FIFO:New()
-self.CID=0
-self.ContactsAO=FIFO:New()
-self.clientmenus=FIFO:New()
-self.TacticalMenu=false
-self.TacticalBaseFreq=130
-self.TacticalIncrFreq=0.5
-self.TacticalModulation=radio.modulation.AM
-self.acticalFrequencies={}
-self.TacticalSubscribers={}
-self.TacticalInterval=120
-self.DetectionSet=SET_GROUP:New()
-self.lid=string.format("%s (%s) | ",self.Name,self.coalition and UTILS.GetCoalitionName(self.coalition)or"unknown")
-self:SetStartState("Stopped")
-self:AddTransition("Stopped","Start","StartUp")
-self:AddTransition("StartUp","Started","Running")
-self:AddTransition("*","Status","*")
-self:AddTransition("*","CheckedIn","*")
-self:AddTransition("*","CheckedOut","*")
-self:AddTransition("*","AssignAnchor","*")
-self:AddTransition("*","AssignedAnchor","*")
-self:AddTransition("*","ReAnchor","*")
-self:AddTransition("*","NewCluster","*")
-self:AddTransition("*","NewContact","*")
-self:AddTransition("*","LostCluster","*")
-self:AddTransition("*","LostContact","*")
-self:AddTransition("*","CheckRadioQueue","*")
-self:AddTransition("*","CheckTacticalQueue","*")
-self:AddTransition("*","EscortShiftChange","*")
-self:AddTransition("*","AwacsShiftChange","*")
-self:AddTransition("*","FlightOnMission","*")
-self:AddTransition("*","Intercept","*")
-self:AddTransition("*","InterceptSuccess","*")
-self:AddTransition("*","InterceptFailure","*")
-self:AddTransition("*","Stop","Stopped")
-local text=string.format("%sAWACS Version %s Initiated",self.lid,self.version)
-self:I(text)
-self:HandleEvent(EVENTS.PlayerEnterAircraft,self._EventHandler)
-self:HandleEvent(EVENTS.PlayerEnterUnit,self._EventHandler)
-self:HandleEvent(EVENTS.PlayerLeaveUnit,self._EventHandler)
-self:HandleEvent(EVENTS.Ejection,self._EventHandler)
-self:HandleEvent(EVENTS.Crash,self._EventHandler)
-self:HandleEvent(EVENTS.Dead,self._EventHandler)
-self:HandleEvent(EVENTS.UnitLost,self._EventHandler)
-self:HandleEvent(EVENTS.BDA,self._EventHandler)
-self:HandleEvent(EVENTS.PilotDead,self._EventHandler)
-self:HandleEvent(EVENTS.Shot,self._EventHandler)
-self:_InitLocalization()
-return self
-end
-function AWACS:SetTacticalRadios(BaseFreq,Increase,Modulation,Interval,Number)
-self:T(self.lid.."SetTacticalRadios")
-self.TacticalMenu=true
-self.TacticalBaseFreq=BaseFreq or 130
-self.TacticalIncrFreq=Increase or 0.5
-self.TacticalModulation=Modulation or radio.modulation.AM
-self.TacticalInterval=Interval or 120
-local number=Number or 10
-if number<1 then number=1 end
-if number>10 then number=10 end
-for i=1,number do
-local freq=self.TacticalBaseFreq+((i-1)*self.TacticalIncrFreq)
-self.TacticalFrequencies[freq]=freq
-end
-if self.AwacsSRS then
-self.TacticalSRS=MSRS:New(self.PathToSRS,self.TacticalBaseFreq,self.TacticalModulation,self.Volume)
-self.TacticalSRS:SetCoalition(self.coalition)
-self.TacticalSRS:SetGender(self.Gender)
-self.TacticalSRS:SetCulture(self.Culture)
-self.TacticalSRS:SetVoice(self.Voice)
-self.TacticalSRS:SetPort(self.Port)
-self.TacticalSRS:SetLabel("AWACS")
-if self.PathToGoogleKey then
-self.TacticalSRS:SetGoogle(self.PathToGoogleKey)
-end
-self.TacticalSRSQ=MSRSQUEUE:New("Tactical AWACS")
-end
-return self
-end
-function AWACS:_RefreshMenuNonSubscribed()
-self:T(self.lid.."_RefreshMenuNonSubscribed")
-local aliveset=self.clientset:GetAliveSet()
-for _,_group in pairs(aliveset)do
-local grp=_group
-local Group=grp:GetGroup()
-local gname=nil
-if Group and Group:IsAlive()then
-gname=Group:GetName()
-self:T(gname)
-end
-local menustr=self.clientmenus:ReadByID(gname)
-local menu=menustr.tactical
-if not self.TacticalSubscribers[gname]and menu then
-menu:RemoveSubMenus()
-for _,_freq in UTILS.spairs(self.TacticalFrequencies)do
-local modu=UTILS.GetModulationName(self.TacticalModulation)
-local text=string.format("Subscribe to %.3f %s",_freq,modu)
-local entry=MENU_GROUP_COMMAND:New(Group,text,menu,self._SubScribeTactRadio,self,Group,_freq)
-end
-end
-end
-return self
-end
-function AWACS:_UnsubScribeTactRadio(Group)
-self:T(self.lid.."_UnsubScribeTactRadio")
-local text=""
-local textScreen=""
-local GID,Outcome=self:_GetManagedGrpID(Group)
-local gcallsign=self:_GetCallSign(Group,GID)or"Ghost 1"
-local gname=Group:GetName()or"unknown"
-if Outcome and self.TacticalSubscribers[gname]then
-local Freq=self.TacticalSubscribers[gname]
-self.TacticalFrequencies[Freq]=Freq
-self.TacticalSubscribers[gname]=nil
-local modu=self.TacticalModulation==0 and"AM"or"FM"
-text=string.format("%s, %s, switch back to AWACS main frequency!",gcallsign,self.callsigntxt)
-self:_NewRadioEntry(text,text,GID,true,true,true,false,true)
-self:_RefreshMenuNonSubscribed()
-elseif self.AwacsFG then
-local nocheckin=self.gettext:GetEntry("NOTCHECKEDIN",self.locale)
-text=string.format(nocheckin,self:_GetCallSign(Group,GID)or"Ghost 1",self.callsigntxt)
-self:_NewRadioEntry(text,text,GID,Outcome,true,true,false)
-end
-return self
-end
-function AWACS:_SubScribeTactRadio(Group,Frequency)
-self:T(self.lid.."_SubScribeTactRadio")
-local text=""
-local textScreen=""
-local GID,Outcome=self:_GetManagedGrpID(Group)
-local gcallsign=self:_GetCallSign(Group,GID)or"Ghost 1"
-local gname=Group:GetName()or"unknown"
-if Outcome then
-self.TacticalSubscribers[gname]=Frequency
-self.TacticalFrequencies[Frequency]=nil
-local modu=self.TacticalModulation==0 and"AM"or"FM"
-text=string.format("%s, %s, switch to %.3f %s for tactical information!",gcallsign,self.callsigntxt,Frequency,modu)
-self:_NewRadioEntry(text,text,GID,true,true,true,false,true)
-local menustr=self.clientmenus:ReadByID(gname)
-local menu=menustr.tactical
-if menu then
-menu:RemoveSubMenus()
-local text=string.format("Unsubscribe %.3f %s",Frequency,modu)
-local entry=MENU_GROUP_COMMAND:New(Group,text,menu,self._UnsubScribeTactRadio,self,Group)
-end
-elseif self.AwacsFG then
-local nocheckin=self.gettext:GetEntry("NOTCHECKEDIN",self.locale)
-text=string.format(nocheckin,self:_GetCallSign(Group,GID)or"Ghost 1",self.callsigntxt)
-self:_NewRadioEntry(text,text,GID,Outcome,true,true,false)
-end
-return self
-end
-function AWACS:_CheckSubscribers()
-self:T(self.lid.."_InitLocalization")
-for _name,_freq in pairs(self.TacticalSubscribers or{})do
-local grp=GROUP:FindByName(_name)
-if(not grp)or(not grp:IsAlive())then
-self.TacticalFrequencies[_freq]=_freq
-self.TacticalSubscribers[_name]=nil
-end
-end
-return self
-end
-function AWACS:_InitLocalization()
-self:T(self.lid.."_InitLocalization")
-self.gettext=TEXTANDSOUND:New("AWACS","en")
-self.locale="en"
-for locale,table in pairs(self.Messages)do
-local Locale=string.lower(tostring(locale))
-self:T("**** Adding locale: "..Locale)
-for ID,Text in pairs(table)do
-self:T(string.format('Adding ID %s',tostring(ID)))
-self.gettext:AddEntry(Locale,tostring(ID),Text)
-end
-end
-return self
-end
-function AWACS:SetLocale(Locale)
-self:T(self.lid.."SetLocale")
-self.locale=Locale or"en"
-return self
-end
-function AWACS:AddFrequencyAndModulation(Frequency,Modulation)
-self:T(self.lid.."AddFrequencyAndModulation")
-table.insert(self.MultiFrequency,Frequency)
-table.insert(self.MultiModulation,Modulation)
-if self.AwacsSRS then
-self.AwacsSRS:SetFrequencies(self.MultiFrequency)
-self.AwacsSRS:SetModulations(self.MultiModulation)
-end
-return self
-end
-function AWACS:SetAsGCI(EWR,Delay)
-self:T(self.lid.."SetGCI")
-local delay=Delay or-5
-if type(EWR)=="string"then
-self.GCIGroup=GROUP:FindByName(EWR)
-else
-self.GCIGroup=EWR
-end
-self.GCI=true
-self:SetEscort(0)
-return self
-end
-function AWACS:_NewRadioEntry(TextTTS,TextScreen,GID,IsGroup,ToScreen,IsNew,FromAI,IsPrio,Tactical)
-self:T(self.lid.."_NewRadioEntry")
-local RadioEntry={}
-RadioEntry.IsNew=IsNew
-RadioEntry.TextTTS=TextTTS
-RadioEntry.TextScreen=TextScreen or TextTTS
-RadioEntry.GroupID=GID
-RadioEntry.ToScreen=ToScreen
-RadioEntry.Duration=STTS.getSpeechTime(TextTTS,0.95,false)or 8
-RadioEntry.FromAI=FromAI
-RadioEntry.IsGroup=IsGroup
-if Tactical then
-self.TacticalQueue:Push(RadioEntry)
-elseif IsPrio then
-self.PrioRadioQueue:Push(RadioEntry)
-else
-self.RadioQueue:Push(RadioEntry)
-end
-return self
-end
-function AWACS:SetBullsEyeAlias(Name)
-self:T(self.lid.."_SetBullsEyeAlias")
-self.AOName=Name or"Rock"
-return self
-end
-function AWACS:SetTOS(AICHours,CapHours)
-self:T(self.lid.."SetTOS")
-self.AwacsTimeOnStation=AICHours or 4
-self.CAPTimeOnStation=CapHours or 4
-return self
-end
-function AWACS:SetReassignmentPause(Seconds)
-self.ReassignmentPause=Seconds or 180
-return self
-end
-function AWACS:SuppressScreenMessages(Switch)
-self:T(self.lid.."_SetBullsEyeAlias")
-self.SuppressScreenOutput=Switch or false
-return self
-end
-function AWACS:ZipLip()
-self:T(self.lid.."ZipLip")
-self:SuppressScreenMessages(true)
-self.PlayerGuidance=false
-self.callsignshort=true
-self.NoMissileCalls=true
-return self
-end
-function AWACS:SetCustomCallsigns(translationTable)
-self.callsignTranslations=translationTable
-end
-function AWACS:_GetGIDFromGroupOrName(Group)
-self:T(self.lid.."_GetGIDFromGroupOrName")
-self:T({Group})
-local GID=0
-local Outcome=false
-local CallSign="Ghost 1"
-local nametocheck=CallSign
-if Group and type(Group)=="string"then
-nametocheck=Group
-elseif Group and Group:IsInstanceOf("GROUP")then
-nametocheck=Group:GetName()
-else
-return false,0,CallSign
-end
-local managedgrps=self.ManagedGrps or{}
-for _,_managed in pairs(managedgrps)do
-local managed=_managed
-if managed.GroupName==nametocheck then
-GID=managed.GID
-Outcome=true
-CallSign=managed.CallSign
-end
-end
-self:T({Outcome,GID,CallSign})
-return Outcome,GID,CallSign
-end
-function AWACS:_EventHandler(EventData)
-self:T(self.lid.."_EventHandler")
-self:T({Event=EventData.id})
-local Event=EventData
-if Event.id==EVENTS.PlayerEnterAircraft or Event.id==EVENTS.PlayerEnterUnit then
-if Event.IniCoalition==self.coalition then
-self:_SetClientMenus()
-end
-end
-if Event.id==EVENTS.PlayerLeaveUnit then
-self:T("Player group left unit: "..Event.IniGroupName)
-self:T("Player name left: "..Event.IniPlayerName)
-self:T("Coalition = "..UTILS.GetCoalitionName(Event.IniCoalition))
-if Event.IniCoalition==self.coalition then
-local Outcome,GID,CallSign=self:_GetGIDFromGroupOrName(Event.IniGroupName)
-if Outcome and GID>0 then
-self:T("Task Abort and Checkout Called")
-self:_TaskAbort(Event.IniGroupName)
-self:_CheckOut(nil,GID,true)
-end
-end
-end
-if Event.id==EVENTS.Ejection or Event.id==EVENTS.Crash or Event.id==EVENTS.Dead or Event.id==EVENTS.PilotDead then
-if Event.IniCoalition==self.coalition then
-local Outcome,GID,CallSign=self:_GetGIDFromGroupOrName(Event.IniGroupName)
-if Outcome and GID>0 then
-self:_TaskAbort(Event.IniGroupName)
-self:_CheckOut(nil,GID,true)
-end
-end
-end
-if Event.id==EVENTS.Shot and self.PlayerGuidance and not self.NoMissileCalls then
-if Event.IniCoalition~=self.coalition then
-self:T("Shot from: "..Event.IniGroupName)
-local position=Event.IniGroup:GetCoordinate()
-if not position then return self end
-local Category=Event.WeaponCategory
-local WeaponDesc=EventData.Weapon:getDesc()
-self:T({WeaponDesc})
-if WeaponDesc.category==1 and(WeaponDesc.missileCategory==1 or WeaponDesc.missileCategory==2)then
-self:T("AAM or SAM Missile fired")
-local warndist=25
-local Type="SAM"
-if WeaponDesc.category==1 then
-Type="Missile"
-local guidance=WeaponDesc.guidance or 4
-if guidance==2 then
-warndist=10
-elseif guidance==3 then
-warndist=25
-elseif guidance==4 then
-warndist=15
-elseif guidance==5 then
-warndist=10
-end
-end
-self:_MissileWarning(position,Type,warndist)
-end
-end
-end
-return self
-end
-function AWACS:_MissileWarning(Coordinate,Type,Warndist)
-self:T(self.lid.."_MissileWarning Type="..Type.." WarnDist="..Warndist)
-if not Coordinate then return self end
-local shotzone=ZONE_RADIUS:New("WarningZone",Coordinate:GetVec2(),UTILS.NMToMeters(Warndist))
-local targetgrpset=SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterCategoryAirplane():FilterActive():FilterZones({shotzone}):FilterOnce()
-if targetgrpset:Count()>0 then
-local targets=targetgrpset:GetSetObjects()
-for _,_grp in pairs(targets)do
-if _grp and _grp:IsAlive()then
-local isPlayer=_grp:IsPlayer()
-if isPlayer then
-local callsign=self:_GetCallSign(_grp)
-local defend=self.gettext:GetEntry("DEFEND",self.locale)
-local text=string.format(defend,callsign,Type,Type,Type)
-self:_NewRadioEntry(text,text,0,false,self.debug,true,false,true)
-end
-end
-end
-end
-return self
-end
-function AWACS:SetRadarBlur(Percent)
-local percent=Percent or 15
-if percent<0 then percent=0 end
-if percent>100 then percent=100 end
-self.RadarBlur=Percent
-return self
-end
-function AWACS:SetColdWar()
-self.ModernEra=false
-self.AwacsROT=AWACS.ROT.PASSIVE
-self.AwacsROE=AWACS.ROE.VID
-self.RadarBlur=25
-self:SetInterceptTimeline(35,25,15)
-return self
-end
-function AWACS:SetModernEra()
-self.ModernEra=true
-self.AwacsROT=AWACS.ROT.EVADE
-self.AwacsROE=AWACS.ROE.BVR
-self.RadarBlur=15
-return self
-end
-function AWACS:SetModernEraDefensive()
-self.ModernEra=true
-self.AwacsROT=AWACS.ROT.EVADE
-self.AwacsROE=AWACS.ROE.IFF
-self.RadarBlur=15
-return self
-end
-function AWACS:SetModernEraAggressive()
-self.ModernEra=true
-self.AwacsROT=AWACS.ROT.RETURNFIRE
-self.AwacsROE=AWACS.ROE.BVR
-self.RadarBlur=15
-return self
-end
-function AWACS:SetPolicingModern()
-self.ModernEra=true
-self.AwacsROT=AWACS.ROT.BYPASSESCAPE
-self.AwacsROE=AWACS.ROE.VID
-self.RadarBlur=15
-return self
-end
-function AWACS:SetPolicingColdWar()
-self.ModernEra=false
-self.AwacsROT=AWACS.ROT.BYPASSESCAPE
-self.AwacsROE=AWACS.ROE.VID
-self.RadarBlur=25
-self:SetInterceptTimeline(35,25,15)
-return self
-end
-function AWACS:SetPlayerGuidance(Switch)
-if(Switch==nil)or(Switch==true)then
-self.PlayerGuidance=true
-else
-self.PlayerGuidance=false
-end
-return self
-end
-function AWACS:GetName()
-return self.Name or"not set"
-end
-function AWACS:SetInterceptTimeline(TacDistance,MeldDistance,ThreatDistance)
-self.TacDistance=TacDistance or 45
-self.MeldDistance=MeldDistance or 35
-self.ThreatDistance=ThreatDistance or 25
-return self
-end
-function AWACS:SetAdditionalZone(Zone,Draw)
-self:T(self.lid.."SetAdditionalZone")
-self.BorderZone=Zone
-if self.debug then
-Zone:DrawZone(self.coalition,{1,0.64,0},1,{1,0.64,0},0.2,1,true)
-MARKER:New(Zone:GetCoordinate(),"Defensive Zone"):ToCoalition(self.coalition)
-elseif Draw then
-Zone:DrawZone(self.coalition,{1,0.64,0},1,{1,0.64,0},0.2,1,true)
-end
-return self
-end
-function AWACS:SetRejectionZone(Zone,Draw)
-self:T(self.lid.."SetRejectionZone")
-self.RejectZone=Zone
-if Draw then
-Zone:DrawZone(self.coalition,{1,0.64,0},1,{1,0.64,0},0.2,1,true)
-elseif self.debug then
-Zone:DrawZone(self.coalition,{1,0.64,0},1,{1,0.64,0},0.2,1,true)
-MARKER:New(Zone:GetCoordinate(),"Rejection Zone"):ToCoalition(self.coalition)
-end
-return self
-end
-function AWACS:DrawFEZ()
-self.OpsZone:DrawZone(self.coalition,{1,0,0},1,{1,0,0},0.2,5,true)
-return self
-end
-function AWACS:SetAwacsDetails(CallSign,CallSignNo,Angels,Speed,Heading,Leg)
-self:T(self.lid.."SetAwacsDetails")
-self.CallSign=CallSign or CALLSIGN.AWACS.Magic
-self.CallSignNo=CallSignNo or 1
-self.AwacsAngels=Angels or 25
-local speed=Speed or 250
-self.SpeedBase=speed
-self.Speed=speed
-self.Heading=Heading or 0
-self.Leg=Leg or 25
-return self
-end
-function AWACS:SetCustomAWACSCallSign(CallsignTable)
-self:T(self.lid.."SetCustomAWACSCallSign")
-self.CallSignClear=CallsignTable
-return self
-end
-function AWACS:AddGroupToDetection(Group)
-self:T(self.lid.."AddGroupToDetection")
-if Group and Group.ClassName and Group.ClassName=="GROUP"then
-self.DetectionSet:AddGroup(Group)
-elseif Group and Group.ClassName and Group.ClassName=="SET_GROUP"then
-self.DetectionSet:AddSet(Group)
-end
-return self
-end
-function AWACS:SetSRS(PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey)
-self:T(self.lid.."SetSRS")
-self.PathToSRS=PathToSRS or"C:\\Program Files\\DCS-SimpleRadio-Standalone"
-self.Gender=Gender or"male"
-self.Culture=Culture or"en-US"
-self.Port=Port or 5002
-self.Voice=Voice
-self.PathToGoogleKey=PathToGoogleKey
-self.Volume=Volume or 1.0
-self.AwacsSRS=MSRS:New(self.PathToSRS,self.MultiFrequency,self.MultiModulation,self.Volume)
-self.AwacsSRS:SetCoalition(self.coalition)
-self.AwacsSRS:SetGender(self.Gender)
-self.AwacsSRS:SetCulture(self.Culture)
-self.AwacsSRS:SetVoice(self.Voice)
-self.AwacsSRS:SetPort(self.Port)
-self.AwacsSRS:SetLabel("AWACS")
-if self.PathToGoogleKey then
-self.AwacsSRS:SetGoogle(self.PathToGoogleKey)
-end
-return self
-end
-function AWACS:SetSRSVoiceCAP(Gender,Culture,Voice)
-self:T(self.lid.."SetSRSVoiceCAP")
-self.CAPGender=Gender or"male"
-self.CAPCulture=Culture or"en-US"
-self.CAPVoice=Voice or"en-GB-Standard-B"
-return self
-end
-function AWACS:SetAICAPDetails(Callsign,MaxAICap,TOS,Speed)
-self:T(self.lid.."SetAICAPDetails")
-self.CapSpeedBase=Speed or 270
-self.CAPTimeOnStation=TOS or 4
-self.MaxAIonCAP=MaxAICap or 4
-self.AICAPCAllName=Callsign or CALLSIGN.Aircraft.Colt
-return self
-end
-function AWACS:SetEscort(EscortNumber)
-self:T(self.lid.."SetEscort")
-if EscortNumber and EscortNumber>0 then
-self.HasEscorts=true
-self.EscortNumber=EscortNumber
-else
-self.HasEscorts=false
-self.EscortNumber=0
-end
-return self
-end
-function AWACS:_MessageVector(GID,Tag,Coordinate,Angels)
-self:T(self.lid.."_MessageVector")
-local managedgroup=self.ManagedGrps[GID]
-local Tag=Tag or""
-if managedgroup and Coordinate then
-local tocallsign=managedgroup.CallSign or"Ghost 1"
-local group=managedgroup.Group
-local groupposition=group:GetCoordinate()
-local BRtext,BRtextTTS=self:_ToStringBR(groupposition,Coordinate)
-local vector=self.gettext:GetEntry("VECTORTO",self.locale)
-local vectortts=self.gettext:GetEntry("VECTORTOTTS",self.locale)
-local angelstxt=self.gettext:GetEntry("ANGELS",self.locale)
-local text=string.format(vectortts,tocallsign,self.callsigntxt,Tag,BRtextTTS)
-local textScreen=string.format(vector,tocallsign,self.callsigntxt,Tag,BRtext)
-if Angels then
-text=text..angelstxt..tostring(Angels).."."
-textScreen=textScreen..angelstxt..tostring(Angels).."."
-end
-self:_NewRadioEntry(text,textScreen,0,false,self.debug,true,false)
-end
-return self
-end
-function AWACS:_StartEscorts(Shiftchange)
-self:T(self.lid.."_StartEscorts")
-local AwacsFG=self.AwacsFG
-local group=AwacsFG:GetGroup()
-local timeonstation=(self.EscortsTimeOnStation+self.ShiftChangeTime)*3600
-for i=1,self.EscortNumber do
-local escort=AUFTRAG:NewESCORT(group,{x=-100*((i+(i%2))/2),y=0,z=(100+100*((i+(i%2))/2))*(-1)^i},45,{"Air"})
-escort:SetRequiredAssets(1)
-escort:SetTime(nil,timeonstation)
-self.AirWing:AddMission(escort)
-self.CatchAllMissions[#self.CatchAllMissions+1]=escort
-if Shiftchange then
-self.EscortMissionReplacement[i]=escort
-else
-self.EscortMission[i]=escort
-end
-end
-return self
-end
-function AWACS:_StartSettings(FlightGroup,Mission)
-self:T(self.lid.."_StartSettings")
-local Mission=Mission
-local AwacsFG=FlightGroup
-if self.AwacsMission:GetName()==Mission:GetName()then
-self:T("Setting up Awacs")
-AwacsFG:SetDefaultRadio(self.Frequency,self.Modulation,false)
-AwacsFG:SwitchRadio(self.Frequency,self.Modulation)
-AwacsFG:SetDefaultAltitude(self.AwacsAngels*1000)
-AwacsFG:SetHomebase(self.Airbase)
-AwacsFG:SetDefaultCallsign(self.CallSign,self.CallSignNo)
-AwacsFG:SetDefaultROE(ENUMS.ROE.WeaponHold)
-AwacsFG:SetDefaultAlarmstate(AI.Option.Ground.val.ALARM_STATE.GREEN)
-AwacsFG:SetDefaultEPLRS(self.ModernEra)
-AwacsFG:SetDespawnAfterLanding()
-AwacsFG:SetFuelLowRTB(true)
-AwacsFG:SetFuelLowThreshold(20)
-local group=AwacsFG:GetGroup()
-group:SetCommandInvisible(self.invisible)
-group:SetCommandImmortal(self.immortal)
-group:CommandSetCallsign(self.CallSign,self.CallSignNo,2)
-group:CommandEPLRS(self.ModernEra,5)
-self.AwacsFG=AwacsFG
-self.callsigntxt=string.format("%s",self.CallSignClear[self.CallSign])
-self:__CheckRadioQueue(10)
-if self.HasEscorts then
-self:_StartEscorts()
-end
-self.AwacsTimeStamp=timer.getTime()
-self.EscortsTimeStamp=timer.getTime()
-self.PictureTimeStamp=timer.getTime()+10*60
-self.AwacsReady=true
-self:Started()
-elseif self.ShiftChangeAwacsRequested and self.AwacsMissionReplacement and self.AwacsMissionReplacement:GetName()==Mission:GetName()then
-self:T("Setting up Awacs Replacement")
-AwacsFG:SetDefaultRadio(self.Frequency,self.Modulation,false)
-AwacsFG:SwitchRadio(self.Frequency,self.Modulation)
-AwacsFG:SetDefaultAltitude(self.AwacsAngels*1000)
-AwacsFG:SetHomebase(self.Airbase)
-self.CallSignNo=self.CallSignNo+1
-AwacsFG:SetDefaultCallsign(self.CallSign,self.CallSignNo)
-AwacsFG:SetDefaultROE(ENUMS.ROE.WeaponHold)
-AwacsFG:SetDefaultAlarmstate(AI.Option.Ground.val.ALARM_STATE.GREEN)
-AwacsFG:SetDefaultEPLRS(self.ModernEra)
-AwacsFG:SetDespawnAfterLanding()
-AwacsFG:SetFuelLowRTB(true)
-AwacsFG:SetFuelLowThreshold(20)
-local group=AwacsFG:GetGroup()
-group:SetCommandInvisible(self.invisible)
-group:SetCommandImmortal(self.immortal)
-group:CommandSetCallsign(self.CallSign,self.CallSignNo,2)
-self.callsigntxt=string.format("%s",self.CallSignClear[self.CallSign])
-local shifting=self.gettext:GetEntry("SHIFTCHANGE",self.locale)
-local text=string.format(shifting,self.callsigntxt,self.AOName or"Rock")
-self:T(self.lid..text)
-AwacsFG:RadioTransmission(text,1,false)
-self.AwacsFG=AwacsFG
-if self.HasEscorts then
-self:_StartEscorts(true)
-end
-self.AwacsTimeStamp=timer.getTime()
-self.EscortsTimeStamp=timer.getTime()
-self.AwacsReady=true
-end
-return self
-end
-function AWACS:_ToStringBULLS(Coordinate,ssml,TTS)
-self:T(self.lid.."_ToStringBULLS")
-local bullseyename=self.AOName or"Rock"
-local BullsCoordinate=self.AOCoordinate
-local DirectionVec3=BullsCoordinate:GetDirectionVec3(Coordinate)
-local AngleRadians=Coordinate:GetAngleRadians(DirectionVec3)
-local Distance=Coordinate:Get2DDistance(BullsCoordinate)
-local AngleDegrees=UTILS.Round(UTILS.ToDegree(AngleRadians),0)
-local Bearing=string.format('%03d',AngleDegrees)
-local Distance=UTILS.Round(UTILS.MetersToNM(Distance),0)
-if ssml then
-return string.format("%s %03d, %d",bullseyename,Bearing,Distance)
-end
-if TTS then
-Bearing=self:_ToStringBullsTTS(Bearing)
-local zero=self.gettext:GetEntry("ZERO",self.locale)
-local BearingTTS=string.gsub(Bearing,"0",zero)
-return string.format("%s %s, %d",bullseyename,BearingTTS,Distance)
-else
-return string.format("%s %s, %d",bullseyename,Bearing,Distance)
-end
-end
-function AWACS:_ToStringBullsTTS(Text)
-local text=Text
-text=string.gsub(text,"Bullseye","Bulls eye")
-text=string.gsub(text,"%d","%1 ")
-text=string.gsub(text," ,",".")
-text=string.gsub(text," $","")
-return text
-end
-function AWACS:_GetManagedGrpID(Group)
-if not Group or not Group:IsAlive()then
-self:T(self.lid.."_GetManagedGrpID - Requested Group is not alive!")
-return 0,false,""
-end
-self:T(self.lid.."_GetManagedGrpID for "..Group:GetName())
-local GID=0
-local Outcome=false
-local CallSign="Ghost 1"
-local nametocheck=Group:GetName()
-local managedgrps=self.ManagedGrps or{}
-for _,_managed in pairs(managedgrps)do
-local managed=_managed
-if managed.GroupName==nametocheck then
-GID=managed.GID
-Outcome=true
-CallSign=managed.CallSign
-end
-end
-return GID,Outcome,CallSign
-end
-function AWACS:_GetCallSign(Group,GID,IsPlayer)
-self:T(self.lid.."_GetCallSign - GID "..tostring(GID))
-if GID and type(GID)=="number"and GID>0 then
-local managedgroup=self.ManagedGrps[GID]
-self:T("Saved Callsign for TTS = "..tostring(managedgroup.CallSign))
-return managedgroup.CallSign
-end
-local callsign="Ghost 1"
-if Group and Group:IsAlive()then
-callsign=Group:GetCustomCallSign(self.callsignshort,self.keepnumber,self.callsignTranslations)
-end
-return callsign
-end
-function AWACS:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTranslations)
-if not ShortCallsign or ShortCallsign==false then
-self.callsignshort=false
-else
-self.callsignshort=true
-end
-self.keepnumber=Keepnumber or false
-self.callsignTranslations=CallsignTranslations
-return self
-end
-function AWACS:_UpdateContactFromCluster(CID)
-self:T(self.lid.."_UpdateContactFromCluster CID="..CID)
-local existingcontact=self.Contacts:PullByID(CID)
-local ContactTable=existingcontact.Cluster.Contacts or{}
-local function GetFirstAliveContact(table)
-for _,_contact in pairs(table)do
-local contact=_contact
-if contact and contact.group and contact.group:IsAlive()then
-return contact
-end
-end
-return nil
-end
-local NewContact=GetFirstAliveContact(ContactTable)
-if NewContact then
-existingcontact.Contact=NewContact
-self.Contacts:Push(existingcontact,existingcontact.CID)
-end
-return self
-end
-function AWACS:_CheckMerges()
-self:T(self.lid.."_CheckMerges")
-for _id,_pilot in pairs(self.ManagedGrps)do
-local pilot=_pilot
-if pilot.Group and pilot.Group:IsAlive()then
-local ppos=pilot.Group:GetCoordinate()
-local pcallsign=pilot.CallSign
-self:T(self.lid.."Checking for "..pcallsign)
-if ppos then
-self.Contacts:ForEach(
-function(Contact)
-local contact=Contact
-local cpos=contact.Cluster.coordinate or contact.Contact.position or contact.Contact.group:GetCoordinate()
-local dist=ppos:Get2DDistance(cpos)
-local distnm=UTILS.Round(UTILS.MetersToNM(dist),0)
-if(pilot.IsPlayer or self.debug)and distnm<=5 and not contact.MergeCallDone then
-local label=contact.EngagementTag or""
-if not contact.MergeCallDone or not string.find(label,pcallsign)then
-self:T(self.lid.."Merged")
-self:_MergedCall(_id)
-contact.MergeCallDone=true
-end
-end
-end
-)
-end
-end
-end
-return self
-end
-function AWACS:_CleanUpContacts()
-self:T(self.lid.."_CleanUpContacts")
-if self.Contacts:Count()>0 then
-local deadcontacts=FIFO:New()
-self.Contacts:ForEach(
-function(Contact)
-local contact=Contact
-if not contact.Contact.group:IsAlive()or contact.Target:IsDead()or contact.Target:IsDestroyed()or contact.Target:CountTargets()==0 then
-deadcontacts:Push(contact,contact.CID)
-self:T("DEAD contact CID="..contact.CID)
-end
-end
-)
-if deadcontacts:Count()>0 and(not self.NoGroupTags)then
-self:T("DEAD count="..deadcontacts:Count())
-deadcontacts:ForEach(
-function(Contact)
-local contact=Contact
-local vanished=self.gettext:GetEntry("VANISHED",self.locale)
-local vanishedtts=self.gettext:GetEntry("VANISHEDTTS",self.locale)
-local text=string.format(vanishedtts,self.callsigntxt,contact.TargetGroupNaming)
-local textScreen=string.format(vanished,self.callsigntxt,contact.TargetGroupNaming)
-self:_NewRadioEntry(text,textScreen,0,false,self.debug,true,false,true)
-self.Contacts:PullByID(contact.CID)
-end
-)
-end
-if self.Contacts:Count()>0 then
-self.Contacts:ForEach(
-function(Contact)
-local contact=Contact
-self:_UpdateContactFromCluster(contact.CID)
-end
-)
-end
-deadcontacts:Clear()
-end
-return self
-end
-function AWACS:_GetIdlePilots()
-self:T(self.lid.."_GetIdlePilots")
-local AIPilots={}
-local HumanPilots={}
-for _name,_entry in pairs(self.ManagedGrps)do
-local entry=_entry
-self:T("Looking at entry "..entry.GID.." Name "..entry.GroupName)
-local managedtask=self:_ReadAssignedTaskFromGID(entry.GID)
-local overridetask=false
-if managedtask then
-self:T("Current task = "..(managedtask.ToDo or"Unknown"))
-if managedtask.ToDo==AWACS.TaskDescription.ANCHOR then
-overridetask=true
-end
-end
-if entry.IsAI then
-if entry.FlightGroup:IsAirborne()and((not entry.HasAssignedTask)or overridetask)then
-self:T("Adding AI with Callsign: "..entry.CallSign)
-AIPilots[#AIPilots+1]=_entry
-end
-elseif entry.IsPlayer and(not entry.Blocked)and(not entry.Group:IsHelicopter())then
-if(not entry.HasAssignedTask)or overridetask then
-local TNow=timer.getTime()
-if entry.LastTasking and(TNow-entry.LastTasking>self.ReassignTime)then
-self:T("Adding Human with Callsign: "..entry.CallSign)
-HumanPilots[#HumanPilots+1]=_entry
-end
-end
-end
-end
-return AIPilots,HumanPilots
-end
-function AWACS:_TargetSelectionProcess(Untargeted)
-self:T(self.lid.."_TargetSelectionProcess")
-local maxtargets=3
-local contactstable=self.Contacts:GetDataTable()
-local targettable=FIFO:New()
-local sortedtargets=FIFO:New()
-local prefiltered=FIFO:New()
-local HaveTargets=false
-self:T(self.lid.."Initial count: "..self.Contacts:Count())
-if Untargeted then
-self.Contacts:ForEach(
-function(Contact)
-local contact=Contact
-if contact.Contact.group:IsAlive()and(contact.Status==AWACS.TaskStatus.IDLE or contact.Status==AWACS.TaskStatus.UNASSIGNED)then
-if self.AwacsROE==AWACS.ROE.POLICE or self.AwacsROE==AWACS.ROE.VID then
-if not(contact.IFF==AWACS.IFF.FRIENDLY or contact.IFF==AWACS.IFF.NEUTRAL)then
-prefiltered:Push(contact,contact.CID)
-end
-else
-prefiltered:Push(contact,contact.CID)
-end
-end
-end
-)
-contactstable=prefiltered:GetDataTable()
-self:T(self.lid.."Untargeted: "..prefiltered:Count())
-end
-for _,_contact in pairs(contactstable)do
-local contact=_contact
-local checked=false
-local contactname=contact.TargetGroupNaming or"ZETA"
-local typename=contact.ReportingName or"Unknown"
-self:T(self.lid..string.format("Looking at group %s type %s",contactname,typename))
-local contactcoord=contact.Cluster.coordinate or contact.Contact.position or contact.Contact.group:GetCoordinate()
-local contactvec2=contactcoord:GetVec2()
-if self.RejectZone then
-local isinrejzone=self.RejectZone:IsVec2InZone(contactvec2)
-if isinrejzone then
-self:T(self.lid.."Across Border = YES - ignore")
-checked=true
-end
-end
-if not self.GCI then
-local HVTCoordinate=self.OrbitZone:GetCoordinate()
-local distance=UTILS.NMToMeters(200)
-if contactcoord then
-distance=HVTCoordinate:Get2DDistance(contactcoord)
-end
-self:T(self.lid.."HVT Distance = "..UTILS.Round(UTILS.MetersToNM(distance),0))
-if UTILS.MetersToNM(distance)<=45 and not checked then
-self:T(self.lid.."In HVT Distance = YES")
-targettable:Push(contact,distance)
-checked=true
-end
-end
-local isinopszone=self.OpsZone:IsVec2InZone(contactvec2)
-local distance=self.OpsZone:Get2DDistance(contactcoord)
-if isinopszone and not checked then
-self:T(self.lid.."In FEZ = YES")
-targettable:Push(contact,distance)
-checked=true
-end
-local isinopszone=self.ControlZone:IsVec2InZone(contactvec2)
-if isinopszone and not checked then
-self:T(self.lid.."In Radar Zone = YES")
-local distance=self.AOCoordinate:Get2DDistance(contactcoord)
-local AOdist=UTILS.Round(UTILS.MetersToNM(distance),0)
-if not contactcoord.Heading then
-contactcoord.Heading=self.intel:CalcClusterDirection(contact.Cluster)
-end
-local aspect=contactcoord:ToStringAspect(self.ControlZone:GetCoordinate())
-local sizing=contact.Cluster.size or self.intel:ClusterCountUnits(contact.Cluster)or 1
-sizing=math.fmod((sizing*0.1),1)
-local AOdist2=(AOdist/2)*sizing
-AOdist2=UTILS.Round((AOdist/2)+((AOdist/2)-AOdist2),0)
-self:T(self.lid.."Aspect = "..aspect.." | Size = "..sizing)
-if(AOdist2<75)or(aspect=="Hot")then
-local text=string.format("In AO(Adj) dist = %d(%d) NM",AOdist,AOdist2)
-self:T(self.lid..text)
-targettable:Push(contact,distance)
-checked=true
-end
-end
-if self.BorderZone then
-local isinborderzone=self.BorderZone:IsVec2InZone(contactvec2)
-if isinborderzone and not checked then
-self:T(self.lid.."In BorderZone = YES")
-targettable:Push(contact,distance)
-checked=true
-end
-end
-end
-self:T(self.lid.."Post filter count: "..targettable:Count())
-if targettable:Count()>maxtargets then
-local targets=targettable:GetSortedDataTable()
-targettable:Clear()
-for i=1,maxtargets do
-targettable:Push(targets[i])
-end
-end
-sortedtargets:Clear()
-prefiltered:Clear()
-if targettable:Count()>0 then
-HaveTargets=true
-end
-return HaveTargets,targettable
-end
-function AWACS:_CreatePicture(AO,Callsign,GID,MaxEntries,IsGeneral)
-self:T(self.lid.."_CreatePicture AO="..tostring(AO).." for "..Callsign.." GID "..GID)
-local managedgroup=nil
-local group=nil
-local groupcoord=nil
-if not IsGeneral then
-managedgroup=self.ManagedGrps[GID]
-group=managedgroup.Group
-groupcoord=group:GetCoordinate()
-end
-local fifo=self.PictureAO
-local maxentries=self.maxspeakentries or 3
-if MaxEntries and MaxEntries>0 and MaxEntries<=3 then
-maxentries=MaxEntries
-end
-local counter=0
-if not AO then
-end
-local entries=fifo:GetSize()
-if entries1 then
-text=text.." "..threatsizetext.."."
-textScreen=textScreen.." "..threatsizetext.."."
-end
-if contact.EngagementTag then
-text=text.." "..contact.EngagementTag
-textScreen=textScreen.." "..contact.EngagementTag
-end
-local RadioEntry_IsGroup=false
-local RadioEntry_ToScreen=self.debug
-if managedgroup and not IsGeneral then
-RadioEntry_IsGroup=managedgroup.IsPlayer
-RadioEntry_ToScreen=managedgroup.IsPlayer
-end
-self:_NewRadioEntry(text,textScreen,GID,RadioEntry_IsGroup,RadioEntry_ToScreen,true,false)
-end
-end
-fifo:Clear()
-return self
-end
-function AWACS:_CreateBogeyDope(Callsign,GID,Tactical)
-self:T(self.lid.."_CreateBogeyDope for "..Callsign.." GID "..GID)
-local managedgroup=self.ManagedGrps[GID]
-local group=managedgroup.Group
-local groupcoord=group:GetCoordinate()
-local fifo=self.ContactsAO
-local maxentries=1
-local counter=0
-local entries=fifo:GetSize()
-if entries0 then
-local IDstack=self.PictureEWR:GetSortedDataTable()
-local weneed=3-clustersAO
-self:T(string.format("Picture - adding %d/%d contacts from EWR",weneed,clustersEWR))
-if weneed>clustersEWR then
-weneed=clustersEWR
-end
-for i=1,weneed do
-self.PictureAO:Push(IDstack[i])
-end
-end
-clustersAO=self.PictureAO:GetSize()
-if clustersAO==0 and clustersEWR==0 then
-self:_NewRadioEntry(text,text,GID,Outcome,true,true,false)
-else
-if clustersAO>0 then
-local picture=self.gettext:GetEntry("PICTURE",self.locale)
-text=string.format("%s, %s. %s. ",gcallsign,self.callsigntxt,picture)
-textScreen=string.format("%s, %s. %s. ",gcallsign,self.callsigntxt,picture)
-local onetxt=self.gettext:GetEntry("ONE",self.locale)
-local grptxt=self.gettext:GetEntry("GROUP",self.locale)
-local groupstxt=self.gettext:GetEntry("GROUPMULTI",self.locale)
-if clustersAO==1 then
-text=string.format("%s%s %s. ",text,onetxt,grptxt)
-textScreen=string.format("%s%s %s.\n",textScreen,onetxt,grptxt)
-else
-text=string.format("%s%d %s. ",text,clustersAO,groupstxt)
-textScreen=string.format("%s%d %s.\n",textScreen,clustersAO,groupstxt)
-end
-self:_NewRadioEntry(text,textScreen,GID,Outcome,true,true,false)
-self:_CreatePicture(true,gcallsign,GID,3,general)
-self.PictureAO:Clear()
-self.PictureEWR:Clear()
-end
-end
-elseif self.AwacsFG then
-local nocheckin=self.gettext:GetEntry("NOTCHECKEDIN",self.locale)
-text=string.format(nocheckin,gcallsign,self.callsigntxt)
-self:_NewRadioEntry(text,text,GID,Outcome,true,true,false)
-end
-return self
-end
-function AWACS:_BogeyDope(Group,Tactical)
-self:T(self.lid.."_BogeyDope")
-local text=""
-local textScreen=""
-local GID,Outcome=self:_GetManagedGrpID(Group)
-local gcallsign=self:_GetCallSign(Group,GID)or"Ghost 1"
-if not self.intel then
-local clean=self.gettext:GetEntry("CLEAN",self.locale)
-text=string.format(clean,self:_GetCallSign(Group,GID)or"Ghost 1",self.callsigntxt)
-self:_NewRadioEntry(text,text,0,false,true,true,false,true,Tactical)
-return self
-end
-if Outcome then
-local managedgroup=self.ManagedGrps[GID]
-local pilotgroup=managedgroup.Group
-local pilotcoord=managedgroup.Group:GetCoordinate()
-local contactstable=self.Contacts:GetDataTable()
-for _,_contact in pairs(contactstable)do
-local managedcontact=_contact
-local contactposition=managedcontact.Cluster.coordinate or managedcontact.Contact.position
-local coordVec2=contactposition:GetVec2()
-local dist=pilotcoord:Get2DDistance(contactposition)
-if self.ControlZone:IsVec2InZone(coordVec2)then
-self.ContactsAO:Push(managedcontact,dist)
-elseif self.BorderZone and self.BorderZone:IsVec2InZone(coordVec2)then
-self.ContactsAO:Push(managedcontact,dist)
-else
-if self.OrbitZone then
-local distance=contactposition:Get2DDistance(self.OrbitZone:GetCoordinate())
-if(distance<=UTILS.NMToMeters(45))then
-self.ContactsAO:Push(managedcontact,distance)
-end
-end
-end
-end
-local contactsAO=self.ContactsAO:GetSize()
-if contactsAO==0 then
-local clean=self.gettext:GetEntry("CLEAN",self.locale)
-text=string.format(clean,self:_GetCallSign(Group,GID)or"Ghost 1",self.callsigntxt)
-self:_NewRadioEntry(text,textScreen,GID,Outcome,Outcome,true,false,true,Tactical)
-else
-if contactsAO>0 then
-local dope=self.gettext:GetEntry("DOPE",self.locale)
-text=string.format(dope,self:_GetCallSign(Group,GID)or"Ghost 1",self.callsigntxt)
-textScreen=string.format(dope,self:_GetCallSign(Group,GID)or"Ghost 1",self.callsigntxt)
-local onetxt=self.gettext:GetEntry("ONE",self.locale)
-local grptxt=self.gettext:GetEntry("GROUP",self.locale)
-local groupstxt=self.gettext:GetEntry("GROUPMULTI",self.locale)
-if contactsAO==1 then
-text=string.format("%s%s %s. ",text,onetxt,grptxt)
-textScreen=string.format("%s%s %s.\n",textScreen,onetxt,grptxt)
-else
-text=string.format("%s%d %s. ",text,contactsAO,groupstxt)
-textScreen=string.format("%s%d %s.\n",textScreen,contactsAO,groupstxt)
-end
-self:_NewRadioEntry(text,textScreen,GID,Outcome,true,true,false,true,Tactical)
-self:_CreateBogeyDope(self:_GetCallSign(Group,GID)or"Ghost 1",GID,Tactical)
-end
-end
-elseif self.AwacsFG then
-local nocheckin=self.gettext:GetEntry("NOTCHECKEDIN",self.locale)
-text=string.format(nocheckin,self:_GetCallSign(Group,GID)or"Ghost 1",self.callsigntxt)
-self:_NewRadioEntry(text,text,GID,Outcome,true,true,false,Tactical)
-end
-return self
-end
-function AWACS:_ShowAwacsInfo(Group)
-self:T(self.lid.."_ShowAwacsInfo")
-local report=REPORT:New("Info")
-report:Add("====================")
-report:Add(string.format("AWACS %s",self.callsigntxt))
-report:Add(string.format("Radio: %.3f %s",self.Frequency,UTILS.GetModulationName(self.Modulation)))
-report:Add(string.format("Bulls Alias: %s",self.AOName))
-report:Add(string.format("Coordinate: %s",self.AOCoordinate:ToStringLLDDM()))
-report:Add("====================")
-report:Add(string.format("Assignment Distance: %d NM",self.maxassigndistance))
-report:Add(string.format("TAC Distance: %d NM",self.TacDistance))
-report:Add(string.format("MELD Distance: %d NM",self.MeldDistance))
-report:Add(string.format("THREAT Distance: %d NM",self.ThreatDistance))
-report:Add("====================")
-report:Add(string.format("ROE/ROT: %s, %s",self.AwacsROE,self.AwacsROT))
-MESSAGE:New(report:Text(),45,"AWACS"):ToGroup(Group)
-return self
-end
-function AWACS:_VID(Group,Declaration)
-self:T(self.lid.."_VID")
-local GID,Outcome,Callsign=self:_GetManagedGrpID(Group)
-local text=""
-local TextTTS=""
-if Outcome then
-local managedgroup=self.ManagedGrps[GID]
-local group=managedgroup.Group
-local position=group:GetCoordinate()
-local radius=UTILS.NMToMeters(self.DeclareRadius)or UTILS.NMToMeters(5)
-local TID=managedgroup.CurrentTask or 0
-if TID>0 then
-local task=self.ManagedTasks:ReadByID(TID)
-if task.ToDo~=AWACS.TaskDescription.VID then
-return self
-end
-if task.Status~=AWACS.TaskStatus.ASSIGNED then
-return self
-end
-local CID=task.Cluster.CID
-local cluster=self.Contacts:ReadByID(CID)
-if cluster then
-local gposition=cluster.Contact.group:GetCoordinate()
-local cposition=gposition or cluster.Cluster.coordinate or cluster.Contact.position
-local distance=cposition:Get2DDistance(position)
-distance=UTILS.Round(distance,0)+1
-if distance<=radius or self.debug then
-self:T("Contact VID as "..Declaration)
-cluster.IFF=Declaration
-task.Status=AWACS.TaskStatus.SUCCESS
-self.ManagedTasks:PullByID(TID)
-self.ManagedTasks:Push(task,TID)
-self.Contacts:PullByID(CID)
-self.Contacts:Push(cluster,CID)
-local vidpos=self.gettext:GetEntry("VIDPOS",self.locale)
-text=string.format(vidpos,Callsign,self.callsigntxt,Declaration)
-self:T(text)
-else
-self:T("Contact VID not close enough")
-local vidneg=self.gettext:GetEntry("VIDNEG",self.locale)
-text=string.format(vidneg,Callsign,self.callsigntxt)
-self:T(text)
-end
-self:_NewRadioEntry(text,text,GID,Outcome,true,true,false,true)
-end
-end
-elseif self.AwacsFG then
-local nocheckin=self.gettext:GetEntry("NOTCHECKEDIN",self.locale)
-text=string.format(nocheckin,self:_GetCallSign(Group,GID)or"Ghost 1",self.callsigntxt)
-self:_NewRadioEntry(text,text,GID,Outcome,true,true,false)
-end
-return self
-end
-function AWACS:_Declare(Group)
-self:T(self.lid.."_Declare")
-local GID,Outcome,Callsign=self:_GetManagedGrpID(Group)
-local text=""
-local TextTTS=""
-if Outcome then
-local managedgroup=self.ManagedGrps[GID]
-local group=managedgroup.Group
-local position=group:GetCoordinate()
-local radius=UTILS.NMToMeters(self.DeclareRadius)or UTILS.NMToMeters(5)
-local groupzone=ZONE_GROUP:New(group:GetName(),group,radius)
-local Coalitions={"red","neutral"}
-if self.coalition==coalition.side.NEUTRAL then
-Coalitions={"red","blue"}
-elseif self.coalition==coalition.side.RED then
-Coalitions={"blue","neutral"}
-end
-local contactset=SET_GROUP:New():FilterCategoryAirplane():FilterCoalitions(Coalitions):FilterZones({groupzone}):FilterOnce()
-local numbercontacts=contactset:CountAlive()or 0
-local foundcontacts={}
-if numbercontacts>0 then
-contactset:ForEach(
-function(airpl)
-local distance=position:Get2DDistance(airpl:GetCoordinate())
-distance=UTILS.Round(distance,0)+1
-foundcontacts[distance]=airpl
-end
-,{}
-)
-for _dist,_contact in UTILS.spairs(foundcontacts)do
-local distanz=_dist
-local contact=_contact
-local ccoalition=contact:GetCoalition()
-local ctypename=contact:GetTypeName()
-local ffneutral=self.gettext:GetEntry("FFNEUTRAL",self.locale)
-local fffriend=self.gettext:GetEntry("FFFRIEND",self.locale)
-local ffhostile=self.gettext:GetEntry("FFHOSTILE",self.locale)
-local ffspades=self.gettext:GetEntry("FFSPADES",self.locale)
-local friendorfoe=ffneutral
-if self.self.ModernEra then
-if ccoalition==self.coalition then
-friendorfoe=fffriend
-elseif ccoalition==coalition.side.NEUTRAL then
-friendorfoe=ffneutral
-elseif ccoalition~=self.coalition then
-friendorfoe=ffhostile
-end
-else
-friendorfoe=ffspades
-end
-self:T(string.format("Distance %d ContactName %s Coalition %d (%s) TypeName %s",distanz,contact:GetName(),ccoalition,friendorfoe,ctypename))
-text=string.format("%s. %s. %s.",Callsign,self.callsigntxt,friendorfoe)
-TextTTS=text
-if self.ModernEra then
-text=string.format("%s %s.",text,ctypename)
-end
-break
-end
-else
-local ffclean=self.gettext:GetEntry("FFCLEAN",self.locale)
-text=string.format("%s. %s. %s.",Callsign,self.callsigntxt,ffclean)
-TextTTS=text
-end
-self:_NewRadioEntry(TextTTS,text,GID,Outcome,true,true,false,true)
-elseif self.AwacsFG then
-local nocheckin=self.gettext:GetEntry("NOTCHECKEDIN",self.locale)
-text=string.format(nocheckin,self:_GetCallSign(Group,GID)or"Ghost 1",self.callsigntxt)
-self:_NewRadioEntry(text,text,GID,Outcome,true,true,false)
-end
-return self
-end
-function AWACS:_Commit(Group)
-self:T(self.lid.."_Commit")
-local GID,Outcome=self:_GetManagedGrpID(Group)
-local text=""
-if Outcome then
-local Pilot=self.ManagedGrps[GID]
-local currtaskid=Pilot.CurrentTask
-local managedtask=self.ManagedTasks:ReadByID(currtaskid)
-self:T(string.format("TID %d(%d) | ToDo %s | Status %s",currtaskid,managedtask.TID,managedtask.ToDo,managedtask.Status))
-if managedtask then
-if managedtask.Status==AWACS.TaskStatus.REQUESTED then
-managedtask=self.ManagedTasks:PullByID(currtaskid)
-managedtask.Status=AWACS.TaskStatus.ASSIGNED
-self.ManagedTasks:Push(managedtask,currtaskid)
-self:T(string.format("COMMITTED - TID %d(%d) for GID %d | ToDo %s | Status %s",currtaskid,GID,managedtask.TID,managedtask.ToDo,managedtask.Status))
-Pilot.HasAssignedTask=true
-Pilot.CurrentTask=currtaskid
-self.ManagedGrps[GID]=Pilot
-local copy=self.gettext:GetEntry("COPY",self.locale)
-local targetedby=self.gettext:GetEntry("TARGETEDBY",self.locale)
-text=string.format(copy,self:_GetCallSign(Group,GID)or"Ghost 1",self.callsigntxt)
-local EngagementTag=string.format(targetedby,Pilot.CallSign)
-self:_UpdateContactEngagementTag(Pilot.ContactCID,EngagementTag,false,false,AWACS.TaskStatus.ASSIGNED)
-self:_NewRadioEntry(text,text,GID,Outcome,true,true,false,true)
-else
-self:E(self.lid.."Cannot find REQUESTED managed task with TID="..currtaskid.." for GID="..GID)
-end
-else
-self:E(self.lid.."Cannot find managed task with TID="..currtaskid.." for GID="..GID)
-end
-elseif self.AwacsFG then
-local nocheckin=self.gettext:GetEntry("NOTCHECKEDIN",self.locale)
-text=string.format(nocheckin,self:_GetCallSign(Group,GID)or"Ghost 1",self.callsigntxt)
-self:_NewRadioEntry(text,text,GID,Outcome,true,true,false)
-end
-return self
-end
-function AWACS:_Judy(Group)
-self:T(self.lid.."_Judy")
-local GID,Outcome=self:_GetManagedGrpID(Group)
-local text=""
-if Outcome then
-local Pilot=self.ManagedGrps[GID]
-local currtaskid=Pilot.CurrentTask
-local managedtask=self.ManagedTasks:ReadByID(currtaskid)
-if managedtask then
-if managedtask.Status==AWACS.TaskStatus.REQUESTED or managedtask.Status==AWACS.TaskStatus.UNASSIGNED then
-managedtask=self.ManagedTasks:PullByID(currtaskid)
-managedtask.Status=AWACS.TaskStatus.ASSIGNED
-self.ManagedTasks:Push(managedtask,currtaskid)
-local copy=self.gettext:GetEntry("COPY",self.locale)
-local targetedby=self.gettext:GetEntry("TARGETEDBY",self.locale)
-text=string.format(copy,self:_GetCallSign(Group,GID)or"Ghost 1",self.callsigntxt)
-local EngagementTag=string.format(targetedby,Pilot.CallSign)
-self:_UpdateContactEngagementTag(Pilot.ContactCID,EngagementTag,false,false,AWACS.TaskStatus.ASSIGNED)
-self:_NewRadioEntry(text,text,GID,Outcome,true,true,false,true)
-else
-self:E(self.lid.."Cannot find REQUESTED or UNASSIGNED managed task with TID="..currtaskid.." for GID="..GID)
-end
-else
-self:E(self.lid.."Cannot find managed task with TID="..currtaskid.." for GID="..GID)
-end
-elseif self.AwacsFG then
-local nocheckin=self.gettext:GetEntry("NOTCHECKEDIN",self.locale)
-text=string.format(nocheckin,self:_GetCallSign(Group,GID)or"Ghost 1",self.callsigntxt)
-self:_NewRadioEntry(text,text,GID,Outcome,true,true,false)
-end
-return self
-end
-function AWACS:_Unable(Group)
-self:T(self.lid.."_Unable")
-local GID,Outcome=self:_GetManagedGrpID(Group)
-local text=""
-if Outcome then
-local Pilot=self.ManagedGrps[GID]
-local currtaskid=Pilot.CurrentTask
-local managedtask=self.ManagedTasks:ReadByID(currtaskid)
-self:T(string.format("UNABLE for TID %d(%d) | ToDo %s | Status %s",currtaskid,managedtask.TID,managedtask.ToDo,managedtask.Status))
-if managedtask then
-if managedtask.Status==AWACS.TaskStatus.REQUESTED then
-managedtask=self.ManagedTasks:PullByID(currtaskid)
-managedtask.IsUnassigned=true
-managedtask.Status=AWACS.TaskStatus.FAILED
-self.ManagedTasks:Push(managedtask,currtaskid)
-self:T(string.format("REJECTED - TID %d(%d) for GID %d | ToDo %s | Status %s",currtaskid,GID,managedtask.TID,managedtask.ToDo,managedtask.Status))
-Pilot.HasAssignedTask=false
-Pilot.CurrentTask=0
-Pilot.LastTasking=timer.getTime()
-self.ManagedGrps[GID]=Pilot
-local copy=self.gettext:GetEntry("COPY",self.locale)
-text=string.format(copy,self:_GetCallSign(Group,GID)or"Ghost 1",self.callsigntxt)
-local EngagementTag=""
-self:_UpdateContactEngagementTag(Pilot.ContactCID,EngagementTag,false,false,AWACS.TaskStatus.UNASSIGNED)
-self:_NewRadioEntry(text,text,GID,Outcome,true,true,false,true)
-else
-self:E(self.lid.."Cannot find REQUESTED managed task with TID="..currtaskid.." for GID="..GID)
-end
-else
-self:E(self.lid.."Cannot find managed task with TID="..currtaskid.." for GID="..GID)
-end
-elseif self.AwacsFG then
-local nocheckin=self.gettext:GetEntry("NOTCHECKEDIN",self.locale)
-text=string.format(nocheckin,self:_GetCallSign(Group,GID)or"Ghost 1",self.callsigntxt)
-self:_NewRadioEntry(text,text,GID,Outcome,true,true,false)
-end
-return self
-end
-function AWACS:_TaskAbort(Group)
-self:T(self.lid.."_TaskAbort")
-local Outcome,GID=self:_GetGIDFromGroupOrName(Group)
-local text=""
-if Outcome then
-local Pilot=self.ManagedGrps[GID]
-self:T({Pilot})
-local currtaskid=Pilot.CurrentTask
-local managedtask=self.ManagedTasks:ReadByID(currtaskid)
-if managedtask then
-self:T(string.format("ABORT for TID %d(%d) | ToDo %s | Status %s",currtaskid,managedtask.TID,managedtask.ToDo,managedtask.Status))
-if managedtask.Status==AWACS.TaskStatus.ASSIGNED then
-managedtask=self.ManagedTasks:PullByID(currtaskid)
-managedtask.Status=AWACS.TaskStatus.FAILED
-managedtask.IsUnassigned=true
-self.ManagedTasks:Push(managedtask,currtaskid)
-self:T(string.format("ABORTED - TID %d(%d) for GID %d | ToDo %s | Status %s",currtaskid,GID,managedtask.TID,managedtask.ToDo,managedtask.Status))
-Pilot.HasAssignedTask=false
-Pilot.CurrentTask=0
-Pilot.LastTasking=timer.getTime()
-self.ManagedGrps[GID]=Pilot
-local copy=self.gettext:GetEntry("COPY",self.locale)
-text=string.format(copy,self:_GetCallSign(Group,GID)or"Ghost 1",self.callsigntxt)
-local EngagementTag=""
-self:_UpdateContactEngagementTag(Pilot.ContactCID,EngagementTag,false,false,AWACS.TaskStatus.UNASSIGNED)
-self:_NewRadioEntry(text,text,GID,Outcome,true,true,false,true)
-else
-self:E(self.lid.."Cannot find ASSIGNED managed task with TID="..currtaskid.." for GID="..GID)
-end
-else
-self:E(self.lid.."Cannot find managed task with TID="..currtaskid.." for GID="..GID)
-end
-elseif self.AwacsFG then
-local nocheckin=self.gettext:GetEntry("NOTCHECKEDIN",self.locale)
-text=string.format(nocheckin,self:_GetCallSign(Group,GID)or"Ghost 1",self.callsigntxt)
-self:_NewRadioEntry(text,text,GID,Outcome,true,true,false)
-end
-return self
-end
-function AWACS:_Showtask(Group)
-self:T(self.lid.."_Showtask")
-local GID,Outcome,Callsign=self:_GetManagedGrpID(Group)
-local text=""
-if Outcome then
-local managedgroup=self.ManagedGrps[GID]
-if managedgroup.IsPlayer then
-if managedgroup.CurrentTask>0 and self.ManagedTasks:HasUniqueID(managedgroup.CurrentTask)then
-local currenttask=self.ManagedTasks:ReadByID(managedgroup.CurrentTask)
-if currenttask then
-local status=currenttask.Status
-local targettype=currenttask.Target:GetCategory()
-local targetstatus=currenttask.Target:GetState()
-local ToDo=currenttask.ToDo
-local description=currenttask.ScreenText
-local descTTS=currenttask.ScreenText
-local callsign=Callsign
-if self.debug then
-local taskreport=REPORT:New("AWACS Tasking Display")
-taskreport:Add("===============")
-taskreport:Add(string.format("Task for Callsign: %s",Callsign))
-taskreport:Add(string.format("Task: %s with Status: %s",ToDo,status))
-taskreport:Add(string.format("Target of Type: %s",targettype))
-taskreport:Add(string.format("Target in State: %s",targetstatus))
-taskreport:Add("===============")
-self:I(taskreport:Text())
-end
-local pposition=managedgroup.Group:GetCoordinate()or managedgroup.LastKnownPosition
-if currenttask.ToDo==AWACS.TaskDescription.INTERCEPT or currenttask.ToDo==AWACS.TaskDescription.VID then
-local targetpos=currenttask.Target:GetCoordinate()
-if pposition and targetpos then
-local alti=currenttask.Cluster.altitude or currenttask.Contact.altitude or currenttask.Contact.group:GetAltitude()
-local direction,direcTTS=self:_ToStringBRA(pposition,targetpos,alti)
-description=description.."\nBRA "..direction
-descTTS=descTTS..";BRA "..direcTTS
-end
-elseif currenttask.ToDo==AWACS.TaskDescription.ANCHOR or currenttask.ToDo==AWACS.TaskDescription.REANCHOR then
-local targetpos=currenttask.Target:GetCoordinate()
-local direction,direcTTS=self:_ToStringBR(pposition,targetpos)
-description=description.."\nBR "..direction
-descTTS=descTTS..";BR "..direcTTS
-end
-local statustxt=self.gettext:GetEntry("STATUS",self.locale)
-local text=string.format("%s\n%s %s",description,statustxt,status)
-local ttstext=string.format("%s. %s. %s",managedgroup.CallSign,self.callsigntxt,descTTS)
-ttstext=string.gsub(ttstext,"\\n",";")
-ttstext=string.gsub(ttstext,"VID","V I D")
-self:_NewRadioEntry(ttstext,text,GID,true,true,false,false,true)
-end
-end
-end
-elseif self.AwacsFG then
-local nocheckin=self.gettext:GetEntry("NOTCHECKEDIN",self.locale)
-text=string.format(nocheckin,self:_GetCallSign(Group,GID)or"Ghost 1",self.callsigntxt)
-self:_NewRadioEntry(text,text,GID,Outcome,true,true,false)
-end
-return self
-end
-function AWACS:_CheckIn(Group)
-self:T(self.lid.."_CheckIn "..Group:GetName())
-local GID,Outcome=self:_GetManagedGrpID(Group)
-local text=""
-local textTTS=""
-if not Outcome then
-self.ManagedGrpID=self.ManagedGrpID+1
-local managedgroup={}
-managedgroup.Group=Group
-managedgroup.GroupName=Group:GetName()
-managedgroup.IsPlayer=true
-managedgroup.IsAI=false
-managedgroup.CallSign=self:_GetCallSign(Group,GID,true)or"Ghost 1"
-managedgroup.CurrentAuftrag=0
-managedgroup.CurrentTask=0
-managedgroup.HasAssignedTask=true
-managedgroup.Blocked=true
-managedgroup.GID=self.ManagedGrpID
-managedgroup.LastKnownPosition=Group:GetCoordinate()
-managedgroup.LastTasking=timer.getTime()
-GID=managedgroup.GID
-self.ManagedGrps[self.ManagedGrpID]=managedgroup
-local alphacheckbulls=self:_ToStringBULLS(Group:GetCoordinate())
-local alphacheckbullstts=self:_ToStringBULLS(Group:GetCoordinate(),false,true)
-local alpha=self.gettext:GetEntry("ALPHACHECK",self.locale)
-text=string.format("%s. %s. %s. %s",managedgroup.CallSign,self.callsigntxt,alpha,alphacheckbulls)
-textTTS=string.format("%s. %s. %s. %s",managedgroup.CallSign,self.callsigntxt,alpha,alphacheckbullstts)
-self:__CheckedIn(1,managedgroup.GID)
-if self.PlayerStationName then
-self:__AssignAnchor(5,managedgroup.GID,true,self.PlayerStationName)
-else
-self:__AssignAnchor(5,managedgroup.GID)
-end
-elseif self.AwacsFG then
-local nocheckin=self.gettext:GetEntry("ALREADYCHECKEDIN",self.locale)
-text=string.format(nocheckin,self:_GetCallSign(Group,GID)or"Ghost 1",self.callsigntxt)
-textTTS=text
-end
-self:_NewRadioEntry(textTTS,text,GID,Outcome,true,true,false)
-return self
-end
-function AWACS:_CheckInAI(FlightGroup,Group,AuftragsNr)
-self:T(self.lid.."_CheckInAI "..Group:GetName().." to Auftrag Nr "..AuftragsNr)
-local GID,Outcome=self:_GetManagedGrpID(Group)
-local text=""
-if not Outcome then
-self.ManagedGrpID=self.ManagedGrpID+1
-local managedgroup={}
-managedgroup.Group=Group
-managedgroup.GroupName=Group:GetName()
-managedgroup.FlightGroup=FlightGroup
-managedgroup.IsPlayer=false
-managedgroup.IsAI=true
-local callsignstring=UTILS.GetCallsignName(self.AICAPCAllName)
-if self.callsignTranslations and self.callsignTranslations[callsignstring]then
-callsignstring=self.callsignTranslations[callsignstring]
-end
-local callsignmajor=math.fmod(self.AICAPCAllNumber,9)
-local callsign=string.format("%s %d 1",callsignstring,callsignmajor)
-if self.callsignshort then
-callsign=string.format("%s %d",callsignstring,callsignmajor)
-end
-self:T("Assigned Callsign: "..callsign)
-managedgroup.CallSign=callsign
-managedgroup.CurrentAuftrag=AuftragsNr
-managedgroup.HasAssignedTask=false
-managedgroup.GID=self.ManagedGrpID
-managedgroup.LastKnownPosition=Group:GetCoordinate()
-self.ManagedGrps[self.ManagedGrpID]=managedgroup
-FlightGroup:SetDefaultRadio(self.Frequency,self.Modulation,false)
-FlightGroup:SwitchRadio(self.Frequency,self.Modulation)
-local CAPVoice=self.CAPVoice
-if self.PathToGoogleKey then
-CAPVoice=self.CapVoices[math.floor(math.random(1,10))]
-end
-FlightGroup:SetSRS(self.PathToSRS,self.CAPGender,self.CAPCulture,CAPVoice,self.Port,self.PathToGoogleKey,"FLIGHT")
-local checkai=self.gettext:GetEntry("CHECKINAI",self.locale)
-text=string.format(checkai,self.callsigntxt,managedgroup.CallSign,self.CAPTimeOnStation,self.AOName)
-self:_NewRadioEntry(text,text,managedgroup.GID,Outcome,false,true,true)
-local alphacheckbulls=self:_ToStringBULLS(Group:GetCoordinate(),false,true)
-local alpha=self.gettext:GetEntry("ALPHACHECK",self.locale)
-text=string.format("%s. %s. %s. %s",managedgroup.CallSign,self.callsigntxt,alpha,alphacheckbulls)
-self:__CheckedIn(1,managedgroup.GID)
-local AW=FlightGroup.legion
-if AW.HasOwnStation then
-self:__AssignAnchor(5,managedgroup.GID,AW.HasOwnStation,AW.StationName)
-else
-self:__AssignAnchor(5,managedgroup.GID)
-end
-else
-local nocheckin=self.gettext:GetEntry("ALREADYCHECKEDIN",self.locale)
-text=string.format(nocheckin,self:_GetCallSign(Group,GID)or"Ghost 1",self.callsigntxt)
-end
-self:_NewRadioEntry(text,text,GID,Outcome,false,true,false)
-return self
-end
-function AWACS:_CheckOut(Group,GID,dead)
-self:T(self.lid.."_CheckOut")
-local GID,Outcome=self:_GetManagedGrpID(Group)
-local text=""
-if Outcome then
-local safeflight=self.gettext:GetEntry("SAFEFLIGHT",self.locale)
-text=string.format(safeflight,self:_GetCallSign(Group,GID)or"Ghost 1",self.callsigntxt)
-self:T(text)
-local managedgroup=self.ManagedGrps[GID]
-local Stack=managedgroup.AnchorStackNo
-local Angels=managedgroup.AnchorStackAngels
-local GroupName=managedgroup.GroupName
-if managedgroup.IsPlayer then
-if self.clientmenus:HasUniqueID(GroupName)then
-local menus=self.clientmenus:PullByID(GroupName)
-menus.basemenu:Remove()
-if self.TacticalSubscribers[GroupName]then
-local Freq=self.TacticalSubscribers[GroupName]
-self.TacticalFrequencies[Freq]=Freq
-self.TacticalSubscribers[GroupName]=nil
-end
-end
-end
-if managedgroup.CurrentTask and managedgroup.CurrentTask>0 then
-self.ManagedTasks:PullByID(managedgroup.CurrentTask)
-self:_UpdateContactEngagementTag(managedgroup.ContactCID,"",false,false)
-end
-self.ManagedGrps[GID]=nil
-self:__CheckedOut(1,GID,Stack,Angels)
-else
-if not dead then
-local nocheckin=self.gettext:GetEntry("NOTCHECKEDIN",self.locale)
-text=string.format(nocheckin,self:_GetCallSign(Group,GID)or"Ghost 1",self.callsigntxt)
-end
-end
-if not dead then
-self:_NewRadioEntry(text,text,GID,Outcome,false,true,false)
-end
-return self
-end
-function AWACS:_SetClientMenus()
-self:T(self.lid.."_SetClientMenus")
-local clientset=self.clientset
-local aliveset=clientset:GetSetObjects()or{}
-local clientcount=0
-local clientcheckedin=0
-for _,_group in pairs(aliveset)do
-local grp=_group
-local cgrp=grp:GetGroup()
-local cgrpname=nil
-if cgrp and cgrp:IsAlive()then
-cgrpname=cgrp:GetName()
-self:T(cgrpname)
-end
-if self.MenuStrict then
-if cgrp and cgrp:IsAlive()then
-clientcount=clientcount+1
-local GID,checkedin=self:_GetManagedGrpID(cgrp)
-if checkedin then
-clientcheckedin=clientcheckedin+1
-local hasclientmenu=self.clientmenus:ReadByID(cgrpname)
-local basemenu=hasclientmenu.basemenu
-if hasclientmenu and(not hasclientmenu.menuset)then
-self:T(self.lid.."Setting Menus for "..cgrpname)
-basemenu:RemoveSubMenus()
-local bogeydope=MENU_GROUP_COMMAND:New(cgrp,"Bogey Dope",basemenu,self._BogeyDope,self,cgrp)
-local picture=MENU_GROUP_COMMAND:New(cgrp,"Picture",basemenu,self._Picture,self,cgrp)
-local declare=MENU_GROUP_COMMAND:New(cgrp,"Declare",basemenu,self._Declare,self,cgrp)
-local tasking=MENU_GROUP:New(cgrp,"Tasking",basemenu)
-local showtask=MENU_GROUP_COMMAND:New(cgrp,"Showtask",tasking,self._Showtask,self,cgrp)
-local commit
-local unable
-local abort
-if self.PlayerCapAssignment then
-commit=MENU_GROUP_COMMAND:New(cgrp,"Commit",tasking,self._Commit,self,cgrp)
-unable=MENU_GROUP_COMMAND:New(cgrp,"Unable",tasking,self._Unable,self,cgrp)
-abort=MENU_GROUP_COMMAND:New(cgrp,"Abort",tasking,self._TaskAbort,self,cgrp)
-end
-if self.AwacsROE==AWACS.ROE.POLICE or self.AwacsROE==AWACS.ROE.VID then
-local vid=MENU_GROUP:New(cgrp,"VID as",tasking)
-local hostile=MENU_GROUP_COMMAND:New(cgrp,"Hostile",vid,self._VID,self,cgrp,AWACS.IFF.ENEMY)
-local neutral=MENU_GROUP_COMMAND:New(cgrp,"Neutral",vid,self._VID,self,cgrp,AWACS.IFF.NEUTRAL)
-local friendly=MENU_GROUP_COMMAND:New(cgrp,"Friendly",vid,self._VID,self,cgrp,AWACS.IFF.FRIENDLY)
-end
-local tactical
-if self.TacticalMenu then
-tactical=MENU_GROUP:New(cgrp,"Tactical Radio",basemenu)
-if self.TacticalSubscribers[cgrpname]then
-local entry=MENU_GROUP_COMMAND:New(cgrp,"Unsubscribe",tactical,self._UnsubScribeTactRadio,self,cgrp)
-else
-for _,_freq in UTILS.spairs(self.TacticalFrequencies)do
-local modu=UTILS.GetModulationName(self.TacticalModulation)
-local text=string.format("Subscribe to %.3f %s",_freq,modu)
-local entry=MENU_GROUP_COMMAND:New(cgrp,text,tactical,self._SubScribeTactRadio,self,cgrp,_freq)
-end
-end
-end
-local ainfo=MENU_GROUP_COMMAND:New(cgrp,"Awacs Info",basemenu,self._ShowAwacsInfo,self,cgrp)
-local checkout=MENU_GROUP_COMMAND:New(cgrp,"Check Out",basemenu,self._CheckOut,self,cgrp)
-local menus={
-groupname=cgrpname,
-menuset=true,
-basemenu=basemenu,
-checkout=checkout,
-picture=picture,
-bogeydope=bogeydope,
-declare=declare,
-tasking=tasking,
-showtask=showtask,
-unable=unable,
-abort=abort,
-commit=commit,
-tactical=tactical,
-}
-self.clientmenus:PullByID(cgrpname)
-self.clientmenus:Push(menus,cgrpname)
-end
-elseif not self.clientmenus:HasUniqueID(cgrpname)then
-local basemenu=MENU_GROUP:New(cgrp,self.Name,nil)
-local checkin=MENU_GROUP_COMMAND:New(cgrp,"Check In",basemenu,self._CheckIn,self,cgrp)
-checkin:SetTag(cgrp:GetName())
-basemenu:Refresh()
-local menus={
-groupname=cgrpname,
-menuset=false,
-basemenu=basemenu,
-checkin=checkin,
-}
-self.clientmenus:Push(menus,cgrpname)
-end
-end
-else
-if cgrp and cgrp:IsAlive()and not self.clientmenus:HasUniqueID(cgrpname)then
-local basemenu=MENU_GROUP:New(cgrp,self.Name,nil)
-local picture=MENU_GROUP_COMMAND:New(cgrp,"Picture",basemenu,self._Picture,self,cgrp)
-local bogeydope=MENU_GROUP_COMMAND:New(cgrp,"Bogey Dope",basemenu,self._BogeyDope,self,cgrp)
-local declare=MENU_GROUP_COMMAND:New(cgrp,"Declare",basemenu,self._Declare,self,cgrp)
-local tasking=MENU_GROUP:New(cgrp,"Tasking",basemenu)
-local showtask=MENU_GROUP_COMMAND:New(cgrp,"Showtask",tasking,self._Showtask,self,cgrp)
-local commit=MENU_GROUP_COMMAND:New(cgrp,"Commit",tasking,self._Commit,self,cgrp)
-local unable=MENU_GROUP_COMMAND:New(cgrp,"Unable",tasking,self._Unable,self,cgrp)
-local abort=MENU_GROUP_COMMAND:New(cgrp,"Abort",tasking,self._TaskAbort,self,cgrp)
-if self.AwacsROE==AWACS.ROE.POLICE or self.AwacsROE==AWACS.ROE.VID then
-local vid=MENU_GROUP:New(cgrp,"VID as",tasking)
-local hostile=MENU_GROUP_COMMAND:New(cgrp,"Hostile",vid,self._VID,self,cgrp,AWACS.IFF.ENEMY)
-local neutral=MENU_GROUP_COMMAND:New(cgrp,"Neutral",vid,self._VID,self,cgrp,AWACS.IFF.NEUTRAL)
-local friendly=MENU_GROUP_COMMAND:New(cgrp,"Friendly",vid,self._VID,self,cgrp,AWACS.IFF.FRIENDLY)
-end
-local ainfo=MENU_GROUP_COMMAND:New(cgrp,"Awacs Info",basemenu,self._ShowAwacsInfo,self,cgrp)
-local checkin=MENU_GROUP_COMMAND:New(cgrp,"Check In",basemenu,self._CheckIn,self,cgrp)
-local checkout=MENU_GROUP_COMMAND:New(cgrp,"Check Out",basemenu,self._CheckOut,self,cgrp)
-basemenu:Refresh()
-local menus={
-groupname=cgrpname,
-menuset=true,
-basemenu=basemenu,
-checkin=checkin,
-checkout=checkout,
-picture=picture,
-bogeydope=bogeydope,
-declare=declare,
-showtask=showtask,
-tasking=tasking,
-unable=unable,
-abort=abort,
-commit=commit,
-}
-self.clientmenus:Push(menus,cgrpname)
-end
-end
-end
-self.MonitoringData.Players=clientcount or 0
-self.MonitoringData.PlayersCheckedin=clientcheckedin or 0
-return self
-end
-function AWACS:_DeleteAnchorStackFromMarker(Name,Coord)
-self:T(self.lid.."_DeleteAnchorStackFromMarker")
-if self.AnchorStacks:HasUniqueID(Name)and self.PlayerStationName==Name then
-local stack=self.AnchorStacks:ReadByID(Name)
-local marker=stack.AnchorMarker
-if stack.AnchorAssignedID:Count()==0 then
-marker:Remove()
-if self.debug then
-stack.StationZone:UndrawZone()
-end
-self.AnchorStacks:PullByID(Name)
-self.PlayerStationName=nil
-else
-if self.debug then
-self:I(self.lid.."**** Cannot delete station, there are CAPs assigned!")
-local text=marker:GetText()
-marker:TextUpdate(text.."\nMarked for deletion")
-end
-end
-end
-return self
-end
-function AWACS:_MoveAnchorStackFromMarker(Name,Coord)
-self:T(self.lid.."_MoveAnchorStackFromMarker")
-if self.AnchorStacks:HasUniqueID(Name)and self.PlayerStationName==Name then
-local station=self.AnchorStacks:PullByID(Name)
-local stationtag=string.format("Station: %s\nCoordinate: %s",Name,Coord:ToStringLLDDM())
-local marker=station.AnchorMarker
-local zone=station.StationZone
-if self.debug then
-zone:UndrawZone()
-end
-local radius=self.StationZone:GetRadius()
-if radius<10000 then radius=10000 end
-station.StationZone=ZONE_RADIUS:New(Name,Coord:GetVec2(),radius)
-marker:UpdateCoordinate(Coord)
-marker:UpdateText(stationtag)
-station.AnchorMarker=marker
-if self.debug then
-station.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true)
-end
-self.AnchorStacks:Push(station,Name)
-end
-return self
-end
-function AWACS:_CreateAnchorStackFromMarker(Name,Coord)
-self:T(self.lid.."_CreateAnchorStackFromMarker")
-local AnchorStackOne={}
-AnchorStackOne.AnchorBaseAngels=self.AnchorBaseAngels
-AnchorStackOne.Anchors=FIFO:New()
-AnchorStackOne.AnchorAssignedID=FIFO:New()
-local newname=Name
-for i=1,self.AnchorMaxStacks do
-AnchorStackOne.Anchors:Push((i-1)*self.AnchorStackDistance+self.AnchorBaseAngels)
-end
-local radius=self.StationZone:GetRadius()
-if radius<10000 then radius=10000 end
-AnchorStackOne.StationZone=ZONE_RADIUS:New(newname,Coord:GetVec2(),radius)
-AnchorStackOne.StationZoneCoordinate=Coord
-AnchorStackOne.StationZoneCoordinateText=Coord:ToStringLLDDM()
-AnchorStackOne.StationName=newname
-if self.debug then
-AnchorStackOne.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true)
-local stationtag=string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
-AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
-else
-local stationtag=string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
-AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
-end
-self.AnchorStacks:Push(AnchorStackOne,newname)
-self.PlayerStationName=newname
-return self
-end
-function AWACS:_CreateAnchorStack()
-self:T(self.lid.."_CreateAnchorStack")
-local stackscreated=self.AnchorStacks:GetSize()
-if stackscreated==self.AnchorMaxAnchors then
-return false,0
-end
-local AnchorStackOne={}
-AnchorStackOne.AnchorBaseAngels=self.AnchorBaseAngels
-AnchorStackOne.Anchors=FIFO:New()
-AnchorStackOne.AnchorAssignedID=FIFO:New()
-local newname=self.StationZone:GetName()
-for i=1,self.AnchorMaxStacks do
-AnchorStackOne.Anchors:Push((i-1)*self.AnchorStackDistance+self.AnchorBaseAngels)
-end
-if stackscreated==0 then
-local newsubname=AWACS.AnchorNames[stackscreated+1]or tostring(stackscreated+1)
-newname=self.StationZone:GetName().."-"..newsubname
-AnchorStackOne.StationZone=self.StationZone
-AnchorStackOne.StationZoneCoordinate=self.StationZone:GetCoordinate()
-AnchorStackOne.StationZoneCoordinateText=self.StationZone:GetCoordinate():ToStringLLDDM()
-AnchorStackOne.StationName=newname
-if self.debug then
-AnchorStackOne.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true)
-local stationtag=string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
-AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
-else
-local stationtag=string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
-AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
-end
-self.AnchorStacks:Push(AnchorStackOne,newname)
-else
-local newsubname=AWACS.AnchorNames[stackscreated+1]or tostring(stackscreated+1)
-newname=self.StationZone:GetName().."-"..newsubname
-local anchorbasecoord=self.OpsZone:GetCoordinate()
-local anchorradius=anchorbasecoord:Get2DDistance(self.StationZone:GetCoordinate())
-local angel=self.StationZone:GetCoordinate():GetAngleDegrees(self.OpsZone:GetVec3())
-self:T("Angel Radians= "..angel)
-local turn=math.fmod(self.AnchorTurn*stackscreated,360)
-if self.AnchorTurn<0 then turn=-turn end
-local newanchorbasecoord=anchorbasecoord:Translate(anchorradius,turn+angel)
-local radius=self.StationZone:GetRadius()
-if radius<10000 then radius=10000 end
-AnchorStackOne.StationZone=ZONE_RADIUS:New(newname,newanchorbasecoord:GetVec2(),radius)
-AnchorStackOne.StationZoneCoordinate=newanchorbasecoord
-AnchorStackOne.StationZoneCoordinateText=newanchorbasecoord:ToStringLLDDM()
-AnchorStackOne.StationName=newname
-if self.debug then
-AnchorStackOne.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true)
-local stationtag=string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
-AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
-else
-local stationtag=string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
-AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
-end
-self.AnchorStacks:Push(AnchorStackOne,newname)
-end
-return true,self.AnchorStacks:GetSize()
-end
-function AWACS:_GetFreeAnchorStack()
-self:T(self.lid.."_GetFreeAnchorStack")
-local AnchorStackNo,Free=0,false
-local availablestacks=self.AnchorStacks:GetPointerStack()or{}
-for _id,_entry in pairs(availablestacks)do
-local entry=_entry
-local data=entry.data
-if data.Anchors:IsNotEmpty()then
-AnchorStackNo=_id
-Free=true
-break
-end
-end
-if not Free then
-local created,number=self:_CreateAnchorStack()
-if created then
-self:_GetFreeAnchorStack()
-end
-end
-return AnchorStackNo,Free
-end
-function AWACS:_AssignAnchorToID(GID,HasOwnStation,StationName)
-self:T(self.lid.."_AssignAnchorToID")
-if not HasOwnStation then
-local AnchorStackNo,Free=self:_GetFreeAnchorStack()
-if Free then
-local Anchor=self.AnchorStacks:PullByPointer(AnchorStackNo)
-local freeangels=Anchor.Anchors:Pull()
-Anchor.AnchorAssignedID:Push(GID)
-self.AnchorStacks:Push(Anchor,Anchor.StationName)
-self:T({Anchor,freeangels})
-self:__AssignedAnchor(5,GID,Anchor,AnchorStackNo,freeangels)
-else
-self:E(self.lid.."Cannot assign free anchor stack to GID "..GID.." Trying again in 10secs.")
-self:__AssignAnchor(10,GID)
-end
-else
-local Anchor=self.AnchorStacks:PullByID(StationName)
-local freeangels=Anchor.Anchors:Pull()or 25
-Anchor.AnchorAssignedID:Push(GID)
-self.AnchorStacks:Push(Anchor,StationName)
-self:T({Anchor,freeangels})
-local StackNo=self.AnchorStacks.stackbyid[StationName].pointer
-self:__AssignedAnchor(5,GID,Anchor,StackNo,freeangels)
-end
-return self
-end
-function AWACS:_RemoveIDFromAnchor(GID,AnchorStackNo,Angels)
-local gid=GID or 0
-local stack=AnchorStackNo or 0
-local angels=Angels or 0
-local debugstring=string.format("%s_RemoveIDFromAnchor for GID=%d Stack=%d Angels=%d",self.lid,gid,stack,angels)
-self:T(debugstring)
-if stack>0 and angels>0 then
-local AnchorStackNo=AnchorStackNo or 1
-local Anchor=self.AnchorStacks:ReadByPointer(AnchorStackNo)
-local removedID=Anchor.AnchorAssignedID:PullByID(GID)
-Anchor.Anchors:Push(Angels)
-end
-return self
-end
-function AWACS:_StartIntel(awacs)
-self:T(self.lid.."_StartIntel")
-if self.intelstarted then return self end
-self.DetectionSet:AddGroup(awacs)
-local intel=INTEL:New(self.DetectionSet,self.coalition,self.callsigntxt)
-intel:SetClusterAnalysis(true,false,false)
-local acceptzoneset=SET_ZONE:New()
-acceptzoneset:AddZone(self.ControlZone)
-acceptzoneset:AddZone(self.OpsZone)
-if not self.GCI then
-self.OrbitZone:SetRadius(UTILS.NMToMeters(55))
-acceptzoneset:AddZone(self.OrbitZone)
-end
-if self.BorderZone then
-acceptzoneset:AddZone(self.BorderZone)
-end
-intel:SetAcceptZones(acceptzoneset)
-if self.NoHelos then
-intel:SetFilterCategory({Unit.Category.AIRPLANE})
-else
-intel:SetFilterCategory({Unit.Category.AIRPLANE,Unit.Category.HELICOPTER})
-end
-local function NewCluster(Cluster)
-self:__NewCluster(5,Cluster)
-end
-function intel:OnAfterNewCluster(From,Event,To,Cluster)
-NewCluster(Cluster)
-end
-local function NewContact(Contact)
-self:__NewContact(5,Contact)
-end
-function intel:OnAfterNewContact(From,Event,To,Contact)
-NewContact(Contact)
-end
-local function LostContact(Contact)
-self:__LostContact(5,Contact)
-end
-function intel:OnAfterLostContact(From,Event,To,Contact)
-LostContact(Contact)
-end
-local function LostCluster(Cluster,Mission)
-self:__LostCluster(5,Cluster,Mission)
-end
-function intel:OnAfterLostCluster(From,Event,To,Cluster,Mission)
-LostCluster(Cluster,Mission)
-end
-self.intelstarted=true
-intel.statusupdate=-30
-intel:__Start(5)
-self.intel=intel
-return self
-end
-function AWACS:_GetBlurredSize(size)
-self:T(self.lid.."_GetBlurredSize")
-local threatsize=0
-local blur=self.RadarBlur
-local blurmin=100-blur
-local blurmax=100+blur
-local actblur=math.random(blurmin,blurmax)/100
-threatsize=math.floor(size*actblur)
-if threatsize==0 then threatsize=1 end
-if threatsize then end
-local threatsizetext=AWACS.Shipsize[1]
-if threatsize==2 then
-threatsizetext=AWACS.Shipsize[2]
-elseif threatsize==3 then
-threatsizetext=AWACS.Shipsize[3]
-elseif threatsize>3 then
-threatsizetext=AWACS.Shipsize[4]
-end
-return threatsize,threatsizetext
-end
-function AWACS:_GetThreatLevelText(threatlevel)
-self:T(self.lid.."_GetThreatLevelText")
-local threattext="GREEN"
-if threatlevel<=AWACS.THREATLEVEL.GREEN then
-threattext="GREEN"
-elseif threatlevel<=AWACS.THREATLEVEL.AMBER then
-threattext="AMBER"
-else
-threattext="RED"
-end
-return threattext
-end
-function AWACS:_ToStringBR(FromCoordinate,ToCoordinate)
-self:T(self.lid.."_ToStringBR")
-local BRText=""
-local BRTextTTS=""
-local DirectionVec3=FromCoordinate:GetDirectionVec3(ToCoordinate)
-local AngleRadians=FromCoordinate:GetAngleRadians(DirectionVec3)
-local AngleDegrees=UTILS.Round(UTILS.ToDegree(AngleRadians),0)
-local AngleDegText=string.format("%03d",AngleDegrees)
-local AngleDegTextTTS=""
-local zero=self.gettext:GetEntry("ZERO",self.locale)
-local miles=self.gettext:GetEntry("MILES",self.locale)
-AngleDegText=string.gsub(AngleDegText,"%d","%1 ")
-AngleDegText=string.gsub(AngleDegText," $","")
-AngleDegTextTTS=string.gsub(AngleDegText,"0",zero)
-local Distance=ToCoordinate:Get2DDistance(FromCoordinate)
-local distancenm=UTILS.Round(UTILS.MetersToNM(Distance),0)
-BRText=string.format("%03d, %d %s",AngleDegrees,distancenm,miles)
-BRTextTTS=string.format("%s, %d %s",AngleDegText,distancenm,miles)
-if self.PathToGoogleKey then
-BRTextTTS=string.format("%s, %d %s",AngleDegTextTTS,distancenm,miles)
-end
-self:T(BRText,BRTextTTS)
-return BRText,BRTextTTS
-end
-function AWACS:_ToStringBRA(FromCoordinate,ToCoordinate,Altitude)
-self:T(self.lid.."_ToStringBRA")
-local BRText=""
-local BRTextTTS=""
-local altitude=UTILS.Round(UTILS.MetersToFeet(Altitude)/1000,0)
-local DirectionVec3=FromCoordinate:GetDirectionVec3(ToCoordinate)
-local AngleRadians=FromCoordinate:GetAngleRadians(DirectionVec3)
-local AngleDegrees=UTILS.Round(UTILS.ToDegree(AngleRadians),0)
-local AngleDegText=string.format("%03d",AngleDegrees)
-AngleDegText=string.gsub(AngleDegText,"%d","%1 ")
-AngleDegText=string.gsub(AngleDegText," $","")
-local AngleDegTextTTS=string.gsub(AngleDegText,"0","zero")
-local Distance=ToCoordinate:Get2DDistance(FromCoordinate)
-local distancenm=UTILS.Round(UTILS.MetersToNM(Distance),0)
-local zero=self.gettext:GetEntry("ZERO",self.locale)
-local miles=self.gettext:GetEntry("MILES",self.locale)
-local thsd=self.gettext:GetEntry("THOUSAND",self.locale)
-local vlow=self.gettext:GetEntry("VERYLOW",self.locale)
-if altitude>=1 then
-BRText=string.format("%03d, %d %s, %d %s",AngleDegrees,distancenm,miles,altitude,thsd)
-BRTextTTS=string.format("%s, %d %s, %d %s",AngleDegText,distancenm,miles,altitude,thsd)
-if self.PathToGoogleKey then
-BRTextTTS=string.format("%s, %d %s, %d %s",AngleDegTextTTS,distancenm,miles,altitude,thsd)
-end
-else
-BRText=string.format("%03d, %d %s, %s",AngleDegrees,distancenm,miles,vlow)
-BRTextTTS=string.format("%s, %d %s, %s",AngleDegText,distancenm,miles,vlow)
-if self.PathToGoogleKey then
-BRTextTTS=string.format("%s, %d %s, %s",AngleDegTextTTS,distancenm,miles,vlow)
-end
-end
-self:T(BRText,BRTextTTS)
-return BRText,BRTextTTS
-end
-function AWACS:_GetBRAfromBullsOrAO(clustercoordinate)
-self:T(self.lid.."_GetBRAfromBullsOrAO")
-local refcoord=self.AOCoordinate
-local BRAText=""
-local BRATextTTS=""
-local bullsname=self.AOName or"Rock"
-local stringbr,stringbrtts=self:_ToStringBR(refcoord,clustercoordinate)
-BRAText=string.format("%s %s",bullsname,stringbr)
-BRATextTTS=string.format("%s %s",bullsname,stringbrtts)
-self:T(BRAText,BRATextTTS)
-return BRAText,BRATextTTS
-end
-function AWACS:_CreateTaskForGroup(GroupID,Description,ScreenText,Object,TaskStatus,Auftrag,Cluster,Contact)
-self:T(self.lid.."_CreateTaskForGroup "..GroupID.." Description: "..Description)
-local managedgroup=self.ManagedGrps[GroupID]
-local task={}
-self.ManagedTaskID=self.ManagedTaskID+1
-task.TID=self.ManagedTaskID
-task.AssignedGroupID=GroupID
-task.Status=TaskStatus or AWACS.TaskStatus.ASSIGNED
-task.ToDo=Description
-task.Auftrag=Auftrag
-task.Cluster=Cluster
-task.Contact=Contact
-task.IsPlayerTask=managedgroup.IsPlayer
-task.IsUnassigned=TaskStatus==AWACS.TaskStatus.UNASSIGNED and false or true
-if Object and Object:IsInstanceOf("TARGET")then
-task.Target=Object
-else
-task.Target=TARGET:New(Object)
-end
-task.ScreenText=ScreenText
-if Description==AWACS.TaskDescription.ANCHOR or Description==AWACS.TaskDescription.REANCHOR then
-task.Target.Type=TARGET.ObjectType.ZONE
-end
-task.RequestedTimestamp=timer.getTime()
-self.ManagedTasks:Push(task,task.TID)
-managedgroup.HasAssignedTask=true
-managedgroup.CurrentTask=task.TID
-self:T({managedgroup})
-self.ManagedGrps[GroupID]=managedgroup
-return task.TID
-end
-function AWACS:_ReadAssignedTaskFromGID(GroupID)
-self:T(self.lid.."_GetAssignedTaskFromGID "..GroupID)
-local managedgroup=self.ManagedGrps[GroupID]
-if managedgroup and managedgroup.HasAssignedTask and managedgroup.CurrentTask~=0 then
-local TaskID=managedgroup.CurrentTask
-if self.ManagedTasks:HasUniqueID(TaskID)then
-return self.ManagedTasks:ReadByID(TaskID)
-end
-end
-return nil
-end
-function AWACS:_ReadAssignedGroupFromTID(TaskID)
-self:T(self.lid.."_ReadAssignedGroupFromTID "..TaskID)
-if self.ManagedTasks:HasUniqueID(TaskID)then
-local task=self.ManagedTasks:ReadByID(TaskID)
-if task and task.AssignedGroupID and task.AssignedGroupID>0 then
-return self.ManagedGrps[task.AssignedGroupID]
-end
-end
-return nil
-end
-function AWACS:_MessageAIReadyForTasking(GID)
-self:T(self.lid.."_MessageAIReadyForTasking")
-if GID>0 and self.ManagedGrps[GID]then
-local managedgroup=self.ManagedGrps[GID]
-local GFCallsign=self:_GetCallSign(managedgroup.Group)
-local aionst=self.gettext:GetEntry("AIONSTATION",self.locale)
-local TextTTS=string.format(aionst,GFCallsign,self.callsigntxt,managedgroup.AnchorStackNo or 1,managedgroup.AnchorStackAngels or 25)
-self:_NewRadioEntry(TextTTS,TextTTS,GID,false,false,true,true)
-end
-return self
-end
-function AWACS:_UpdateContactEngagementTag(CID,Text,TAC,MELD,TaskStatus)
-self:T(self.lid.."_UpdateContactEngagementTag")
-local text=Text or""
-local contact=self.Contacts:PullByID(CID)
-if contact then
-contact.EngagementTag=text
-contact.TACCallDone=TAC or false
-contact.MeldCallDone=MELD or false
-contact.Status=TaskStatus or AWACS.TaskStatus.UNASSIGNED
-self.Contacts:Push(contact,CID)
-end
-return self
-end
-function AWACS:_CheckTaskQueue()
-self:T(self.lid.."_CheckTaskQueue")
-local opentasks=0
-local assignedtasks=0
-for _id,_managedgroup in pairs(self.ManagedGrps)do
-local group=_managedgroup
-if group.Group and group.Group:IsAlive()then
-local coordinate=group.Group:GetCoordinate()
-if coordinate then
-local NewCoordinate=COORDINATE:New(0,0,0)
-group.LastKnownPosition=group.LastKnownPosition:UpdateFromCoordinate(coordinate)
-self.ManagedGrps[_id]=group
-end
-end
-end
-if self.ManagedTasks:IsNotEmpty()then
-opentasks=self.ManagedTasks:GetSize()
-self:T("Assigned Tasks: "..opentasks)
-local taskstack=self.ManagedTasks:GetPointerStack()
-for _id,_entry in pairs(taskstack)do
-local data=_entry
-local entry=data.data
-local target=entry.Target
-local description=entry.ToDo
-if description==AWACS.TaskDescription.ANCHOR or description==AWACS.TaskDescription.REANCHOR then
-self:T("Open Task ANCHOR/REANCHOR")
-local managedgroup=self.ManagedGrps[entry.AssignedGroupID]
-if managedgroup then
-local group=managedgroup.Group
-if group and group:IsAlive()then
-local groupcoord=group:GetCoordinate()
-local zone=target:GetObject()
-self:T({zone})
-if group:IsInZone(zone)then
-self:T("Open Task ANCHOR/REANCHOR success for GroupID "..entry.AssignedGroupID)
-target:Stop()
-if managedgroup.IsAI then
-self:_MessageAIReadyForTasking(managedgroup.GID)
-end
-managedgroup.HasAssignedTask=false
-self.ManagedGrps[entry.AssignedGroupID]=managedgroup
-self.ManagedTasks:PullByID(entry.TID)
-else
-self:T("Open Task ANCHOR/REANCHOR executing for GroupID "..entry.AssignedGroupID)
-end
-else
-self.ManagedTasks:PullByID(entry.TID)
-end
-end
-elseif description==AWACS.TaskDescription.INTERCEPT then
-self:T("Open Tasks INTERCEPT")
-local taskstatus=entry.Status
-local targetstatus=entry.Target:GetState()
-if taskstatus==AWACS.TaskStatus.UNASSIGNED then
-self.ManagedTasks:PullByID(entry.TID)
-break
-end
-local managedgroup=self.ManagedGrps[entry.AssignedGroupID]
-local targetgrp=entry.Contact.group
-local position=entry.Contact.position or entry.Cluster.coordinate
-if targetgrp and targetgrp:IsAlive()and managedgroup then
-if position and managedgroup.Group and managedgroup.Group:IsAlive()then
-local grouposition=managedgroup.Group:GetCoordinate()or managedgroup.Group:GetCoordinate()
-local distance=1000
-if grouposition then
-distance=grouposition:Get2DDistance(position)
-distance=UTILS.Round(UTILS.MetersToNM(distance),0)
-end
-self:T("TAC/MELD distance check: "..distance.."NM!")
-if distance<=self.TacDistance and distance>=self.MeldDistance then
-self:T("TAC distance: "..distance.."NM!")
-local Contact=self.Contacts:ReadByID(entry.Contact.CID)
-self:_TACRangeCall(entry.AssignedGroupID,Contact)
-elseif distance<=self.MeldDistance and distance>=self.ThreatDistance then
-self:T("MELD distance: "..distance.."NM!")
-local Contact=self.Contacts:ReadByID(entry.Contact.CID)
-self:_MeldRangeCall(entry.AssignedGroupID,Contact)
-end
-end
-end
-local auftrag=entry.Auftrag
-local auftragstatus="Not Known"
-if auftrag then
-auftragstatus=auftrag:GetState()
-end
-local text=string.format("ID=%d | Status=%s | TargetState=%s | AuftragState=%s",entry.TID,taskstatus,targetstatus,auftragstatus)
-self:T(text)
-if auftrag then
-if auftrag:IsExecuting()then
-entry.Status=AWACS.TaskStatus.EXECUTING
-elseif auftrag:IsSuccess()then
-entry.Status=AWACS.TaskStatus.SUCCESS
-elseif auftrag:GetState()==AUFTRAG.Status.FAILED then
-entry.Status=AWACS.TaskStatus.FAILED
-end
-if targetstatus=="Dead"then
-entry.Status=AWACS.TaskStatus.SUCCESS
-elseif targetstatus=="Alive"and auftrag:IsOver()then
-entry.Status=AWACS.TaskStatus.FAILED
-end
-elseif entry.IsPlayerTask then
-if entry.Target:IsDead()or entry.Target:IsDestroyed()or entry.Target:CountTargets()==0 then
-entry.Status=AWACS.TaskStatus.SUCCESS
-elseif entry.Target:IsAlive()then
-local targetpos=entry.Target:GetCoordinate()
-local outofzones=false
-self.RejectZoneSet:ForEachZone(
-function(Zone,Position)
-local zone=Zone
-local pos=Position
-if pos and zone:IsVec2InZone(pos)then
-outofzones=true
-end
-end,
-targetpos:GetVec2()
-)
-if not outofzones then
-outofzones=true
-self.ZoneSet:ForEachZone(
-function(Zone,Position)
-local zone=Zone
-local pos=Position
-if pos and zone:IsVec2InZone(pos)then
-outofzones=false
-end
-end,
-targetpos:GetVec2()
-)
-end
-if outofzones then
-entry.Status=AWACS.TaskStatus.SUCCESS
-end
-end
-end
-if entry.Status==AWACS.TaskStatus.SUCCESS then
-self:T("Open Tasks INTERCEPT success for GroupID "..entry.AssignedGroupID)
-if managedgroup then
-self:_UpdateContactEngagementTag(managedgroup.ContactCID,"",true,true,AWACS.TaskStatus.SUCCESS)
-managedgroup.HasAssignedTask=false
-managedgroup.ContactCID=0
-managedgroup.LastTasking=timer.getTime()
-if managedgroup.IsAI then
-managedgroup.CurrentAuftrag=0
-else
-managedgroup.CurrentTask=0
-end
-self.ManagedGrps[entry.AssignedGroupID]=managedgroup
-self.ManagedTasks:PullByID(entry.TID)
-self:__InterceptSuccess(1)
-self:__ReAnchor(5,managedgroup.GID)
-end
-elseif entry.Status==AWACS.TaskStatus.FAILED then
-self:T("Open Tasks INTERCEPT failed for GroupID "..entry.AssignedGroupID)
-if managedgroup then
-managedgroup.HasAssignedTask=false
-self:_UpdateContactEngagementTag(managedgroup.ContactCID,"",false,false,AWACS.TaskStatus.UNASSIGNED)
-managedgroup.ContactCID=0
-managedgroup.LastTasking=timer.getTime()
-if managedgroup.IsAI then
-managedgroup.CurrentAuftrag=0
-else
-managedgroup.CurrentTask=0
-end
-if managedgroup.IsPlayer then
-entry.IsPlayerTask=false
-end
-self.ManagedGrps[entry.AssignedGroupID]=managedgroup
-if managedgroup.Group:IsAlive()or(managedgroup.FlightGroup and managedgroup.FlightGroup:IsAlive())then
-self:__ReAnchor(5,managedgroup.GID)
-end
-end
-self.ManagedTasks:PullByID(entry.TID)
-self:__InterceptFailure(1)
-elseif entry.Status==AWACS.TaskStatus.REQUESTED then
-self:T("Open Tasks INTERCEPT REQUESTED for GroupID "..entry.AssignedGroupID)
-local created=entry.RequestedTimestamp or timer.getTime()-120
-local Tnow=timer.getTime()
-local Trunning=(Tnow-created)/60
-local text=string.format("Task TID %s Requested %d minutes ago.",entry.TID,Trunning)
-if Trunning>self.ReassignmentPause then
-entry.Status=AWACS.TaskStatus.UNASSIGNED
-self.ManagedTasks:PullByID(entry.TID)
-end
-self:T(text)
-end
-elseif description==AWACS.TaskDescription.VID then
-local managedgroup=self.ManagedGrps[entry.AssignedGroupID]
-if(not managedgroup)or(not managedgroup.Group:IsAlive())then
-self.ManagedTasks:PullByID(entry.TID)
-return self
-end
-if entry.Target:IsDead()or entry.Target:IsDestroyed()or entry.Target:CountTargets()==0 then
-entry.Status=AWACS.TaskStatus.SUCCESS
-elseif entry.Target:IsAlive()then
-self:T("Checking VID target out of bounds")
-local targetpos=entry.Target:GetCoordinate()
-local outofzones=false
-self.RejectZoneSet:ForEachZone(
-function(Zone,Position)
-local zone=Zone
-local pos=Position
-if pos and zone:IsVec2InZone(pos)then
-outofzones=true
-end
-end,
-targetpos:GetVec2()
-)
-if not outofzones then
-outofzones=true
-self.ZoneSet:ForEachZone(
-function(Zone,Position)
-local zone=Zone
-local pos=Position
-if pos and zone:IsVec2InZone(pos)then
-outofzones=false
-end
-end,
-targetpos:GetVec2()
-)
-end
-if outofzones then
-entry.Status=AWACS.TaskStatus.SUCCESS
-self:T("Out of bounds - SUCCESS")
-end
-end
-if entry.Status==AWACS.TaskStatus.REQUESTED then
-self:T("Open Tasks VID REQUESTED for GroupID "..entry.AssignedGroupID)
-local created=entry.RequestedTimestamp or timer.getTime()-120
-local Tnow=timer.getTime()
-local Trunning=(Tnow-created)/60
-local text=string.format("Task TID %s Requested %d minutes ago.",entry.TID,Trunning)
-if Trunning>self.ReassignmentPause then
-entry.Status=AWACS.TaskStatus.UNASSIGNED
-self.ManagedTasks:PullByID(entry.TID)
-end
-self:T(text)
-elseif entry.Status==AWACS.TaskStatus.ASSIGNED then
-self:T("Open Tasks VID ASSIGNED for GroupID "..entry.AssignedGroupID)
-local targetgrp=entry.Contact.group
-local position=entry.Contact.position or entry.Cluster.coordinate
-if targetgrp and targetgrp:IsAlive()and managedgroup then
-if position and managedgroup.Group and managedgroup.Group:IsAlive()then
-local grouposition=managedgroup.Group:GetCoordinate()or managedgroup.Group:GetCoordinate()
-local distance=1000
-if grouposition then
-distance=grouposition:Get2DDistance(position)
-distance=UTILS.Round(UTILS.MetersToNM(distance),0)
-end
-self:T("TAC/MELD distance check: "..distance.."NM!")
-if distance<=self.TacDistance and distance>=self.MeldDistance then
-self:T("TAC distance: "..distance.."NM!")
-local Contact=self.Contacts:ReadByID(entry.Contact.CID)
-self:_TACRangeCall(entry.AssignedGroupID,Contact)
-elseif distance<=self.MeldDistance and distance>=self.ThreatDistance then
-self:T("MELD distance: "..distance.."NM!")
-local Contact=self.Contacts:ReadByID(entry.Contact.CID)
-self:_MeldRangeCall(entry.AssignedGroupID,Contact)
-end
-end
-end
-elseif entry.Status==AWACS.TaskStatus.SUCCESS then
-self:T("Open Tasks VID success for GroupID "..entry.AssignedGroupID)
-self.ManagedTasks:PullByID(entry.TID)
-local Contact=self.Contacts:ReadByID(entry.Contact.CID)
-if Contact and(Contact.IFF==AWACS.IFF.FRIENDLY or Contact.IFF==AWACS.IFF.NEUTRAL)then
-self:T("IFF outcome friendly/neutral for GroupID "..entry.AssignedGroupID)
-if managedgroup then
-managedgroup.HasAssignedTask=false
-self:_UpdateContactEngagementTag(managedgroup.ContactCID,"",false,false,AWACS.TaskStatus.UNASSIGNED)
-managedgroup.ContactCID=0
-managedgroup.LastTasking=timer.getTime()
-if managedgroup.IsAI then
-managedgroup.CurrentAuftrag=0
-else
-managedgroup.CurrentTask=0
-end
-if managedgroup.IsPlayer then
-entry.IsPlayerTask=false
-end
-self.ManagedGrps[entry.AssignedGroupID]=managedgroup
-self:__ReAnchor(5,managedgroup.GID)
-end
-elseif Contact and Contact.IFF==AWACS.IFF.ENEMY then
-self:T("IFF outcome hostile for GroupID "..entry.AssignedGroupID)
-entry.ToDo=AWACS.TaskDescription.INTERCEPT
-entry.Status=AWACS.TaskStatus.ASSIGNED
-local cname=Contact.TargetGroupNaming
-entry.ScreenText=string.format("Engage hostile %s group.",cname)
-self.ManagedTasks:Push(entry,entry.TID)
-local TextTTS=string.format("%s, %s. Engage hostile target!",managedgroup.CallSign,self.callsigntxt)
-self:_NewRadioEntry(TextTTS,TextTTS,managedgroup.GID,true,self.debug,true,false,true)
-elseif not Contact then
-self:T("IFF outcome target DEAD for GroupID "..entry.AssignedGroupID)
-if managedgroup then
-managedgroup.HasAssignedTask=false
-self:_UpdateContactEngagementTag(managedgroup.ContactCID,"",false,false,AWACS.TaskStatus.UNASSIGNED)
-managedgroup.ContactCID=0
-managedgroup.LastTasking=timer.getTime()
-if managedgroup.IsAI then
-managedgroup.CurrentAuftrag=0
-else
-managedgroup.CurrentTask=0
-end
-if managedgroup.IsPlayer then
-entry.IsPlayerTask=false
-end
-self.ManagedGrps[entry.AssignedGroupID]=managedgroup
-if managedgroup.Group:IsAlive()or managedgroup.FlightGroup:IsAlive()then
-self:__ReAnchor(5,managedgroup.GID)
-end
-end
-end
-elseif entry.Status==AWACS.TaskStatus.FAILED then
-self:T("Open Tasks VID failed for GroupID "..entry.AssignedGroupID)
-if managedgroup then
-managedgroup.HasAssignedTask=false
-self:_UpdateContactEngagementTag(managedgroup.ContactCID,"",false,false,AWACS.TaskStatus.UNASSIGNED)
-managedgroup.ContactCID=0
-managedgroup.LastTasking=timer.getTime()
-if managedgroup.IsAI then
-managedgroup.CurrentAuftrag=0
-else
-managedgroup.CurrentTask=0
-end
-if managedgroup.IsPlayer then
-entry.IsPlayerTask=false
-end
-self.ManagedGrps[entry.AssignedGroupID]=managedgroup
-if managedgroup.Group:IsAlive()or managedgroup.FlightGroup:IsAlive()then
-self:__ReAnchor(5,managedgroup.GID)
-end
-end
-self.ManagedTasks:PullByID(entry.TID)
-self:__InterceptFailure(1)
-end
-end
-end
-end
-return self
-end
-function AWACS:_LogStatistics()
-self:T(self.lid.."_LogStatistics")
-local text=string.gsub(UTILS.OneLineSerialize(self.MonitoringData),",","\n")
-local text=string.gsub(text,"{","\n")
-local text=string.gsub(text,"}","")
-local text=string.gsub(text,"="," = ")
-self:T(text)
-if self.MonitoringOn then
-MESSAGE:New(text,20,"AWACS",false):ToAll()
-end
-return self
-end
-function AWACS:AddCAPAirWing(AirWing,Zone)
-self:T(self.lid.."AddCAPAirWing")
-if AirWing then
-AirWing:SetUsingOpsAwacs(self)
-local distance=self.AOCoordinate:Get2DDistance(AirWing:GetCoordinate())
-if Zone then
-local stackscreated=self.AnchorStacks:GetSize()
-if stackscreated==self.AnchorMaxAnchors then
-self:E(self.lid.."Max number of stacks already created!")
-else
-local AnchorStackOne={}
-AnchorStackOne.AnchorBaseAngels=self.AnchorBaseAngels
-AnchorStackOne.Anchors=FIFO:New()
-AnchorStackOne.AnchorAssignedID=FIFO:New()
-local newname=Zone:GetName()
-for i=1,self.AnchorMaxStacks do
-AnchorStackOne.Anchors:Push((i-1)*self.AnchorStackDistance+self.AnchorBaseAngels)
-end
-local newsubname=AWACS.AnchorNames[stackscreated+1]or tostring(stackscreated+1)
-newname=Zone:GetName().."-"..newsubname
-AnchorStackOne.StationZone=Zone
-AnchorStackOne.StationZoneCoordinate=Zone:GetCoordinate()
-AnchorStackOne.StationZoneCoordinateText=Zone:GetCoordinate():ToStringLLDDM()
-AnchorStackOne.StationName=newname
-if self.debug then
-AnchorStackOne.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true)
-local stationtag=string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
-AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
-else
-local stationtag=string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
-AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
-end
-self.AnchorStacks:Push(AnchorStackOne,newname)
-AirWing.HasOwnStation=true
-AirWing.StationName=newname
-end
-end
-self.CAPAirwings:Push(AirWing,distance)
-end
-return self
-end
-function AWACS:_AnnounceContact(Contact,IsNew,Group,IsBogeyDope,Tag,IsPopup,ReportingName,Tactical)
-self:T(self.lid.."_AnnounceContact")
-local tag=""
-local Tag=Tag
-local CID=0
-if not Tag then
-CID=Contact.CID or 0
-Tag=Contact.TargetGroupNaming or""
-end
-if self.NoGroupTags then
-Tag=nil
-end
-local isGroup=false
-local GID=0
-local grpcallsign="Ghost 1"
-if Group and Group:IsAlive()then
-GID,isGroup,grpcallsign=self:_GetManagedGrpID(Group)
-self:T("GID="..GID.." CheckedIn = "..tostring(isGroup))
-end
-local cluster=Contact.Cluster
-local intel=self.intel
-local size=self.intel:ClusterCountUnits(cluster)
-local threatsize,threatsizetext=self:_GetBlurredSize(size)
-local clustercoordinate=Contact.Cluster.coordinate or Contact.Contact.position
-local heading=Contact.Contact.group:GetHeading()or self.intel:CalcClusterDirection(cluster)
-clustercoordinate:SetHeading(Contact.Contact.group:GetHeading())
-local BRAfromBulls,BRAfromBullsTTS=self:_GetBRAfromBullsOrAO(clustercoordinate)
-self:T(BRAfromBulls)
-self:T(BRAfromBullsTTS)
-BRAfromBulls=BRAfromBulls.."."
-BRAfromBullsTTS=BRAfromBullsTTS.."."
-if isGroup then
-BRAfromBulls=clustercoordinate:ToStringBRAANATO(Group:GetCoordinate(),true,true)
-BRAfromBullsTTS=string.gsub(BRAfromBulls,"BRAA","brah")
-BRAfromBullsTTS=string.gsub(BRAfromBullsTTS,"BRA","brah")
-if self.PathToGoogleKey then
-BRAfromBullsTTS=clustercoordinate:ToStringBRAANATO(Group:GetCoordinate(),true,true,true,false,true)
-end
-end
-local BRAText=""
-local TextScreen=""
-if isGroup then
-BRAText=string.format("%s, %s.",grpcallsign,self.callsigntxt)
-TextScreen=string.format("%s, %s.",grpcallsign,self.callsigntxt)
-else
-BRAText=string.format("%s.",self.callsigntxt)
-TextScreen=string.format("%s.",self.callsigntxt)
-end
-local newgrp=self.gettext:GetEntry("NEWGROUP",self.locale)
-local grptxt=self.gettext:GetEntry("GROUP",self.locale)
-local GRPtxt=self.gettext:GetEntry("GROUPCAP",self.locale)
-local popup=self.gettext:GetEntry("POPUP",self.locale)
-if IsNew and self.PlayerGuidance then
-BRAText=string.format("%s %s.",BRAText,newgrp)
-TextScreen=string.format("%s %s.",TextScreen,newgrp)
-elseif IsPopup then
-BRAText=string.format("%s %s %s.",BRAText,popup,grptxt)
-TextScreen=string.format("%s %s %s.",TextScreen,popup,grptxt)
-elseif IsBogeyDope and Tag and Tag~=""then
-BRAText=string.format("%s %s %s.",BRAText,Tag,grptxt)
-TextScreen=string.format("%s %s %s.",TextScreen,Tag,grptxt)
-else
-BRAText=string.format("%s %s.",BRAText,GRPtxt)
-TextScreen=string.format("%s %s.",TextScreen,GRPtxt)
-end
-if not IsBogeyDope then
-if Tag and Tag~=""then
-BRAText=BRAText.." "..Tag.."."
-TextScreen=TextScreen.." "..Tag.."."
-end
-end
-if threatsize>1 then
-BRAText=BRAText.." "..BRAfromBullsTTS.." "..threatsizetext.."."
-TextScreen=TextScreen.." "..BRAfromBulls.." "..threatsizetext.."."
-else
-BRAText=BRAText.." "..BRAfromBullsTTS
-TextScreen=TextScreen.." "..BRAfromBulls
-end
-if self.ModernEra then
-local high=self.gettext:GetEntry("HIGH",self.locale)
-local vfast=self.gettext:GetEntry("VERYFAST",self.locale)
-local fast=self.gettext:GetEntry("FAST",self.locale)
-if ReportingName and ReportingName~="Bogey"then
-ReportingName=string.gsub(ReportingName,"_"," ")
-BRAText=BRAText.." "..ReportingName.."."
-TextScreen=TextScreen.." "..ReportingName.."."
-end
-local height=Contact.Contact.group:GetHeight()
-local height=UTILS.Round(UTILS.MetersToFeet(height)/1000,0)
-if height>=40 then
-BRAText=BRAText..high
-TextScreen=TextScreen..high
-end
-local speed=Contact.Contact.group:GetVelocityKNOTS()
-if speed>900 then
-BRAText=BRAText..vfast
-TextScreen=TextScreen..vfast
-elseif speed>=600 and speed<=900 then
-BRAText=BRAText..fast
-TextScreen=TextScreen..fast
-end
-end
-BRAText=string.gsub(BRAText,"BRAA","brah")
-BRAText=string.gsub(BRAText,"BRA","brah")
-local prio=IsNew or IsBogeyDope
-self:_NewRadioEntry(BRAText,TextScreen,GID,isGroup,true,IsNew,false,prio,Tactical)
-return self
-end
-function AWACS:_GetAliveOpsGroupFromTable(OpsGroups)
-self:T(self.lid.."_GetAliveOpsGroupFromTable")
-local handback=nil
-for _,_OG in pairs(OpsGroups or{})do
-local OG=_OG
-if OG and OG:IsAlive()then
-handback=OG
-break
-end
-end
-return handback
-end
-function AWACS:_CleanUpAIMissionStack()
-self:T(self.lid.."_CleanUpAIMissionStack")
-local CAPMissions=0
-local Alert5Missions=0
-local InterceptMissions=0
-local MissionStack=FIFO:New()
-self:T("Checking MissionStack")
-for _,_mission in pairs(self.CatchAllMissions)do
-local mission=_mission
-local type=mission:GetType()
-if type==AUFTRAG.Type.ALERT5 and mission:IsNotOver()then
-MissionStack:Push(mission,mission.auftragsnummer)
-Alert5Missions=Alert5Missions+1
-elseif type==AUFTRAG.Type.CAP and mission:IsNotOver()then
-MissionStack:Push(mission,mission.auftragsnummer)
-CAPMissions=CAPMissions+1
-elseif type==AUFTRAG.Type.INTERCEPT and mission:IsNotOver()then
-MissionStack:Push(mission,mission.auftragsnummer)
-InterceptMissions=InterceptMissions+1
-end
-end
-self.AICAPMissions=nil
-self.AICAPMissions=MissionStack
-return CAPMissions,Alert5Missions,InterceptMissions
-end
-function AWACS:_ConsistencyCheck()
-self:T(self.lid.."_ConsistencyCheck")
-if self.debug then
-self:T("CatchAllMissions")
-local catchallm={}
-local report1=REPORT:New("CatchAll")
-report1:Add("====================")
-report1:Add("CatchAllMissions")
-report1:Add("====================")
-for _,_mission in pairs(self.CatchAllMissions)do
-local mission=_mission
-local nummer=mission.auftragsnummer or 0
-local type=mission:GetType()
-local state=mission:GetState()
-local FG=mission:GetOpsGroups()
-local OG=self:_GetAliveOpsGroupFromTable(FG)
-local OGName="UnknownFromMission"
-if OG then
-OGName=OG:GetName()
-end
-report1:Add(string.format("Auftrag Nr %d Type %s State %s FlightGroup %s",nummer,type,state,OGName))
-if mission:IsNotOver()then
-catchallm[#catchallm+1]=mission
-end
-end
-self.CatchAllMissions=nil
-self.CatchAllMissions=catchallm
-local catchallfg={}
-self:T("CatchAllFGs")
-report1:Add("====================")
-report1:Add("CatchAllFGs")
-report1:Add("====================")
-for _,_fg in pairs(self.CatchAllFGs)do
-local FG=_fg
-local mission=FG:GetMissionCurrent()
-local OGName=FG:GetName()or"UnknownFromFG"
-local nummer=0
-local type="No Type"
-local state="None"
-if mission then
-type=mission:GetType()
-nummer=mission.auftragsnummer or 0
-state=mission:GetState()
-end
-report1:Add(string.format("Auftrag Nr %d Type %s State %s FlightGroup %s",nummer,type,state,OGName))
-if FG:IsAlive()then
-catchallfg[#catchallfg+1]=FG
-end
-end
-report1:Add("====================")
-self:T(report1:Text())
-self.CatchAllFGs=nil
-self.CatchAllFGs=catchallfg
-end
-return self
-end
-function AWACS:_CheckAICAPOnStation()
-self:T(self.lid.."_CheckAICAPOnStation")
-self:_ConsistencyCheck()
-local capmissions,alert5missions,interceptmissions=self:_CleanUpAIMissionStack()
-self:T("CAP="..capmissions.." ALERT5="..alert5missions.." Requested="..self.AIRequested)
-if self.MaxAIonCAP>0 then
-local onstation=capmissions+alert5missions
-if capmissions>self.MaxAIonCAP then
-self:T(string.format("*** Onstation %d > MaxAIOnCAP %d",onstation,self.MaxAIonCAP))
-local mission=self.AICAPMissions:Pull()
-local Groups=mission:GetOpsGroups()
-local OpsGroup=self:_GetAliveOpsGroupFromTable(Groups)
-local GID,checkedin=self:_GetManagedGrpID(OpsGroup)
-mission:__Cancel(30)
-self.AIRequested=self.AIRequested-1
-if checkedin then
-self:_CheckOut(OpsGroup,GID)
-end
-end
-if capmissions0 then
-local report=REPORT:New("CAP Mission Status")
-report:Add("===============")
-local missions=self.AICAPMissions:GetDataTable()
-local i=1
-for _,_Mission in pairs(missions)do
-local mission=_Mission
-if mission then
-i=i+1
-report:Add(string.format("Entry %d",i))
-report:Add(string.format("Mission No %d",mission.auftragsnummer))
-report:Add(string.format("Mission Type %s",mission:GetType()))
-report:Add(string.format("Mission State %s",mission:GetState()))
-local OpsGroups=mission:GetOpsGroups()
-local OpsGroup=self:_GetAliveOpsGroupFromTable(OpsGroups)
-if OpsGroup then
-local OpsName=OpsGroup:GetName()or"Unknown"
-local found,GID,OpsCallSign=self:_GetGIDFromGroupOrName(OpsGroup)
-report:Add(string.format("Mission FG %s",OpsName))
-report:Add(string.format("Callsign %s",OpsCallSign))
-report:Add(string.format("Mission FG State %s",OpsGroup:GetState()))
-else
-report:Add("***** Cannot obtain (yet) this missions OpsGroup!")
-end
-report:Add(string.format("Target Type %s",mission:GetTargetType()))
-end
-report:Add("===============")
-end
-if self.debug then
-self:I(report:Text())
-end
-end
-end
-return self
-end
-function AWACS:_SetAIROE(FlightGroup,Group)
-self:T(self.lid.."_SetAIROE")
-local ROE=self.AwacsROE or AWACS.ROE.POLICE
-local ROT=self.AwacsROT or AWACS.ROT.PASSIVE
-Group:OptionAlarmStateGreen()
-Group:OptionECM_OnlyLockByRadar()
-Group:OptionROEHoldFire()
-Group:OptionROTEvadeFire()
-Group:OptionRTBBingoFuel(true)
-Group:OptionKeepWeaponsOnThreat()
-local callname=self.AICAPCAllName or CALLSIGN.Aircraft.Colt
-self.AICAPCAllNumber=self.AICAPCAllNumber+1
-Group:CommandSetCallsign(callname,math.fmod(self.AICAPCAllNumber,9))
-FlightGroup:SetDefaultAlarmstate(AI.Option.Ground.val.ALARM_STATE.GREEN)
-FlightGroup:SetDefaultCallsign(callname,math.fmod(self.AICAPCAllNumber,9))
-if ROE==AWACS.ROE.POLICE or ROE==AWACS.ROE.VID then
-FlightGroup:SetDefaultROE(ENUMS.ROE.WeaponHold)
-elseif ROE==AWACS.ROE.IFF then
-FlightGroup:SetDefaultROE(ENUMS.ROE.ReturnFire)
-elseif ROE==AWACS.ROE.BVR then
-FlightGroup:SetDefaultROE(ENUMS.ROE.OpenFire)
-end
-if ROT==AWACS.ROT.BYPASSESCAPE or ROT==AWACS.ROT.PASSIVE then
-FlightGroup:SetDefaultROT(ENUMS.ROT.PassiveDefense)
-elseif ROT==AWACS.ROT.OPENFIRE or ROT==AWACS.ROT.RETURNFIRE then
-FlightGroup:SetDefaultROT(ENUMS.ROT.BypassAndEscape)
-elseif ROT==AWACS.ROT.EVADE then
-FlightGroup:SetDefaultROT(ENUMS.ROT.EvadeFire)
-end
-FlightGroup:SetFuelLowRTB(true)
-FlightGroup:SetFuelLowThreshold(0.2)
-FlightGroup:SetEngageDetectedOff()
-FlightGroup:SetOutOfAAMRTB(true)
-return self
-end
-function AWACS:_TACRangeCall(GID,Contact)
-self:T(self.lid.."_TACRangeCall")
-if not Contact then return self end
-local pilotcallsign=self:_GetCallSign(nil,GID)
-local managedgroup=self.ManagedGrps[GID]
-local contact=Contact.Contact
-local contacttag=Contact.TargetGroupNaming
-if contact and not Contact.TACCallDone then
-local position=contact.position
-if position then
-local distance=position:Get2DDistance(managedgroup.Group:GetCoordinate())
-distance=UTILS.Round(UTILS.MetersToNM(distance))
-local grptxt=self.gettext:GetEntry("GROUP",self.locale)
-local miles=self.gettext:GetEntry("MILES",self.locale)
-local text=string.format("%s. %s. %s %s, %d %s.",self.callsigntxt,pilotcallsign,contacttag,grptxt,distance,miles)
-self:_NewRadioEntry(text,text,GID,true,self.debug,true,false,true)
-self:_UpdateContactEngagementTag(Contact.CID,Contact.EngagementTag,true,false,AWACS.TaskStatus.EXECUTING)
-end
-end
-return self
-end
-function AWACS:_MeldRangeCall(GID,Contact)
-self:T(self.lid.."_MeldRangeCall")
-if not Contact then return self end
-local pilotcallsign=self:_GetCallSign(nil,GID)
-local managedgroup=self.ManagedGrps[GID]
-local flightpos=managedgroup.Group:GetCoordinate()
-local contact=Contact.Contact
-local contacttag=Contact.TargetGroupNaming
-if contact and not Contact.MeldCallDone then
-local position=contact.position
-if position then
-local BRATExt=""
-if self.PathToGoogleKey then
-BRATExt=position:ToStringBRAANATO(flightpos,false,false,true,false,true)
-else
-BRATExt=position:ToStringBRAANATO(flightpos,false,false)
-end
-local grptxt=self.gettext:GetEntry("GROUP",self.locale)
-local text=string.format("%s. %s. %s %s, %s",self.callsigntxt,pilotcallsign,contacttag,grptxt,BRATExt)
-self:_NewRadioEntry(text,text,GID,true,self.debug,true,false,true)
-self:_UpdateContactEngagementTag(Contact.CID,Contact.EngagementTag,true,true,AWACS.TaskStatus.EXECUTING)
-end
-end
-return self
-end
-function AWACS:_ThreatRangeCall(GID,Contact)
-self:T(self.lid.."_ThreatRangeCall")
-if not Contact then return self end
-local pilotcallsign=self:_GetCallSign(nil,GID)
-local managedgroup=self.ManagedGrps[GID]
-local flightpos=managedgroup.Group:GetCoordinate()or managedgroup.LastKnownPosition
-local contact=Contact.Contact
-local contacttag=Contact.TargetGroupNaming
-if contact then
-local position=contact.position or contact.group:GetCoordinate()
-if position then
-local BRATExt=""
-if self.PathToGoogleKey then
-BRATExt=position:ToStringBRAANATO(flightpos,false,false,true,false,true)
-else
-BRATExt=position:ToStringBRAANATO(flightpos,false,false)
-end
-local grptxt=self.gettext:GetEntry("GROUP",self.locale)
-local thrt=self.gettext:GetEntry("THREAT",self.locale)
-local text=string.format("%s. %s. %s %s, %s. %s",self.callsigntxt,pilotcallsign,contacttag,grptxt,thrt,BRATExt)
-self:_NewRadioEntry(text,text,GID,true,self.debug,true,false,true)
-end
-end
-return self
-end
-function AWACS:_MergedCall(GID)
-self:T(self.lid.."_MergedCall")
-local pilotcallsign=self:_GetCallSign(nil,GID)
-local merge=self.gettext:GetEntry("MERGED",self.locale)
-local text=string.format("%s. %s. %s.",self.callsigntxt,pilotcallsign,merge)
-self:_NewRadioEntry(text,text,GID,true,self.debug,true,false,true)
-if GID and GID~=0 then
-local managedgroup=self.ManagedGrps[GID]
-if managedgroup and managedgroup.Group and managedgroup.Group:IsAlive()then
-local name=managedgroup.GroupName
-if self.TacticalSubscribers[name]then
-self:_NewRadioEntry(text,text,GID,true,self.debug,true,false,true,true)
-end
-end
-end
-return self
-end
-function AWACS:_AssignPilotToTarget(Pilots,Targets)
-self:T(self.lid.."_AssignPilotToTarget")
-local inreach=false
-local Pilot=nil
-local closest=UTILS.NMToMeters(self.maxassigndistance+1)
-local targets=Targets:GetDataTable()
-local Target=nil
-for _,_target in pairs(targets)do
-local targetgroupcoord=_target.Contact.position
-for _,_Pilot in pairs(Pilots)do
-local pilotcoord=_Pilot.Group:GetCoordinate()
-local targetdist=targetgroupcoord:Get2DDistance(pilotcoord)
-if UTILS.MetersToNM(targetdist) "..self.maxassigndistance.."NM! No Assignment!")
-end
-end
-end
-if inreach and Pilot and Pilot.IsPlayer then
-local callsign=Pilot.CallSign
-self.ManagedTasks:PullByID(Pilot.CurrentTask)
-Pilot.HasAssignedTask=true
-local TargetPosition=Target.Target:GetCoordinate()
-local PlayerPositon=Pilot.LastKnownPosition
-local TargetAlt=Target.Contact.altitude or Target.Cluster.altitude or Target.Contact.group:GetAltitude()
-local TargetDirections,TargetDirectionsTTS=self:_ToStringBRA(PlayerPositon,TargetPosition,TargetAlt)
-local ScreenText=""
-local TaskType=AWACS.TaskDescription.INTERCEPT
-if self.AwacsROE==AWACS.ROE.POLICE or self.AwacsROE==AWACS.ROE.VID then
-local interc=self.gettext:GetEntry("SCREENVID",self.locale)
-ScreenText=string.format(interc,Target.TargetGroupNaming)
-TaskType=AWACS.TaskDescription.VID
-else
-local interc=self.gettext:GetEntry("SCREENINTER",self.locale)
-ScreenText=string.format(interc,Target.TargetGroupNaming)
-end
-Pilot.CurrentTask=self:_CreateTaskForGroup(Pilot.GID,TaskType,ScreenText,Target.Target,AWACS.TaskStatus.REQUESTED,nil,Target.Cluster,Target.Contact)
-Pilot.ContactCID=Target.CID
-self.ManagedGrps[Pilot.GID]=Pilot
-Target.LinkedTask=Pilot.CurrentTask
-Target.LinkedGroup=Pilot.GID
-Target.Status=AWACS.TaskStatus.REQUESTED
-local targeted=self.gettext:GetEntry("ENGAGETAG",self.locale)
-Target.EngagementTag=string.format(targeted,Pilot.CallSign)
-self.Contacts:PullByID(Target.CID)
-self.Contacts:Push(Target,Target.CID)
-local reqcomm=self.gettext:GetEntry("REQCOMMIT",self.locale)
-local text=string.format(reqcomm,self.callsigntxt,Target.TargetGroupNaming,TargetDirectionsTTS,Pilot.CallSign)
-local textScreen=string.format(reqcomm,self.callsigntxt,Target.TargetGroupNaming,TargetDirections,Pilot.CallSign)
-self:_NewRadioEntry(text,textScreen,Pilot.GID,true,self.debug,true,false,true)
-elseif inreach and Pilot and Pilot.IsAI then
-local callsign=Pilot.CallSign
-local FGStatus=Pilot.FlightGroup:GetState()
-self:T("Pilot AI Callsign: "..callsign)
-self:T("Pilot FG State: "..FGStatus)
-local targetstatus=Target.Target:GetState()
-self:T("Target State: "..targetstatus)
-local currmission=Pilot.FlightGroup:GetMissionCurrent()
-if currmission then
-self:T("Current Mission: "..currmission:GetType())
-end
-local ZoneSet=self.ZoneSet
-local RejectZoneSet=self.RejectZoneSet
-local intercept=AUFTRAG:NewINTERCEPT(Target.Target)
-intercept:SetWeaponExpend(AI.Task.WeaponExpend.ALL)
-intercept:SetWeaponType(ENUMS.WeaponFlag.Auto)
-intercept:AddConditionSuccess(
-function(target,zoneset,rzoneset)
-local success=true
-local target=target
-if target:IsDestroyed()or target:IsDead()or target:CountTargets()==0 then return true end
-local tgtcoord=target:GetCoordinate()
-local tgtvec2=nil
-if tgtcoord then
-tgtvec2=tgtcoord:GetVec2()
-end
-local zones=zoneset
-local rzones=rzoneset
-if tgtvec2 then
-zones:ForEachZone(
-function(zone)
-if zone:IsVec2InZone(tgtvec2)then
-success=false
-end
-end
-)
-rzones:ForEachZone(
-function(zone)
-if zone:IsVec2InZone(tgtvec2)then
-success=true
-end
-end
-)
-end
-return success
-end,
-Target.Target,
-ZoneSet,
-RejectZoneSet
-)
-Pilot.FlightGroup:AddMission(intercept)
-local Angels=Pilot.AnchorStackAngels or 25
-Angels=Angels*1000
-local AnchorSpeed=self.CapSpeedBase or 270
-AnchorSpeed=UTILS.KnotsToAltKIAS(AnchorSpeed,Angels)
-local Anchor=self.AnchorStacks:ReadByPointer(Pilot.AnchorStackNo)
-local capauftrag=AUFTRAG:NewCAP(Anchor.StationZone,Angels,AnchorSpeed,Anchor.StationZoneCoordinate,0,15,{})
-capauftrag:SetTime(nil,((self.CAPTimeOnStation*3600)+(15*60)))
-Pilot.FlightGroup:AddMission(capauftrag)
-if currmission then
-currmission:__Cancel(3)
-end
-self.CatchAllMissions[#self.CatchAllMissions+1]=intercept
-self.CatchAllMissions[#self.CatchAllMissions+1]=capauftrag
-self.ManagedTasks:PullByID(Pilot.CurrentTask)
-Pilot.HasAssignedTask=true
-Pilot.CurrentTask=self:_CreateTaskForGroup(Pilot.GID,AWACS.TaskDescription.INTERCEPT,"Intercept Task",Target.Target,AWACS.TaskStatus.ASSIGNED,intercept,Target.Cluster,Target.Contact)
-Pilot.CurrentAuftrag=intercept.auftragsnummer
-Pilot.ContactCID=Target.CID
-self.ManagedGrps[Pilot.GID]=Pilot
-Target.LinkedTask=Pilot.CurrentTask
-Target.LinkedGroup=Pilot.GID
-Target.Status=AWACS.TaskStatus.ASSIGNED
-local targeted=self.gettext:GetEntry("ENGAGETAG",self.locale)
-Target.EngagementTag=string.format(targeted,Pilot.CallSign)
-self.Contacts:PullByID(Target.CID)
-self.Contacts:Push(Target,Target.CID)
-local altitude=Target.Contact.altitude or Target.Contact.group:GetAltitude()
-local position=Target.Cluster.coordinate or Target.Contact.position
-if not position then
-self.intel:GetClusterCoordinate(Target.Cluster,true)
-end
-local bratext,bratexttts=self:_ToStringBRA(Pilot.Group:GetCoordinate(),position,altitude or 8000)
-local aicomm=self.gettext:GetEntry("AICOMMIT",self.locale)
-local text=string.format(aicomm,self.callsigntxt,Target.TargetGroupNaming,bratexttts,Pilot.CallSign)
-local textScreen=string.format(aicomm,self.callsigntxt,Target.TargetGroupNaming,bratext,Pilot.CallSign)
-self:_NewRadioEntry(text,textScreen,Pilot.GID,true,self.debug,true,false,true)
-local comm=self.gettext:GetEntry("COMMIT",self.locale)
-local text=string.format("%s. %s.",Pilot.CallSign,comm)
-self:_NewRadioEntry(text,text,Pilot.GID,true,self.debug,true,true,true)
-self:__Intercept(2)
-end
-return self
-end
-function AWACS:onbeforeStart(From,Event,To)
-self:T({From,Event,To})
-if self.IncludeHelicopters then
-self.clientset:FilterCategories("helicopter")
-end
-return self
-end
-function AWACS:onafterStart(From,Event,To)
-self:T({From,Event,To})
-local controlzonename="FEZ-"..self.AOName
-self.ControlZone=ZONE_RADIUS:New(controlzonename,self.OpsZone:GetVec2(),UTILS.NMToMeters(self.ControlZoneRadius))
-if self.debug then
-self.ControlZone:DrawZone(self.coalition,{0,1,0},1,{1,0,0},0.05,3,true)
-self.OpsZone:DrawZone(self.coalition,{1,0,0},1,{1,0,0},0.2,5,true)
-local AOCoordString=self.AOCoordinate:ToStringLLDDM()
-local Rocktag=string.format("FEZ: %s\nBulls Coordinate: %s",self.AOName,AOCoordString)
-MARKER:New(self.AOCoordinate,Rocktag):ToCoalition(self.coalition)
-self.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true)
-local stationtag=string.format("Station: %s\nCoordinate: %s",self.StationZoneName,self.StationZone:GetCoordinate():ToStringLLDDM())
-if not self.GCI then
-MARKER:New(self.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
-self.OrbitZone:DrawZone(self.coalition,{0,1,0},1,{0,1,0},0.2,5,true)
-MARKER:New(self.OrbitZone:GetCoordinate(),"AIC Orbit Zone"):ToCoalition(self.coalition)
-end
-else
-local AOCoordString=self.AOCoordinate:ToStringLLDDM()
-local Rocktag=string.format("FEZ: %s\nBulls Coordinate: %s",self.AOName,AOCoordString)
-MARKER:New(self.AOCoordinate,Rocktag):ToCoalition(self.coalition)
-if not self.GCI then
-MARKER:New(self.OrbitZone:GetCoordinate(),"AIC Orbit Zone"):ToCoalition(self.coalition)
-end
-local stationtag=string.format("Station: %s\nCoordinate: %s",self.StationZoneName,self.StationZone:GetCoordinate():ToStringLLDDM())
-MARKER:New(self.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
-end
-if not self.GCI then
-local AwacsAW=self.AirWing
-local mission=AUFTRAG:NewORBIT_RACETRACK(self.OrbitZone:GetCoordinate(),self.AwacsAngels*1000,self.Speed,self.Heading,self.Leg)
-local timeonstation=(self.AwacsTimeOnStation+self.ShiftChangeTime)*3600
-mission:SetTime(nil,timeonstation)
-self.CatchAllMissions[#self.CatchAllMissions+1]=mission
-AwacsAW:AddMission(mission)
-self.AwacsMission=mission
-self.AwacsInZone=false
-self.AwacsReady=false
-else
-self.AwacsInZone=true
-self.AwacsReady=true
-self:_StartIntel(self.GCIGroup)
-if self.GCIGroup:IsGround()then
-self.AwacsFG=ARMYGROUP:New(self.GCIGroup)
-self.AwacsFG:SetDefaultRadio(self.Frequency,self.Modulation)
-self.AwacsFG:SwitchRadio(self.Frequency,self.Modulation)
-elseif self.GCIGroup:IsShip()then
-self.AwacsFG=NAVYGROUP:New(self.GCIGroup)
-self.AwacsFG:SetDefaultRadio(self.Frequency,self.Modulation)
-self.AwacsFG:SwitchRadio(self.Frequency,self.Modulation)
-else
-self:E(self.lid.."**** Group unsuitable for GCI ops! Needs to be a GROUND or SHIP type group!")
-self:Stop()
-return self
-end
-self.callsigntxt=string.format("%s",self.CallSignClear[self.CallSign])
-self:__CheckRadioQueue(-10)
-local sunrise=self.gettext:GetEntry("SUNRISE",self.locale)
-local text=string.format(sunrise,self.callsigntxt,self.callsigntxt)
-self:_NewRadioEntry(text,text,0,false,false,false,false,true)
-self:T(self.lid..text)
-self.sunrisedone=true
-end
-local ZoneSet=SET_ZONE:New()
-ZoneSet:AddZone(self.ControlZone)
-if not self.GCI then
-ZoneSet:AddZone(self.OrbitZone)
-end
-if self.BorderZone then
-ZoneSet:AddZone(self.BorderZone)
-end
-local RejectZoneSet=SET_ZONE:New()
-if self.RejectZone then
-RejectZoneSet:AddZone(self.RejectZone)
-end
-self.ZoneSet=ZoneSet
-self.RejectZoneSet=RejectZoneSet
-if self.AllowMarkers then
-local MarkerOps=MARKEROPS_BASE:New("AWACS",{"Station","Delete","Move"})
-local function Handler(Keywords,Coord,Text)
-self:T(Text)
-for _,_word in pairs(Keywords)do
-if string.lower(_word)=="station"then
-local Name=string.match(Text," ([%a]+)$")
-self:_CreateAnchorStackFromMarker(Name,Coord)
-break
-elseif string.lower(_word)=="delete"then
-local Name=string.match(Text," ([%a]+)$")
-self:_DeleteAnchorStackFromMarker(Name,Coord)
-break
-elseif string.lower(_word)=="move"then
-local Name=string.match(Text," ([%a]+)$")
-self:_MoveAnchorStackFromMarker(Name,Coord)
-break
-end
-end
-end
-function MarkerOps:OnAfterMarkAdded(From,Event,To,Text,Keywords,Coord)
-Handler(Keywords,Coord,Text)
-end
-function MarkerOps:OnAfterMarkChanged(From,Event,To,Text,Keywords,Coord)
-Handler(Keywords,Coord,Text)
-end
-function MarkerOps:OnAfterMarkDeleted(From,Event,To)
-end
-self.MarkerOps=MarkerOps
-end
-if self.GCI then
-self:__Started(-5)
-end
-if self.TacticalMenu then
-self:__CheckTacticalQueue(55)
-end
-self:__Status(-30)
-return self
-end
-function AWACS:_CheckAwacsStatus()
-self:T(self.lid.."_CheckAwacsStatus")
-local awacs=nil
-if self.AwacsFG then
-awacs=self.AwacsFG:GetGroup()
-end
-local monitoringdata=self.MonitoringData
-if not self.GCI then
-if awacs and awacs:IsAlive()and not self.AwacsInZone then
-local orbitzone=self.OrbitZone
-if awacs:IsInZone(orbitzone)then
-self.AwacsInZone=true
-self:T(self.lid.."Arrived in Orbit Zone: "..orbitzone:GetName())
-local onstationtxt=self.gettext:GetEntry("AWONSTATION",self.locale)
-local text=string.format(onstationtxt,self.callsigntxt,self.AOName or"Rock")
-local textScreen=text
-self:_NewRadioEntry(text,textScreen,0,false,true,true,false,true)
-end
-end
-end
-if(awacs and awacs:IsAlive())then
-if not self.intelstarted then
-local alt=UTILS.Round(UTILS.MetersToFeet(awacs:GetAltitude())/1000,0)
-if alt>=10 then
-self:_StartIntel(awacs)
-end
-end
-if self.intelstarted and not self.sunrisedone then
-local alt=UTILS.Round(UTILS.MetersToFeet(awacs:GetAltitude())/1000,0)
-if alt>=10 then
-local sunrise=self.gettext:GetEntry("SUNRISE",self.locale)
-local text=string.format(sunrise,self.callsigntxt,self.callsigntxt)
-self:_NewRadioEntry(text,text,0,false,false,false,false,true)
-self:T(self.lid..text)
-self.sunrisedone=true
-end
-end
-local AWmission=self.AwacsMission
-local awstatus=AWmission:GetState()
-local AWmissiontime=(timer.getTime()-self.AwacsTimeStamp)
-local AWTOSLeft=UTILS.Round((((self.AwacsTimeOnStation+self.ShiftChangeTime)*3600)-AWmissiontime),0)
-AWTOSLeft=UTILS.Round(AWTOSLeft/60,0)
-local ChangeTime=UTILS.Round(((self.ShiftChangeTime*3600)/60),0)
-local Changedue="No"
-if not self.ShiftChangeAwacsFlag and(AWTOSLeft<=ChangeTime or AWmission:IsOver())then
-Changedue="Yes"
-self.ShiftChangeAwacsFlag=true
-self:__AwacsShiftChange(2)
-end
-local report=REPORT:New("AWACS:")
-report:Add("====================")
-report:Add("AWACS:")
-report:Add(string.format("Auftrag Status: %s",awstatus))
-report:Add(string.format("TOS Left: %d min",AWTOSLeft))
-report:Add(string.format("Needs ShiftChange: %s",Changedue))
-local OpsGroups=AWmission:GetOpsGroups()
-local OpsGroup=self:_GetAliveOpsGroupFromTable(OpsGroups)
-if OpsGroup then
-local OpsName=OpsGroup:GetName()or"Unknown"
-local OpsCallSign=OpsGroup:GetCallsignName()or"Unknown"
-report:Add(string.format("Mission FG %s",OpsName))
-report:Add(string.format("Callsign %s",OpsCallSign))
-report:Add(string.format("Mission FG State %s",OpsGroup:GetState()))
-else
-report:Add("***** Cannot obtain (yet) this missions OpsGroup!")
-end
-if self.ShiftChangeAwacsFlag and self.ShiftChangeAwacsRequested then
-AWmission=self.AwacsMissionReplacement
-local esstatus=AWmission:GetState()
-local ESmissiontime=(timer.getTime()-self.AwacsTimeStamp)
-local ESTOSLeft=UTILS.Round((((self.AwacsTimeOnStation+self.ShiftChangeTime)*3600)-ESmissiontime),0)
-ESTOSLeft=UTILS.Round(ESTOSLeft/60,0)
-local ChangeTime=UTILS.Round(((self.ShiftChangeTime*3600)/60),0)
-report:Add("AWACS REPLACEMENT:")
-report:Add(string.format("Auftrag Status: %s",esstatus))
-report:Add(string.format("TOS Left: %d min",ESTOSLeft))
-local OpsGroups=AWmission:GetOpsGroups()
-local OpsGroup=self:_GetAliveOpsGroupFromTable(OpsGroups)
-if OpsGroup then
-local OpsName=OpsGroup:GetName()or"Unknown"
-local OpsCallSign=OpsGroup:GetCallsignName()or"Unknown"
-report:Add(string.format("Mission FG %s",OpsName))
-report:Add(string.format("Callsign %s",OpsCallSign))
-report:Add(string.format("Mission FG State %s",OpsGroup:GetState()))
-else
-report:Add("***** Cannot obtain (yet) this missions OpsGroup!")
-end
-if AWmission:IsExecuting()then
-self.ShiftChangeAwacsFlag=false
-self.ShiftChangeAwacsRequested=false
-self.sunrisedone=false
-if self.AwacsMission and self.AwacsMission:IsNotOver()then
-self.AwacsMission:Cancel()
-end
-self.AwacsMission=self.AwacsMissionReplacement
-self.AwacsMissionReplacement=nil
-self.AwacsTimeStamp=timer.getTime()
-report:Add("*** Replacement DONE ***")
-end
-report:Add("====================")
-end
-if self.HasEscorts then
-for i=1,self.EscortNumber do
-local ESmission=self.EscortMission[i]
-if not ESmission then break end
-local esstatus=ESmission:GetState()
-local ESmissiontime=(timer.getTime()-self.EscortsTimeStamp)
-local ESTOSLeft=UTILS.Round((((self.EscortsTimeOnStation+self.ShiftChangeTime)*3600)-ESmissiontime),0)
-ESTOSLeft=UTILS.Round(ESTOSLeft/60,0)
-local ChangeTime=UTILS.Round(((self.ShiftChangeTime*3600)/60),0)
-local Changedue="No"
-if(ESTOSLeft<=ChangeTime and not self.ShiftChangeEscortsFlag)or(ESmission:IsOver()and not self.ShiftChangeEscortsFlag)then
-Changedue="Yes"
-self.ShiftChangeEscortsFlag=true
-self:__EscortShiftChange(2)
-end
-report:Add("====================")
-report:Add("ESCORTS:")
-report:Add(string.format("Auftrag Status: %s",esstatus))
-report:Add(string.format("TOS Left: %d min",ESTOSLeft))
-report:Add(string.format("Needs ShiftChange: %s",Changedue))
-local OpsGroups=ESmission:GetOpsGroups()
-local OpsGroup=self:_GetAliveOpsGroupFromTable(OpsGroups)
-if OpsGroup then
-local OpsName=OpsGroup:GetName()or"Unknown"
-local OpsCallSign=OpsGroup:GetCallsignName()or"Unknown"
-report:Add(string.format("Mission FG %s",OpsName))
-report:Add(string.format("Callsign %s",OpsCallSign))
-report:Add(string.format("Mission FG State %s",OpsGroup:GetState()))
-monitoringdata.EscortsStateMission[i]=esstatus
-monitoringdata.EscortsStateFG[i]=OpsGroup:GetState()
-else
-report:Add("***** Cannot obtain (yet) this missions OpsGroup!")
-end
-report:Add("====================")
-if self.ShiftChangeEscortsFlag and self.ShiftChangeEscortsRequested then
-ESmission=self.EscortMissionReplacement[i]
-local esstatus=ESmission:GetState()
-local ESmissiontime=(timer.getTime()-self.EscortsTimeStamp)
-local ESTOSLeft=UTILS.Round((((self.EscortsTimeOnStation+self.ShiftChangeTime)*3600)-ESmissiontime),0)
-ESTOSLeft=UTILS.Round(ESTOSLeft/60,0)
-local ChangeTime=UTILS.Round(((self.ShiftChangeTime*3600)/60),0)
-report:Add("ESCORTS REPLACEMENT:")
-report:Add(string.format("Auftrag Status: %s",esstatus))
-report:Add(string.format("TOS Left: %d min",ESTOSLeft))
-local OpsGroups=ESmission:GetOpsGroups()
-local OpsGroup=self:_GetAliveOpsGroupFromTable(OpsGroups)
-if OpsGroup then
-local OpsName=OpsGroup:GetName()or"Unknown"
-local OpsCallSign=OpsGroup:GetCallsignName()or"Unknown"
-report:Add(string.format("Mission FG %s",OpsName))
-report:Add(string.format("Callsign %s",OpsCallSign))
-report:Add(string.format("Mission FG State %s",OpsGroup:GetState()))
-else
-report:Add("***** Cannot obtain (yet) this missions OpsGroup!")
-end
-if ESmission:IsExecuting()then
-self.ShiftChangeEscortsFlag=false
-self.ShiftChangeEscortsRequested=false
-if ESmission and ESmission:IsNotOver()then
-ESmission:Cancel()
-end
-self.EscortMission[i]=self.EscortMissionReplacement[i]
-self.EscortMissionReplacement[i]=nil
-self.EscortsTimeStamp=timer.getTime()
-report:Add("*** Replacement DONE ***")
-end
-report:Add("====================")
-end
-end
-end
-if self.debug then
-self:T(report:Text())
-end
-else
-local AWmission=self.AwacsMission
-local awstatus=AWmission:GetState()
-if AWmission:IsOver()then
-self:I(self.lid.."*****AWACS is dead!*****")
-self.ShiftChangeAwacsFlag=true
-self:__AwacsShiftChange(2)
-end
-end
-return monitoringdata
-end
-function AWACS:onafterStatus(From,Event,To)
-self:T({From,Event,To})
-self:_SetClientMenus()
-local monitoringdata=self.MonitoringData
-if not self.GCI then
-monitoringdata=self:_CheckAwacsStatus()
-end
-local awacsalive=false
-if self.AwacsFG then
-local awacs=self.AwacsFG:GetGroup()
-if awacs and awacs:IsAlive()then
-awacsalive=true
-end
-end
-if self:Is("Running")and(awacsalive or self.AwacsInZone)then
-if self.AwacsSRS then
-self.AwacsSRS:SetCoordinate(self.AwacsFG:GetCoordinate())
-if self.TacticalSRS then
-self.TacticalSRS:SetCoordinate(self.AwacsFG:GetCoordinate())
-end
-end
-self:_CheckAICAPOnStation()
-self:_CleanUpContacts()
-self:_CheckMerges()
-self:_CheckSubscribers()
-local outcome,targets=self:_TargetSelectionProcess(true)
-self:_CheckTaskQueue()
-local AI,Humans=self:_GetIdlePilots()
-if outcome and#Humans>0 and self.PlayerCapAssignment then
-self:_AssignPilotToTarget(Humans,targets)
-end
-if outcome and#AI>0 then
-self:_AssignPilotToTarget(AI,targets)
-end
-end
-if not self.GCI then
-monitoringdata.AwacsShiftChange=self.ShiftChangeAwacsFlag
-if self.AwacsFG then
-monitoringdata.AwacsStateFG=self.AwacsFG:GetState()
-end
-monitoringdata.AwacsStateMission=self.AwacsMission:GetState()
-monitoringdata.EscortsShiftChange=self.ShiftChangeEscortsFlag
-end
-monitoringdata.AICAPCurrent=self.AICAPMissions:Count()
-monitoringdata.AICAPMax=self.MaxAIonCAP
-monitoringdata.Airwings=self.CAPAirwings:Count()
-self.MonitoringData=monitoringdata
-if self.debug then
-self:_LogStatistics()
-end
-local picturetime=timer.getTime()-self.PictureTimeStamp
-if self.AwacsInZone and picturetime>self.PictureInterval then
-self.PictureTimeStamp=timer.getTime()
-self:_Picture(nil,true)
-end
-self:__Status(30)
-return self
-end
-function AWACS:onafterStop(From,Event,To)
-self:T({From,Event,To})
-self.intel:Stop()
-local AWFiFo=self.CAPAirwings
-local AWStack=AWFiFo:GetPointerStack()
-for _ID,_AWID in pairs(AWStack)do
-local SubAW=self.CAPAirwings:ReadByPointer(_ID)
-if SubAW then
-SubAW:RemoveUsingOpsAwacs()
-end
-end
-self:UnHandleEvent(EVENTS.PlayerEnterAircraft)
-self:UnHandleEvent(EVENTS.PlayerEnterUnit)
-self:UnHandleEvent(EVENTS.PlayerLeaveUnit)
-self:UnHandleEvent(EVENTS.Ejection)
-self:UnHandleEvent(EVENTS.Crash)
-self:UnHandleEvent(EVENTS.Dead)
-self:UnHandleEvent(EVENTS.UnitLost)
-self:UnHandleEvent(EVENTS.BDA)
-self:UnHandleEvent(EVENTS.PilotDead)
-self:UnHandleEvent(EVENTS.Shot)
-return self
-end
-function AWACS:onafterAssignAnchor(From,Event,To,GID,HasOwnStation,StationName)
-self:T({From,Event,To,"GID = "..GID})
-self:_AssignAnchorToID(GID,HasOwnStation,StationName)
-return self
-end
-function AWACS:onafterCheckedOut(From,Event,To,GID,AnchorStackNo,Angels)
-self:T({From,Event,To,"GID = "..GID})
-self:_RemoveIDFromAnchor(GID,AnchorStackNo,Angels)
-return self
-end
-function AWACS:onafterAssignedAnchor(From,Event,To,GID,Anchor,AnchorStackNo,AnchorAngels)
-self:T({From,Event,To,"GID="..GID,"Stack="..AnchorStackNo})
-local managedgroup=self.ManagedGrps[GID]
-if not managedgroup then
-self:E(self.lid.."**** GID "..GID.." Not Registered!")
-return self
-end
-managedgroup.AnchorStackNo=AnchorStackNo
-managedgroup.AnchorStackAngels=AnchorAngels
-managedgroup.Blocked=false
-local isPlayer=managedgroup.IsPlayer
-local isAI=managedgroup.IsAI
-local Group=managedgroup.Group
-local CallSign=managedgroup.CallSign or"Ghost 1"
-local AnchorName=Anchor.StationName or"unknown"
-local AnchorCoordTxt=Anchor.StationZoneCoordinateText or"unknown"
-local Angels=AnchorAngels or 25
-local AnchorSpeed=self.CapSpeedBase or 270
-local AuftragsNr=managedgroup.CurrentAuftrag
-local textTTS=""
-if self.PikesSpecialSwitch then
-local stationtxt=self.gettext:GetEntry("STATIONAT",self.locale)
-textTTS=string.format(stationtxt,CallSign,self.callsigntxt,AnchorName,Angels)
-else
-local stationtxt=self.gettext:GetEntry("STATIONATLONG",self.locale)
-textTTS=string.format(stationtxt,CallSign,self.callsigntxt,AnchorName,Angels,AnchorSpeed)
-end
-local ROEROT=self.AwacsROE..", "..self.AwacsROT
-local stationtxtsc=self.gettext:GetEntry("STATIONSCREEN",self.locale)
-local stationtxtta=self.gettext:GetEntry("STATIONTASK",self.locale)
-local textScreen=string.format(stationtxtsc,CallSign,self.callsigntxt,AnchorName,Angels,AnchorSpeed,AnchorCoordTxt,ROEROT)
-local TextTasking=string.format(stationtxtta,AnchorName,Angels,AnchorSpeed,AnchorCoordTxt,ROEROT)
-self:_NewRadioEntry(textTTS,textScreen,GID,isPlayer,isPlayer,true,false)
-managedgroup.CurrentTask=self:_CreateTaskForGroup(GID,AWACS.TaskDescription.ANCHOR,TextTasking,Anchor.StationZone)
-if isAI then
-local auftrag=managedgroup.FlightGroup:GetMissionCurrent()
-if auftrag then
-local auftragtype=auftrag:GetType()
-if auftragtype==AUFTRAG.Type.ALERT5 then
-local capauftrag=AUFTRAG:NewCAP(Anchor.StationZone,Angels*1000,AnchorSpeed,Anchor.StationZone:GetCoordinate(),0,15,{})
-capauftrag:SetTime(nil,((self.CAPTimeOnStation*3600)+(15*60)))
-capauftrag:AddAsset(managedgroup.FlightGroup)
-self.CatchAllMissions[#self.CatchAllMissions+1]=capauftrag
-managedgroup.FlightGroup:AddMission(capauftrag)
-auftrag:Cancel()
-else
-self:E("**** AssignedAnchor but Auftrag NOT ALERT5!")
-end
-else
-self:E("**** AssignedAnchor but NO Auftrag!")
-end
-end
-self.ManagedGrps[GID]=managedgroup
-return self
-end
-function AWACS:onafterNewCluster(From,Event,To,Cluster)
-self:T({From,Event,To,Cluster.index})
-self.CID=self.CID+1
-self.Countactcounter=self.Countactcounter+1
-local ContactTable=Cluster.Contacts or{}
-local function GetFirstAliveContact(table)
-for _,_contact in pairs(table)do
-local contact=_contact
-if contact and contact.group and contact.group:IsAlive()then
-return contact,contact.group
-end
-end
-return nil
-end
-local Contact,Group=GetFirstAliveContact(ContactTable)
-if not Contact then return self end
-if Group and not Group:IsAirborne()then
-return self
-end
-local targetset=SET_GROUP:New()
-for _,_grp in pairs(ContactTable)do
-local grp=_grp
-targetset:AddGroup(grp.group,true)
-end
-local managedcontact={}
-managedcontact.CID=self.CID
-managedcontact.Contact=Contact
-managedcontact.Cluster=Cluster
-managedcontact.IFF=AWACS.IFF.BOGEY
-managedcontact.Target=TARGET:New(targetset)
-managedcontact.LinkedGroup=0
-managedcontact.LinkedTask=0
-managedcontact.Status=AWACS.TaskStatus.IDLE
-local phoneid=math.fmod(self.Countactcounter,27)
-if phoneid==0 then phoneid=1 end
-managedcontact.TargetGroupNaming=AWACS.Phonetic[phoneid]
-managedcontact.ReportingName=Contact.group:GetNatoReportingName()
-managedcontact.TACCallDone=false
-managedcontact.MeldCallDone=false
-managedcontact.EngagementTag=""
-local IsPopup=false
-if self.OpsZone:IsVec2InZone(Contact.position:GetVec2())then
-IsPopup=true
-end
-Contact.CID=managedcontact.CID
-Contact.TargetGroupNaming=managedcontact.TargetGroupNaming
-Cluster.CID=managedcontact.CID
-Cluster.TargetGroupNaming=managedcontact.TargetGroupNaming
-self.Contacts:Push(managedcontact,self.CID)
-local ContactCoordinate=Contact.position:GetVec2()
-local incontrolzone=self.ControlZone:IsVec2InZone(ContactCoordinate)
-local distance=1000000
-if not self.GCI then
-distance=Contact.position:Get2DDistance(self.OrbitZone:GetCoordinate())
-end
-local inborderzone=false
-if self.BorderZone then
-inborderzone=self.BorderZone:IsVec2InZone(ContactCoordinate)
-end
-if incontrolzone or inborderzone or(distance<=UTILS.NMToMeters(55))or IsPopup then
-self:_AnnounceContact(managedcontact,true,nil,false,managedcontact.TargetGroupNaming,IsPopup,managedcontact.ReportingName)
-end
-return self
-end
-function AWACS:onafterNewContact(From,Event,To,Contact)
-self:T({From,Event,To,Contact})
-local tdist=self.ThreatDistance
-for _gid,_mgroup in pairs(self.ManagedGrps)do
-local managedgroup=_mgroup
-local group=managedgroup.Group
-if group and group:IsAlive()and group:IsAirborne()then
-local cpos=Contact.position or Contact.group:GetCoordinate()
-local mpos=group:GetCoordinate()
-local dist=cpos:Get2DDistance(mpos)
-dist=UTILS.Round(UTILS.MetersToNM(dist),0)
-if dist<=tdist then
-self:_ThreatRangeCall(_gid,Contact)
-end
-end
-end
-return self
-end
-function AWACS:onafterLostContact(From,Event,To,Contact)
-self:T({From,Event,To,Contact})
-return self
-end
-function AWACS:onafterLostCluster(From,Event,To,Cluster,Mission)
-self:T({From,Event,To})
-return self
-end
-function AWACS:onafterCheckTacticalQueue(From,Event,To)
-self:T({From,Event,To})
-if self.clientset:CountAlive()==0 then
-self:T(self.lid.."No player connected.")
-self:__CheckTacticalQueue(-5)
-return self
-end
-for _name,_freq in pairs(self.TacticalSubscribers)do
-local Group=nil
-if _name then
-Group=GROUP:FindByName(_name)
-end
-if Group and Group:IsAlive()then
-self:_BogeyDope(Group,true)
-end
-end
-if(self.TacticalQueue:IsNotEmpty())then
-while self.TacticalQueue:Count()>0 do
-local RadioEntry=self.TacticalQueue:Pull()
-self:T({RadioEntry})
-local frequency=self.TacticalBaseFreq
-if RadioEntry.GroupID and RadioEntry.GroupID~=0 then
-local managedgroup=self.ManagedGrps[RadioEntry.GroupID]
-if managedgroup and managedgroup.Group and managedgroup.Group:IsAlive()then
-local name=managedgroup.GroupName
-frequency=self.TacticalSubscribers[name]
-end
-end
-local gtext=RadioEntry.TextTTS
-if self.PathToGoogleKey then
-gtext=string.format("%s",gtext)
-end
-self.TacticalSRSQ:NewTransmission(gtext,nil,self.TacticalSRS,nil,0.5,nil,nil,nil,frequency,self.TacticalModulation,nil,nil,nil,nil,nil)
-self:T(RadioEntry.TextTTS)
-if RadioEntry.ToScreen and RadioEntry.TextScreen and(not self.SuppressScreenOutput)then
-if RadioEntry.GroupID and RadioEntry.GroupID~=0 then
-local managedgroup=self.ManagedGrps[RadioEntry.GroupID]
-if managedgroup and managedgroup.Group and managedgroup.Group:IsAlive()then
-MESSAGE:New(RadioEntry.TextScreen,20,"AWACS"):ToGroup(managedgroup.Group)
-self:T(RadioEntry.TextScreen)
-end
-else
-MESSAGE:New(RadioEntry.TextScreen,20,"AWACS"):ToCoalition(self.coalition)
-end
-end
-end
-end
-if self:Is("Running")then
-self:__CheckTacticalQueue(-self.TacticalInterval)
-end
-return self
-end
-function AWACS:onafterCheckRadioQueue(From,Event,To)
-self:T({From,Event,To})
-local nextcall=10
-if(self.RadioQueue:IsNotEmpty()or self.PrioRadioQueue:IsNotEmpty())then
-local RadioEntry=nil
-if self.PrioRadioQueue:IsNotEmpty()then
-RadioEntry=self.PrioRadioQueue:Pull()
-else
-RadioEntry=self.RadioQueue:Pull()
-end
-self:T({RadioEntry})
-if self.clientset:CountAlive()==0 then
-self:T(self.lid.."No player connected.")
-self:__CheckRadioQueue(-5)
-return self
-end
-if not RadioEntry.FromAI then
-if self.PathToGoogleKey then
-local gtext=RadioEntry.TextTTS
-gtext=string.format("%s",gtext)
-self.AwacsSRS:PlayTextExt(gtext,nil,self.MultiFrequency,self.MultiModulation,self.Gender,self.Culture,self.Voice,self.Volume,"AWACS")
-else
-self.AwacsSRS:PlayTextExt(RadioEntry.TextTTS,nil,self.MultiFrequency,self.MultiModulation,self.Gender,self.Culture,self.Voice,self.Volume,"AWACS")
-end
-self:T(RadioEntry.TextTTS)
-else
-if RadioEntry.GroupID and RadioEntry.GroupID~=0 then
-local managedgroup=self.ManagedGrps[RadioEntry.GroupID]
-if managedgroup and managedgroup.FlightGroup and managedgroup.FlightGroup:IsAlive()then
-if self.PathToGoogleKey then
-local gtext=RadioEntry.TextTTS
-gtext=string.format("%s",gtext)
-managedgroup.FlightGroup:RadioTransmission(gtext,1,false)
-else
-managedgroup.FlightGroup:RadioTransmission(RadioEntry.TextTTS,1,false)
-end
-self:T(RadioEntry.TextTTS)
-end
-end
-end
-if RadioEntry.Duration then nextcall=RadioEntry.Duration end
-if RadioEntry.ToScreen and RadioEntry.TextScreen and(not self.SuppressScreenOutput)then
-if RadioEntry.GroupID and RadioEntry.GroupID~=0 then
-local managedgroup=self.ManagedGrps[RadioEntry.GroupID]
-if managedgroup and managedgroup.Group and managedgroup.Group:IsAlive()then
-MESSAGE:New(RadioEntry.TextScreen,20,"AWACS"):ToGroup(managedgroup.Group)
-self:T(RadioEntry.TextScreen)
-end
-else
-MESSAGE:New(RadioEntry.TextScreen,20,"AWACS"):ToCoalition(self.coalition)
-end
-end
-end
-if self:Is("Running")then
-if self.PathToGoogleKey then
-nextcall=nextcall+self.GoogleTTSPadding
-else
-nextcall=nextcall+self.WindowsTTSPadding
-end
-self:__CheckRadioQueue(-nextcall)
-end
-return self
-end
-function AWACS:onafterEscortShiftChange(From,Event,To)
-self:T({From,Event,To})
-if self.AwacsFG and self.ShiftChangeEscortsFlag and not self.ShiftChangeEscortsRequested then
-local awacs=self.AwacsFG:GetGroup()
-if awacs and awacs:IsAlive()then
-self.ShiftChangeEscortsRequested=true
-self.EscortsTimeStamp=timer.getTime()
-self:_StartEscorts(true)
-else
-self:E("**** AWACS group dead at onafterEscortShiftChange!")
-end
-end
-return self
-end
-function AWACS:onafterAwacsShiftChange(From,Event,To)
-self:T({From,Event,To})
-if self.AwacsFG and self.ShiftChangeAwacsFlag and not self.ShiftChangeAwacsRequested then
-self.ShiftChangeAwacsRequested=true
-self.AwacsTimeStamp=timer.getTime()
-local AwacsAW=self.AirWing
-local mission=AUFTRAG:NewORBIT_RACETRACK(self.OrbitZone:GetCoordinate(),self.AwacsAngels*1000,self.Speed,self.Heading,self.Leg)
-self.CatchAllMissions[#self.CatchAllMissions+1]=mission
-local timeonstation=(self.AwacsTimeOnStation+self.ShiftChangeTime)*3600
-mission:SetTime(nil,timeonstation)
-AwacsAW:AddMission(mission)
-self.AwacsMissionReplacement=mission
-end
-return self
-end
-function AWACS:onafterFlightOnMission(From,Event,To,FlightGroup,Mission)
-self:T({From,Event,To})
-self:T("FlightGroup "..FlightGroup:GetName().." Mission "..Mission:GetName().." Type "..Mission:GetType())
-self.CatchAllFGs[#self.CatchAllFGs+1]=FlightGroup
-if not self:Is("Stopped")then
-if not self.AwacsReady or self.ShiftChangeAwacsFlag or self.ShiftChangeEscortsFlag then
-self:_StartSettings(FlightGroup,Mission)
-elseif Mission and(Mission:GetType()==AUFTRAG.Type.CAP or Mission:GetType()==AUFTRAG.Type.ALERT5 or Mission:GetType()==AUFTRAG.Type.ORBIT)then
-if not self.FlightGroups:HasUniqueID(FlightGroup:GetName())then
-self:T("Pushing FG "..FlightGroup:GetName().." to stack!")
-self.FlightGroups:Push(FlightGroup,FlightGroup:GetName())
-end
-end
-end
-return self
-end
-function AWACS:onafterReAnchor(From,Event,To,GID)
-self:T({From,Event,To,GID})
-local managedgroup=self.ManagedGrps[GID]
-if managedgroup then
-if managedgroup.IsAI then
-local AIFG=managedgroup.FlightGroup
-if AIFG and AIFG:IsAlive()then
-if AIFG:IsFuelLow()or AIFG:IsOutOfMissiles()or AIFG:IsOutOfAmmo()then
-local destbase=AIFG.homebase
-if not destbase then destbase=self.Airbase end
-AIFG:RTB(destbase)
-self:_CheckOut(AIFG:GetGroup(),GID)
-self.AIRequested=self.AIRequested-1
-else
-local Anchor=self.AnchorStacks:ReadByPointer(managedgroup.AnchorStackNo)
-local StationZone=Anchor.StationZone
-managedgroup.CurrentTask=self:_CreateTaskForGroup(GID,AWACS.TaskDescription.ANCHOR,"Re-Station AI",StationZone)
-managedgroup.HasAssignedTask=true
-local mission=AIFG:GetMissionCurrent()
-if mission then
-managedgroup.CurrentAuftrag=mission.auftragsnummer or 0
-else
-managedgroup.CurrentAuftrag=0
-end
-managedgroup.ContactCID=0
-self.ManagedGrps[GID]=managedgroup
-local tostation=self.gettext:GetEntry("VECTORSTATION",self.locale)
-self:_MessageVector(GID,tostation,Anchor.StationZoneCoordinate,managedgroup.AnchorStackAngels)
-end
-else
-local savedcallsign=managedgroup.CallSign
-local textoptions={}
-textoptions[1]=self.gettext:GetEntry("TEXTOPTIONS1",self.locale)
-textoptions[2]=self.gettext:GetEntry("TEXTOPTIONS2",self.locale)
-textoptions[3]=self.gettext:GetEntry("TEXTOPTIONS3",self.locale)
-textoptions[4]=self.gettext:GetEntry("TEXTOPTIONS4",self.locale)
-local allstations=self.gettext:GetEntry("ALLSTATIONS",self.locale)
-local milestxt=self.gettext:GetEntry("MILES",self.locale)
-if managedgroup.LastKnownPosition then
-local lastknown=UTILS.DeepCopy(managedgroup.LastKnownPosition)
-local faded=textoptions[math.random(1,4)]
-local text=string.format("%s. %s. %s %s.",allstations,self.callsigntxt,faded,savedcallsign)
-local textScreen=string.format("%s, %s. %s %s.",allstations,self.callsigntxt,faded,savedcallsign)
-local brtext=self:_ToStringBULLS(lastknown)
-local brtexttts=self:_ToStringBULLS(lastknown,false,true)
-text=text.." "..brtexttts.." "..milestxt.."."
-textScreen=textScreen.." "..brtext.." "..milestxt.."."
-self:_NewRadioEntry(text,textScreen,0,false,self.debug,true,false,true)
-end
-self.ManagedGrps[GID]=nil
-end
-elseif managedgroup.IsPlayer then
-local PLFG=managedgroup.Group
-if PLFG and PLFG:IsAlive()then
-local Anchor=self.AnchorStacks:ReadByPointer(managedgroup.AnchorStackNo)
-local AnchorName=Anchor.StationName or"unknown"
-local AnchorCoordTxt=Anchor.StationZoneCoordinateText or"unknown"
-local Angels=managedgroup.AnchorStackAngels or 25
-local AnchorSpeed=self.CapSpeedBase or 270
-local StationZone=Anchor.StationZone
-local ROEROT=self.AwacsROE.." "..self.AwacsROT
-local stationtxt=self.gettext:GetEntry("STATIONTASK",self.locale)
-local TextTasking=string.format(stationtxt,AnchorName,Angels,AnchorSpeed,AnchorCoordTxt,ROEROT)
-managedgroup.CurrentTask=self:_CreateTaskForGroup(GID,AWACS.TaskDescription.ANCHOR,TextTasking,StationZone)
-managedgroup.HasAssignedTask=true
-managedgroup.ContactCID=0
-self.ManagedGrps[GID]=managedgroup
-local vectortxt=self.gettext:GetEntry("VECTORSTATION",self.locale)
-self:_MessageVector(GID,vectortxt,Anchor.StationZoneCoordinate,managedgroup.AnchorStackAngels)
-else
-local savedcallsign=managedgroup.CallSign
-local textoptions={}
-textoptions[1]=self.gettext:GetEntry("TEXTOPTIONS1",self.locale)
-textoptions[2]=self.gettext:GetEntry("TEXTOPTIONS2",self.locale)
-textoptions[3]=self.gettext:GetEntry("TEXTOPTIONS3",self.locale)
-textoptions[4]=self.gettext:GetEntry("TEXTOPTIONS4",self.locale)
-local allstations=self.gettext:GetEntry("ALLSTATIONS",self.locale)
-local milestxt=self.gettext:GetEntry("MILES",self.locale)
-local faded=textoptions[math.random(1,4)]
-local text=string.format("%s. %s. %s %s.",allstations,self.callsigntxt,faded,savedcallsign)
-local textScreen=string.format("%s, %s. %s %s.",allstations,self.callsigntxt,faded,savedcallsign)
-if managedgroup.LastKnownPosition then
-local lastknown=UTILS.DeepCopy(managedgroup.LastKnownPosition)
-local brtext=self:_ToStringBULLS(lastknown)
-local brtexttts=self:_ToStringBULLS(lastknown,false,true)
-text=text.." "..brtexttts.." "..milestxt.."."
-textScreen=textScreen.." "..brtext.." "..milestxt.."."
-self:_NewRadioEntry(text,textScreen,0,false,self.debug,true,false,true)
-end
-self.ManagedGrps[GID]=nil
-end
-end
-end
-end
-end
-BRIGADE={
-ClassName="BRIGADE",
-verbose=0,
-rearmingZones={},
-refuellingZones={},
-}
-BRIGADE.version="0.1.1"
-function BRIGADE:New(WarehouseName,BrigadeName)
-local self=BASE:Inherit(self,LEGION:New(WarehouseName,BrigadeName))
-if not self then
-BASE:E(string.format("ERROR: Could not find warehouse %s!",WarehouseName))
-return nil
-end
-self.lid=string.format("BRIGADE %s | ",self.alias)
-self:SetRetreatZones()
-if self:IsShip()then
-local wh=self.warehouse
-local group=wh:GetGroup()
-self.warehouseOpsGroup=NAVYGROUP:New(group)
-self.warehouseOpsElement=self.warehouseOpsGroup:GetElementByName(wh:GetName())
-end
-self:AddTransition("*","ArmyOnMission","*")
-return self
-end
-function BRIGADE:AddPlatoon(Platoon)
-table.insert(self.cohorts,Platoon)
-self:AddAssetToPlatoon(Platoon,Platoon.Ngroups)
-Platoon:SetBrigade(self)
-if Platoon:IsStopped()then
-Platoon:Start()
-end
-return self
-end
-function BRIGADE:AddAssetToPlatoon(Platoon,Nassets)
-if Platoon then
-local Group=GROUP:FindByName(Platoon.templatename)
-if Group then
-local text=string.format("Adding asset %s to platoon %s",Group:GetName(),Platoon.name)
-self:T(self.lid..text)
-self:AddAsset(Group,Nassets,nil,nil,nil,nil,Platoon.skill,Platoon.livery,Platoon.name)
-else
-self:E(self.lid.."ERROR: Group does not exist!")
-end
-else
-self:E(self.lid.."ERROR: Platoon does not exit!")
-end
-return self
-end
-function BRIGADE:SetRetreatZones(RetreatZoneSet)
-self.retreatZones=RetreatZoneSet or SET_ZONE:New()
-return self
-end
-function BRIGADE:AddRetreatZone(RetreatZone)
-self.retreatZones:AddZone(RetreatZone)
-return self
-end
-function BRIGADE:GetRetreatZones()
-return self.retreatZones
-end
-function BRIGADE:AddRearmingZone(RearmingZone)
-local rearmingzone={}
-rearmingzone.zone=RearmingZone
-rearmingzone.mission=nil
-rearmingzone.marker=MARKER:New(rearmingzone.zone:GetCoordinate(),"Rearming Zone"):ToCoalition(self:GetCoalition())
-table.insert(self.rearmingZones,rearmingzone)
-return rearmingzone
-end
-function BRIGADE:AddRefuellingZone(RefuellingZone)
-local supplyzone={}
-supplyzone.zone=RefuellingZone
-supplyzone.mission=nil
-supplyzone.marker=MARKER:New(supplyzone.zone:GetCoordinate(),"Refuelling Zone"):ToCoalition(self:GetCoalition())
-table.insert(self.refuellingZones,supplyzone)
-return supplyzone
-end
-function BRIGADE:GetPlatoon(PlatoonName)
-local platoon=self:_GetCohort(PlatoonName)
-return platoon
-end
-function BRIGADE:GetPlatoonOfAsset(Asset)
-local platoon=self:GetPlatoon(Asset.squadname)
-return platoon
-end
-function BRIGADE:RemoveAssetFromPlatoon(Asset)
-local platoon=self:GetPlatoonOfAsset(Asset)
-if platoon then
-platoon:DelAsset(Asset)
-end
-end
-function BRIGADE:LoadBackAssetInPosition(Templatename,Position)
-self:T(self.lid.."LoadBackAssetInPosition: "..tostring(Templatename))
-local nametbl=UTILS.Split(Templatename,"_")
-local name=nametbl[1]
-self:T(string.format("*** Target Platoon = %s ***",name))
-local cohorts=self.cohorts or{}
-local thisasset=nil
-local found=false
-for _,_cohort in pairs(cohorts)do
-local asset=_cohort:GetName()
-self:T(string.format("*** Looking at Platoon = %s ***",asset))
-if asset==name then
-self:T("**** Found Platoon ****")
-local cohassets=_cohort.assets or{}
-for _,_zug in pairs(cohassets)do
-local zug=_zug
-if zug.assignment==name and zug.requested==false then
-self:T("**** Found Asset ****")
-found=true
-thisasset=zug
-break
-end
-end
-end
-end
-if found then
-thisasset.rid=thisasset.uid
-thisasset.requested=false
-thisasset.score=100
-thisasset.missionTask="CAS"
-thisasset.spawned=true
-local template=thisasset.templatename
-local alias=thisasset.spawngroupname
-local spawnasset=SPAWN:NewWithAlias(template,alias)
-:InitDelayOff()
-:SpawnFromCoordinate(Position)
-local request={}
-request.assignment=name
-request.warehouse=self
-request.assets={thisasset}
-request.ntransporthome=0
-request.ndelivered=0
-request.ntransport=0
-request.cargoattribute=thisasset.attribute
-request.category=thisasset.category
-request.cargoassets={thisasset}
-request.assetdesc=WAREHOUSE.Descriptor.ASSETLIST
-request.cargocategory=thisasset.category
-request.toself=true
-request.transporttype=WAREHOUSE.TransportType.SELFPROPELLED
-request.assetproblem={}
-request.born=true
-request.prio=50
-request.uid=thisasset.uid
-request.airbase=nil
-request.timestamp=timer.getAbsTime()
-request.assetdescval={thisasset}
-request.nasset=1
-request.cargogroupset=SET_GROUP:New()
-request.cargogroupset:AddGroup(spawnasset)
-request.iscargo=true
-self:__AssetSpawned(2,spawnasset,thisasset,request)
-end
-return self
-end
-function BRIGADE:onafterStart(From,Event,To)
-self:GetParent(self,BRIGADE).onafterStart(self,From,Event,To)
-self:I(self.lid..string.format("Starting BRIGADE v%s",BRIGADE.version))
-end
-function BRIGADE:onafterStatus(From,Event,To)
-self:GetParent(self).onafterStatus(self,From,Event,To)
-local fsmstate=self:GetState()
-self:CheckTransportQueue()
-self:CheckMissionQueue()
-for _,_rearmingzone in pairs(self.rearmingZones)do
-local rearmingzone=_rearmingzone
-if(not rearmingzone.mission)or rearmingzone.mission:IsOver()then
-rearmingzone.mission=AUFTRAG:NewAMMOSUPPLY(rearmingzone.zone)
-self:AddMission(rearmingzone.mission)
-end
-end
-for _,_supplyzone in pairs(self.refuellingZones)do
-local supplyzone=_supplyzone
-if(not supplyzone.mission)or supplyzone.mission:IsOver()then
-supplyzone.mission=AUFTRAG:NewFUELSUPPLY(supplyzone.zone)
-self:AddMission(supplyzone.mission)
-end
-end
-if self.verbose>=1 then
-local Nmissions=self:CountMissionsInQueue()
-local Npq,Np,Nq=self:CountAssetsOnMission()
-local assets=string.format("%d [OnMission: Total=%d, Active=%d, Queued=%d]",self:CountAssets(),Npq,Np,Nq)
-local text=string.format("%s: Missions=%d, Platoons=%d, Assets=%s",fsmstate,Nmissions,#self.cohorts,assets)
-self:I(self.lid..text)
-end
-if self.verbose>=2 then
-local text=string.format("Missions Total=%d:",#self.missionqueue)
-for i,_mission in pairs(self.missionqueue)do
-local mission=_mission
-local prio=string.format("%d/%s",mission.prio,tostring(mission.importance));if mission.urgent then prio=prio.." (!)"end
-local assets=string.format("%d/%d",mission:CountOpsGroups(),mission.Nassets or 0)
-local target=string.format("%d/%d Damage=%.1f",mission:CountMissionTargets(),mission:GetTargetInitialNumber(),mission:GetTargetDamage())
-text=text..string.format("\n[%d] %s %s: Status=%s, Prio=%s, Assets=%s, Targets=%s",i,mission.name,mission.type,mission.status,prio,assets,target)
-end
-self:I(self.lid..text)
-end
-if self.verbose>=2 then
-local text=string.format("Transports Total=%d:",#self.transportqueue)
-for i,_transport in pairs(self.transportqueue)do
-local transport=_transport
-local prio=string.format("%d/%s",transport.prio,tostring(transport.importance));if transport.urgent then prio=prio.." (!)"end
-local carriers=string.format("Ncargo=%d/%d, Ncarriers=%d",transport.Ncargo,transport.Ndelivered,transport.Ncarrier)
-text=text..string.format("\n[%d] UID=%d: Status=%s, Prio=%s, Cargo: %s",i,transport.uid,transport:GetState(),prio,carriers)
-end
-self:I(self.lid..text)
-end
-if self.verbose>=3 then
-local text="Platoons:"
-for i,_platoon in pairs(self.cohorts)do
-local platoon=_platoon
-local callsign=platoon.callsignName and UTILS.GetCallsignName(platoon.callsignName)or"N/A"
-local modex=platoon.modex and platoon.modex or-1
-local skill=platoon.skill and tostring(platoon.skill)or"N/A"
-text=text..string.format("\n* %s %s: %s*%d/%d, Callsign=%s, Modex=%d, Skill=%s",platoon.name,platoon:GetState(),platoon.aircrafttype,platoon:CountAssets(true),#platoon.assets,callsign,modex,skill)
-end
-self:I(self.lid..text)
-end
-if self.verbose>=4 then
-local text="Rearming Zones:"
-for i,_rearmingzone in pairs(self.rearmingZones)do
-local rearmingzone=_rearmingzone
-text=text..string.format("\n* %s: Mission status=%s, suppliers=%d",rearmingzone.zone:GetName(),rearmingzone.mission:GetState(),rearmingzone.mission:CountOpsGroups())
-end
-self:I(self.lid..text)
-end
-if self.verbose>=4 then
-local text="Refuelling Zones:"
-for i,_refuellingzone in pairs(self.refuellingZones)do
-local refuellingzone=_refuellingzone
-text=text..string.format("\n* %s: Mission status=%s, suppliers=%d",refuellingzone.zone:GetName(),refuellingzone.mission:GetState(),refuellingzone.mission:CountOpsGroups())
-end
-self:I(self.lid..text)
-end
-if self.verbose>=5 then
-local text="Assets in stock:"
-for i,_asset in pairs(self.stock)do
-local asset=_asset
-text=text..string.format("\n* %s: spawned=%s",asset.spawngroupname,tostring(asset.spawned))
-end
-self:I(self.lid..text)
-end
-end
-function BRIGADE:onafterArmyOnMission(From,Event,To,ArmyGroup,Mission)
-self:T(self.lid..string.format("Group %s on %s mission %s",ArmyGroup:GetName(),Mission:GetType(),Mission:GetName()))
-end
-CHIEF={
-ClassName="CHIEF",
-verbose=0,
-lid=nil,
-targetqueue={},
-zonequeue={},
-borderzoneset=nil,
-yellowzoneset=nil,
-engagezoneset=nil,
-tacview=false,
-Nsuccess=0,
-Nfailure=0,
-}
-CHIEF.DEFCON={
-GREEN="Green",
-YELLOW="Yellow",
-RED="Red",
-}
-CHIEF.Strategy={
-PASSIVE="Passive",
-DEFENSIVE="Defensive",
-OFFENSIVE="Offensive",
-AGGRESSIVE="Aggressive",
-TOTALWAR="Total War"
-}
-CHIEF.version="0.6.0"
-function CHIEF:New(Coalition,AgentSet,Alias)
-Alias=Alias or"CHIEF"
-if type(Coalition)=="string"then
-if string.lower(Coalition)=="blue"then
-Coalition=coalition.side.BLUE
-elseif string.lower(Coalition)=="red"then
-Coalition=coalition.side.RED
-else
-Coalition=coalition.side.NEUTRAL
-end
-end
-local self=BASE:Inherit(self,INTEL:New(AgentSet,Coalition,Alias))
-self:SetBorderZones()
-self:SetConflictZones()
-self:SetAttackZones()
-self:SetThreatLevelRange()
-self.Defcon=CHIEF.DEFCON.GREEN
-self.strategy=CHIEF.Strategy.DEFENSIVE
-self.TransportCategories={Group.Category.HELICOPTER}
-self.commander=COMMANDER:New(Coalition)
-self:AddTransition("*","MissionAssign","*")
-self:AddTransition("*","MissionCancel","*")
-self:AddTransition("*","TransportCancel","*")
-self:AddTransition("*","OpsOnMission","*")
-self:AddTransition("*","ZoneCaptured","*")
-self:AddTransition("*","ZoneLost","*")
-self:AddTransition("*","ZoneEmpty","*")
-self:AddTransition("*","ZoneAttacked","*")
-self:AddTransition("*","DefconChange","*")
-self:AddTransition("*","StrategyChange","*")
-self:AddTransition("*","LegionLost","*")
-return self
-end
-function CHIEF:SetAirToAny()
-self:SetFilterCategory({})
-return self
-end
-function CHIEF:SetAirToAir()
-self:SetFilterCategory({Unit.Category.AIRPLANE,Unit.Category.HELICOPTER})
-return self
-end
-function CHIEF:SetAirToGround()
-self:SetFilterCategory({Unit.Category.GROUND_UNIT})
-return self
-end
-function CHIEF:SetAirToSea()
-self:SetFilterCategory({Unit.Category.SHIP})
-return self
-end
-function CHIEF:SetAirToSurface()
-self:SetFilterCategory({Unit.Category.GROUND_UNIT,Unit.Category.SHIP})
-return self
-end
-function CHIEF:SetThreatLevelRange(ThreatLevelMin,ThreatLevelMax)
-self.threatLevelMin=ThreatLevelMin or 1
-self.threatLevelMax=ThreatLevelMax or 10
-return self
-end
-function CHIEF:SetDefcon(Defcon)
-local gotit=false
-for _,defcon in pairs(CHIEF.DEFCON)do
-if defcon==Defcon then
-gotit=true
-end
-end
-if not gotit then
-self:E(self.lid..string.format("ERROR: Unknown DEFCON specified! Dont know defcon=%s",tostring(Defcon)))
-return self
-end
-if Defcon~=self.Defcon then
-self:DefconChange(Defcon)
-end
-self.Defcon=Defcon
-return self
-end
-function CHIEF:CreateResource(MissionType,Nmin,Nmax,Attributes,Properties,Categories)
-local resources={}
-local resource=self:AddToResource(resources,MissionType,Nmin,Nmax,Attributes,Properties,Categories)
-return resources,resource
-end
-function CHIEF:AddToResource(Resource,MissionType,Nmin,Nmax,Attributes,Properties,Categories)
-local resource={}
-resource.MissionType=MissionType
-resource.Nmin=Nmin or 1
-resource.Nmax=Nmax or Nmin
-resource.Attributes=UTILS.EnsureTable(Attributes,true)
-resource.Properties=UTILS.EnsureTable(Properties,true)
-resource.Categories=UTILS.EnsureTable(Categories,true)
-resource.carrierNmin=nil
-resource.carrierNmax=nil
-resource.carrierAttributes=nil
-resource.carrierProperties=nil
-resource.carrierCategories=nil
-table.insert(Resource,resource)
-if self.verbose>10 then
-local text="Resource:"
-for _,_r in pairs(Resource)do
-local r=_r
-text=text..string.format("\nmission=%s, Nmin=%d, Nmax=%d, attribute=%s, properties=%s",r.MissionType,r.Nmin,r.Nmax,tostring(r.Attributes[1]),tostring(r.Properties[1]))
-end
-self:I(self.lid..text)
-end
-return resource
-end
-function CHIEF:AddTransportToResource(Resource,Nmin,Nmax,CarrierAttributes,CarrierProperties,CarrierCategories)
-Resource.carrierNmin=Nmin or 1
-Resource.carrierNmax=Nmax or Nmin
-Resource.carrierCategories=UTILS.EnsureTable(CarrierCategories,true)
-Resource.carrierAttributes=UTILS.EnsureTable(CarrierAttributes,true)
-Resource.carrierProperties=UTILS.EnsureTable(CarrierProperties,true)
-return self
-end
-function CHIEF:DeleteFromResource(Resource,MissionType)
-for i=#Resource,1,-1 do
-local resource=Resource[i]
-if resource.MissionType==MissionType then
-if resource.mission and resource.mission:IsNotOver()then
-resource.mission:Cancel()
-end
-table.remove(Resource,i)
-end
-end
-return self
-end
-function CHIEF:SetResponseOnTarget(NassetsMin,NassetsMax,ThreatLevel,TargetCategory,MissionType,Nunits,Defcon,Strategy)
-local bla={}
-bla.nAssetMin=NassetsMin or 1
-bla.nAssetMax=NassetsMax or bla.nAssetMin
-bla.threatlevel=ThreatLevel or 0
-bla.targetCategory=TargetCategory
-bla.missionType=MissionType
-bla.nUnits=Nunits or 1
-bla.defcon=Defcon
-bla.strategy=Strategy
-self.assetNumbers=self.assetNumbers or{}
-table.insert(self.assetNumbers,bla)
-end
-function CHIEF:_GetAssetsForTarget(Target,MissionType)
-local threatlevel=Target:GetThreatLevelMax()
-local nUnits=Target.N0
-local targetcategory=Target:GetCategory()
-self:T(self.lid..string.format("Getting number of assets for target with TL=%d, Category=%s, nUnits=%s, MissionType=%s",threatlevel,targetcategory,nUnits,tostring(MissionType)))
-local candidates={}
-local threatlevelMatch=nil
-for _,_assetnumber in pairs(self.assetNumbers or{})do
-local assetnumber=_assetnumber
-if(threatlevelMatch==nil and threatlevel>=assetnumber.threatlevel)or(threatlevelMatch~=nil and threatlevelMatch==threatlevel)then
-if threatlevelMatch==nil then
-threatlevelMatch=threatlevel
-end
-local nMatch=0
-local cand=true
-if assetnumber.targetCategory~=nil then
-if assetnumber.targetCategory==targetcategory then
-nMatch=nMatch+1
-else
-cand=false
-end
-end
-if MissionType and assetnumber.missionType~=nil then
-if assetnumber.missionType==MissionType then
-nMatch=nMatch+1
-else
-cand=false
-end
-end
-if assetnumber.nUnits~=nil then
-if assetnumber.nUnits>=nUnits then
-nMatch=nMatch+1
-else
-cand=false
-end
-end
-if assetnumber.defcon~=nil then
-if assetnumber.defcon==self.Defcon then
-nMatch=nMatch+1
-else
-cand=false
-end
-end
-if assetnumber.strategy~=nil then
-if assetnumber.strategy==self.strategy then
-nMatch=nMatch+1
-else
-cand=false
-end
-end
-if cand then
-table.insert(candidates,{assetnumber=assetnumber,nMatch=nMatch})
-end
-end
-end
-if#candidates>0 then
-local function _sort(a,b)
-return a.nMatch>b.nMatch
-end
-table.sort(candidates,_sort)
-local candidate=candidates[1]
-local an=candidate.assetnumber
-self:T(self.lid..string.format("Picking candidate with %d matches: NassetsMin=%d, NassetsMax=%d, ThreatLevel=%d, TargetCategory=%s, MissionType=%s, Defcon=%s, Strategy=%s",
-candidate.nMatch,an.nAssetMin,an.nAssetMax,an.threatlevel,tostring(an.targetCategory),tostring(an.missionType),tostring(an.defcon),tostring(an.strategy)))
-return an.nAssetMin,an.nAssetMax
-else
-return 1,1
-end
-end
-function CHIEF:GetDefcon(Defcon)
-return self.Defcon
-end
-function CHIEF:SetLimitMission(Limit,MissionType)
-self.commander:SetLimitMission(Limit,MissionType)
-return self
-end
-function CHIEF:SetTacticalOverviewOn()
-self.tacview=true
-return self
-end
-function CHIEF:SetTacticalOverviewOff()
-self.tacview=false
-return self
-end
-function CHIEF:SetStrategy(Strategy)
-if Strategy~=self.strategy then
-self:StrategyChange(Strategy)
-end
-self.strategy=Strategy
-return self
-end
-function CHIEF:GetDefcon(Defcon)
-return self.Defcon
-end
-function CHIEF:GetCommander()
-return self.commander
-end
-function CHIEF:AddAirwing(Airwing)
-self:AddLegion(Airwing)
-return self
-end
-function CHIEF:AddBrigade(Brigade)
-self:AddLegion(Brigade)
-return self
-end
-function CHIEF:AddFleet(Fleet)
-self:AddLegion(Fleet)
-return self
-end
-function CHIEF:AddLegion(Legion)
-Legion.chief=self
-self.commander:AddLegion(Legion)
-return self
-end
-function CHIEF:RemoveLegion(Legion)
-Legion.chief=nil
-self.commander:RemoveLegion(Legion)
-return self
-end
-function CHIEF:AddMission(Mission)
-Mission.chief=self
-Mission.statusChief=AUFTRAG.Status.PLANNED
-self:I(self.lid..string.format("Adding mission #%d",Mission.auftragsnummer))
-self.commander:AddMission(Mission)
-return self
-end
-function CHIEF:RemoveMission(Mission)
-Mission.chief=nil
-self.commander:RemoveMission(Mission)
-return self
-end
-function CHIEF:AddOpsTransport(Transport)
-Transport.chief=self
-self.commander:AddOpsTransport(Transport)
-return self
-end
-function CHIEF:RemoveTransport(Transport)
-Transport.chief=nil
-self.commander:RemoveTransport(Transport)
-return self
-end
-function CHIEF:AddTarget(Target)
-if not self:IsTarget(Target)then
-Target.chief=self
-table.insert(self.targetqueue,Target)
-end
-return self
-end
-function CHIEF:IsTarget(Target)
-for _,_target in pairs(self.targetqueue)do
-local target=_target
-if target.uid==Target.uid or target:GetName()==Target:GetName()then
-return true
-end
-end
-return false
-end
-function CHIEF:RemoveTarget(Target)
-for i,_target in pairs(self.targetqueue)do
-local target=_target
-if target.uid==Target.uid then
-self:T(self.lid..string.format("Removing target %s from queue",Target.name))
-table.remove(self.targetqueue,i)
-break
-end
-end
-return self
-end
-function CHIEF:AddStrategicZone(OpsZone,Priority,Importance,ResourceOccupied,ResourceEmpty)
-local stratzone={}
-stratzone.opszone=OpsZone
-stratzone.prio=Priority or 50
-stratzone.importance=Importance
-stratzone.missions={}
-if OpsZone:IsStopped()then
-OpsZone:Start()
-end
-if ResourceOccupied then
-stratzone.resourceOccup=UTILS.DeepCopy(ResourceOccupied)
-else
-stratzone.resourceOccup=self:CreateResource(AUFTRAG.Type.ARTY,1,2)
-self:AddToResource(stratzone.resourceOccup,AUFTRAG.Type.CASENHANCED,1,2)
-end
-if ResourceEmpty then
-stratzone.resourceEmpty=UTILS.DeepCopy(ResourceEmpty)
-else
-local resourceEmpty,resourceInfantry=self:CreateResource(AUFTRAG.Type.ONGUARD,1,3,GROUP.Attribute.GROUND_INFANTRY)
-self:AddToResource(resourceEmpty,AUFTRAG.Type.ONGUARD,0,1,GROUP.Attribute.GROUND_TANK)
-self:AddToResource(resourceEmpty,AUFTRAG.Type.ONGUARD,0,1,GROUP.Attribute.GROUND_IFV)
-self:AddTransportToResource(resourceInfantry,0,1,{GROUP.Attribute.AIR_TRANSPORTHELO,GROUP.Attribute.GROUND_APC})
-stratzone.resourceEmpty=resourceEmpty
-end
-table.insert(self.zonequeue,stratzone)
-OpsZone:_AddChief(self)
-return stratzone
-end
-function CHIEF:SetStrategicZoneResourceEmpty(StrategicZone,Resource,NoCopy)
-if NoCopy then
-StrategicZone.resourceEmpty=Resource
-else
-StrategicZone.resourceEmpty=UTILS.DeepCopy(Resource)
-end
-return self
-end
-function CHIEF:SetStrategicZoneResourceOccupied(StrategicZone,Resource,NoCopy)
-if NoCopy then
-StrategicZone.resourceOccup=Resource
-else
-StrategicZone.resourceOccup=UTILS.DeepCopy(Resource)
-end
-return self
-end
-function CHIEF:GetStrategicZoneResourceEmpty(StrategicZone)
-return StrategicZone.resourceEmpty
-end
-function CHIEF:GetStrategicZoneResourceOccupied(StrategicZone)
-return StrategicZone.resourceOccup
-end
-function CHIEF:RemoveStrategicZone(OpsZone,Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,CHIEF.RemoveStrategicZone,self,OpsZone)
-else
-for i=#self.zonequeue,1,-1 do
-local stratzone=self.zonequeue[i]
-if OpsZone.zoneName==stratzone.opszone.zoneName then
-self:T(self.lid..string.format("Removing OPS zone \"%s\" from queue! All running missions will be cancelled",OpsZone.zoneName))
-for _,_resource in pairs(stratzone.resourceEmpty)do
-local resource=_resource
-if resource.mission and resource.mission:IsNotOver()then
-resource.mission:Cancel()
-end
-end
-for _,_resource in pairs(stratzone.resourceOccup)do
-local resource=_resource
-if resource.mission and resource.mission:IsNotOver()then
-resource.mission:Cancel()
-end
-end
-table.remove(self.zonequeue,i)
-return self
-end
-end
-end
-return self
-end
-function CHIEF:AddRearmingZone(RearmingZone)
-local supplyzone=self.commander:AddRearmingZone(RearmingZone)
-return supplyzone
-end
-function CHIEF:AddRefuellingZone(RefuellingZone)
-local supplyzone=self.commander:AddRefuellingZone(RefuellingZone)
-return supplyzone
-end
-function CHIEF:AddCapZone(Zone,Altitude,Speed,Heading,Leg)
-local zone=self.commander:AddCapZone(Zone,Altitude,Speed,Heading,Leg)
-return zone
-end
-function CHIEF:AddGciCapZone(Zone,Altitude,Speed,Heading,Leg)
-local zone=self.commander:AddGciCapZone(Zone,Altitude,Speed,Heading,Leg)
-return zone
-end
-function CHIEF:RemoveGciCapZone(Zone)
-local zone=self.commander:RemoveGciCapZone(Zone)
-return zone
-end
-function CHIEF:AddAwacsZone(Zone,Altitude,Speed,Heading,Leg)
-local zone=self.commander:AddAwacsZone(Zone,Altitude,Speed,Heading,Leg)
-return zone
-end
-function CHIEF:RemoveAwacsZone(Zone)
-local zone=self.commander:RemoveAwacsZone(Zone)
-return zone
-end
-function CHIEF:AddTankerZone(Zone,Altitude,Speed,Heading,Leg,RefuelSystem)
-local zone=self.commander:AddTankerZone(Zone,Altitude,Speed,Heading,Leg,RefuelSystem)
-return zone
-end
-function CHIEF:RemoveTankerZone(Zone)
-local zone=self.commander:RemoveTankerZone(Zone)
-return zone
-end
-function CHIEF:SetBorderZones(BorderZoneSet)
-self.borderzoneset=BorderZoneSet or SET_ZONE:New()
-return self
-end
-function CHIEF:AddBorderZone(Zone)
-self.borderzoneset:AddZone(Zone)
-return self
-end
-function CHIEF:RemoveBorderZone(Zone)
-self.borderzoneset:Remove(Zone:GetName())
-return self
-end
-function CHIEF:SetConflictZones(ZoneSet)
-self.yellowzoneset=ZoneSet or SET_ZONE:New()
-return self
-end
-function CHIEF:AddConflictZone(Zone)
-self.yellowzoneset:AddZone(Zone)
-return self
-end
-function CHIEF:RemoveConflictZone(Zone)
-self.yellowzoneset:Remove(Zone:GetName())
-return self
-end
-function CHIEF:SetAttackZones(ZoneSet)
-self.engagezoneset=ZoneSet or SET_ZONE:New()
-return self
-end
-function CHIEF:AddAttackZone(Zone)
-self.engagezoneset:AddZone(Zone)
-return self
-end
-function CHIEF:RemoveAttackZone(Zone)
-self.engagezoneset:Remove(Zone:GetName())
-return self
-end
-function CHIEF:AllowGroundTransport()
-env.warning("WARNING: CHIEF:AllowGroundTransport() is deprecated and will be removed in the future!")
-self.TransportCategories={Group.Category.GROUND,Group.Category.HELICOPTER}
-return self
-end
-function CHIEF:ForbidGroundTransport()
-env.warning("WARNING: CHIEF:ForbidGroundTransport() is deprecated and will be removed in the future!")
-self.TransportCategories={Group.Category.HELICOPTER}
-return self
-end
-function CHIEF:IsPassive()
-return self.strategy==CHIEF.Strategy.PASSIVE
-end
-function CHIEF:IsDefensive()
-return self.strategy==CHIEF.Strategy.DEFENSIVE
-end
-function CHIEF:IsOffensive()
-return self.strategy==CHIEF.Strategy.OFFENSIVE
-end
-function CHIEF:IsAgressive()
-return self.strategy==CHIEF.Strategy.AGGRESSIVE
-end
-function CHIEF:IsTotalWar()
-return self.strategy==CHIEF.Strategy.TOTALWAR
-end
-function CHIEF:onafterStart(From,Event,To)
-local text=string.format("Starting Chief of Staff")
-self:I(self.lid..text)
-self:GetParent(self).onafterStart(self,From,Event,To)
-if self.commander then
-if self.commander:GetState()=="NotReadyYet"then
-self.commander:Start()
-end
-end
-end
-function CHIEF:onafterStatus(From,Event,To)
-self:GetParent(self).onafterStatus(self,From,Event,To)
-local fsmstate=self:GetState()
-for _,_contact in pairs(self.ContactsLost)do
-local contact=_contact
-if contact.mission and contact.mission:IsNotOver()then
-local text=string.format("Lost contact to target %s! %s mission %s will be cancelled.",contact.groupname,contact.mission.type:upper(),contact.mission.name)
-MESSAGE:New(text,120,"CHIEF"):ToAll()
-self:T(self.lid..text)
-contact.mission:Cancel()
-end
-if contact.target then
-self:RemoveTarget(contact.target)
-end
-end
-self.Nborder=0;self.Nconflict=0;self.Nattack=0
-for _,_contact in pairs(self.Contacts)do
-local contact=_contact
-local group=contact.group
-local inred=self:CheckGroupInBorder(group)
-if inred then
-self.Nborder=self.Nborder+1
-end
-local inyellow=self:CheckGroupInConflict(group)
-if inyellow then
-self.Nconflict=self.Nconflict+1
-end
-local inattack=self:CheckGroupInAttack(group)
-if inattack then
-self.Nattack=self.Nattack+1
-end
-if not contact.target then
-local Target=TARGET:New(contact.group)
-contact.target=Target
-Target.contact=contact
-self:AddTarget(Target)
-end
-end
-if self.Nborder>0 then
-self:SetDefcon(CHIEF.DEFCON.RED)
-elseif self.Nconflict>0 then
-self:SetDefcon(CHIEF.DEFCON.YELLOW)
-else
-self:SetDefcon(CHIEF.DEFCON.GREEN)
-end
-self:CheckTargetQueue()
-for _,_target in pairs(self.targetqueue)do
-local target=_target
-if target and target:IsAlive()and target.chief and target.mission and target.mission:IsNotOver()then
-local inborder=self:CheckTargetInZones(target,self.borderzoneset)
-local inyellow=self:CheckTargetInZones(target,self.yellowzoneset)
-local inattack=self:CheckTargetInZones(target,self.engagezoneset)
-if self.strategy==CHIEF.Strategy.PASSIVE then
-self:T(self.lid..string.format("Cancelling mission for target %s as strategy is PASSIVE",target:GetName()))
-target.mission:Cancel()
-elseif self.strategy==CHIEF.Strategy.DEFENSIVE then
-if not inborder then
-self:T(self.lid..string.format("Cancelling mission for target %s as strategy is DEFENSIVE and not inside border",target:GetName()))
-target.mission:Cancel()
-end
-elseif self.strategy==CHIEF.Strategy.OFFENSIVE then
-if not(inborder or inyellow)then
-self:T(self.lid..string.format("Cancelling mission for target %s as strategy is OFFENSIVE and not inside border or conflict",target:GetName()))
-target.mission:Cancel()
-end
-elseif self.strategy==CHIEF.Strategy.AGGRESSIVE then
-if not(inborder or inyellow or inattack)then
-self:T(self.lid..string.format("Cancelling mission for target %s as strategy is AGGRESSIVE and not inside border, conflict or attack",target:GetName()))
-target.mission:Cancel()
-end
-elseif self.strategy==CHIEF.Strategy.TOTALWAR then
-end
-end
-end
-self:CheckOpsZoneQueue()
-self:_TacticalOverview()
-if self.verbose>=1 then
-local Nassets=self.commander:CountAssets()
-local Ncontacts=#self.Contacts
-local Nmissions=#self.commander.missionqueue
-local Ntargets=#self.targetqueue
-local text=string.format("Defcon=%s Strategy=%s: Assets=%d, Contacts=%d [Border=%d, Conflict=%d, Attack=%d], Targets=%d, Missions=%d",
-self.Defcon,self.strategy,Nassets,Ncontacts,self.Nborder,self.Nconflict,self.Nattack,Ntargets,Nmissions)
-self:I(self.lid..text)
-end
-if self.verbose>=2 and#self.Contacts>0 then
-local text="Contacts:"
-for i,_contact in pairs(self.Contacts)do
-local contact=_contact
-local mtext="N/A"
-if contact.mission then
-mtext=string.format("\"%s\" [%s] %s",contact.mission:GetName(),contact.mission:GetType(),contact.mission.status:upper())
-end
-text=text..string.format("\n[%d] %s Type=%s (%s): Threat=%d Mission=%s",i,contact.groupname,contact.categoryname,contact.typename,contact.threatlevel,mtext)
-end
-self:I(self.lid..text)
-end
-if self.verbose>=3 and#self.targetqueue>0 then
-local text="Targets:"
-for i,_target in pairs(self.targetqueue)do
-local target=_target
-local mtext="N/A"
-if target.mission then
-mtext=string.format("\"%s\" [%s] %s",target.mission:GetName(),target.mission:GetType(),target.mission.status:upper())
-end
-text=text..string.format("\n[%d] %s: Category=%s, prio=%d, importance=%d, alive=%s [%.1f/%.1f], Mission=%s",
-i,target:GetName(),target.category,target.prio,target.importance or-1,tostring(target:IsAlive()),target:GetLife(),target:GetLife0(),mtext)
-end
-self:I(self.lid..text)
-end
-if self.verbose>=4 and#self.commander.missionqueue>0 then
-local text="Mission queue:"
-for i,_mission in pairs(self.commander.missionqueue)do
-local mission=_mission
-local target=mission:GetTargetName()or"unknown"
-text=text..string.format("\n[%d] %s (%s): status=%s, target=%s",i,mission.name,mission.type,mission.status,target)
-end
-self:I(self.lid..text)
-end
-if self.verbose>=4 and#self.zonequeue>0 then
-local text="Zone queue:"
-for i,_stratzone in pairs(self.zonequeue)do
-local stratzone=_stratzone
-local opszone=stratzone.opszone
-local owner=UTILS.GetCoalitionName(opszone.ownerCurrent)
-local prevowner=UTILS.GetCoalitionName(opszone.ownerPrevious)
-text=text..string.format("\n[%d] %s [%s]: owner=%s [%s] (prio=%d, importance=%s): Blue=%d, Red=%d, Neutral=%d",
-i,opszone.zone:GetName(),opszone:GetState(),owner,prevowner,stratzone.prio,tostring(stratzone.importance),opszone.Nblu,opszone.Nred,opszone.Nnut)
-end
-self:I(self.lid..text)
-end
-if self.verbose>=5 then
-local text="Assets:"
-for _,missiontype in pairs(AUFTRAG.Type)do
-local N=self.commander:CountAssets(nil,missiontype)
-if N>0 then
-text=text..string.format("\n- %s: %d",missiontype,N)
-end
-end
-self:I(self.lid..text)
-local text="Assets:"
-for _,attribute in pairs(WAREHOUSE.Attribute)do
-local N=self.commander:CountAssets(nil,nil,attribute)
-if N>0 or self.verbose>=10 then
-text=text..string.format("\n- %s: %d",attribute,N)
-end
-end
-self:T(self.lid..text)
-end
-end
-function CHIEF:onafterMissionAssign(From,Event,To,Mission,Legions)
-if self.commander then
-self:T(self.lid..string.format("Assigning mission %s (%s) to COMMANDER",Mission.name,Mission.type))
-Mission.chief=self
-Mission.statusChief=AUFTRAG.Status.QUEUED
-self.commander:MissionAssign(Mission,Legions)
-else
-self:E(self.lid..string.format("Mission cannot be assigned as no COMMANDER is defined!"))
-end
-end
-function CHIEF:onafterMissionCancel(From,Event,To,Mission)
-self:T(self.lid..string.format("Cancelling mission %s (%s) in status %s",Mission.name,Mission.type,Mission.status))
-Mission.statusChief=AUFTRAG.Status.CANCELLED
-if Mission:IsPlanned()then
-self:RemoveMission(Mission)
-else
-if Mission.commander then
-Mission.commander:MissionCancel(Mission)
-end
-end
-end
-function CHIEF:onafterTransportCancel(From,Event,To,Transport)
-self:T(self.lid..string.format("Cancelling transport UID=%d in status %s",Transport.uid,Transport:GetState()))
-if Transport:IsPlanned()then
-self:RemoveTransport(Transport)
-else
-if Transport.commander then
-Transport.commander:TransportCancel(Transport)
-end
-end
-end
-function CHIEF:onafterDefconChange(From,Event,To,Defcon)
-self:T(self.lid..string.format("Changing Defcon from %s --> %s",self.Defcon,Defcon))
-end
-function CHIEF:onafterStrategyChange(From,Event,To,Strategy)
-self:T(self.lid..string.format("Changing Strategy from %s --> %s",self.strategy,Strategy))
-end
-function CHIEF:onafterOpsOnMission(From,Event,To,OpsGroup,Mission)
-self:T(self.lid..string.format("Group %s on mission %s [%s]",OpsGroup:GetName(),Mission:GetName(),Mission:GetType()))
-end
-function CHIEF:onafterZoneCaptured(From,Event,To,OpsZone)
-self:T(self.lid..string.format("Zone %s captured!",OpsZone:GetName()))
-end
-function CHIEF:onafterZoneLost(From,Event,To,OpsZone)
-self:T(self.lid..string.format("Zone %s lost!",OpsZone:GetName()))
-end
-function CHIEF:onafterZoneEmpty(From,Event,To,OpsZone)
-self:T(self.lid..string.format("Zone %s empty!",OpsZone:GetName()))
-end
-function CHIEF:onafterZoneAttacked(From,Event,To,OpsZone)
-self:T(self.lid..string.format("Zone %s attacked!",OpsZone:GetName()))
-end
-function CHIEF:_TacticalOverview()
-if self.tacview then
-local NassetsTotal=self.commander:CountAssets()
-local NassetsStock=self.commander:CountAssets(true)
-local Ncontacts=#self.Contacts
-local NmissionsTotal=#self.commander.missionqueue
-local NmissionsRunni=self.commander:CountMissions(AUFTRAG.Type,true)
-local Ntargets=#self.targetqueue
-local Nzones=#self.zonequeue
-local Nagents=self.detectionset:CountAlive()
-local text=string.format("Tactical Overview\n")
-text=text..string.format("=================\n")
-text=text..string.format("Strategy: %s - Defcon: %s - Agents=%s\n",self.strategy,self.Defcon,Nagents)
-text=text..string.format("Contacts: %d [Border=%d, Conflict=%d, Attack=%d]\n",Ncontacts,self.Nborder,self.Nconflict,self.Nattack)
-text=text..string.format("Assets: %d [Active=%d, Stock=%d]\n",NassetsTotal,NassetsTotal-NassetsStock,NassetsStock)
-text=text..string.format("Targets: %d\n",Ntargets)
-text=text..string.format("Missions: %d [Running=%d/%d - Success=%d, Failure=%d]\n",NmissionsTotal,NmissionsRunni,self:GetMissionLimit("Total"),self.Nsuccess,self.Nfailure)
-for _,mtype in pairs(AUFTRAG.Type)do
-local n=self.commander:CountMissions(mtype)
-if n>0 then
-local N=self.commander:CountMissions(mtype,true)
-local limit=self:GetMissionLimit(mtype)
-text=text..string.format(" - %s: %d [Running=%d/%d]\n",mtype,n,N,limit)
-end
-end
-text=text..string.format("Strategic Zones: %d\n",Nzones)
-for _,_stratzone in pairs(self.zonequeue)do
-local stratzone=_stratzone
-local owner=stratzone.opszone:GetOwnerName()
-text=text..string.format(" - %s: %s - %s [I=%d, P=%d]\n",stratzone.opszone:GetName(),owner,stratzone.opszone:GetState(),stratzone.importance or 0,stratzone.prio or 0)
-end
-local Ntransports=#self.commander.transportqueue
-if Ntransports>0 then
-text=text..string.format("Transports: %d\n",Ntransports)
-for _,_transport in pairs(self.commander.transportqueue)do
-local transport=_transport
-text=text..string.format(" - %s",transport:GetState())
-end
-end
-MESSAGE:New(text,60,nil,true):ToCoalition(self.coalition)
-if self.verbose>=4 then
-self:I(self.lid..text)
-end
-end
-end
-function CHIEF:CheckTargetQueue()
-local Ntargets=#self.targetqueue
-if Ntargets==0 then
-return nil
-end
-local NoLimit=self:_CheckMissionLimit("Total")
-if NoLimit==false then
-return nil
-end
-local function _sort(a,b)
-local taskA=a
-local taskB=b
-return(taskA.priotaskB.threatlevel0)
-end
-table.sort(self.targetqueue,_sort)
-local vip=math.huge
-for _,_target in pairs(self.targetqueue)do
-local target=_target
-if target:IsAlive()and target.importance and target.importance=self.threatLevelMin and threatlevel<=self.threatLevelMax
-if target.category==TARGET.Category.AIRBASE or target.category==TARGET.Category.ZONE or target.Category==TARGET.Category.COORDINATE then
-isThreat=true
-end
-local text=string.format("Target %s: Alive=%s, Threat=%s, Important=%s",target:GetName(),tostring(isAlive),tostring(isThreat),tostring(isImportant))
-if target.mission then
-text=text..string.format(", Mission \"%s\" (%s) [%s]",target.mission:GetName(),target.mission:GetState(),target.mission:GetType())
-if target.mission:IsOver()then
-text=text..string.format(" - DONE ==> removing mission")
-target.mission=nil
-end
-else
-text=text..string.format(", NO mission yet")
-end
-self:T2(self.lid..text)
-if isAlive and isThreat and isImportant and not target.mission then
-local valid=false
-if self.strategy==CHIEF.Strategy.PASSIVE then
-valid=false
-elseif self.strategy==CHIEF.Strategy.DEFENSIVE then
-if self:CheckTargetInZones(target,self.borderzoneset)then
-valid=true
-end
-elseif self.strategy==CHIEF.Strategy.OFFENSIVE then
-if self:CheckTargetInZones(target,self.borderzoneset)or self:CheckTargetInZones(target,self.yellowzoneset)then
-valid=true
-end
-elseif self.strategy==CHIEF.Strategy.AGGRESSIVE then
-if self:CheckTargetInZones(target,self.borderzoneset)or self:CheckTargetInZones(target,self.yellowzoneset)or self:CheckTargetInZones(target,self.engagezoneset)then
-valid=true
-end
-elseif self.strategy==CHIEF.Strategy.TOTALWAR then
-valid=true
-end
-if valid then
-self:T(self.lid..string.format("Got valid target %s: category=%s, threatlevel=%d",target:GetName(),target.category,threatlevel))
-local MissionPerformances=self:_GetMissionPerformanceFromTarget(target)
-local mission=nil
-local Legions=nil
-if#MissionPerformances>0 then
-for _,_mp in pairs(MissionPerformances)do
-local mp=_mp
-local notlimited=self:_CheckMissionLimit(mp.MissionType)
-if notlimited then
-local NassetsMin,NassetsMax=self:_GetAssetsForTarget(target,mp.MissionType)
-self:T2(self.lid..string.format("Recruiting assets for mission type %s [performance=%d] of target %s",mp.MissionType,mp.Performance,target:GetName()))
-local recruited,assets,legions=self.commander:RecruitAssetsForTarget(target,mp.MissionType,NassetsMin,NassetsMax)
-if recruited then
-self:T(self.lid..string.format("Recruited %d assets for mission type %s [performance=%d] of target %s",#assets,mp.MissionType,mp.Performance,target:GetName()))
-mission=AUFTRAG:NewFromTarget(target,mp.MissionType)
-if mission then
-mission:_AddAssets(assets)
-Legions=legions
-break
-end
-else
-self:T(self.lid..string.format("Could NOT recruit assets for mission type %s [performance=%d] of target %s",mp.MissionType,mp.Performance,target:GetName()))
-end
-end
-end
-end
-if mission and Legions then
-target.mission=mission
-mission.prio=target.prio
-mission.importance=target.importance
-self:MissionAssign(mission,Legions)
-return
-end
-end
-end
-end
-end
-function CHIEF:_CheckMissionLimit(MissionType)
-return self.commander:_CheckMissionLimit(MissionType)
-end
-function CHIEF:GetMissionLimit(MissionType)
-local l=self.commander.limitMission[MissionType]
-if not l then
-l=999
-end
-return l
-end
-function CHIEF:CheckOpsZoneQueue()
-local Nzones=#self.zonequeue
-if Nzones==0 then
-return nil
-end
-for i=Nzones,1,-1 do
-local stratzone=self.zonequeue[i]
-if stratzone.opszone:IsStopped()then
-self:RemoveStrategicZone(stratzone.opszone)
-end
-end
-for _,_startzone in pairs(self.zonequeue)do
-local stratzone=_startzone
-local ownercoalition=stratzone.opszone:GetOwner()
-if ownercoalition==self.coalition or stratzone.opszone:IsEmpty()then
-for _,_resource in pairs(stratzone.resourceOccup or{})do
-local resource=_resource
-if resource.mission then
-resource.mission:Cancel()
-end
-end
-end
-end
-if self:IsPassive()then
-return
-end
-local NoLimit=self:_CheckMissionLimit("Total")
-if NoLimit==false then
-return nil
-end
-local function _sort(a,b)
-local taskA=a
-local taskB=b
-return(taskA.prio Recruiting for mission type %s: Nmin=%d, Nmax=%d",zoneName,missionType,resource.Nmin,resource.Nmax))
-local recruited=self:RecruitAssetsForZone(stratzone,resource)
-if recruited then
-self:T(self.lid..string.format("Successfully recruited assets for empty zone \"%s\" [mission type=%s]",zoneName,missionType))
-else
-self:T(self.lid..string.format("Could not recruited assets for empty zone \"%s\" [mission type=%s]",zoneName,missionType))
-end
-end
-end
-else
-for _,_resource in pairs(stratzone.resourceOccup or{})do
-local resource=_resource
-local missionType=resource.MissionType
-if(not resource.mission)or resource.mission:IsOver()then
-self:T2(self.lid..string.format("Zone %s is NOT empty ==> Recruiting for mission type %s: Nmin=%d, Nmax=%d",zoneName,missionType,resource.Nmin,resource.Nmax))
-local recruited=self:RecruitAssetsForZone(stratzone,resource)
-if recruited then
-self:T(self.lid..string.format("Successfully recruited assets for occupied zone %s, mission type=%s",zoneName,missionType))
-else
-self:T(self.lid..string.format("Could not recruited assets for occupied zone %s, mission type=%s",zoneName,missionType))
-end
-end
-end
-end
-end
-end
-end
-function CHIEF:CheckGroupInBorder(group)
-local inside=self:CheckGroupInZones(group,self.borderzoneset)
-return inside
-end
-function CHIEF:CheckGroupInConflict(group)
-local inside=self:CheckGroupInZones(group,self.yellowzoneset)
-return inside
-end
-function CHIEF:CheckGroupInAttack(group)
-local inside=self:CheckGroupInZones(group,self.engagezoneset)
-return inside
-end
-function CHIEF:CheckGroupInZones(group,zoneset)
-for _,_zone in pairs(zoneset.Set or{})do
-local zone=_zone
-if group:IsInZone(zone)then
-return true
-end
-end
-return false
-end
-function CHIEF:CheckTargetInZones(target,zoneset)
-for _,_zone in pairs(zoneset.Set or{})do
-local zone=_zone
-if zone:IsCoordinateInZone(target:GetCoordinate())then
-return true
-end
-end
-return false
-end
-function CHIEF:_CreateMissionPerformance(MissionType,Performance)
-local mp={}
-mp.MissionType=MissionType
-mp.Performance=Performance
-return mp
-end
-function CHIEF:_GetMissionPerformanceFromTarget(Target)
-local group=nil
-local airbase=nil
-local scenery=nil
-local static=nil
-local coordinate=nil
-local target=Target:GetObject()
-if target:IsInstanceOf("GROUP")then
-group=target
-elseif target:IsInstanceOf("UNIT")then
-group=target:GetGroup()
-elseif target:IsInstanceOf("AIRBASE")then
-airbase=target
-elseif target:IsInstanceOf("STATIC")then
-static=target
-elseif target:IsInstanceOf("SCENERY")then
-scenery=target
-end
-local TargetCategory=Target:GetCategory()
-local missionperf={}
-if group then
-local category=group:GetCategory()
-local attribute=group:GetAttribute()
-if category==Group.Category.AIRPLANE or category==Group.Category.HELICOPTER then
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.INTERCEPT,100))
-elseif category==Group.Category.GROUND or category==Group.Category.TRAIN then
-if attribute==GROUP.Attribute.GROUND_SAM then
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.SEAD,100))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.GROUNDATTACK,50))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.ARTY,30))
-elseif attribute==GROUP.Attribute.GROUND_EWR then
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.BAI,100))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.GROUNDATTACK,50))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.ARTY,30))
-elseif attribute==GROUP.Attribute.GROUND_AAA then
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.BAI,100))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.GROUNDATTACK,50))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.ARMORATTACK,40))
-elseif attribute==GROUP.Attribute.GROUND_ARTILLERY then
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.BAI,100))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.GROUNDATTACK,75))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.ARMORATTACK,70))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.BOMBING,70))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.ARTY,30))
-elseif attribute==GROUP.Attribute.GROUND_INFANTRY then
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.BAI,100))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.GROUNDATTACK,50))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.ARMORATTACK,40))
-elseif attribute==GROUP.Attribute.GROUND_TANK then
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.BAI,100))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.CAS,90))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.CASENHANCED,90))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.GROUNDATTACK,50))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.ARMORATTACK,40))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.ARTY,30))
-else
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.BAI,100))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.GROUNDATTACK,50))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.ARTY,30))
-end
-elseif category==Group.Category.SHIP then
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.ANTISHIP,100))
-else
-self:E(self.lid.."ERROR: Unknown Group category!")
-end
-elseif airbase then
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.BOMBRUNWAY,100))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.ARTY,30))
-elseif static then
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.BAI,100))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.BOMBING,70))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.BOMBCARPET,50))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.ARTY,30))
-elseif scenery then
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.STRIKE,100))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.BOMBING,70))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.BOMBCARPET,50))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.ARTY,30))
-elseif coordinate then
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.BOMBING,100))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.BOMBCARPET,50))
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.ARTY,30))
-end
-return missionperf
-end
-function CHIEF:_GetMissionTypeForGroupAttribute(Attribute)
-local missionperf={}
-if Attribute==GROUP.Attribute.AIR_ATTACKHELO then
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.INTERCEPT),100)
-elseif Attribute==GROUP.Attribute.GROUND_AAA then
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.BAI),100)
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.BOMBING),80)
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.BOMBCARPET),70)
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.ARTY),30)
-elseif Attribute==GROUP.Attribute.GROUND_SAM then
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.SEAD),100)
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.BAI),90)
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.ARTY),50)
-elseif Attribute==GROUP.Attribute.GROUND_EWR then
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.SEAD),100)
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.BAI),100)
-table.insert(missionperf,self:_CreateMissionPerformance(AUFTRAG.Type.ARTY),50)
-end
-return missionperf
-end
-function CHIEF:RecruitAssetsForZone(StratZone,Resource)
-local Cohorts=self.commander:_GetCohorts()
-local MissionType=Resource.MissionType
-local NassetsMin=Resource.Nmin
-local NassetsMax=Resource.Nmax
-local Categories=Resource.Categories
-local Attributes=Resource.Attributes
-local Properties=Resource.Properties
-local TargetVec2=StratZone.opszone.zone:GetVec2()
-local RangeMax=nil
-if MissionType==AUFTRAG.Type.PATROLZONE or MissionType==AUFTRAG.Type.ONGUARD then
-RangeMax=UTILS.NMToMeters(250)
-end
-if MissionType==AUFTRAG.Type.ARMOREDGUARD then
-RangeMax=UTILS.NMToMeters(50)
-end
-self:T(self.lid.."Missiontype="..MissionType)
-self:T({categories=Categories})
-self:T({attributes=Attributes})
-self:T({properties=Properties})
-local recruited,assets,legions=LEGION.RecruitCohortAssets(Cohorts,MissionType,nil,NassetsMin,NassetsMax,TargetVec2,nil,RangeMax,nil,nil,nil,nil,Categories,Attributes,Properties)
-if recruited then
-local mission=nil
-self:T2(self.lid..string.format("Recruited %d assets for %s mission STRATEGIC zone %s",#assets,MissionType,tostring(StratZone.opszone.zoneName)))
-local TargetZone=StratZone.opszone.zone
-local TargetCoord=TargetZone:GetCoordinate()
-local transport=nil
-if Resource.carrierNmin and Resource.carrierNmax and Resource.carrierNmax>0 then
-local cargoassets=CHIEF._FilterAssets(assets,Resource.Categories,Resource.Attributes,Resource.Properties)
-if#cargoassets>0 then
-recruited,transport=LEGION.AssignAssetsForTransport(self.commander,self.commander.legions,cargoassets,
-Resource.carrierNmin,Resource.carrierNmax,TargetZone,nil,Resource.carrierCategories,Resource.carrierAttributes,Resource.carrierProperties)
-end
-end
-if not recruited then
-self:T(self.lid..string.format("Could not allocate assets or transport of OPSZONE!"))
-LEGION.UnRecruitAssets(assets)
-return false
-end
-self:T2(self.lid..string.format("Recruited %d assets for mission %s",#assets,MissionType))
-if MissionType==AUFTRAG.Type.PATROLZONE or MissionType==AUFTRAG.Type.ONGUARD then
-if MissionType==AUFTRAG.Type.PATROLZONE then
-mission=AUFTRAG:NewPATROLZONE(TargetZone)
-elseif MissionType==AUFTRAG.Type.ONGUARD then
-mission=AUFTRAG:NewONGUARD(TargetZone:GetRandomCoordinate(nil,nil,{land.SurfaceType.LAND}))
-end
-mission:SetEngageDetected(25,{"Ground Units","Light armed ships","Helicopters"})
-elseif MissionType==AUFTRAG.Type.CAPTUREZONE then
-mission=AUFTRAG:NewCAPTUREZONE(StratZone.opszone,self.coalition)
-elseif MissionType==AUFTRAG.Type.CASENHANCED then
-local height=UTILS.MetersToFeet(TargetCoord:GetLandHeight())+2500
-local Speed=200
-if assets[1]then
-if assets[1].speedmax then
-Speed=UTILS.KmphToKnots(assets[1].speedmax*0.7)or 200
-end
-end
-mission=AUFTRAG:NewCASENHANCED(TargetZone,height,Speed)
-elseif MissionType==AUFTRAG.Type.CAS then
-local height=UTILS.MetersToFeet(TargetCoord:GetLandHeight())+2500
-local Speed=200
-if assets[1]then
-if assets[1].speedmax then
-Speed=UTILS.KmphToKnots(assets[1].speedmax*0.7)or 200
-end
-end
-TargetZone=StratZone.opszone.zoneCircular
-local Leg=TargetZone:GetRadius()<=10000 and 5 or UTILS.MetersToNM(TargetZone:GetRadius())
-mission=AUFTRAG:NewCAS(TargetZone,height,Speed,TargetCoord,math.random(0,359),Leg)
-elseif MissionType==AUFTRAG.Type.ARTY then
-local Radius=TargetZone:GetRadius()
-mission=AUFTRAG:NewARTY(TargetCoord,120,Radius)
-elseif MissionType==AUFTRAG.Type.ARMOREDGUARD then
-mission=AUFTRAG:NewARMOREDGUARD(TargetCoord)
-elseif MissionType==AUFTRAG.Type.BOMBCARPET then
-mission=AUFTRAG:NewBOMBCARPET(TargetCoord,nil,1000)
-elseif MissionType==AUFTRAG.Type.BOMBING then
-local coord=TargetZone:GetRandomCoordinate()
-mission=AUFTRAG:NewBOMBING(TargetCoord)
-elseif MissionType==AUFTRAG.Type.RECON then
-mission=AUFTRAG:NewRECON(TargetZone,nil,5000)
-elseif MissionType==AUFTRAG.Type.BARRAGE then
-mission=AUFTRAG:NewBARRAGE(TargetZone)
-elseif MissionType==AUFTRAG.Type.AMMOSUPPLY then
-mission=AUFTRAG:NewAMMOSUPPLY(TargetZone)
-end
-if mission then
-mission:_AddAssets(assets)
-self:MissionAssign(mission,legions)
-StratZone.opszone:_AddMission(self.coalition,MissionType,mission)
-Resource.mission=mission
-if transport then
-mission.opstransport=transport
-transport.opszone=StratZone.opszone
-transport.chief=self
-transport.commander=self.commander
-end
-return true
-else
-self:E(self.lid..string.format("ERROR: Mission type not supported for OPSZONE! Unrecruiting assets..."))
-LEGION.UnRecruitAssets(assets)
-return false
-end
-end
-self:T2(self.lid..string.format("Could NOT recruit assets for %s mission of STRATEGIC zone %s",MissionType,tostring(StratZone.opszone.zoneName)))
-return false
-end
-function CHIEF._FilterAssets(Assets,Categories,Attributes,Properties)
-local filtered={}
-for _,_asset in pairs(Assets)do
-local asset=_asset
-local hasCat=CHIEF._CheckAssetCategories(asset,Categories)
-local hasAtt=CHIEF._CheckAssetAttributes(asset,Attributes)
-local hasPro=CHIEF._CheckAssetProperties(asset,Properties)
-if hasAtt and hasCat and hasPro then
-table.insert(filtered,asset)
-end
-end
-return filtered
-end
-function CHIEF._CheckAssetAttributes(Asset,Attributes)
-if not Attributes then
-return true
-end
-for _,attribute in pairs(UTILS.EnsureTable(Attributes))do
-if attribute==Asset.attribute then
-return true
-end
-end
-return false
-end
-function CHIEF._CheckAssetCategories(Asset,Categories)
-if not Categories then
-return true
-end
-for _,attribute in pairs(UTILS.EnsureTable(Categories))do
-if attribute==Asset.category then
-return true
-end
-end
-return false
-end
-function CHIEF._CheckAssetProperties(Asset,Properties)
-if not Properties then
-return true
-end
-for _,attribute in pairs(UTILS.EnsureTable(Properties))do
-if attribute==Asset.DCSdesc then
-return true
-end
-end
-return false
-end
-COHORT={
-ClassName="COHORT",
-verbose=0,
-lid=nil,
-name=nil,
-templatename=nil,
-assets={},
-missiontypes={},
-repairtime=0,
-maintenancetime=0,
-livery=nil,
-skill=nil,
-legion=nil,
-Ngroups=0,
-engageRange=nil,
-tacanChannel={},
-weightAsset=99999,
-cargobayLimit=0,
-descriptors={},
-properties={},
-operations={},
-}
-COHORT.version="0.3.5"
-function COHORT:New(TemplateGroupName,Ngroups,CohortName)
-local self=BASE:Inherit(self,FSM:New())
-self.templatename=TemplateGroupName
-self.name=tostring(CohortName or TemplateGroupName)
-self.lid=string.format("COHORT %s | ",self.name)
-self.templategroup=GROUP:FindByName(self.templatename)
-if not self.templategroup then
-self:E(self.lid..string.format("ERROR: Template group %s does not exist!",tostring(self.templatename)))
-return nil
-end
-self.attribute=self.templategroup:GetAttribute()
-self.category=self.templategroup:GetCategory()
-self.aircrafttype=self.templategroup:GetTypeName()
-self.descriptors=self.templategroup:GetUnit(1):GetDesc()
-self.properties=self.descriptors.attributes
-self.Ngroups=Ngroups or 3
-self:SetSkill(AI.Skill.GOOD)
-if self.category==Group.Category.AIRPLANE then
-self:SetMissionRange(200)
-elseif self.category==Group.Category.HELICOPTER then
-self:SetMissionRange(150)
-elseif self.category==Group.Category.GROUND then
-self:SetMissionRange(75)
-elseif self.category==Group.Category.SHIP then
-self:SetMissionRange(100)
-elseif self.category==Group.Category.TRAIN then
-self:SetMissionRange(100)
-else
-self:SetMissionRange(150)
-end
-local units=self.templategroup:GetUnits()
-self.weightAsset=0
-for i,_unit in pairs(units)do
-local unit=_unit
-local desc=unit:GetDesc()
-local mass=666
-if desc then
-mass=desc.massMax or desc.massEmpty
-end
-self.weightAsset=self.weightAsset+(mass or 666)
-if i==1 then
-self.cargobayLimit=unit:GetCargoBayFreeWeight()
-end
-end
-self:SetStartState("Stopped")
-self:AddTransition("Stopped","Start","OnDuty")
-self:AddTransition("*","Status","*")
-self:AddTransition("OnDuty","Pause","Paused")
-self:AddTransition("Paused","Unpause","OnDuty")
-self:AddTransition("OnDuty","Relocate","Relocating")
-self:AddTransition("Relocating","Relocated","OnDuty")
-self:AddTransition("*","Stop","Stopped")
-return self
-end
-function COHORT:SetLivery(LiveryName)
-self.livery=LiveryName
-return self
-end
-function COHORT:SetSkill(Skill)
-self.skill=Skill
-return self
-end
-function COHORT:SetVerbosity(VerbosityLevel)
-self.verbose=VerbosityLevel or 0
-return self
-end
-function COHORT:SetTurnoverTime(MaintenanceTime,RepairTime)
-self.maintenancetime=MaintenanceTime and MaintenanceTime*60 or 0
-self.repairtime=RepairTime and RepairTime*60 or 0
-return self
-end
-function COHORT:SetRadio(Frequency,Modulation)
-self.radioFreq=Frequency or 251
-self.radioModu=Modulation or radio.modulation.AM
-return self
-end
-function COHORT:SetGrouping(nunits)
-self.ngrouping=nunits or 2
-return self
-end
-function COHORT:AddMissionCapability(MissionTypes,Performance)
-if MissionTypes and type(MissionTypes)~="table"then
-MissionTypes={MissionTypes}
-end
-self.missiontypes=self.missiontypes or{}
-for _,missiontype in pairs(MissionTypes)do
-local Capability=self:GetMissionCapability(missiontype)
-if Capability then
-self:E(self.lid.."WARNING: Mission capability already present! No need to add it twice. Will update the performance though!")
-Capability.Performance=Performance or 50
-else
-local capability={}
-capability.MissionType=missiontype
-capability.Performance=Performance or 50
-table.insert(self.missiontypes,capability)
-self:T(self.lid..string.format("Adding mission capability %s, performance=%d",tostring(capability.MissionType),capability.Performance))
-end
-end
-self:T2(self.missiontypes)
-return self
-end
-function COHORT:GetMissionCapability(MissionType)
-for _,_capability in pairs(self.missiontypes)do
-local capability=_capability
-if capability.MissionType==MissionType then
-return capability
-end
-end
-return nil
-end
-function COHORT:HasProperty(Property)
-for _,property in pairs(self.properties)do
-if Property==property then
-return true
-end
-end
-return false
-end
-function COHORT:GetMissionTypes()
-local missiontypes={}
-for _,Capability in pairs(self.missiontypes)do
-local capability=Capability
-table.insert(missiontypes,capability.MissionType)
-end
-return missiontypes
-end
-function COHORT:GetMissionCapabilities()
-return self.missiontypes
-end
-function COHORT:GetMissionPeformance(MissionType)
-for _,Capability in pairs(self.missiontypes)do
-local capability=Capability
-if capability.MissionType==MissionType then
-return capability.Performance
-end
-end
-return-1
-end
-function COHORT:SetMissionRange(Range)
-self.engageRange=UTILS.NMToMeters(Range or 150)
-return self
-end
-function COHORT:SetCallsign(Callsign,Index)
-self.callsignName=Callsign
-self.callsignIndex=Index
-return self
-end
-function COHORT:SetAttribute(Attribute)
-self.attribute=Attribute
-return self
-end
-function COHORT:GetAttribute()
-return self.attribute
-end
-function COHORT:GetCategory()
-return self.category
-end
-function COHORT:GetProperties()
-return self.properties
-end
-function COHORT:SetModex(Modex,Prefix,Suffix)
-self.modex=Modex
-self.modexPrefix=Prefix
-self.modexSuffix=Suffix
-return self
-end
-function COHORT:SetLegion(Legion)
-self.legion=Legion
-return self
-end
-function COHORT:AddAsset(Asset)
-self:T(self.lid..string.format("Adding asset %s of type %s",Asset.spawngroupname,Asset.unittype))
-Asset.squadname=self.name
-Asset.legion=self.legion
-Asset.cohort=self
-table.insert(self.assets,Asset)
-return self
-end
-function COHORT:DelAsset(Asset)
-for i,_asset in pairs(self.assets)do
-local asset=_asset
-if Asset.uid==asset.uid then
-self:T2(self.lid..string.format("Removing asset %s",asset.spawngroupname))
-table.remove(self.assets,i)
-break
-end
-end
-return self
-end
-function COHORT:DelGroup(GroupName)
-for i,_asset in pairs(self.assets)do
-local asset=_asset
-if GroupName==asset.spawngroupname then
-self:T2(self.lid..string.format("Removing asset %s",asset.spawngroupname))
-table.remove(self.assets,i)
-break
-end
-end
-return self
-end
-function COHORT:GetName()
-return self.name
-end
-function COHORT:GetRadio()
-return self.radioFreq,self.radioModu
-end
-function COHORT:GetCallsign(Asset)
-if self.callsignName then
-Asset.callsign={}
-for i=1,Asset.nunits do
-local callsign={}
-callsign[1]=self.callsignName
-callsign[2]=math.floor(self.callsigncounter/10)
-callsign[3]=self.callsigncounter%10
-if callsign[3]==0 then
-callsign[3]=1
-self.callsigncounter=self.callsigncounter+2
-else
-self.callsigncounter=self.callsigncounter+1
-end
-Asset.callsign[i]=callsign
-self:T3({callsign=callsign})
-end
-end
-end
-function COHORT:GetModex(Asset)
-if self.modex then
-Asset.modex={}
-for i=1,Asset.nunits do
-Asset.modex[i]=string.format("%03d",self.modex+self.modexcounter)
-self.modexcounter=self.modexcounter+1
-self:T3({modex=Asset.modex[i]})
-end
-end
-end
-function COHORT:AddTacanChannel(ChannelMin,ChannelMax)
-ChannelMax=ChannelMax or ChannelMin
-if ChannelMin>126 then
-self:E(self.lid.."ERROR: TACAN Channel must be <= 126! Will not add to available channels")
-return self
-end
-if ChannelMax>126 then
-self:E(self.lid.."WARNING: TACAN Channel must be <= 126! Adjusting ChannelMax to 126")
-ChannelMax=126
-end
-for i=ChannelMin,ChannelMax do
-self.tacanChannel[i]=true
-end
-return self
-end
-function COHORT:FetchTacan()
-local freechannel=nil
-for channel,free in pairs(self.tacanChannel)do
-if free then
-if freechannel==nil or channel=2 then
-local text="Weapon data:"
-for _,_weapondata in pairs(self.weaponData)do
-local weapondata=_weapondata
-text=text..string.format("\n- Bit=%s, Rmin=%d m, Rmax=%d m",tostring(weapondata.BitType),weapondata.RangeMin,weapondata.RangeMax)
-end
-self:I(self.lid..text)
-end
-return self
-end
-function COHORT:GetWeaponData(BitType)
-return self.weaponData[tostring(BitType)]
-end
-function COHORT:IsOnDuty()
-return self:Is("OnDuty")
-end
-function COHORT:IsStopped()
-return self:Is("Stopped")
-end
-function COHORT:IsPaused()
-return self:Is("Paused")
-end
-function COHORT:IsRelocating()
-return self:Is("Relocating")
-end
-function COHORT:onafterStart(From,Event,To)
-local text=string.format("Starting %s v%s %s [%s]",self.ClassName,self.version,self.name,self.attribute)
-self:I(self.lid..text)
-self:__Status(-1)
-end
-function COHORT:_CheckAssetStatus()
-if self.verbose>=2 and#self.assets>0 then
-local text=""
-for j,_asset in pairs(self.assets)do
-local asset=_asset
-text=text..string.format("\n[%d] %s (%s*%d): ",j,asset.spawngroupname,asset.unittype,asset.nunits)
-if asset.spawned then
-local mission=self.legion and self.legion:GetAssetCurrentMission(asset)or false
-if mission then
-local distance=asset.flightgroup and UTILS.MetersToNM(mission:GetTargetDistance(asset.flightgroup.group:GetCoordinate()))or 0
-text=text..string.format("Mission %s - %s: Status=%s, Dist=%.1f NM",mission.name,mission.type,mission.status,distance)
-else
-text=text.."Mission None"
-end
-text=text..", Flight: "
-if asset.flightgroup and asset.flightgroup:IsAlive()then
-local status=asset.flightgroup:GetState()
-text=text..string.format("%s",status)
-if asset.flightgroup:IsFlightgroup()then
-local fuelmin=asset.flightgroup:GetFuelMin()
-local fuellow=asset.flightgroup:IsFuelLow()
-local fuelcri=asset.flightgroup:IsFuelCritical()
-text=text..string.format("Fuel=%d",fuelmin)
-if fuelcri then
-text=text.." (Critical!)"
-elseif fuellow then
-text=text.." (Low)"
-end
-end
-local lifept,lifept0=asset.flightgroup:GetLifePoints()
-text=text..string.format(", Life=%d/%d",lifept,lifept0)
-local ammo=asset.flightgroup:GetAmmoTot()
-text=text..string.format(", Ammo=%d [G=%d, R=%d, B=%d, M=%d]",ammo.Total,ammo.Guns,ammo.Rockets,ammo.Bombs,ammo.Missiles)
-else
-text=text.."N/A"
-end
-if asset.flightgroup:IsFlightgroup()then
-local payload=asset.payload and table.concat(self.legion:GetPayloadMissionTypes(asset.payload),", ")or"None"
-text=text..", Payload={"..payload.."}"
-end
-else
-text=text..string.format("In Stock")
-if self:IsRepaired(asset)then
-text=text..", Combat Ready"
-else
-text=text..string.format(", Repaired in %d sec",self:GetRepairTime(asset))
-if asset.damage then
-text=text..string.format(" (Damage=%.1f)",asset.damage)
-end
-end
-if asset.Treturned then
-local T=timer.getAbsTime()-asset.Treturned
-text=text..string.format(", Returned for %d sec",T)
-end
-end
-end
-self:T(self.lid..text)
-end
-end
-function COHORT:onafterStop(From,Event,To)
-self:T(self.lid.."STOPPING Cohort and removing all assets!")
-for i=#self.assets,1,-1 do
-local asset=self.assets[i]
-self:DelAsset(asset)
-end
-self.CallScheduler:Clear()
-end
-function COHORT:CanMission(Mission)
-local cando=true
-if not self:IsOnDuty()then
-self:T(self.lid..string.format("Cohort in not OnDuty but in state %s. Cannot do mission %s with target %s",self:GetState(),Mission.name,Mission:GetTargetName()))
-return false
-end
-if not AUFTRAG.CheckMissionType(Mission.type,self:GetMissionTypes())then
-self:T(self.lid..string.format("INFO: Cohort cannot do mission type %s (%s, %s)",Mission.type,Mission.name,Mission:GetTargetName()))
-return false
-end
-if Mission.type==AUFTRAG.Type.TANKER then
-if Mission.refuelSystem and Mission.refuelSystem==self.tankerSystem then
-else
-self:T(self.lid..string.format("INFO: Wrong refueling system requested=%s != %s=available",tostring(Mission.refuelSystem),tostring(self.tankerSystem)))
-return false
-end
-end
-local TargetDistance=Mission:GetTargetDistance(self.legion:GetCoordinate())
-local engagerange=Mission.engageRange and math.max(self.engageRange,Mission.engageRange)or self.engageRange
-if TargetDistance>engagerange then
-self:T(self.lid..string.format("INFO: Cohort is not in range. Target dist=%d > %d NM max mission Range",UTILS.MetersToNM(TargetDistance),UTILS.MetersToNM(engagerange)))
-return false
-end
-return true
-end
-function COHORT:CountAssets(InStock,MissionTypes,Attributes)
-local N=0
-for _,_asset in pairs(self.assets)do
-local asset=_asset
-if MissionTypes==nil or AUFTRAG.CheckMissionCapability(MissionTypes,self.missiontypes)then
-if Attributes==nil or self:CheckAttribute(Attributes)then
-if asset.spawned then
-if InStock==false or InStock==nil then
-N=N+1
-end
-else
-if InStock==true or InStock==nil then
-N=N+1
-end
-end
-end
-end
-end
-return N
-end
-function COHORT:GetOpsGroups(MissionTypes,Attributes)
-local set=SET_OPSGROUP:New()
-for _,_asset in pairs(self.assets)do
-local asset=_asset
-if MissionTypes==nil or AUFTRAG.CheckMissionCapability(MissionTypes,self.missiontypes)then
-if Attributes==nil or self:CheckAttribute(Attributes)then
-if asset.flightgroup and asset.flightgroup:IsAlive()then
-set:AddGroup(asset.flightgroup)
-end
-end
-end
-end
-return set
-end
-function COHORT:RecruitAssets(MissionType,Npayloads)
-self:T2(self.lid..string.format("Recruiting asset for Mission type=%s",MissionType))
-local assets={}
-for _,_asset in pairs(self.assets)do
-local asset=_asset
-local isRequested=asset.requested
-local isReserved=asset.isReserved
-local isSpawned=asset.spawned
-local isOnMission=self.legion:IsAssetOnMission(asset)
-local opsgroup=asset.flightgroup
-self:T(self.lid..string.format("Asset %s: requested=%s, reserved=%s, spawned=%s, onmission=%s",
-asset.spawngroupname,tostring(isRequested),tostring(isReserved),tostring(isSpawned),tostring(isOnMission)))
-if not(isRequested or isReserved)then
-if self.legion:IsAssetOnMission(asset)then
-if MissionType==AUFTRAG.Type.RELOCATECOHORT then
-table.insert(assets,asset)
-elseif self.legion:IsAssetOnMission(asset,AUFTRAG.Type.NOTHING)then
-table.insert(assets,asset)
-elseif self.legion:IsAssetOnMission(asset,{AUFTRAG.Type.GCICAP,AUFTRAG.Type.PATROLRACETRACK})and MissionType==AUFTRAG.Type.INTERCEPT then
-self:T(self.lid..string.format("Adding asset on GCICAP mission for an INTERCEPT mission"))
-table.insert(assets,asset)
-elseif self.legion:IsAssetOnMission(asset,AUFTRAG.Type.ONGUARD)and(MissionType==AUFTRAG.Type.ARTY or MissionType==AUFTRAG.Type.GROUNDATTACK)then
-if not opsgroup:IsOutOfAmmo()then
-self:T(self.lid..string.format("Adding asset on ONGUARD mission for an XXX mission"))
-table.insert(assets,asset)
-end
-elseif self.legion:IsAssetOnMission(asset,AUFTRAG.Type.PATROLZONE)and(MissionType==AUFTRAG.Type.ARTY or MissionType==AUFTRAG.Type.GROUNDATTACK)then
-if not opsgroup:IsOutOfAmmo()then
-self:T(self.lid..string.format("Adding asset on PATROLZONE mission for an XXX mission"))
-table.insert(assets,asset)
-end
-elseif self.legion:IsAssetOnMission(asset,AUFTRAG.Type.ALERT5)and AUFTRAG.CheckMissionCapability(MissionType,asset.payload.capabilities)and MissionType~=AUFTRAG.Type.ALERT5 then
-self:T(self.lid..string.format("Adding asset on ALERT 5 mission for %s mission",MissionType))
-table.insert(assets,asset)
-end
-else
-if asset.spawned then
-local flightgroup=asset.flightgroup
-if flightgroup and flightgroup:IsAlive()and not(flightgroup:IsDead()or flightgroup:IsStopped())then
-local combatready=true
-if flightgroup:IsFlightgroup()then
-if flightgroup:IsFuelLow()then
-combatready=false
-end
-if MissionType==AUFTRAG.Type.INTERCEPT and not flightgroup:CanAirToAir()then
-combatready=false
-else
-local excludeguns=MissionType==AUFTRAG.Type.BOMBING or MissionType==AUFTRAG.Type.BOMBRUNWAY or MissionType==AUFTRAG.Type.BOMBCARPET or MissionType==AUFTRAG.Type.SEAD or MissionType==AUFTRAG.Type.ANTISHIP
-if excludeguns and not flightgroup:CanAirToGround(excludeguns)then
-combatready=false
-end
-end
-if flightgroup:IsHolding()or flightgroup:IsLanding()or flightgroup:IsLanded()or flightgroup:IsArrived()then
-combatready=false
-end
-if asset.payload and not AUFTRAG.CheckMissionCapability(MissionType,asset.payload.capabilities)then
-combatready=false
-end
-else
-if flightgroup:IsArmygroup()then
-if asset.attribute==WAREHOUSE.Attribute.GROUND_ARTILLERY or
-asset.attribute==WAREHOUSE.Attribute.GROUND_TANK or
-asset.attribute==WAREHOUSE.Attribute.GROUND_INFANTRY or
-asset.attribute==WAREHOUSE.Attribute.GROUND_AAA or
-asset.attribute==WAREHOUSE.Attribute.GROUND_SAM
-then
-combatready=true
-end
-else
-combatready=false
-end
-if flightgroup:IsRearming()or flightgroup:IsRetreating()or flightgroup:IsReturning()then
-combatready=false
-end
-end
-if flightgroup:IsLoading()or flightgroup:IsTransporting()or flightgroup:IsUnloading()or flightgroup:IsPickingup()or flightgroup:IsCarrier()then
-combatready=false
-end
-if flightgroup:IsCargo()or flightgroup:IsBoarding()or flightgroup:IsAwaitingLift()then
-combatready=false
-end
-if combatready then
-self:T(self.lid.."Adding SPAWNED asset to ANOTHER mission as it is COMBATREADY")
-table.insert(assets,asset)
-end
-end
-else
-if Npayloads>0 and self:IsRepaired(asset)then
-table.insert(assets,asset)
-Npayloads=Npayloads-1
-end
-end
-end
-end
-end
-self:T2(self.lid..string.format("Recruited %d assets for Mission type=%s",#assets,MissionType))
-return assets,Npayloads
-end
-function COHORT:GetRepairTime(Asset)
-if Asset.Treturned then
-local t=self.maintenancetime
-t=t+Asset.damage*self.repairtime
-local dt=timer.getAbsTime()-Asset.Treturned
-local T=t-dt
-return T
-else
-return 0
-end
-end
-function COHORT:GetMissionRange(WeaponTypes)
-if WeaponTypes and type(WeaponTypes)~="table"then
-WeaponTypes={WeaponTypes}
-end
-local function checkWeaponType(Weapon)
-local weapon=Weapon
-if WeaponTypes and#WeaponTypes>0 then
-for _,weapontype in pairs(WeaponTypes)do
-if weapontype==weapon.BitType then
-return true
-end
-end
-return false
-end
-return true
-end
-local WeaponRange=0
-for _,_weapon in pairs(self.weaponData or{})do
-local weapon=_weapon
-if weapon.RangeMax>WeaponRange and checkWeaponType(weapon)then
-WeaponRange=weapon.RangeMax
-end
-end
-return self.engageRange+WeaponRange
-end
-function COHORT:IsRepaired(Asset)
-if Asset.Treturned then
-local Tnow=timer.getAbsTime()
-local Trepaired=Asset.Treturned+self.maintenancetime
-if Tnow>=Trepaired then
-return true
-else
-return false
-end
-else
-return true
-end
-end
-function COHORT:CheckAttribute(Attributes)
-if type(Attributes)~="table"then
-Attributes={Attributes}
-end
-for _,attribute in pairs(Attributes)do
-if attribute==self.attribute then
-return true
-end
-end
-return false
-end
-function COHORT:_CheckAmmo()
-local units=self.templategroup:GetUnits()
-local nammo=0
-local nguns=0
-local nshells=0
-local nrockets=0
-local nmissiles=0
-local nmissilesAA=0
-local nmissilesAG=0
-local nmissilesAS=0
-local nmissilesSA=0
-local nmissilesBM=0
-local nmissilesCR=0
-local ntorps=0
-local nbombs=0
-for _,_unit in pairs(units)do
-local unit=_unit
-local text=string.format("Unit %s:\n",unit:GetName())
-local ammotable=unit:GetAmmo()
-if ammotable then
-self:T3(ammotable)
-for w=1,#ammotable do
-local weapon=ammotable[w]
-local Desc=weapon["desc"]
-local Warhead=Desc["warhead"]
-local Nammo=weapon["count"]
-local Category=Desc["category"]
-local MissileCategory=(Category==Weapon.Category.MISSILE)and Desc.missileCategory or nil
-local TypeName=Desc["typeName"]
-local weaponString=UTILS.Split(TypeName,"%.")
-local WeaponName=weaponString[#weaponString]
-local Rmin=Desc["rangeMin"]or 0
-local Rmax=Desc["rangeMaxAltMin"]or 0
-local Caliber=Warhead and Warhead["caliber"]or 0
-if Category==Weapon.Category.SHELL then
-if Caliber<70 then
-nguns=nguns+Nammo
-else
-nshells=nshells+Nammo
-end
-text=text..string.format("- %d shells [%s]: caliber=%d mm, range=%d - %d meters\n",Nammo,WeaponName,Caliber,Rmin,Rmax)
-elseif Category==Weapon.Category.ROCKET then
-nrockets=nrockets+Nammo
-text=text..string.format("- %d rockets [%s]: caliber=%d mm, range=%d - %d meters\n",Nammo,WeaponName,Caliber,Rmin,Rmax)
-elseif Category==Weapon.Category.BOMB then
-nbombs=nbombs+Nammo
-text=text..string.format("- %d bombs [%s]: caliber=%d mm, range=%d - %d meters\n",Nammo,WeaponName,Caliber,Rmin,Rmax)
-elseif Category==Weapon.Category.MISSILE then
-if MissileCategory==Weapon.MissileCategory.AAM then
-nmissiles=nmissiles+Nammo
-nmissilesAA=nmissilesAA+Nammo
-if Rmax>0 then
-self:AddWeaponRange(UTILS.MetersToNM(Rmin),UTILS.MetersToNM(Rmax),ENUMS.WeaponFlag.AnyAA)
-end
-elseif MissileCategory==Weapon.MissileCategory.SAM then
-nmissiles=nmissiles+Nammo
-nmissilesSA=nmissilesSA+Nammo
-if Rmax>0 then
-end
-elseif MissileCategory==Weapon.MissileCategory.ANTI_SHIP then
-nmissiles=nmissiles+Nammo
-nmissilesAS=nmissilesAS+Nammo
-if Rmax>0 then
-self:AddWeaponRange(UTILS.MetersToNM(Rmin),UTILS.MetersToNM(Rmax),ENUMS.WeaponFlag.AntiShipMissile)
-end
-elseif MissileCategory==Weapon.MissileCategory.BM then
-nmissiles=nmissiles+Nammo
-nmissilesBM=nmissilesBM+Nammo
-if Rmax>0 then
-end
-elseif MissileCategory==Weapon.MissileCategory.CRUISE then
-nmissiles=nmissiles+Nammo
-nmissilesCR=nmissilesCR+Nammo
-if Rmax>0 then
-self:AddWeaponRange(UTILS.MetersToNM(Rmin),UTILS.MetersToNM(Rmax),ENUMS.WeaponFlag.CruiseMissile)
-end
-elseif MissileCategory==Weapon.MissileCategory.OTHER then
-nmissiles=nmissiles+Nammo
-nmissilesAG=nmissilesAG+Nammo
-end
-text=text..string.format("- %d %s missiles [%s]: caliber=%d mm, range=%d - %d meters\n",Nammo,self:_MissileCategoryName(MissileCategory),WeaponName,Caliber,Rmin,Rmax)
-elseif Category==Weapon.Category.TORPEDO then
-ntorps=ntorps+Nammo
-text=text..string.format("- %d torpedos [%s]: caliber=%d mm, range=%d - %d meters\n",Nammo,WeaponName,Caliber,Rmin,Rmax)
-else
-text=text..string.format("- %d unknown ammo of type %s (category=%d, missile category=%s)\n",Nammo,TypeName,Category,tostring(MissileCategory))
-end
-end
-end
-if self.verbose>=5 then
-self:I(self.lid..text)
-else
-self:T2(self.lid..text)
-end
-end
-nammo=nguns+nshells+nrockets+nmissiles+nbombs+ntorps
-local ammo={}
-ammo.Total=nammo
-ammo.Guns=nguns
-ammo.Shells=nshells
-ammo.Rockets=nrockets
-ammo.Bombs=nbombs
-ammo.Torpedos=ntorps
-ammo.Missiles=nmissiles
-ammo.MissilesAA=nmissilesAA
-ammo.MissilesAG=nmissilesAG
-ammo.MissilesAS=nmissilesAS
-ammo.MissilesCR=nmissilesCR
-ammo.MissilesBM=nmissilesBM
-ammo.MissilesSA=nmissilesSA
-return ammo
-end
-function COHORT:_MissileCategoryName(categorynumber)
-local cat="unknown"
-if categorynumber==Weapon.MissileCategory.AAM then
-cat="air-to-air"
-elseif categorynumber==Weapon.MissileCategory.SAM then
-cat="surface-to-air"
-elseif categorynumber==Weapon.MissileCategory.BM then
-cat="ballistic"
-elseif categorynumber==Weapon.MissileCategory.ANTI_SHIP then
-cat="anti-ship"
-elseif categorynumber==Weapon.MissileCategory.CRUISE then
-cat="cruise"
-elseif categorynumber==Weapon.MissileCategory.OTHER then
-cat="other"
-end
-return cat
-end
-function COHORT:_AddOperation(Operation)
-self.operations[Operation.name]=Operation
-end
-COMMANDER={
-ClassName="COMMANDER",
-verbose=0,
-coalition=nil,
-legions={},
-missionqueue={},
-transportqueue={},
-targetqueue={},
-opsqueue={},
-rearmingZones={},
-refuellingZones={},
-capZones={},
-gcicapZones={},
-awacsZones={},
-tankerZones={},
-limitMission={},
-}
-COMMANDER.version="0.1.3"
-function COMMANDER:New(Coalition,Alias)
-local self=BASE:Inherit(self,FSM:New())
-if Coalition==nil then
-env.error("ERROR: Coalition parameter is nil in COMMANDER:New() call!")
-return nil
-end
-self.coalition=Coalition
-self.alias=Alias
-if self.alias==nil then
-if Coalition==coalition.side.BLUE then
-self.alias="George S. Patton"
-elseif Coalition==coalition.side.RED then
-self.alias="Georgy Zhukov"
-elseif Coalition==coalition.side.NEUTRAL then
-self.alias="Mahatma Gandhi"
-end
-end
-self.lid=string.format("COMMANDER %s [%s] | ",self.alias,UTILS.GetCoalitionName(self.coalition))
-self:SetStartState("NotReadyYet")
-self:AddTransition("NotReadyYet","Start","OnDuty")
-self:AddTransition("*","Status","*")
-self:AddTransition("*","Stop","Stopped")
-self:AddTransition("*","MissionAssign","*")
-self:AddTransition("*","MissionCancel","*")
-self:AddTransition("*","TransportAssign","*")
-self:AddTransition("*","TransportCancel","*")
-self:AddTransition("*","OpsOnMission","*")
-self:AddTransition("*","LegionLost","*")
-return self
-end
-function COMMANDER:SetVerbosity(VerbosityLevel)
-self.verbose=VerbosityLevel or 0
-return self
-end
-function COMMANDER:SetLimitMission(Limit,MissionType)
-MissionType=MissionType or"Total"
-if MissionType then
-self.limitMission[MissionType]=Limit or 10
-else
-self:E(self.lid.."ERROR: No mission type given for setting limit!")
-end
-return self
-end
-function COMMANDER:GetCoalition()
-return self.coalition
-end
-function COMMANDER:AddAirwing(Airwing)
-self:AddLegion(Airwing)
-return self
-end
-function COMMANDER:AddBrigade(Brigade)
-self:AddLegion(Brigade)
-return self
-end
-function COMMANDER:AddFleet(Fleet)
-self:AddLegion(Fleet)
-return self
-end
-function COMMANDER:AddLegion(Legion)
-Legion.commander=self
-table.insert(self.legions,Legion)
-return self
-end
-function COMMANDER:RemoveLegion(Legion)
-for i,_legion in pairs(self.legions)do
-local legion=_legion
-if legion.alias==Legion.alias then
-table.remove(self.legions,i)
-Legion.commander=nil
-end
-end
-return self
-end
-function COMMANDER:AddMission(Mission)
-if not self:IsMission(Mission)then
-Mission.commander=self
-Mission.statusCommander=AUFTRAG.Status.PLANNED
-table.insert(self.missionqueue,Mission)
-end
-return self
-end
-function COMMANDER:AddOpsTransport(Transport)
-Transport.commander=self
-Transport.statusCommander=OPSTRANSPORT.Status.PLANNED
-table.insert(self.transportqueue,Transport)
-return self
-end
-function COMMANDER:RemoveMission(Mission)
-for i,_mission in pairs(self.missionqueue)do
-local mission=_mission
-if mission.auftragsnummer==Mission.auftragsnummer then
-self:T(self.lid..string.format("Removing mission %s (%s) status=%s from queue",Mission.name,Mission.type,Mission.status))
-mission.commander=nil
-table.remove(self.missionqueue,i)
-break
-end
-end
-return self
-end
-function COMMANDER:RemoveTransport(Transport)
-for i,_transport in pairs(self.transportqueue)do
-local transport=_transport
-if transport.uid==Transport.uid then
-self:T(self.lid..string.format("Removing transport UID=%d status=%s from queue",transport.uid,transport:GetState()))
-transport.commander=nil
-table.remove(self.transportqueue,i)
-break
-end
-end
-return self
-end
-function COMMANDER:AddTarget(Target)
-if not self:IsTarget(Target)then
-table.insert(self.targetqueue,Target)
-end
-return self
-end
-function COMMANDER:AddOperation(Operation)
-table.insert(self.opsqueue,Operation)
-return self
-end
-function COMMANDER:IsTarget(Target)
-for _,_target in pairs(self.targetqueue)do
-local target=_target
-if target.uid==Target.uid or target:GetName()==Target:GetName()then
-return true
-end
-end
-return false
-end
-function COMMANDER:RemoveTarget(Target)
-for i,_target in pairs(self.targetqueue)do
-local target=_target
-if target.uid==Target.uid then
-self:T(self.lid..string.format("Removing target %s from queue",Target.name))
-table.remove(self.targetqueue,i)
-break
-end
-end
-return self
-end
-function COMMANDER:AddRearmingZone(RearmingZone)
-local rearmingzone={}
-rearmingzone.zone=RearmingZone
-rearmingzone.mission=nil
-table.insert(self.rearmingZones,rearmingzone)
-return rearmingzone
-end
-function COMMANDER:AddRefuellingZone(RefuellingZone)
-local rearmingzone={}
-rearmingzone.zone=RefuellingZone
-rearmingzone.mission=nil
-table.insert(self.refuellingZones,rearmingzone)
-return rearmingzone
-end
-function COMMANDER:AddCapZone(Zone,Altitude,Speed,Heading,Leg)
-local patrolzone={}
-patrolzone.zone=Zone
-patrolzone.altitude=Altitude or 12000
-patrolzone.heading=Heading or 270
-patrolzone.speed=UTILS.KnotsToAltKIAS(Speed or 350,patrolzone.altitude)
-patrolzone.leg=Leg or 30
-patrolzone.mission=nil
-table.insert(self.capZones,patrolzone)
-return patrolzone
-end
-function COMMANDER:AddGciCapZone(Zone,Altitude,Speed,Heading,Leg)
-local patrolzone={}
-patrolzone.zone=Zone
-patrolzone.altitude=Altitude or 12000
-patrolzone.heading=Heading or 270
-patrolzone.speed=UTILS.KnotsToAltKIAS(Speed or 350,patrolzone.altitude)
-patrolzone.leg=Leg or 30
-patrolzone.mission=nil
-table.insert(self.gcicapZones,patrolzone)
-return patrolzone
-end
-function COMMANDER:RemoveGciCapZone(Zone)
-local patrolzone={}
-patrolzone.zone=Zone
-for i,_patrolzone in pairs(self.gcicapZones)do
-if _patrolzone.zone==patrolzone.zone then
-if _patrolzone.mission and _patrolzone.mission:IsNotOver()then
-_patrolzone.mission:Cancel()
-end
-table.remove(self.gcicapZones,i)
-break
-end
-end
-return patrolzone
-end
-function COMMANDER:AddAwacsZone(Zone,Altitude,Speed,Heading,Leg)
-local awacszone={}
-awacszone.zone=Zone
-awacszone.altitude=Altitude or 12000
-awacszone.heading=Heading or 270
-awacszone.speed=UTILS.KnotsToAltKIAS(Speed or 350,awacszone.altitude)
-awacszone.leg=Leg or 30
-awacszone.mission=nil
-table.insert(self.awacsZones,awacszone)
-return awacszone
-end
-function COMMANDER:RemoveAwacsZone(Zone)
-local awacszone={}
-awacszone.zone=Zone
-for i,_awacszone in pairs(self.awacsZones)do
-if _awacszone.zone==awacszone.zone then
-if _awacszone.mission and _awacszone.mission:IsNotOver()then
-_awacszone.mission:Cancel()
-end
-table.remove(self.awacsZones,i)
-break
-end
-end
-return awacszone
-end
-function COMMANDER:AddTankerZone(Zone,Altitude,Speed,Heading,Leg,RefuelSystem)
-local tankerzone={}
-tankerzone.zone=Zone
-tankerzone.altitude=Altitude or 12000
-tankerzone.heading=Heading or 270
-tankerzone.speed=UTILS.KnotsToAltKIAS(Speed or 350,tankerzone.altitude)
-tankerzone.leg=Leg or 30
-tankerzone.refuelsystem=RefuelSystem
-tankerzone.mission=nil
-tankerzone.marker=MARKER:New(tankerzone.zone:GetCoordinate(),"Tanker Zone"):ToCoalition(self:GetCoalition())
-table.insert(self.tankerZones,tankerzone)
-return tankerzone
-end
-function COMMANDER:RemoveTankerZone(Zone)
-local tankerzone={}
-tankerzone.zone=Zone
-for i,_tankerzone in pairs(self.tankerZones)do
-if _tankerzone.zone==tankerzone.zone then
-if _tankerzone.mission and _tankerzone.mission:IsNotOver()then
-_tankerzone.mission:Cancel()
-end
-table.remove(self.tankerZones,i)
-break
-end
-end
-return tankerzone
-end
-function COMMANDER:IsMission(Mission)
-for _,_mission in pairs(self.missionqueue)do
-local mission=_mission
-if mission.auftragsnummer==Mission.auftragsnummer then
-return true
-end
-end
-return false
-end
-function COMMANDER:RelocateCohort(Cohort,Legion,Delay,NcarriersMin,NcarriersMax,TransportLegions)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,COMMANDER.RelocateCohort,self,Cohort,Legion,0,NcarriersMin,NcarriersMax,TransportLegions)
-else
-if Legion:IsCohort(Cohort.name)then
-self:E(self.lid..string.format("ERROR: Cohort %s is already part of new legion %s ==> CANNOT Relocate!",Cohort.name,Legion.alias))
-return self
-else
-table.insert(Legion.cohorts,Cohort)
-end
-local LegionOld=Cohort.legion
-if not LegionOld:IsCohort(Cohort.name)then
-self:E(self.lid..string.format("ERROR: Cohort %s is NOT part of this legion %s ==> CANNOT Relocate!",Cohort.name,self.alias))
-return self
-end
-if LegionOld.alias==Legion.alias then
-self:E(self.lid..string.format("ERROR: old legion %s is same as new legion %s ==> CANNOT Relocate!",LegionOld.alias,Legion.alias))
-return self
-end
-Cohort:Relocate()
-local mission=AUFTRAG:_NewRELOCATECOHORT(Legion,Cohort)
-mission:AssignCohort(Cohort)
-mission:SetRequiredAssets(#Cohort.assets)
-if NcarriersMin and NcarriersMin>0 then
-mission:SetRequiredTransport(Legion.spawnzone,NcarriersMin,NcarriersMax)
-end
-if TransportLegions then
-for _,legion in pairs(TransportLegions)do
-mission:AssignTransportLegion(legion)
-end
-else
-for _,legion in pairs(self.legions)do
-mission:AssignTransportLegion(legion)
-end
-end
-mission:SetMissionRange(10000)
-self:AddMission(mission)
-end
-return self
-end
-function COMMANDER:onafterStart(From,Event,To)
-local text=string.format("Starting Commander")
-self:I(self.lid..text)
-for _,_legion in pairs(self.legions)do
-local legion=_legion
-if legion:GetState()=="NotReadyYet"then
-legion:Start()
-end
-end
-self:__Status(-1)
-end
-function COMMANDER:onafterStatus(From,Event,To)
-local fsmstate=self:GetState()
-if self.verbose>=1 then
-local text=string.format("Status %s: Legions=%d, Missions=%d, Targets=%d, Transports=%d",fsmstate,#self.legions,#self.missionqueue,#self.targetqueue,#self.transportqueue)
-self:T(self.lid..text)
-end
-self:CheckOpsQueue()
-self:CheckTargetQueue()
-self:CheckMissionQueue()
-self:CheckTransportQueue()
-for _,_rearmingzone in pairs(self.rearmingZones)do
-local rearmingzone=_rearmingzone
-if(not rearmingzone.mission)or rearmingzone.mission:IsOver()then
-rearmingzone.mission=AUFTRAG:NewAMMOSUPPLY(rearmingzone.zone)
-self:AddMission(rearmingzone.mission)
-end
-end
-for _,_supplyzone in pairs(self.refuellingZones)do
-local supplyzone=_supplyzone
-if(not supplyzone.mission)or supplyzone.mission:IsOver()then
-supplyzone.mission=AUFTRAG:NewFUELSUPPLY(supplyzone.zone)
-self:AddMission(supplyzone.mission)
-end
-end
-for _,_patrolzone in pairs(self.capZones)do
-local patrolzone=_patrolzone
-if(not patrolzone.mission)or patrolzone.mission:IsOver()then
-local Coordinate=patrolzone.zone:GetCoordinate()
-patrolzone.mission=AUFTRAG:NewCAP(patrolzone.zone,patrolzone.altitude,patrolzone.speed,Coordinate,patrolzone.heading,patrolzone.leg)
-self:AddMission(patrolzone.mission)
-end
-end
-for _,_patrolzone in pairs(self.gcicapZones)do
-local patrolzone=_patrolzone
-if(not patrolzone.mission)or patrolzone.mission:IsOver()then
-local Coordinate=patrolzone.zone:GetCoordinate()
-patrolzone.mission=AUFTRAG:NewGCICAP(Coordinate,patrolzone.altitude,patrolzone.speed,patrolzone.heading,patrolzone.leg)
-self:AddMission(patrolzone.mission)
-end
-end
-for _,_awacszone in pairs(self.awacsZones)do
-local awacszone=_awacszone
-if(not awacszone.mission)or awacszone.mission:IsOver()then
-local Coordinate=awacszone.zone:GetCoordinate()
-awacszone.mission=AUFTRAG:NewAWACS(Coordinate,awacszone.altitude,awacszone.speed,awacszone.heading,awacszone.leg)
-self:AddMission(awacszone.mission)
-end
-end
-for _,_tankerzone in pairs(self.tankerZones)do
-local tankerzone=_tankerzone
-if(not tankerzone.mission)or tankerzone.mission:IsOver()then
-local Coordinate=tankerzone.zone:GetCoordinate()
-tankerzone.mission=AUFTRAG:NewTANKER(Coordinate,tankerzone.altitude,tankerzone.speed,tankerzone.heading,tankerzone.leg,tankerzone.refuelsystem)
-self:AddMission(tankerzone.mission)
-end
-end
-if self.verbose>=2 and#self.legions>0 then
-local text="Legions:"
-for _,_legion in pairs(self.legions)do
-local legion=_legion
-local Nassets=legion:CountAssets()
-local Nastock=legion:CountAssets(true)
-text=text..string.format("\n* %s [%s]: Assets=%s stock=%s",legion.alias,legion:GetState(),Nassets,Nastock)
-for _,aname in pairs(AUFTRAG.Type)do
-local na=legion:CountAssets(true,{aname})
-local np=legion:CountPayloadsInStock({aname})
-local nm=legion:CountAssetsOnMission({aname})
-if na>0 or np>0 then
-text=text..string.format("\n - %s: assets=%d, payloads=%d, on mission=%d",aname,na,np,nm)
-end
-end
-end
-self:T(self.lid..text)
-if self.verbose>=3 then
-local Ntotal=0
-local Nspawned=0
-local Nrequested=0
-local Nreserved=0
-local Nstock=0
-local text="\n===========================================\n"
-text=text.."Assets:"
-for _,_legion in pairs(self.legions)do
-local legion=_legion
-for _,_cohort in pairs(legion.cohorts)do
-local cohort=_cohort
-for _,_asset in pairs(cohort.assets)do
-local asset=_asset
-local state="In Stock"
-if asset.flightgroup then
-state=asset.flightgroup:GetState()
-local mission=legion:GetAssetCurrentMission(asset)
-if mission then
-state=state..string.format(", Mission \"%s\" [%s]",mission:GetName(),mission:GetType())
-end
-else
-if asset.spawned then
-env.info("FF ERROR: asset has opsgroup but is NOT spawned!")
-end
-if asset.requested and asset.isReserved then
-env.info("FF ERROR: asset is requested and reserved. Should not be both!")
-state="Reserved+Requested!"
-elseif asset.isReserved then
-state="Reserved"
-elseif asset.requested then
-state="Requested"
-end
-end
-text=text..string.format("\n[UID=%03d] %s Legion=%s [%s]: State=%s [RID=%s]",
-asset.uid,asset.spawngroupname,legion.alias,cohort.name,state,tostring(asset.rid))
-if asset.spawned then
-Nspawned=Nspawned+1
-end
-if asset.requested then
-Nrequested=Nrequested+1
-end
-if asset.isReserved then
-Nreserved=Nreserved+1
-end
-if not(asset.spawned or asset.requested or asset.isReserved)then
-Nstock=Nstock+1
-end
-Ntotal=Ntotal+1
-end
-end
-end
-text=text.."\n-------------------------------------------"
-text=text..string.format("\nNstock = %d",Nstock)
-text=text..string.format("\nNreserved = %d",Nreserved)
-text=text..string.format("\nNrequested = %d",Nrequested)
-text=text..string.format("\nNspawned = %d",Nspawned)
-text=text..string.format("\nNtotal = %d (=%d)",Ntotal,Nstock+Nspawned+Nrequested+Nreserved)
-text=text.."\n==========================================="
-self:I(self.lid..text)
-end
-end
-if self.verbose>=2 and#self.missionqueue>0 then
-local text="Mission queue:"
-for i,_mission in pairs(self.missionqueue)do
-local mission=_mission
-local target=mission:GetTargetName()or"unknown"
-text=text..string.format("\n[%d] %s (%s): status=%s, target=%s",i,mission.name,mission.type,mission.status,target)
-end
-self:I(self.lid..text)
-end
-if self.verbose>=2 and#self.targetqueue>0 then
-local text="Target queue:"
-for i,_target in pairs(self.targetqueue)do
-local target=_target
-text=text..string.format("\n[%d] %s: status=%s, life=%d",i,target:GetName(),target:GetState(),target:GetLife())
-end
-self:I(self.lid..text)
-end
-if self.verbose>=2 and#self.transportqueue>0 then
-local text="Transport queue:"
-for i,_transport in pairs(self.transportqueue)do
-local transport=_transport
-text=text..string.format("\n[%d] UID=%d: status=%s",i,transport.uid,transport:GetState())
-end
-self:I(self.lid..text)
-end
-self:__Status(-30)
-end
-function COMMANDER:onafterMissionAssign(From,Event,To,Mission,Legions)
-self:AddMission(Mission)
-Mission.statusCommander=AUFTRAG.Status.QUEUED
-for _,_Legion in pairs(Legions)do
-local Legion=_Legion
-self:T(self.lid..string.format("Assigning mission \"%s\" [%s] to legion \"%s\"",Mission.name,Mission.type,Legion.alias))
-Legion:AddMission(Mission)
-Legion:MissionRequest(Mission)
-end
-end
-function COMMANDER:onafterMissionCancel(From,Event,To,Mission)
-self:T(self.lid..string.format("Cancelling mission \"%s\" [%s] in status %s",Mission.name,Mission.type,Mission.status))
-Mission.statusCommander=AUFTRAG.Status.CANCELLED
-if Mission:IsPlanned()then
-self:RemoveMission(Mission)
-else
-if#Mission.legions>0 then
-for _,_legion in pairs(Mission.legions)do
-local legion=_legion
-legion:MissionCancel(Mission)
-end
-end
-end
-end
-function COMMANDER:onafterTransportAssign(From,Event,To,Transport,Legions)
-Transport.statusCommander=OPSTRANSPORT.Status.QUEUED
-for _,_Legion in pairs(Legions)do
-local Legion=_Legion
-self:T(self.lid..string.format("Assigning transport UID=%d to legion \"%s\"",Transport.uid,Legion.alias))
-Legion:AddOpsTransport(Transport)
-Legion:TransportRequest(Transport)
-end
-end
-function COMMANDER:onafterTransportCancel(From,Event,To,Transport)
-self:T(self.lid..string.format("Cancelling Transport UID=%d in status %s",Transport.uid,Transport:GetState()))
-Transport.statusCommander=OPSTRANSPORT.Status.CANCELLED
-if Transport:IsPlanned()then
-self:RemoveTransport(Transport)
-else
-if#Transport.legions>0 then
-for _,_legion in pairs(Transport.legions)do
-local legion=_legion
-legion:TransportCancel(Transport)
-end
-end
-end
-end
-function COMMANDER:onafterOpsOnMission(From,Event,To,OpsGroup,Mission)
-self:T2(self.lid..string.format("Group \"%s\" on mission \"%s\" [%s]",OpsGroup:GetName(),Mission:GetName(),Mission:GetType()))
-end
-function COMMANDER:CheckOpsQueue()
-local Nops=#self.opsqueue
-if Nops==0 then
-return nil
-end
-for _,_ops in pairs(self.opsqueue)do
-local operation=_ops
-if operation:IsRunning()then
-for _,_mission in pairs(operation.missions or{})do
-local mission=_mission
-if mission.phase==nil or(mission.phase and mission.phase==operation.phase)and mission:IsPlanned()then
-self:AddMission(mission)
-end
-end
-for _,_target in pairs(operation.targets or{})do
-local target=_target
-if(target.phase==nil or(target.phase and target.phase==operation.phase))and(not self:IsTarget(target))then
-self:AddTarget(target)
-end
-end
-end
-end
-end
-function COMMANDER:CheckTargetQueue()
-local Ntargets=#self.targetqueue
-if Ntargets==0 then
-return nil
-end
-for i=#self.targetqueue,1,-1 do
-local target=self.targetqueue[i]
-if(not target:IsAlive())or target:EvalConditionsAny(target.conditionStop)then
-for _,_resource in pairs(target.resources)do
-local resource=_resource
-if resource.mission and resource.mission:IsNotOver()then
-self:MissionCancel(resource.mission)
-end
-end
-table.remove(self.targetqueue,i)
-end
-end
-local NoLimit=self:_CheckMissionLimit("Total")
-if NoLimit==false then
-return nil
-end
-local function _sort(a,b)
-local taskA=a
-local taskB=b
-return(taskA.priotaskB.threatlevel0)
-end
-table.sort(self.targetqueue,_sort)
-local vip=math.huge
-for _,_target in pairs(self.targetqueue)do
-local target=_target
-if target:IsAlive()and target.importance and target.importance Creating mission type %s: Nmin=%d, Nmax=%d",target:GetName(),missionType,resource.Nmin,resource.Nmax))
-local mission=AUFTRAG:NewFromTarget(target,missionType)
-if mission then
-mission:SetRequiredAssets(resource.Nmin,resource.Nmax)
-mission:SetRequiredAttribute(resource.Attributes)
-mission:SetRequiredProperty(resource.Properties)
-mission.operation=target.operation
-resource.mission=mission
-self:AddMission(resource.mission)
-end
-end
-end
-end
-end
-end
-function COMMANDER:CheckMissionQueue()
-local Nmissions=#self.missionqueue
-if Nmissions==0 then
-return nil
-end
-local NoLimit=self:_CheckMissionLimit("Total")
-if NoLimit==false then
-return nil
-end
-local function _sort(a,b)
-local taskA=a
-local taskB=b
-return(taskA.prio0)or(Cohorts and#Cohorts>0)then
-for _,_legion in pairs(Legions or{})do
-local legion=_legion
-local Runway=legion:IsAirwing()and legion:IsRunwayOperational()or true
-if legion:IsRunning()and Runway then
-for _,_cohort in pairs(legion.cohorts)do
-local cohort=_cohort
-if CheckOperation(cohort.legion)or CheckOperation(cohort)then
-table.insert(cohorts,cohort)
-end
-end
-end
-end
-for _,_cohort in pairs(Cohorts or{})do
-local cohort=_cohort
-if CheckOperation(cohort)then
-table.insert(cohorts,cohort)
-end
-end
-else
-for _,_legion in pairs(self.legions)do
-local legion=_legion
-local Runway=legion:IsAirwing()and legion:IsRunwayOperational()or true
-if legion:IsRunning()and Runway then
-for _,_cohort in pairs(legion.cohorts)do
-local cohort=_cohort
-if CheckOperation(cohort.legion)or CheckOperation(cohort)then
-table.insert(cohorts,cohort)
-end
-end
-end
-end
-end
-return cohorts
-end
-function COMMANDER:RecruitAssetsForMission(Mission)
-self:T2(self.lid..string.format("Recruiting assets for mission \"%s\" [%s]",Mission:GetName(),Mission:GetType()))
-local NreqMin,NreqMax=Mission:GetRequiredAssets()
-local TargetVec2=Mission:GetTargetVec2()
-local Payloads=Mission.payloads
-local MaxWeight=nil
-if Mission.NcarriersMin then
-local legions=self.legions
-local cohorts=nil
-if Mission.transportLegions or Mission.transportCohorts then
-legions=Mission.transportLegions
-cohorts=Mission.transportCohorts
-end
-local Cohorts=LEGION._GetCohorts(legions,cohorts)
-local transportcohorts={}
-for _,_cohort in pairs(Cohorts)do
-local cohort=_cohort
-local can=LEGION._CohortCan(cohort,AUFTRAG.Type.OPSTRANSPORT,Mission.carrierCategories,Mission.carrierAttributes,Mission.carrierProperties,nil,TargetVec2)
-if can and(MaxWeight==nil or cohort.cargobayLimit>MaxWeight)then
-MaxWeight=cohort.cargobayLimit
-end
-end
-self:T(self.lid..string.format("Largest cargo bay available=%.1f",MaxWeight))
-end
-local legions=self.legions
-local cohorts=nil
-if Mission.specialLegions or Mission.specialCohorts then
-legions=Mission.specialLegions
-cohorts=Mission.specialCohorts
-end
-local Cohorts=LEGION._GetCohorts(legions,cohorts,Mission.operation,self.opsqueue)
-self:T(self.lid..string.format("Found %d cohort candidates for mission",#Cohorts))
-local recruited,assets,legions=LEGION.RecruitCohortAssets(Cohorts,Mission.type,Mission.alert5MissionType,NreqMin,NreqMax,TargetVec2,Payloads,
-Mission.engageRange,Mission.refuelSystem,nil,nil,MaxWeight,nil,Mission.attributes,Mission.properties,{Mission.engageWeaponType})
-return recruited,assets,legions
-end
-function COMMANDER:RecruitAssetsForEscort(Mission,Assets)
-if Mission.NescortMin and Mission.NescortMax and(Mission.NescortMin>0 or Mission.NescortMax>0)then
-local Cohorts=self:_GetCohorts(Mission.escortLegions,Mission.escortCohorts,Mission.operation)
-local assigned=LEGION.AssignAssetsForEscort(self,Cohorts,Assets,Mission.NescortMin,Mission.NescortMax,Mission.escortMissionType,Mission.escortTargetTypes,Mission.escortEngageRange)
-return assigned
-end
-return true
-end
-function COMMANDER:RecruitAssetsForTarget(Target,MissionType,NassetsMin,NassetsMax)
-local Cohorts=self:_GetCohorts()
-local TargetVec2=Target:GetVec2()
-local recruited,assets,legions=LEGION.RecruitCohortAssets(Cohorts,MissionType,nil,NassetsMin,NassetsMax,TargetVec2)
-return recruited,assets,legions
-end
-function COMMANDER:CheckTransportQueue()
-local Ntransports=#self.transportqueue
-if Ntransports==0 then
-return nil
-end
-local function _sort(a,b)
-local taskA=a
-local taskB=b
-return(taskA.prio0 then
-for _,_opsgroup in pairs(cargoOpsGroups)do
-local opsgroup=_opsgroup
-local weight=opsgroup:GetWeightTotal()
-if weight>weightGroup then
-weightGroup=weight
-end
-TotalWeight=TotalWeight+weight
-end
-end
-if weightGroup>0 then
-local recruited,assets,legions=self:RecruitAssetsForTransport(transport,weightGroup,TotalWeight)
-if recruited then
-for _,_asset in pairs(assets)do
-local asset=_asset
-transport:AddAsset(asset)
-end
-self:TransportAssign(transport,legions)
-return
-else
-LEGION.UnRecruitAssets(assets)
-end
-end
-else
-end
-end
-end
-function COMMANDER:RecruitAssetsForTransport(Transport,CargoWeight,TotalWeight)
-if CargoWeight==0 then
-return false,{},{}
-end
-local Cohorts=self:_GetCohorts()
-local TargetVec2=Transport:GetDeployZone():GetVec2()
-local NreqMin,NreqMax=Transport:GetRequiredCarriers()
-local recruited,assets,legions=LEGION.RecruitCohortAssets(Cohorts,AUFTRAG.Type.OPSTRANSPORT,nil,NreqMin,NreqMax,TargetVec2,nil,nil,nil,CargoWeight,TotalWeight)
-return recruited,assets,legions
-end
-function COMMANDER:_CheckMissionLimit(MissionType)
-local limit=self.limitMission[MissionType]
-if limit then
-if MissionType=="Total"then
-MissionType=AUFTRAG.Type
-end
-local N=self:CountMissions(MissionType,true)
-if N>=limit then
-return false
-end
-end
-return true
-end
-function COMMANDER:CountAssets(InStock,MissionTypes,Attributes)
-local N=0
-for _,_legion in pairs(self.legions)do
-local legion=_legion
-N=N+legion:CountAssets(InStock,MissionTypes,Attributes)
-end
-return N
-end
-function COMMANDER:CountMissions(MissionTypes,OnlyRunning)
-local N=0
-for _,_mission in pairs(self.missionqueue)do
-local mission=_mission
-if(not OnlyRunning)or(mission.statusCommander~=AUFTRAG.Status.PLANNED)then
-if AUFTRAG.CheckMissionType(mission.type,MissionTypes)then
-N=N+1
-end
-end
-end
-return N
-end
-function COMMANDER:GetAssets(InStock,Legions,MissionTypes,Attributes)
-local assets={}
-for _,_legion in pairs(Legions or self.legions)do
-local legion=_legion
-for _,_cohort in pairs(legion.cohorts)do
-local cohort=_cohort
-for _,_asset in pairs(cohort.assets)do
-local asset=_asset
-if not(asset.spawned or asset.isReserved or asset.requested)then
-table.insert(assets,asset)
-end
-end
-end
-end
-return assets
-end
-function COMMANDER:GetLegionsForMission(Mission)
-local legions={}
-for _,_legion in pairs(self.legions)do
-local legion=_legion
-local Nassets=0
-if legion:IsAirwing()then
-Nassets=legion:CountAssetsWithPayloadsInStock(Mission.payloads,{Mission.type},Attributes)
-else
-Nassets=legion:CountAssets(true,{Mission.type},Attributes)
-end
-if Nassets>0 and false then
-local coord=Mission:GetTargetCoordinate()
-if coord then
-local distance=UTILS.MetersToNM(coord:Get2DDistance(legion:GetCoordinate()))
-local dist=UTILS.Round(distance/10,0)
-self:T(self.lid..string.format("Got legion %s with Nassets=%d and dist=%.1f NM, rounded=%.1f",legion.alias,Nassets,distance,dist))
-table.insert(legions,{airwing=legion,distance=distance,dist=dist,targetcoord=coord,nassets=Nassets})
-end
-end
-if Nassets>0 then
-table.insert(legions,legion)
-end
-end
-return legions
-end
-function COMMANDER:GetAssetsOnMission(MissionTypes)
-local assets={}
-for _,_mission in pairs(self.missionqueue)do
-local mission=_mission
-if AUFTRAG.CheckMissionType(mission.type,MissionTypes)then
-for _,_asset in pairs(mission.assets or{})do
-local asset=_asset
-table.insert(assets,asset)
-end
-end
-end
-return assets
-end
-CSAR={
-ClassName="CSAR",
-verbose=0,
-lid="",
-coalition=1,
-coalitiontxt="blue",
-FreeVHFFrequencies={},
-UsedVHFFrequencies={},
-takenOff={},
-csarUnits={},
-downedPilots={},
-landedStatus={},
-addedTo={},
-woundedGroups={},
-inTransitGroups={},
-smokeMarkers={},
-heliVisibleMessage={},
-heliCloseMessage={},
-max_units=6,
-hoverStatus={},
-pilotDisabled={},
-pilotLives={},
-useprefix=true,
-csarPrefix={},
-template=nil,
-mash={},
-smokecolor=4,
-rescues=0,
-rescuedpilots=0,
-limitmaxdownedpilots=true,
-maxdownedpilots=10,
-allheligroupset=nil,
-topmenuname="CSAR",
-ADFRadioPwr=1000,
-PilotWeight=80,
-}
-CSAR.AircraftType={}
-CSAR.AircraftType["SA342Mistral"]=2
-CSAR.AircraftType["SA342Minigun"]=2
-CSAR.AircraftType["SA342L"]=4
-CSAR.AircraftType["SA342M"]=4
-CSAR.AircraftType["UH-1H"]=8
-CSAR.AircraftType["Mi-8MTV2"]=12
-CSAR.AircraftType["Mi-8MT"]=12
-CSAR.AircraftType["Mi-24P"]=8
-CSAR.AircraftType["Mi-24V"]=8
-CSAR.AircraftType["Bell-47"]=2
-CSAR.AircraftType["UH-60L"]=10
-CSAR.AircraftType["AH-64D_BLK_II"]=2
-CSAR.AircraftType["Bronco-OV-10A"]=2
-CSAR.version="1.0.18"
-function CSAR:New(Coalition,Template,Alias)
-local self=BASE:Inherit(self,FSM:New())
-BASE:T({Coalition,Template,Alias})
-if Coalition and type(Coalition)=="string"then
-if Coalition=="blue"then
-self.coalition=coalition.side.BLUE
-self.coalitiontxt=Coalition
-elseif Coalition=="red"then
-self.coalition=coalition.side.RED
-self.coalitiontxt=Coalition
-elseif Coalition=="neutral"then
-self.coalition=coalition.side.NEUTRAL
-self.coalitiontxt=Coalition
-else
-self:E("ERROR: Unknown coalition in CSAR!")
-end
-else
-self.coalition=Coalition
-self.coalitiontxt=string.lower(UTILS.GetCoalitionName(self.coalition))
-end
-if Alias then
-self.alias=tostring(Alias)
-else
-self.alias="Red Cross"
-if self.coalition then
-if self.coalition==coalition.side.RED then
-self.alias="IFRC"
-elseif self.coalition==coalition.side.BLUE then
-self.alias="CSAR"
-end
-end
-end
-self.lid=string.format("%s (%s) | ",self.alias,self.coalition and UTILS.GetCoalitionName(self.coalition)or"unknown")
-self:SetStartState("Stopped")
-self:AddTransition("Stopped","Start","Running")
-self:AddTransition("*","Status","*")
-self:AddTransition("*","PilotDown","*")
-self:AddTransition("*","Approach","*")
-self:AddTransition("*","Landed","*")
-self:AddTransition("*","Boarded","*")
-self:AddTransition("*","Returning","*")
-self:AddTransition("*","Rescued","*")
-self:AddTransition("*","KIA","*")
-self:AddTransition("*","Load","*")
-self:AddTransition("*","Save","*")
-self:AddTransition("*","Stop","Stopped")
-self.addedTo={}
-self.allheligroupset={}
-self.csarUnits={}
-self.FreeVHFFrequencies={}
-self.heliVisibleMessage={}
-self.heliCloseMessage={}
-self.hoverStatus={}
-self.inTransitGroups={}
-self.landedStatus={}
-self.lastCrash={}
-self.takenOff={}
-self.smokeMarkers={}
-self.UsedVHFFrequencies={}
-self.woundedGroups={}
-self.downedPilots={}
-self.downedpilotcounter=1
-self.rescues=0
-self.rescuedpilots=0
-self.csarOncrash=false
-self.allowDownedPilotCAcontrol=false
-self.enableForAI=false
-self.smokecolor=4
-self.coordtype=2
-self.immortalcrew=true
-self.invisiblecrew=false
-self.messageTime=15
-self.pilotRuntoExtractPoint=true
-self.loadDistance=75
-self.extractDistance=500
-self.loadtimemax=135
-self.radioSound="beacon.ogg"
-self.beaconRefresher=29
-self.allowFARPRescue=true
-self.FARPRescueDistance=1000
-self.max_units=6
-self.useprefix=true
-self.csarPrefix={"helicargo","MEDEVAC"}
-self.template=Template or"generic"
-self.mashprefix={"MASH"}
-self.autosmoke=false
-self.autosmokedistance=2000
-self.limitmaxdownedpilots=true
-self.maxdownedpilots=25
-self:_GenerateVHFrequencies()
-self.approachdist_far=5000
-self.approachdist_near=3000
-self.pilotmustopendoors=false
-self.suppressmessages=false
-self.rescuehoverheight=20
-self.rescuehoverdistance=10
-self.countryblue=country.id.USA
-self.countryred=country.id.RUSSIA
-self.countryneutral=country.id.UN_PEACEKEEPERS
-self.csarUsePara=false
-self.wetfeettemplate=nil
-self.usewetfeet=false
-self.allowbronco=false
-self.ADFRadioPwr=1000
-self.PilotWeight=80
-self.useSRS=false
-self.SRSPath="E:\\Program Files\\DCS-SimpleRadio-Standalone"
-self.SRSchannel=300
-self.SRSModulation=radio.modulation.AM
-self.SRSport=5002
-self.SRSCulture="en-GB"
-self.SRSVoice=nil
-self.SRSGPathToCredentials=nil
-self.SRSVolume=1.0
-self.SRSGender="male"
-self.CSARVoice=MSRS.Voices.Google.Standard.en_US_Standard_A
-self.CSARVoiceMS=MSRS.Voices.Microsoft.Hedda
-self.coordinate=nil
-local AliaS=string.gsub(self.alias," ","_")
-self.filename=string.format("CSAR_%s_Persist.csv",AliaS)
-self.enableLoadSave=false
-self.filepath=nil
-self.saveinterval=600
-return self
-end
-function CSAR:_CreateDownedPilotTrack(Group,Groupname,Side,OriginalUnit,Description,Typename,Frequency,Playername,Wetfeet)
-self:T({"_CreateDownedPilotTrack",Groupname,Side,OriginalUnit,Description,Typename,Frequency,Playername})
-local DownedPilot={}
-DownedPilot.desc=Description or""
-DownedPilot.frequency=Frequency or 0
-DownedPilot.index=self.downedpilotcounter
-DownedPilot.name=Groupname or""
-DownedPilot.originalUnit=OriginalUnit or""
-DownedPilot.player=Playername or""
-DownedPilot.side=Side or 0
-DownedPilot.typename=Typename or""
-DownedPilot.group=Group
-DownedPilot.timestamp=0
-DownedPilot.alive=true
-DownedPilot.wetfeet=Wetfeet or false
-local PilotTable=self.downedPilots
-local counter=self.downedpilotcounter
-PilotTable[counter]={}
-PilotTable[counter]=DownedPilot
-self:T({Table=PilotTable})
-self.downedPilots=PilotTable
-self.downedpilotcounter=self.downedpilotcounter+1
-return self
-end
-function CSAR:_PilotsOnboard(_heliName)
-self:T(self.lid.." _PilotsOnboard")
-local count=0
-if self.inTransitGroups[_heliName]then
-for _,_group in pairs(self.inTransitGroups[_heliName])do
-count=count+1
-end
-end
-return count
-end
-function CSAR:_DoubleEjection(_unitname)
-if self.lastCrash[_unitname]then
-local _time=self.lastCrash[_unitname]
-if timer.getTime()-_time<10 then
-self:E(self.lid.."Caught double ejection!")
-return true
-end
-end
-self.lastCrash[_unitname]=timer.getTime()
-return false
-end
-function CSAR:AddPlayerTask(PlayerTask)
-self:T(self.lid.." AddPlayerTask")
-if not self.PlayerTaskQueue then
-self.PlayerTaskQueue=FIFO:New()
-end
-self.PlayerTaskQueue:Push(PlayerTask,PlayerTask.PlayerTaskNr)
-return self
-end
-function CSAR:_SpawnPilotInField(country,point,frequency,wetfeet)
-self:T({country,point,frequency,tostring(wetfeet)})
-local freq=frequency or 1000
-local freq=freq/1000
-for i=1,10 do
-math.random(i,10000)
-end
-if point:IsSurfaceTypeWater()or wetfeet then
-point.y=0
-end
-local template=self.template
-if self.usewetfeet and wetfeet then
-template=self.wetfeettemplate
-end
-local alias=string.format("Pilot %.2fkHz-%d",freq,math.random(1,99))
-local coalition=self.coalition
-local pilotcacontrol=self.allowDownedPilotCAcontrol
-local _spawnedGroup=SPAWN
-:NewWithAlias(template,alias)
-:InitCoalition(coalition)
-:InitCountry(country)
-:InitAIOnOff(pilotcacontrol)
-:InitDelayOff()
-:SpawnFromCoordinate(point)
-return _spawnedGroup,alias
-end
-function CSAR:_AddSpecialOptions(group)
-self:T(self.lid.." _AddSpecialOptions")
-self:T({group})
-local immortalcrew=self.immortalcrew
-local invisiblecrew=self.invisiblecrew
-if immortalcrew then
-local _setImmortal={
-id='SetImmortal',
-params={
-value=true
-}
-}
-group:SetCommand(_setImmortal)
-end
-if invisiblecrew then
-local _setInvisible={
-id='SetInvisible',
-params={
-value=true
-}
-}
-group:SetCommand(_setInvisible)
-end
-group:OptionAlarmStateGreen()
-group:OptionROEHoldFire()
-return self
-end
-function CSAR:_AddCsar(_coalition,_country,_point,_typeName,_unitName,_playerName,_freq,noMessage,_description,forcedesc)
-self:T(self.lid.." _AddCsar")
-self:T({_coalition,_country,_point,_typeName,_unitName,_playerName,_freq,noMessage,_description})
-local template=self.template
-local wetfeet=false
-local surface=_point:GetSurfaceType()
-if surface==land.SurfaceType.WATER then
-wetfeet=true
-end
-if not _freq then
-_freq=self:_GenerateADFFrequency()
-if not _freq then _freq=333000 end
-end
-local _spawnedGroup,_alias=self:_SpawnPilotInField(_country,_point,_freq,wetfeet)
-local _typeName=_typeName or"Pilot"
-if not noMessage then
-if _freq~=0 then
-self:_DisplayToAllSAR("MAYDAY MAYDAY! ".._typeName.." is down. ",self.coalition,self.messageTime)
-else
-self:_DisplayToAllSAR("Troops In Contact. ".._typeName.." requests CASEVAC. ",self.coalition,self.messageTime)
-end
-end
-if(_freq and _freq~=0)then
-self:_AddBeaconToGroup(_spawnedGroup,_freq)
-end
-self:_AddSpecialOptions(_spawnedGroup)
-local _text=_description
-if not forcedesc then
-if _playerName~=nil then
-if _freq~=0 then
-_text="Pilot ".._playerName
-else
-_text="TIC - ".._playerName
-end
-elseif _unitName~=nil then
-if _freq~=0 then
-_text="AI Pilot of ".._unitName
-else
-_text="TIC - ".._unitName
-end
-end
-end
-self:T({_spawnedGroup,_alias})
-local _GroupName=_spawnedGroup:GetName()or _alias
-self:_CreateDownedPilotTrack(_spawnedGroup,_GroupName,_coalition,_unitName,_text,_typeName,_freq,_playerName,wetfeet)
-self:_InitSARForPilot(_spawnedGroup,_unitName,_freq,noMessage)
-return self
-end
-function CSAR:_SpawnCsarAtZone(_zone,_coalition,_description,_randomPoint,_nomessage,unitname,typename,forcedesc)
-self:T(self.lid.." _SpawnCsarAtZone")
-local freq=self:_GenerateADFFrequency()
-local _triggerZone=nil
-if type(_zone)=="string"then
-_triggerZone=ZONE:New(_zone)
-elseif type(_zone)=="table"and _zone.ClassName then
-if string.find(_zone.ClassName,"ZONE",1)then
-_triggerZone=_zone
-end
-end
-if _triggerZone==nil then
-self:E(self.lid.."ERROR: Can\'t find zone called ".._zone,10)
-return
-end
-local _description=_description or"PoW"
-local unitname=unitname or"Old Rusty"
-local typename=typename or"Phantom II"
-local pos={}
-if _randomPoint then
-local _pos=_triggerZone:GetRandomPointVec3()
-pos=COORDINATE:NewFromVec3(_pos)
-else
-pos=_triggerZone:GetCoordinate()
-end
-local _country=0
-if _coalition==coalition.side.BLUE then
-_country=self.countryblue
-elseif _coalition==coalition.side.RED then
-_country=self.countryred
-else
-_country=self.countryneutral
-end
-self:_AddCsar(_coalition,_country,pos,typename,unitname,_description,freq,_nomessage,_description,forcedesc)
-return self
-end
-function CSAR:SpawnCSARAtZone(Zone,Coalition,Description,RandomPoint,Nomessage,Unitname,Typename,Forcedesc)
-self:_SpawnCsarAtZone(Zone,Coalition,Description,RandomPoint,Nomessage,Unitname,Typename,Forcedesc)
-return self
-end
-function CSAR:_SpawnCASEVAC(_Point,_coalition,_description,_nomessage,unitname,typename,forcedesc)
-self:T(self.lid.." _SpawnCASEVAC")
-local _description=_description or"CASEVAC"
-local unitname=unitname or"CASEVAC"
-local typename=typename or"Ground Commander"
-local pos={}
-pos=_Point
-local _country=0
-if _coalition==coalition.side.BLUE then
-_country=self.countryblue
-elseif _coalition==coalition.side.RED then
-_country=self.countryred
-else
-_country=self.countryneutral
-end
-self:_AddCsar(_coalition,_country,pos,typename,unitname,_description,0,_nomessage,_description,forcedesc)
-return self
-end
-function CSAR:SpawnCASEVAC(Point,Coalition,Description,Nomessage,Unitname,Typename,Forcedesc)
-self:_SpawnCASEVAC(Point,Coalition,Description,Nomessage,Unitname,Typename,Forcedesc)
-return self
-end
-function CSAR:_EventHandler(EventData)
-self:T(self.lid.." _EventHandler")
-self:T({Event=EventData.id})
-local _event=EventData
-if self.enableForAI==false and _event.IniPlayerName==nil then
-return self
-end
-if _event==nil or _event.initiator==nil then
-return self
-elseif _event.id==EVENTS.Takeoff then
-self:T(self.lid.." Event unit - Takeoff")
-local _coalition=_event.IniCoalition
-if _coalition~=self.coalition then
-return self
-end
-if _event.IniGroupName then
-self.takenOff[_event.IniUnitName]=true
-end
-return self
-elseif _event.id==EVENTS.PlayerEnterAircraft or _event.id==EVENTS.PlayerEnterUnit then
-self:T(self.lid.." Event unit - Player Enter")
-local _coalition=_event.IniCoalition
-self:T("Coalition = "..UTILS.GetCoalitionName(_coalition))
-if _coalition~=self.coalition then
-return self
-end
-if _event.IniPlayerName then
-self.takenOff[_event.IniPlayerName]=nil
-end
-self:T("Taken Off: "..tostring(_event.IniUnit:InAir(true)))
-if _event.IniUnit:InAir(true)then
-self.takenOff[_event.IniPlayerName]=true
-end
-local _unit=_event.IniUnit
-local _group=_event.IniGroup
-local function IsBronco(Group)
-local grp=Group
-local typename=grp:GetTypeName()
-self:T(typename)
-if typename=="Bronco-OV-10A"then return true end
-return false
-end
-if _unit:IsHelicopter()or _group:IsHelicopter()or IsBronco(_group)then
-self:_AddMedevacMenuItem()
-end
-return self
-elseif(_event.id==EVENTS.PilotDead and self.csarOncrash==false)then
-self:T(self.lid.." Event unit - Pilot Dead")
-local _unit=_event.IniUnit
-local _unitname=_event.IniUnitName
-local _group=_event.IniGroup
-if _unit==nil then
-return self
-end
-local _coalition=_event.IniCoalition
-if _coalition~=self.coalition then
-return self
-end
-if self.takenOff[_event.IniUnitName]==true or _group:IsAirborne()then
-if self:_DoubleEjection(_unitname)then
-return self
-end
-else
-self:T(self.lid.." Pilot has not taken off, ignore")
-end
-return self
-elseif _event.id==EVENTS.PilotDead or _event.id==EVENTS.Ejection then
-if _event.id==EVENTS.PilotDead and self.csarOncrash==false then
-return self
-end
-self:T(self.lid.." Event unit - Pilot Ejected")
-local _unit=_event.IniUnit
-local _unitname=_event.IniUnitName
-local _group=_event.IniGroup
-self:T({_unit.UnitName,_unitname,_group.GroupName})
-if _unit==nil then
-self:T("Unit NIL!")
-return self
-end
-local _coalition=_group:GetCoalition()
-if _coalition~=self.coalition then
-self:T("Wrong coalition! Coalition = "..UTILS.GetCoalitionName(_coalition))
-return self
-end
-self:T("Airborne: "..tostring(_group:IsAirborne()))
-self:T("Taken Off: "..tostring(self.takenOff[_event.IniUnitName]))
-if not self.takenOff[_event.IniUnitName]and not _group:IsAirborne()then
-self:T(self.lid.." Pilot has not taken off, ignore")
-end
-if self:_DoubleEjection(_unitname)then
-self:T("Double Ejection!")
-return self
-end
-if self.limitmaxdownedpilots and self:_ReachedPilotLimit()then
-self:T("Maxed Downed Pilot!")
-return self
-end
-local wetfeet=false
-local initdcscoord=nil
-local initcoord=nil
-if _event.id==EVENTS.Ejection then
-initdcscoord=_event.TgtDCSUnit:getPoint()
-initcoord=COORDINATE:NewFromVec3(initdcscoord)
-self:T({initdcscoord})
-else
-initdcscoord=_event.IniDCSUnit:getPoint()
-initcoord=COORDINATE:NewFromVec3(initdcscoord)
-self:T({initdcscoord})
-end
-local surface=initcoord:GetSurfaceType()
-if surface==land.SurfaceType.WATER then
-self:T("Wet feet!")
-wetfeet=true
-end
-if self.csarUsePara==false or(self.csarUsePara and wetfeet)then
-local _freq=self:_GenerateADFFrequency()
-self:_AddCsar(_coalition,_unit:GetCountry(),initcoord,_unit:GetTypeName(),_unit:GetName(),_event.IniPlayerName,_freq,false,"none")
-return self
-end
-elseif _event.id==EVENTS.Land then
-self:T(self.lid.." Landing")
-if _event.IniUnitName then
-self.takenOff[_event.IniUnitName]=nil
-end
-if self.allowFARPRescue then
-local _unit=_event.IniUnit
-if _unit==nil then
-self:T(self.lid.." Unit nil on landing")
-return self
-end
-local _coalition=_event.IniGroup:GetCoalition()
-if _coalition~=self.coalition then
-self:T(self.lid.." Wrong coalition")
-return self
-end
-self.takenOff[_event.IniUnitName]=nil
-local _place=_event.Place
-if _place==nil then
-self:T(self.lid.." Landing Place Nil")
-return self
-end
-if self.inTransitGroups[_event.IniUnitName]==nil then
-return self
-end
-if _place:GetCoalition()==self.coalition or _place:GetCoalition()==coalition.side.NEUTRAL then
-self:__Landed(2,_event.IniUnitName,_place)
-self:_ScheduledSARFlight(_event.IniUnitName,_event.IniGroupName,true)
-else
-self:T(string.format("Airfield %d, Unit %d",_place:GetCoalition(),_unit:GetCoalition()))
-end
-end
-return self
-end
-if(_event.id==EVENTS.LandingAfterEjection and self.csarUsePara==true)then
-self:T("LANDING_AFTER_EJECTION")
-local _LandingPos=COORDINATE:NewFromVec3(_event.initiator:getPosition().p)
-local _unitname="Aircraft"
-local _typename="Ejected Pilot"
-local _country=_event.initiator:getCountry()
-local _coalition=coalition.getCountryCoalition(_country)
-self:T("Country = ".._country.." Coalition = ".._coalition)
-if _coalition==self.coalition then
-local _freq=self:_GenerateADFFrequency()
-self:I({coalition=_coalition,country=_country,coord=_LandingPos,name=_unitname,player=_event.IniPlayerName,freq=_freq})
-self:_AddCsar(_coalition,_country,_LandingPos,nil,_unitname,_event.IniPlayerName,_freq,false,"none")
-Unit.destroy(_event.initiator)
-end
-end
-return self
-end
-function CSAR:_InitSARForPilot(_downedGroup,_GroupName,_freq,_nomessage)
-self:T(self.lid.." _InitSARForPilot")
-local _leader=_downedGroup:GetUnit(1)
-local _groupName=_GroupName
-local _freqk=_freq/1000
-local _coordinatesText=self:_GetPositionOfWounded(_downedGroup)
-local _leadername=_leader:GetName()
-if not _nomessage then
-if _freq~=0 then
-local _text=string.format("%s requests SAR at %s, beacon at %.2f KHz",_groupName,_coordinatesText,_freqk)
-self:_DisplayToAllSAR(_text,self.coalition,self.messageTime)
-else
-local _text=string.format("Pickup Zone at %s.",_coordinatesText)
-self:_DisplayToAllSAR(_text,self.coalition,self.messageTime)
-end
-end
-for _,_heliName in pairs(self.csarUnits)do
-self:_CheckWoundedGroupStatus(_heliName,_groupName)
-end
-self:__PilotDown(2,_downedGroup,_freqk,_groupName,_coordinatesText)
-return self
-end
-function CSAR:_CheckNameInDownedPilots(name)
-local PilotTable=self.downedPilots
-local found=false
-local table=nil
-for _,_pilot in pairs(PilotTable)do
-if _pilot.name==name and _pilot.alive==true then
-found=true
-table=_pilot
-break
-end
-end
-return found,table
-end
-function CSAR:_RemoveNameFromDownedPilots(name,force)
-local PilotTable=self.downedPilots
-local found=false
-for _index,_pilot in pairs(PilotTable)do
-if _pilot.name==name then
-self.downedPilots[_index].alive=false
-end
-end
-return found
-end
-function CSAR:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTranslations)
-if not ShortCallsign or ShortCallsign==false then
-self.ShortCallsign=false
-else
-self.ShortCallsign=true
-end
-self.Keepnumber=Keepnumber or false
-self.CallsignTranslations=CallsignTranslations
-return self
-end
-function CSAR:_GetCustomCallSign(UnitName)
-local callsign=UnitName
-local unit=UNIT:FindByName(UnitName)
-if unit and unit:IsAlive()then
-local group=unit:GetGroup()
-callsign=group:GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
-end
-return callsign
-end
-function CSAR:_CheckWoundedGroupStatus(heliname,woundedgroupname)
-self:T(self.lid.." _CheckWoundedGroupStatus")
-local _heliName=heliname
-local _woundedGroupName=woundedgroupname
-self:T({Heli=_heliName,Downed=_woundedGroupName})
-local _found,_downedpilot=self:_CheckNameInDownedPilots(_woundedGroupName)
-if not _found then
-self:T("...not found in list!")
-return
-end
-local _woundedGroup=_downedpilot.group
-if _woundedGroup~=nil and _woundedGroup:IsAlive()then
-local _heliUnit=self:_GetSARHeli(_heliName)
-local _lookupKeyHeli=_heliName.."_".._woundedGroupName
-if _heliUnit==nil then
-self.heliVisibleMessage[_lookupKeyHeli]=nil
-self.heliCloseMessage[_lookupKeyHeli]=nil
-self.landedStatus[_lookupKeyHeli]=nil
-self:T("...heliunit nil!")
-return
-end
-local _heliCoord=_heliUnit:GetCoordinate()
-local _leaderCoord=_woundedGroup:GetCoordinate()
-local _distance=self:_GetDistance(_heliCoord,_leaderCoord)
-if(self.autosmoke==true)and(_distance0 then
-if self:_CheckCloseWoundedGroup(_distance,_heliUnit,_heliName,_woundedGroup,_woundedGroupName)==true then
-_downedpilot.timestamp=timer.getAbsTime()
-self:__Approach(-5,heliname,woundedgroupname)
-end
-elseif _distance>=self.approachdist_near and _distance_lastSmoke then
-local _smokecolor=self.smokecolor
-local _smokecoord=_woundedLeader:GetCoordinate():Translate(6,math.random(1,360))
-_smokecoord:Smoke(_smokecolor)
-self.smokeMarkers[_woundedGroupName]=timer.getTime()+300
-end
-return self
-end
-function CSAR:_PickupUnit(_heliUnit,_pilotName,_woundedGroup,_woundedGroupName)
-self:T(self.lid.." _PickupUnit")
-local _heliName=_heliUnit:GetName()
-local _groups=self.inTransitGroups[_heliName]
-local _unitsInHelicopter=self:_PilotsOnboard(_heliName)
-if not _groups then
-self.inTransitGroups[_heliName]={}
-_groups=self.inTransitGroups[_heliName]
-end
-local _maxUnits=self.AircraftType[_heliUnit:GetTypeName()]
-if _maxUnits==nil then
-_maxUnits=self.max_units
-end
-if _unitsInHelicopter+1>_maxUnits then
-self:_DisplayMessageToSAR(_heliUnit,string.format("%s, %s. We\'re already crammed with %d guys! Sorry!",_pilotName,self:_GetCustomCallSign(_heliName),_unitsInHelicopter,_unitsInHelicopter),self.messageTime,false,false,true)
-return self
-end
-local found,downedgrouptable=self:_CheckNameInDownedPilots(_woundedGroupName)
-local grouptable=downedgrouptable
-self.inTransitGroups[_heliName][_woundedGroupName]=
-{
-originalUnit=grouptable.originalUnit,
-woundedGroup=_woundedGroupName,
-side=self.coalition,
-desc=grouptable.desc,
-player=grouptable.player,
-}
-_woundedGroup:Destroy(false)
-self:_RemoveNameFromDownedPilots(_woundedGroupName,true)
-self:_DisplayMessageToSAR(_heliUnit,string.format("%s: %s I\'m in! Get to the MASH ASAP! ",self:_GetCustomCallSign(_heliName),_pilotName),self.messageTime,true,true)
-self:_UpdateUnitCargoMass(_heliName)
-self:__Boarded(5,_heliName,_woundedGroupName,grouptable.desc)
-return self
-end
-function CSAR:_UpdateUnitCargoMass(_heliName)
-self:T(self.lid.." _UpdateUnitCargoMass")
-local calculatedMass=self:_PilotsOnboard(_heliName)*(self.PilotWeight or 80)
-local Unit=UNIT:FindByName(_heliName)
-if Unit then
-Unit:SetUnitInternalCargo(calculatedMass)
-end
-return self
-end
-function CSAR:_OrderGroupToMoveToPoint(_leader,_destination)
-self:T(self.lid.." _OrderGroupToMoveToPoint")
-local group=_leader
-local coordinate=_destination:GetVec2()
-group:SetAIOn()
-group:RouteToVec2(coordinate,5)
-return self
-end
-function CSAR:_IsLoadingDoorOpen(unit_name)
-self:T(self.lid.." _IsLoadingDoorOpen")
-return UTILS.IsLoadingDoorOpen(unit_name)
-end
-function CSAR:_CheckCloseWoundedGroup(_distance,_heliUnit,_heliName,_woundedGroup,_woundedGroupName)
-self:T(self.lid.." _CheckCloseWoundedGroup")
-local _woundedLeader=_woundedGroup
-local _lookupKeyHeli=_heliUnit:GetName().."_".._woundedGroupName
-local _found,_pilotable=self:_CheckNameInDownedPilots(_woundedGroupName)
-local _pilotName=_pilotable.desc
-local _reset=true
-if(_distance<500)then
-if self.heliCloseMessage[_lookupKeyHeli]==nil then
-if self.autosmoke==true then
-self:_DisplayMessageToSAR(_heliUnit,string.format("%s: %s. You\'re close now! Land or hover at the smoke.",self:_GetCustomCallSign(_heliName),_pilotName),self.messageTime,false,true)
-else
-self:_DisplayMessageToSAR(_heliUnit,string.format("%s: %s. You\'re close now! Land in a safe place, I will go there ",self:_GetCustomCallSign(_heliName),_pilotName),self.messageTime,false,true)
-end
-self.heliCloseMessage[_lookupKeyHeli]=true
-end
-if not _heliUnit:InAir()then
-if self.pilotRuntoExtractPoint==true then
-if(_distance0 then
-self:_DisplayMessageToSAR(_heliUnit,"Hovering above ".._pilotName..". \n\nHold hover for ".._time.." seconds to winch them up. \n\nIf the countdown stops you\'re too far away!",self.messageTime,true)
-else
-if self.pilotmustopendoors and(self:_IsLoadingDoorOpen(_heliName)==false)then
-self:_DisplayMessageToSAR(_heliUnit,"Open the door to let me in!",self.messageTime,true,true)
-return false
-else
-self.hoverStatus[_lookupKeyHeli]=nil
-self:_PickupUnit(_heliUnit,_pilotName,_woundedGroup,_woundedGroupName)
-return true
-end
-end
-_reset=false
-else
-self:_DisplayMessageToSAR(_heliUnit,"Too high to winch ".._pilotName.." \nReduce height and hover for 10 seconds!",self.messageTime,true,true)
-return false
-end
-end
-end
-end
-end
-if _reset then
-self.hoverStatus[_lookupKeyHeli]=nil
-end
-if _distance<500 then
-return true
-else
-return false
-end
-end
-function CSAR:_ScheduledSARFlight(heliname,groupname,isairport)
-self:T(self.lid.." _ScheduledSARFlight")
-self:T({heliname,groupname})
-local _heliUnit=self:_GetSARHeli(heliname)
-local _woundedGroupName=groupname
-if(_heliUnit==nil)then
-self.inTransitGroups[heliname]=nil
-return
-end
-if self.inTransitGroups[heliname]==nil or self.inTransitGroups[heliname][_woundedGroupName]==nil then
-return
-end
-local _dist=self:_GetClosestMASH(_heliUnit)
-if _dist==-1 then
-return
-end
-if(_distsmokedist then smokedist=self.approachdist_far end
-if _closest~=nil and _closest.pilot~=nil and _closest.distance>0 and _closest.distance0 and _closest.distance12 then clock=clock-12 end
-end
-return clock
-end
-function CSAR:_AddBeaconToGroup(_group,_freq)
-self:T(self.lid.." _AddBeaconToGroup")
-local _group=_group
-if _group==nil then
-for _i,_current in ipairs(self.UsedVHFFrequencies)do
-if _current==_freq then
-table.insert(self.FreeVHFFrequencies,_freq)
-table.remove(self.UsedVHFFrequencies,_i)
-end
-end
-return
-end
-if _group:IsAlive()then
-local _radioUnit=_group:GetUnit(1)
-if _radioUnit then
-local name=_radioUnit:GetName()
-local Frequency=_freq
-local name=_radioUnit:GetName()
-local Sound="l10n/DEFAULT/"..self.radioSound
-local vec3=_radioUnit:GetVec3()or _radioUnit:GetPositionVec3()or{x=0,y=0,z=0}
-trigger.action.radioTransmission(Sound,vec3,0,false,Frequency,self.ADFRadioPwr or 1000,name..math.random(1,10000))
-end
-end
-return self
-end
-function CSAR:_RefreshRadioBeacons()
-self:T(self.lid.." _RefreshRadioBeacons")
-if self:_CountActiveDownedPilots()>0 then
-local PilotTable=self.downedPilots
-for _,_pilot in pairs(PilotTable)do
-self:T({_pilot})
-local pilot=_pilot
-local group=pilot.group
-local frequency=pilot.frequency or 0
-if group and group:IsAlive()and frequency>0 then
-self:_AddBeaconToGroup(group,frequency)
-end
-end
-end
-return self
-end
-function CSAR:_CountActiveDownedPilots()
-self:T(self.lid.." _CountActiveDownedPilots")
-local PilotsInFieldN=0
-for _,_unitName in pairs(self.downedPilots)do
-self:T({_unitName.desc})
-if _unitName.alive==true then
-PilotsInFieldN=PilotsInFieldN+1
-end
-end
-return PilotsInFieldN
-end
-function CSAR:_ReachedPilotLimit()
-self:T(self.lid.." _ReachedPilotLimit")
-local limit=self.maxdownedpilots
-local islimited=self.limitmaxdownedpilots
-local count=self:_CountActiveDownedPilots()
-if islimited and(count>=limit)then
-return true
-else
-return false
-end
-end
-function CSAR:onafterStart(From,Event,To)
-self:T({From,Event,To})
-self:I(self.lid.."Started ("..self.version..")")
-self:HandleEvent(EVENTS.Takeoff,self._EventHandler)
-self:HandleEvent(EVENTS.Land,self._EventHandler)
-self:HandleEvent(EVENTS.Ejection,self._EventHandler)
-self:HandleEvent(EVENTS.LandingAfterEjection,self._EventHandler)
-self:HandleEvent(EVENTS.PlayerEnterAircraft,self._EventHandler)
-self:HandleEvent(EVENTS.PlayerEnterUnit,self._EventHandler)
-self:HandleEvent(EVENTS.PilotDead,self._EventHandler)
-if self.allowbronco then
-local prefixes=self.csarPrefix or{}
-self.allheligroupset=SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(prefixes):FilterStart()
-elseif self.useprefix then
-local prefixes=self.csarPrefix or{}
-self.allheligroupset=SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(prefixes):FilterCategoryHelicopter():FilterStart()
-else
-self.allheligroupset=SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterCategoryHelicopter():FilterStart()
-end
-self.mash=SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(self.mashprefix):FilterStart()
-if not self.coordinate then
-local csarhq=self.mash:GetRandom()
-if csarhq then
-self.coordinate=csarhq:GetCoordinate()
-end
-end
-if self.wetfeettemplate then
-self.usewetfeet=true
-end
-if self.useSRS then
-local path=self.SRSPath
-local modulation=self.SRSModulation
-local channel=self.SRSchannel
-self.msrs=MSRS:New(path,channel,modulation)
-self.msrs:SetPort(self.SRSport)
-self.msrs:SetLabel("CSAR")
-self.msrs:SetCulture(self.SRSCulture)
-self.msrs:SetCoalition(self.coalition)
-self.msrs:SetVoice(self.SRSVoice)
-self.msrs:SetGender(self.SRSGender)
-if self.SRSGPathToCredentials then
-self.msrs:SetGoogle(self.SRSGPathToCredentials)
-end
-self.msrs:SetVolume(self.SRSVolume)
-self.msrs:SetLabel("CSAR")
-self.SRSQueue=MSRSQUEUE:New("CSAR")
-end
-self:__Status(-10)
-if self.enableLoadSave then
-local interval=self.saveinterval
-local filename=self.filename
-local filepath=self.filepath
-self:__Save(interval,filepath,filename)
-end
-return self
-end
-function CSAR:_CheckDownedPilotTable()
-local pilots=self.downedPilots
-local npilots={}
-for _ind,_entry in pairs(pilots)do
-local _group=_entry.group
-if _group:IsAlive()then
-npilots[_ind]=_entry
-else
-if _entry.alive then
-self:__KIA(1,_entry.desc)
-end
-end
-end
-self.downedPilots=npilots
-return self
-end
-function CSAR:onbeforeStatus(From,Event,To)
-self:T({From,Event,To})
-self:_AddMedevacMenuItem()
-if not self.BeaconTimer or(self.BeaconTimer and not self.BeaconTimer:IsRunning())then
-self.BeaconTimer=TIMER:New(self._RefreshRadioBeacons,self)
-self.BeaconTimer:Start(2,self.beaconRefresher)
-end
-self:_CheckDownedPilotTable()
-for _,_sar in pairs(self.csarUnits)do
-local PilotTable=self.downedPilots
-for _,_entry in pairs(PilotTable)do
-if _entry.alive then
-local entry=_entry
-local name=entry.name
-local timestamp=entry.timestamp or 0
-local now=timer.getAbsTime()
-if now-timestamp>17 then
-self:_CheckWoundedGroupStatus(_sar,name)
-end
-end
-end
-end
-return self
-end
-function CSAR:onafterStatus(From,Event,To)
-self:T({From,Event,To})
-local NumberOfSARPilots=0
-for _,_unitName in pairs(self.csarUnits)do
-NumberOfSARPilots=NumberOfSARPilots+1
-end
-local PilotsInFieldN=self:_CountActiveDownedPilots()
-local PilotsBoarded=0
-for _,_unitName in pairs(self.inTransitGroups)do
-for _,_units in pairs(_unitName)do
-PilotsBoarded=PilotsBoarded+1
-end
-end
-if self.verbose>0 then
-local text=string.format("%s Active SAR: %d | Downed Pilots in field: %d (max %d) | Pilots boarded: %d | Landings: %d | Pilots rescued: %d",
-self.lid,NumberOfSARPilots,PilotsInFieldN,self.maxdownedpilots,PilotsBoarded,self.rescues,self.rescuedpilots)
-self:T(text)
-if self.verbose<2 then
-self:I(text)
-elseif self.verbose>1 then
-self:I(text)
-local m=MESSAGE:New(text,"10","Status",true):ToCoalition(self.coalition)
-end
-end
-self:__Status(-20)
-return self
-end
-function CSAR:onafterStop(From,Event,To)
-self:T({From,Event,To})
-self:UnHandleEvent(EVENTS.Takeoff)
-self:UnHandleEvent(EVENTS.Land)
-self:UnHandleEvent(EVENTS.Ejection)
-self:UnHandleEvent(EVENTS.LandingAfterEjection)
-self:UnHandleEvent(EVENTS.PlayerEnterUnit)
-self:UnHandleEvent(EVENTS.PlayerEnterAircraft)
-self:UnHandleEvent(EVENTS.PilotDead)
-self:T(self.lid.."Stopped.")
-return self
-end
-function CSAR:onbeforeApproach(From,Event,To,Heliname,Woundedgroupname)
-self:T({From,Event,To,Heliname,Woundedgroupname})
-self:_CheckWoundedGroupStatus(Heliname,Woundedgroupname)
-return self
-end
-function CSAR:onbeforeBoarded(From,Event,To,Heliname,Woundedgroupname)
-self:T({From,Event,To,Heliname,Woundedgroupname})
-self:_ScheduledSARFlight(Heliname,Woundedgroupname)
-local Unit=UNIT:FindByName(Heliname)
-if Unit and Unit:IsPlayer()and self.PlayerTaskQueue then
-local playername=Unit:GetPlayerName()
-local dropcoord=Unit:GetCoordinate()or COORDINATE:New(0,0,0)
-local dropvec2=dropcoord:GetVec2()
-self.PlayerTaskQueue:ForEach(
-function(Task)
-local task=Task
-local subtype=task:GetSubType()
-if Event==subtype and not task:IsDone()then
-local targetzone=task.Target:GetObject()
-if(targetzone and targetzone.ClassName and string.match(targetzone.ClassName,"ZONE")and targetzone:IsVec2InZone(dropvec2))
-or(string.find(task.CSARPilotName,Woundedgroupname))then
-if task.Clients:HasUniqueID(playername)then
-task:__Success(-1)
-end
-end
-end
-end
-)
-end
-return self
-end
-function CSAR:onbeforeReturning(From,Event,To,Heliname,Woundedgroupname,IsAirPort)
-self:T({From,Event,To,Heliname,Woundedgroupname})
-self:_ScheduledSARFlight(Heliname,Woundedgroupname,IsAirPort)
-return self
-end
-function CSAR:onbeforeRescued(From,Event,To,HeliUnit,HeliName,PilotsSaved)
-self:T({From,Event,To,HeliName,HeliUnit})
-self.rescues=self.rescues+1
-self.rescuedpilots=self.rescuedpilots+PilotsSaved
-local Unit=HeliUnit or UNIT:FindByName(HeliName)
-if Unit and Unit:IsPlayer()and self.PlayerTaskQueue then
-local playername=Unit:GetPlayerName()
-self.PlayerTaskQueue:ForEach(
-function(Task)
-local task=Task
-local subtype=task:GetSubType()
-if Event==subtype and not task:IsDone()then
-if task.Clients:HasUniqueID(playername)then
-task:__Success(-1)
-end
-end
-end
-)
-end
-return self
-end
-function CSAR:onbeforePilotDown(From,Event,To,Group,Frequency,Leadername,CoordinatesText)
-self:T({From,Event,To,Group,Frequency,Leadername,CoordinatesText})
-return self
-end
-function CSAR:onbeforeLanded(From,Event,To,HeliName,Airbase)
-self:T({From,Event,To,HeliName,Airbase})
-return self
-end
-function CSAR:onbeforeSave(From,Event,To,path,filename)
-self:T({From,Event,To,path,filename})
-if not self.enableLoadSave then
-return self
-end
-if not io then
-self:E(self.lid.."ERROR: io not desanitized. Can't save current state.")
-return false
-end
-if path==nil and not lfs then
-self:E(self.lid.."WARNING: lfs not desanitized. State will be saved in DCS installation root directory rather than your \"Saved Games\\DCS\" folder.")
-end
-return true
-end
-function CSAR:onafterSave(From,Event,To,path,filename)
-self:T({From,Event,To,path,filename})
-if not self.enableLoadSave then
-return self
-end
-local function _savefile(filename,data)
-local f=assert(io.open(filename,"wb"))
-f:write(data)
-f:close()
-end
-if lfs then
-path=self.filepath or lfs.writedir()
-end
-filename=filename or self.filename
-if path~=nil then
-filename=path.."\\"..filename
-end
-local pilots=self.downedPilots
-local data="playerName,x,y,z,coalition,country,description,typeName,unitName,freq\n"
-local n=0
-for _,_grp in pairs(pilots)do
-local DownedPilot=_grp
-if DownedPilot and DownedPilot.alive then
-local playerName=DownedPilot.player
-local group=DownedPilot.group
-local coalition=group:GetCoalition()
-local country=group:GetCountry()
-local description=DownedPilot.desc
-local typeName=DownedPilot.typename
-local freq=DownedPilot.frequency
-local location=group:GetVec3()
-local unitName=DownedPilot.originalUnit
-local txt=string.format("%s,%d,%d,%d,%s,%s,%s,%s,%s,%d\n",playerName,location.x,location.y,location.z,coalition,country,description,typeName,unitName,freq)
-self:I(self.lid.."Saving to CSAR File: "..txt)
-data=data..txt
-end
-end
-_savefile(filename,data)
-if self.enableLoadSave then
-local interval=self.saveinterval
-local filename=self.filename
-local filepath=self.filepath
-self:__Save(interval,filepath,filename)
-end
-return self
-end
-function CSAR:onbeforeLoad(From,Event,To,path,filename)
-self:T({From,Event,To,path,filename})
-if not self.enableLoadSave then
-return self
-end
-local function _fileexists(name)
-local f=io.open(name,"r")
-if f~=nil then
-io.close(f)
-return true
-else
-return false
-end
-end
-filename=filename or self.filename
-path=path or self.filepath
-if not io then
-self:E(self.lid.."WARNING: io not desanitized. Cannot load file.")
-return false
-end
-if path==nil and not lfs then
-self:E(self.lid.."WARNING: lfs not desanitized. State will be saved in DCS installation root directory rather than your \"Saved Games\\DCS\" folder.")
-end
-if lfs then
-path=path or lfs.writedir()
-end
-if path~=nil then
-filename=path.."\\"..filename
-end
-local exists=_fileexists(filename)
-if exists then
-return true
-else
-self:E(self.lid..string.format("WARNING: State file %s might not exist.",filename))
-return false
-end
-end
-function CSAR:onafterLoad(From,Event,To,path,filename)
-self:T({From,Event,To,path,filename})
-if not self.enableLoadSave then
-return self
-end
-local function _loadfile(filename)
-local f=assert(io.open(filename,"rb"))
-local data=f:read("*all")
-f:close()
-return data
-end
-filename=filename or self.filename
-path=path or self.filepath
-if lfs then
-path=path or lfs.writedir()
-end
-if path~=nil then
-filename=path.."\\"..filename
-end
-local text=string.format("Loading CSAR state from file %s",filename)
-MESSAGE:New(text,10):ToAllIf(self.Debug)
-self:I(self.lid..text)
-local file=assert(io.open(filename,"rb"))
-local loadeddata={}
-for line in file:lines()do
-loadeddata[#loadeddata+1]=line
-end
-file:close()
-table.remove(loadeddata,1)
-for _id,_entry in pairs(loadeddata)do
-local dataset=UTILS.Split(_entry,",")
-local playerName=dataset[1]
-local vec3={}
-vec3.x=tonumber(dataset[2])
-vec3.y=tonumber(dataset[3])
-vec3.z=tonumber(dataset[4])
-local point=COORDINATE:NewFromVec3(vec3)
-local coalition=tonumber(dataset[5])
-local country=tonumber(dataset[6])
-local description=dataset[7]
-local typeName=dataset[8]
-local unitName=dataset[9]
-local freq=tonumber(dataset[10])
-self:_AddCsar(coalition,country,point,typeName,unitName,playerName,freq,nil,description,nil)
-end
-return self
-end
-do
-CTLD_CARGO={
-ClassName="CTLD_CARGO",
-ID=0,
-Name="none",
-Templates={},
-CargoType="none",
-HasBeenMoved=false,
-LoadDirectly=false,
-CratesNeeded=0,
-Positionable=nil,
-HasBeenDropped=false,
-PerCrateMass=0,
-Stock=nil,
-Mark=nil,
-}
-CTLD_CARGO.Enum={
-VEHICLE="Vehicle",
-TROOPS="Troops",
-FOB="FOB",
-CRATE="Crate",
-REPAIR="Repair",
-ENGINEERS="Engineers",
-STATIC="Static",
-}
-function CTLD_CARGO:New(ID,Name,Templates,Sorte,HasBeenMoved,LoadDirectly,CratesNeeded,Positionable,Dropped,PerCrateMass,Stock,Subcategory)
-local self=BASE:Inherit(self,BASE:New())
-self:T({ID,Name,Templates,Sorte,HasBeenMoved,LoadDirectly,CratesNeeded,Positionable,Dropped})
-self.ID=ID or math.random(100000,1000000)
-self.Name=Name or"none"
-self.Templates=Templates or{}
-self.CargoType=Sorte or"type"
-self.HasBeenMoved=HasBeenMoved or false
-self.LoadDirectly=LoadDirectly or false
-self.CratesNeeded=CratesNeeded or 0
-self.Positionable=Positionable or nil
-self.HasBeenDropped=Dropped or false
-self.PerCrateMass=PerCrateMass or 0
-self.Stock=Stock or nil
-self.Mark=nil
-self.Subcategory=Subcategory or"Other"
-return self
-end
-function CTLD_CARGO:GetID()
-return self.ID
-end
-function CTLD_CARGO:GetSubCat()
-return self.Subcategory
-end
-function CTLD_CARGO:GetMass()
-return self.PerCrateMass
-end
-function CTLD_CARGO:GetName()
-return self.Name
-end
-function CTLD_CARGO:GetTemplates()
-return self.Templates
-end
-function CTLD_CARGO:HasMoved()
-return self.HasBeenMoved
-end
-function CTLD_CARGO:WasDropped()
-return self.HasBeenDropped
-end
-function CTLD_CARGO:CanLoadDirectly()
-return self.LoadDirectly
-end
-function CTLD_CARGO:GetCratesNeeded()
-return self.CratesNeeded
-end
-function CTLD_CARGO:GetType()
-return self.CargoType
-end
-function CTLD_CARGO:GetPositionable()
-return self.Positionable
-end
-function CTLD_CARGO:SetHasMoved(moved)
-self.HasBeenMoved=moved or false
-end
-function CTLD_CARGO:Isloaded()
-if self.HasBeenMoved and not self:WasDropped()then
-return true
-else
-return false
-end
-end
-function CTLD_CARGO:SetWasDropped(dropped)
-self.HasBeenDropped=dropped or false
-end
-function CTLD_CARGO:GetStock()
-if self.Stock then
-return self.Stock
-else
-return-1
-end
-end
-function CTLD_CARGO:AddStock(Number)
-if self.Stock then
-local number=Number or 1
-self.Stock=self.Stock+number
-end
-return self
-end
-function CTLD_CARGO:RemoveStock(Number)
-if self.Stock then
-local number=Number or 1
-self.Stock=self.Stock-number
-if self.Stock<0 then self.Stock=0 end
-end
-return self
-end
-function CTLD_CARGO:SetStock(Number)
-self.Stock=Number
-return self
-end
-function CTLD_CARGO:IsRepair()
-if self.CargoType=="Repair"then
-return true
-else
-return false
-end
-end
-function CTLD_CARGO:IsStatic()
-if self.CargoType=="Static"then
-return true
-else
-return false
-end
-end
-function CTLD_CARGO:AddMark(Mark)
-self.Mark=Mark
-return self
-end
-function CTLD_CARGO:GetMark(Mark)
-return self.Mark
-end
-function CTLD_CARGO:WipeMark()
-self.Mark=nil
-return self
-end
-function CTLD_CARGO:GetNetMass()
-return self.CratesNeeded*self.PerCrateMass
-end
-end
-do
-CTLD_ENGINEERING={
-ClassName="CTLD_ENGINEERING",
-lid="",
-Name="none",
-Group=nil,
-Unit=nil,
-HeliGroup=nil,
-HeliUnit=nil,
-State="",
-}
-CTLD_ENGINEERING.Version="0.0.3"
-function CTLD_ENGINEERING:New(Name,GroupName,HeliGroup,HeliUnit)
-local self=BASE:Inherit(self,BASE:New())
-self.Name=Name or"Engineer Squad"
-self.Group=GROUP:FindByName(GroupName)
-self.Unit=self.Group:GetUnit(1)
-self.HeliGroup=HeliGroup
-self.HeliUnit=HeliUnit
-self.currwpt=nil
-self.lid=string.format("%s (%s) | ",self.Name,self.Version)
-self.State="Stopped"
-self.marktimer=300
-self:Start()
-local parent=self:GetParent(self)
-return self
-end
-function CTLD_ENGINEERING:SetStatus(State)
-self.State=State
-return self
-end
-function CTLD_ENGINEERING:GetStatus()
-return self.State
-end
-function CTLD_ENGINEERING:IsStatus(State)
-return self.State==State
-end
-function CTLD_ENGINEERING:IsNotStatus(State)
-return self.State~=State
-end
-function CTLD_ENGINEERING:Start()
-self:T(self.lid.."Start")
-self:SetStatus("Running")
-return self
-end
-function CTLD_ENGINEERING:Stop()
-self:T(self.lid.."Stop")
-self:SetStatus("Stopped")
-return self
-end
-function CTLD_ENGINEERING:Build()
-self:T(self.lid.."Build")
-self:SetStatus("Building")
-return self
-end
-function CTLD_ENGINEERING:Done()
-self:T(self.lid.."Done")
-local grp=self.Group
-grp:RelocateGroundRandomInRadius(7,100,false,false,"Diamond")
-self:SetStatus("Running")
-return self
-end
-function CTLD_ENGINEERING:Search(crates,number)
-self:T(self.lid.."Search")
-self:SetStatus("Searching")
-local dist=self.distance
-local group=self.Group
-local ctable={}
-local ind=0
-if number>0 then
-for _,_cargo in pairs(crates)do
-local cgotype=_cargo:GetType()
-if _cargo:WasDropped()and cgotype~=CTLD_CARGO.Enum.STATIC then
-local ok=false
-local chalk=_cargo:GetMark()
-if chalk==nil then
-ok=true
-else
-local tag=chalk.tag or"none"
-local timestamp=chalk.timestamp or 0
-local gone=timer.getAbsTime()-timestamp
-if gone>=self.marktimer then
-ok=true
-_cargo:WipeMark()
-end
-end
-if ok then
-local chalk={}
-chalk.tag="Engineers"
-chalk.timestamp=timer.getAbsTime()
-_cargo:AddMark(chalk)
-ind=ind+1
-table.insert(ctable,ind,_cargo)
-end
-end
-end
-end
-if ind>0 then
-local crate=ctable[1]
-local static=crate:GetPositionable()
-local crate_pos=static:GetCoordinate()
-local gpos=group:GetCoordinate()
-local distance=self:_GetDistance(gpos,crate_pos)
-self:T(string.format("%s Distance to crate: %d",self.lid,distance))
-if distance>30 and distance~=-1 and self:IsStatus("Searching")then
-group:RouteGroundTo(crate_pos,15,"Line abreast",1)
-self.currwpt=crate_pos
-self:Move()
-elseif distance<=30 and distance~=-1 then
-self:Arrive()
-end
-else
-self:T(self.lid.."No crates in reach!")
-end
-return self
-end
-function CTLD_ENGINEERING:Move()
-self:T(self.lid.."Move")
-self:SetStatus("Moving")
-local group=self.Group
-local tgtpos=self.currwpt
-local gpos=group:GetCoordinate()
-local distance=self:_GetDistance(gpos,tgtpos)
-self:T(string.format("%s Distance remaining: %d",self.lid,distance))
-if distance<=30 and distance~=-1 then
-self:Arrive()
-end
-return self
-end
-function CTLD_ENGINEERING:Arrive()
-self:T(self.lid.."Arrive")
-self:SetStatus("Arrived")
-self.currwpt=nil
-local Grp=self.Group
-Grp:RouteStop()
-return self
-end
-function CTLD_ENGINEERING:_GetDistance(_point1,_point2)
-self:T(self.lid.." _GetDistance")
-if _point1 and _point2 then
-local distance1=_point1:Get2DDistance(_point2)
-local distance2=_point1:DistanceFromPointVec2(_point2)
-if distance1 and type(distance1)=="number"then
-return distance1
-elseif distance2 and type(distance2)=="number"then
-return distance2
-else
-self:E("*****Cannot calculate distance!")
-self:E({_point1,_point2})
-return-1
-end
-else
-self:E("******Cannot calculate distance!")
-self:E({_point1,_point2})
-return-1
-end
-end
-end
-do
-CTLD={
-ClassName="CTLD",
-verbose=0,
-lid="",
-coalition=1,
-coalitiontxt="blue",
-PilotGroups={},
-CtldUnits={},
-FreeVHFFrequencies={},
-FreeUHFFrequencies={},
-FreeFMFrequencies={},
-CargoCounter=0,
-Cargo_Troops={},
-Cargo_Crates={},
-Loaded_Cargo={},
-Spawned_Crates={},
-Spawned_Cargo={},
-CrateDistance=35,
-PackDistance=35,
-debug=false,
-wpZones={},
-dropOffZones={},
-pickupZones={},
-}
-CTLD.RadioModulation={
-AM=0,
-FM=1,
-}
-CTLD.CargoZoneType={
-LOAD="load",
-DROP="drop",
-MOVE="move",
-SHIP="ship",
-BEACON="beacon",
-}
-CTLD.UnitTypeCapabilities={
-["SA342Mistral"]={type="SA342Mistral",crates=false,troops=true,cratelimit=0,trooplimit=4,length=12,cargoweightlimit=400},
-["SA342L"]={type="SA342L",crates=false,troops=true,cratelimit=0,trooplimit=2,length=12,cargoweightlimit=400},
-["SA342M"]={type="SA342M",crates=false,troops=true,cratelimit=0,trooplimit=4,length=12,cargoweightlimit=400},
-["SA342Minigun"]={type="SA342Minigun",crates=false,troops=true,cratelimit=0,trooplimit=2,length=12,cargoweightlimit=400},
-["UH-1H"]={type="UH-1H",crates=true,troops=true,cratelimit=1,trooplimit=8,length=15,cargoweightlimit=700},
-["Mi-8MTV2"]={type="Mi-8MTV2",crates=true,troops=true,cratelimit=2,trooplimit=12,length=15,cargoweightlimit=3000},
-["Mi-8MT"]={type="Mi-8MT",crates=true,troops=true,cratelimit=2,trooplimit=12,length=15,cargoweightlimit=3000},
-["Ka-50"]={type="Ka-50",crates=false,troops=false,cratelimit=0,trooplimit=0,length=15,cargoweightlimit=0},
-["Ka-50_3"]={type="Ka-50_3",crates=false,troops=false,cratelimit=0,trooplimit=0,length=15,cargoweightlimit=0},
-["Mi-24P"]={type="Mi-24P",crates=true,troops=true,cratelimit=2,trooplimit=8,length=18,cargoweightlimit=700},
-["Mi-24V"]={type="Mi-24V",crates=true,troops=true,cratelimit=2,trooplimit=8,length=18,cargoweightlimit=700},
-["Hercules"]={type="Hercules",crates=true,troops=true,cratelimit=7,trooplimit=64,length=25,cargoweightlimit=19000},
-["UH-60L"]={type="UH-60L",crates=true,troops=true,cratelimit=2,trooplimit=20,length=16,cargoweightlimit=3500},
-["AH-64D_BLK_II"]={type="AH-64D_BLK_II",crates=false,troops=true,cratelimit=0,trooplimit=2,length=17,cargoweightlimit=200},
-["Bronco-OV-10A"]={type="Bronco-OV-10A",crates=false,troops=true,cratelimit=0,trooplimit=5,length=13,cargoweightlimit=1450},
-}
-CTLD.version="1.0.44"
-function CTLD:New(Coalition,Prefixes,Alias)
-local self=BASE:Inherit(self,FSM:New())
-BASE:T({Coalition,Prefixes,Alias})
-if Coalition and type(Coalition)=="string"then
-if Coalition=="blue"then
-self.coalition=coalition.side.BLUE
-self.coalitiontxt=Coalition
-elseif Coalition=="red"then
-self.coalition=coalition.side.RED
-self.coalitiontxt=Coalition
-elseif Coalition=="neutral"then
-self.coalition=coalition.side.NEUTRAL
-self.coalitiontxt=Coalition
-else
-self:E("ERROR: Unknown coalition in CTLD!")
-end
-else
-self.coalition=Coalition
-self.coalitiontxt=string.lower(UTILS.GetCoalitionName(self.coalition))
-end
-if Alias then
-self.alias=tostring(Alias)
-else
-self.alias="UNHCR"
-if self.coalition then
-if self.coalition==coalition.side.RED then
-self.alias="Red CTLD"
-elseif self.coalition==coalition.side.BLUE then
-self.alias="Blue CTLD"
-end
-end
-end
-self.lid=string.format("%s (%s) | ",self.alias,self.coalition and UTILS.GetCoalitionName(self.coalition)or"unknown")
-self:SetStartState("Stopped")
-self:AddTransition("Stopped","Start","Running")
-self:AddTransition("*","Status","*")
-self:AddTransition("*","TroopsPickedUp","*")
-self:AddTransition("*","TroopsExtracted","*")
-self:AddTransition("*","CratesPickedUp","*")
-self:AddTransition("*","TroopsDeployed","*")
-self:AddTransition("*","TroopsRTB","*")
-self:AddTransition("*","CratesDropped","*")
-self:AddTransition("*","CratesBuild","*")
-self:AddTransition("*","CratesRepaired","*")
-self:AddTransition("*","CratesBuildStarted","*")
-self:AddTransition("*","CratesRepairStarted","*")
-self:AddTransition("*","Load","*")
-self:AddTransition("*","Save","*")
-self:AddTransition("*","Stop","Stopped")
-self.PilotGroups={}
-self.CtldUnits={}
-self.FreeVHFFrequencies={}
-self.FreeUHFFrequencies={}
-self.FreeFMFrequencies={}
-self.UsedVHFFrequencies={}
-self.UsedUHFFrequencies={}
-self.UsedFMFrequencies={}
-self.RadioSound="beacon.ogg"
-self.RadioSoundFC3="beacon.ogg"
-self.RadioPath="l10n/DEFAULT/"
-self.pickupZones={}
-self.dropOffZones={}
-self.wpZones={}
-self.shipZones={}
-self.droppedBeacons={}
-self.droppedbeaconref={}
-self.droppedbeacontimeout=600
-self.useprecisecoordloads=true
-self.Cargo_Crates={}
-self.Cargo_Troops={}
-self.Cargo_Statics={}
-self.Loaded_Cargo={}
-self.Spawned_Crates={}
-self.Spawned_Cargo={}
-self.MenusDone={}
-self.DroppedTroops={}
-self.DroppedCrates={}
-self.CargoCounter=0
-self.CrateCounter=0
-self.TroopCounter=0
-self.Engineers=0
-self.EngineersInField={}
-self.EngineerSearch=2000
-self.nobuildmenu=false
-self.CrateDistance=35
-self.PackDistance=35
-self.ExtractFactor=3.33
-self.prefixes=Prefixes or{"Cargoheli"}
-self.useprefix=true
-self.maximumHoverHeight=15
-self.minimumHoverHeight=4
-self.forcehoverload=true
-self.hoverautoloading=true
-self.dropcratesanywhere=false
-self.dropAsCargoCrate=false
-self.smokedistance=2000
-self.movetroopstowpzone=true
-self.movetroopsdistance=5000
-self.troopdropzoneradius=100
-self.enableHercules=false
-self.HercMinAngels=165
-self.HercMaxAngels=2000
-self.HercMaxSpeed=77
-self.suppressmessages=false
-self.repairtime=300
-self.buildtime=300
-self.placeCratesAhead=false
-self.cratecountry=country.id.GERMANY
-self.pilotmustopendoors=false
-if self.coalition==coalition.side.RED then
-self.cratecountry=country.id.RUSSIA
-end
-self.enableLoadSave=false
-self.filepath=nil
-self.saveinterval=600
-self.eventoninject=true
-self.usesubcats=false
-self.subcats={}
-self.subcatsTroop={}
-self.nobuildinloadzones=true
-self.movecratesbeforebuild=true
-self.surfacetypes={land.SurfaceType.LAND,land.SurfaceType.ROAD,land.SurfaceType.RUNWAY,land.SurfaceType.SHALLOW_WATER}
-local AliaS=string.gsub(self.alias," ","_")
-self.filename=string.format("CTLD_%s_Persist.csv",AliaS)
-self.allowcratepickupagain=true
-self.enableslingload=false
-self.basetype="container_cargo"
-self.SmokeColor=SMOKECOLOR.Red
-self.FlareColor=FLARECOLOR.Red
-for i=1,100 do
-math.random()
-end
-self:_GenerateVHFrequencies()
-self:_GenerateUHFrequencies()
-self:_GenerateFMFrequencies()
-return self
-end
-function CTLD:_GetUnitCapabilities(Unit)
-self:T(self.lid.." _GetUnitCapabilities")
-local _unit=Unit
-local unittype=_unit:GetTypeName()
-local capabilities=self.UnitTypeCapabilities[unittype]
-if not capabilities or capabilities=={}then
-capabilities={}
-capabilities.troops=false
-capabilities.crates=false
-capabilities.cratelimit=0
-capabilities.trooplimit=0
-capabilities.type="generic"
-capabilities.length=20
-capabilities.cargoweightlimit=0
-end
-return capabilities
-end
-function CTLD:_GenerateUHFrequencies()
-self:T(self.lid.." _GenerateUHFrequencies")
-self.FreeUHFFrequencies={}
-self.FreeUHFFrequencies=UTILS.GenerateUHFrequencies()
-return self
-end
-function CTLD:_GenerateFMFrequencies()
-self:T(self.lid.." _GenerateFMrequencies")
-self.FreeFMFrequencies={}
-self.FreeFMFrequencies=UTILS.GenerateFMFrequencies()
-return self
-end
-function CTLD:_GenerateVHFrequencies()
-self:T(self.lid.." _GenerateVHFrequencies")
-self.FreeVHFFrequencies={}
-self.UsedVHFFrequencies={}
-self.FreeVHFFrequencies=UTILS.GenerateVHFrequencies()
-return self
-end
-function CTLD:SetTroopDropZoneRadius(Radius)
-self:T(self.lid.." SetTroopDropZoneRadius")
-local tradius=Radius or 100
-if tradius<25 then tradius=25 end
-self.troopdropzoneradius=tradius
-return self
-end
-function CTLD:AddPlayerTask(PlayerTask)
-self:T(self.lid.." AddPlayerTask")
-if not self.PlayerTaskQueue then
-self.PlayerTaskQueue=FIFO:New()
-end
-self.PlayerTaskQueue:Push(PlayerTask,PlayerTask.PlayerTaskNr)
-return self
-end
-function CTLD:_EventHandler(EventData)
-self:T(string.format("%s Event = %d",self.lid,EventData.id))
-local event=EventData
-if event.id==EVENTS.PlayerEnterAircraft or event.id==EVENTS.PlayerEnterUnit then
-local _coalition=event.IniCoalition
-if _coalition~=self.coalition then
-return
-end
-local unitname=event.IniUnitName or"none"
-self.MenusDone[unitname]=nil
-local _unit=event.IniUnit
-local _group=event.IniGroup
-if _unit:IsHelicopter()or _group:IsHelicopter()then
-local unitname=event.IniUnitName or"none"
-self.Loaded_Cargo[unitname]=nil
-self:_RefreshF10Menus()
-end
-if self:IsHercules(_unit)and self.enableHercules then
-local unitname=event.IniUnitName or"none"
-self.Loaded_Cargo[unitname]=nil
-self:_RefreshF10Menus()
-end
-return
-elseif event.id==EVENTS.PlayerLeaveUnit then
-local unitname=event.IniUnitName or"none"
-self.CtldUnits[unitname]=nil
-self.Loaded_Cargo[unitname]=nil
-self.MenusDone[unitname]=nil
-end
-return self
-end
-function CTLD:_SendMessage(Text,Time,Clearscreen,Group)
-self:T(self.lid.." _SendMessage")
-if not self.suppressmessages then
-local m=MESSAGE:New(Text,Time,"CTLD",Clearscreen):ToGroup(Group)
-end
-return self
-end
-function CTLD:_FindTroopsCargoObject(Name)
-self:T(self.lid.." _FindTroopsCargoObject")
-local cargo=nil
-for _,_cargo in pairs(self.Cargo_Troops)do
-local cargo=_cargo
-if cargo.Name==Name then
-return cargo
-end
-end
-return nil
-end
-function CTLD:_FindCratesCargoObject(Name)
-self:T(self.lid.." _FindCratesCargoObject")
-local cargo=nil
-for _,_cargo in pairs(self.Cargo_Crates)do
-local cargo=_cargo
-if cargo.Name==Name then
-return cargo
-end
-end
-return nil
-end
-function CTLD:PreloadTroops(Unit,Troopname)
-self:T(self.lid.." PreloadTroops")
-local name=Troopname or"Unknown"
-if Unit and Unit:IsAlive()then
-local cargo=self:_FindTroopsCargoObject(name)
-local group=Unit:GetGroup()
-if cargo then
-self:_LoadTroops(group,Unit,cargo,true)
-else
-self:E(self.lid.." Troops preload - Cargo Object "..name.." not found!")
-end
-end
-return self
-end
-function CTLD:_PreloadCrates(Group,Unit,Cargo,NumberOfCrates)
-local group=Group
-local unit=Unit
-local unitname=unit:GetName()
-local unittype=unit:GetTypeName()
-local capabilities=self:_GetUnitCapabilities(Unit)
-local cancrates=capabilities.crates
-local cratelimit=capabilities.cratelimit
-if not cancrates then
-self:_SendMessage("Sorry this chopper cannot carry crates!",10,false,Group)
-return self
-else
-local numberonboard=0
-local massonboard=0
-local loaded={}
-if self.Loaded_Cargo[unitname]then
-loaded=self.Loaded_Cargo[unitname]
-numberonboard=loaded.Cratesloaded or 0
-massonboard=self:_GetUnitCargoMass(Unit)
-else
-loaded={}
-loaded.Troopsloaded=0
-loaded.Cratesloaded=0
-loaded.Cargo={}
-end
-local crate=Cargo
-local numbercrates=NumberOfCrates or crate:GetCratesNeeded()
-for i=1,numbercrates do
-loaded.Cratesloaded=loaded.Cratesloaded+1
-crate:SetHasMoved(true)
-crate:SetWasDropped(false)
-table.insert(loaded.Cargo,crate)
-crate.Positionable=nil
-self:_SendMessage(string.format("Crate ID %d for %s loaded!",crate:GetID(),crate:GetName()),10,false,Group)
-self.Loaded_Cargo[unitname]=loaded
-self:_UpdateUnitCargoMass(Unit)
-end
-end
-return self
-end
-function CTLD:PreloadCrates(Unit,Cratesname,NumberOfCrates)
-self:T(self.lid.." PreloadCrates")
-local name=Cratesname or"Unknown"
-if Unit and Unit:IsAlive()then
-local cargo=self:_FindCratesCargoObject(name)
-local group=Unit:GetGroup()
-if cargo then
-self:_PreloadCrates(group,Unit,cargo,NumberOfCrates)
-else
-self:E(self.lid.." Crates preload - Cargo Object "..name.." not found!")
-end
-end
-return self
-end
-function CTLD:_LoadTroops(Group,Unit,Cargotype,Inject)
-self:T(self.lid.." _LoadTroops")
-local instock=Cargotype:GetStock()
-local cgoname=Cargotype:GetName()
-local cgotype=Cargotype:GetType()
-local cgonetmass=Cargotype:GetNetMass()
-local maxloadable=self:_GetMaxLoadableMass(Unit)
-if type(instock)=="number"and tonumber(instock)<=0 and tonumber(instock)~=-1 and not Inject then
-self:_SendMessage(string.format("Sorry, all %s are gone!",cgoname),10,false,Group)
-return self
-end
-local grounded=not self:IsUnitInAir(Unit)
-local hoverload=self:CanHoverLoad(Unit)
-local inzone,zonename,zone,distance=self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD)
-if not inzone then
-inzone,zonename,zone,distance=self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP)
-end
-if not Inject then
-if not inzone then
-self:_SendMessage("You are not close enough to a logistics zone!",10,false,Group)
-if not self.debug then return self end
-elseif not grounded and not hoverload then
-self:_SendMessage("You need to land or hover in position to load!",10,false,Group)
-if not self.debug then return self end
-elseif self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName())then
-self:_SendMessage("You need to open the door(s) to load troops!",10,false,Group)
-if not self.debug then return self end
-end
-end
-local group=Group
-local unit=Unit
-local unitname=unit:GetName()
-local cargotype=Cargotype
-local cratename=cargotype:GetName()
-local unittype=unit:GetTypeName()
-local capabilities=self:_GetUnitCapabilities(Unit)
-local cantroops=capabilities.troops
-local trooplimit=capabilities.trooplimit
-local troopsize=cargotype:GetCratesNeeded()
-local numberonboard=0
-local loaded={}
-if self.Loaded_Cargo[unitname]then
-loaded=self.Loaded_Cargo[unitname]
-numberonboard=loaded.Troopsloaded or 0
-else
-loaded={}
-loaded.Troopsloaded=0
-loaded.Cratesloaded=0
-loaded.Cargo={}
-end
-if troopsize+numberonboard>trooplimit then
-self:_SendMessage("Sorry, we\'re crammed already!",10,false,Group)
-return
-elseif maxloadableself.EngineerSearch then
-self:_SendMessage("No unit close enough to repair!",10,false,Group)
-return nil,nil
-end
-local groupname=nearestGroup:GetName()
-local function matchstring(String,Table)
-local match=false
-String=string.gsub(String,"-"," ")
-if type(Table)=="table"then
-for _,_name in pairs(Table)do
-_name=string.gsub(_name,"-"," ")
-if string.find(String,_name)then
-match=true
-break
-end
-end
-else
-if type(String)=="string"then
-Table=string.gsub(Table,"-"," ")
-if string.find(String,Table)then match=true end
-end
-end
-return match
-end
-local Cargotype=nil
-for k,v in pairs(self.Cargo_Crates)do
-if matchstring(groupname,v.Templates)and matchstring(groupname,Repairtype)then
-Cargotype=v
-break
-end
-end
-if Cargotype==nil then
-return nil,nil
-else
-return nearestGroup,Cargotype
-end
-end
-function CTLD:_RepairObjectFromCrates(Group,Unit,Crates,Build,Number,Engineering)
-self:T(self.lid.." _RepairObjectFromCrates")
-local build=Build
-local Repairtype=build.Template
-local NearestGroup,CargoType=self:_FindRepairNearby(Group,Unit,Repairtype)
-if NearestGroup~=nil then
-if self.repairtime<2 then self.repairtime=30 end
-if not Engineering then
-self:_SendMessage(string.format("Repair started using %s taking %d secs",build.Name,self.repairtime),10,false,Group)
-end
-local name=CargoType:GetName()
-local required=CargoType:GetCratesNeeded()
-local template=CargoType:GetTemplates()
-local ctype=CargoType:GetType()
-local object={}
-object.Name=CargoType:GetName()
-object.Required=required
-object.Found=required
-object.Template=template
-object.CanBuild=true
-object.Type=ctype
-self:_CleanUpCrates(Crates,Build,Number)
-local desttimer=TIMER:New(function()NearestGroup:Destroy(false)end,self)
-desttimer:Start(self.repairtime-1)
-local buildtimer=TIMER:New(self._BuildObjectFromCrates,self,Group,Unit,object,true,NearestGroup:GetCoordinate())
-buildtimer:Start(self.repairtime)
-self:__CratesRepairStarted(1,Group,Unit)
-else
-if not Engineering then
-self:_SendMessage("Can't repair this unit with "..build.Name,10,false,Group)
-else
-self:T("Can't repair this unit with "..build.Name)
-end
-end
-return self
-end
-function CTLD:_ExtractTroops(Group,Unit)
-self:T(self.lid.." _ExtractTroops")
-local grounded=not self:IsUnitInAir(Unit)
-local hoverload=self:CanHoverLoad(Unit)
-if not grounded and not hoverload then
-self:_SendMessage("You need to land or hover in position to load!",10,false,Group)
-if not self.debug then return self end
-end
-if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName())then
-self:_SendMessage("You need to open the door(s) to extract troops!",10,false,Group)
-if not self.debug then return self end
-end
-local unit=Unit
-local unitname=unit:GetName()
-local unittype=unit:GetTypeName()
-local capabilities=self:_GetUnitCapabilities(Unit)
-local cantroops=capabilities.troops
-local trooplimit=capabilities.trooplimit
-local unitcoord=unit:GetCoordinate()
-local nearestGroup=nil
-local nearestGroupIndex=-1
-local nearestDistance=10000000
-local nearestList={}
-local distancekeys={}
-local extractdistance=self.CrateDistance*self.ExtractFactor
-for k,v in pairs(self.DroppedTroops)do
-local distance=self:_GetDistance(v:GetCoordinate(),unitcoord)
-if distance<=extractdistance and distance~=-1 then
-nearestGroup=v
-nearestGroupIndex=k
-nearestDistance=distance
-table.insert(nearestList,math.floor(distance),v)
-distancekeys[#distancekeys+1]=math.floor(distance)
-end
-end
-if nearestGroup==nil or nearestDistance>extractdistance then
-self:_SendMessage("No units close enough to extract!",10,false,Group)
-return self
-end
-table.sort(distancekeys)
-local secondarygroups={}
-for i=1,#distancekeys do
-local nearestGroup=nearestList[distancekeys[i]]
-local groupType=string.match(nearestGroup:GetName(),"(.+)-(.+)$")
-local Cargotype=nil
-for k,v in pairs(self.Cargo_Troops)do
-local comparison=""
-if type(v.Templates)=="string"then comparison=v.Templates else comparison=v.Templates[1]end
-if comparison==groupType then
-Cargotype=v
-break
-end
-end
-if Cargotype==nil then
-self:_SendMessage("Can't onboard "..groupType,10,false,Group)
-else
-local troopsize=Cargotype:GetCratesNeeded()
-local numberonboard=0
-local loaded={}
-if self.Loaded_Cargo[unitname]then
-loaded=self.Loaded_Cargo[unitname]
-numberonboard=loaded.Troopsloaded or 0
-else
-loaded={}
-loaded.Troopsloaded=0
-loaded.Cratesloaded=0
-loaded.Cargo={}
-end
-if troopsize+numberonboard>trooplimit then
-self:_SendMessage("Sorry, we\'re crammed already!",10,false,Group)
-else
-self.CargoCounter=self.CargoCounter+1
-local loadcargotype=CTLD_CARGO:New(self.CargoCounter,Cargotype.Name,Cargotype.Templates,Cargotype.CargoType,true,true,Cargotype.CratesNeeded,nil,nil,Cargotype.PerCrateMass)
-self:T({cargotype=loadcargotype})
-loaded.Troopsloaded=loaded.Troopsloaded+troopsize
-table.insert(loaded.Cargo,loadcargotype)
-self.Loaded_Cargo[unitname]=loaded
-self:_SendMessage("Troops boarded!",10,false,Group)
-self:_UpdateUnitCargoMass(Unit)
-self:__TroopsExtracted(1,Group,Unit,nearestGroup)
-if type(Cargotype.Templates)=="table"and Cargotype.Templates[2]then
-for _,_key in pairs(Cargotype.Templates)do
-table.insert(secondarygroups,_key)
-end
-end
-nearestGroup:Destroy(false)
-end
-end
-end
-for _,_name in pairs(secondarygroups)do
-for _,_group in pairs(nearestList)do
-if _group and _group:IsAlive()then
-local groupname=string.match(_group:GetName(),"(.+)-(.+)$")
-if _name==groupname then
-_group:Destroy(false)
-end
-end
-end
-end
-self:CleanDroppedTroops()
-return self
-end
-function CTLD:_GetCrates(Group,Unit,Cargo,number,drop,pack)
-self:T(self.lid.." _GetCrates")
-if not drop and not pack then
-local cgoname=Cargo:GetName()
-local instock=Cargo:GetStock()
-if type(instock)=="number"and tonumber(instock)<=0 and tonumber(instock)~=-1 then
-self:_SendMessage(string.format("Sorry, we ran out of %s",cgoname),10,false,Group)
-return self
-end
-end
-local inzone=false
-local drop=drop or false
-local ship=nil
-local width=20
-local distance=nil
-local zone=nil
-if not drop and not pack then
-inzone=self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD)
-if not inzone then
-inzone,ship,zone,distance,width=self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP)
-end
-elseif drop and not pack then
-if self.dropcratesanywhere then
-inzone=true
-else
-inzone=self:IsUnitInZone(Unit,CTLD.CargoZoneType.DROP)
-end
-elseif pack and not drop then
-inzone=true
-end
-if not inzone then
-self:_SendMessage("You are not close enough to a logistics zone!",10,false,Group)
-if not self.debug then return self end
-end
-local capabilities=self:_GetUnitCapabilities(Unit)
-local canloadcratesno=capabilities.cratelimit
-local loaddist=self.CrateDistance or 35
-local nearcrates,numbernearby=self:_FindCratesNearby(Group,Unit,loaddist,true)
-if numbernearby>=canloadcratesno and not drop then
-self:_SendMessage("There are enough crates nearby already! Take care of those first!",10,false,Group)
-return self
-end
-local IsHerc=self:IsHercules(Unit)
-local cargotype=Cargo
-local number=number or cargotype:GetCratesNeeded()
-local cratesneeded=cargotype:GetCratesNeeded()
-local cratename=cargotype:GetName()
-local cratetemplate="Container"
-local cgotype=cargotype:GetType()
-local cgomass=cargotype:GetMass()
-local isstatic=false
-if cgotype==CTLD_CARGO.Enum.STATIC then
-cratetemplate=cargotype:GetTemplates()
-isstatic=true
-end
-local position=Unit:GetCoordinate()
-local heading=Unit:GetHeading()+1
-local height=Unit:GetHeight()
-local droppedcargo={}
-local cratedistance=0
-local rheading=0
-local angleOffNose=0
-local addon=0
-if IsHerc then
-addon=180
-end
-for i=1,number do
-local cratealias=string.format("%s-%s-%d",cratename,cratetemplate,math.random(1,100000))
-if not self.placeCratesAhead then
-cratedistance=(i-1)*2.5+capabilities.length
-if cratedistance>self.CrateDistance then cratedistance=self.CrateDistance end
-rheading=UTILS.RandomGaussian(0,30,-90,90,100)
-rheading=math.fmod((heading+rheading+addon),360)
-else
-local initialSpacing=IsHerc and 16 or(capabilities.length+2)
-local crateSpacing=4
-local lateralSpacing=4
-local nrSideBySideCrates=3
-if cratesneeded==1 then
-cratedistance=initialSpacing
-rheading=heading
-else
-if(i-1)%nrSideBySideCrates==0 then
-cratedistance=i==1 and initialSpacing or cratedistance+crateSpacing
-angleOffNose=math.ceil(math.deg(math.atan(lateralSpacing/cratedistance)))
-rheading=heading-angleOffNose
-else
-rheading=rheading+angleOffNose
-end
-end
-end
-local cratecoord=position:Translate(cratedistance,rheading)
-local cratevec2=cratecoord:GetVec2()
-self.CrateCounter=self.CrateCounter+1
-local basetype=self.basetype or"container_cargo"
-if isstatic then
-basetype=cratetemplate
-end
-if type(ship)=="string"then
-self:T("Spawning on ship "..ship)
-local Ship=UNIT:FindByName(ship)
-local shipcoord=Ship:GetCoordinate()
-local unitcoord=Unit:GetCoordinate()
-local dist=shipcoord:Get2DDistance(unitcoord)
-dist=dist-(20+math.random(1,10))
-local width=width/2
-local Offy=math.random(-width,width)
-self.Spawned_Crates[self.CrateCounter]=SPAWNSTATIC:NewFromType(basetype,"Cargos",self.cratecountry)
-:InitCargoMass(cgomass)
-:InitCargo(self.enableslingload)
-:InitLinkToUnit(Ship,dist,Offy,0)
-:Spawn(270,cratealias)
-else
-self.Spawned_Crates[self.CrateCounter]=SPAWNSTATIC:NewFromType(basetype,"Cargos",self.cratecountry)
-:InitCoordinate(cratecoord)
-:InitCargoMass(cgomass)
-:InitCargo(self.enableslingload)
-:Spawn(270,cratealias)
-end
-local templ=cargotype:GetTemplates()
-local sorte=cargotype:GetType()
-local subcat=cargotype.Subcategory
-self.CargoCounter=self.CargoCounter+1
-local realcargo=nil
-if drop then
-realcargo=CTLD_CARGO:New(self.CargoCounter,cratename,templ,sorte,true,false,cratesneeded,self.Spawned_Crates[self.CrateCounter],true,cargotype.PerCrateMass,nil,subcat)
-table.insert(droppedcargo,realcargo)
-else
-realcargo=CTLD_CARGO:New(self.CargoCounter,cratename,templ,sorte,false,false,cratesneeded,self.Spawned_Crates[self.CrateCounter],false,cargotype.PerCrateMass,nil,subcat)
-Cargo:RemoveStock()
-end
-table.insert(self.Spawned_Cargo,realcargo)
-end
-local text=string.format("Crates for %s have been positioned near you!",cratename)
-if drop then
-text=string.format("Crates for %s have been dropped!",cratename)
-self:__CratesDropped(1,Group,Unit,droppedcargo)
-end
-self:_SendMessage(text,10,false,Group)
-return self
-end
-function CTLD:InjectStatics(Zone,Cargo,RandomCoord)
-self:T(self.lid.." InjectStatics")
-local cratecoord=Zone:GetCoordinate()
-if RandomCoord then
-cratecoord=Zone:GetRandomCoordinate(5,20)
-end
-local surface=cratecoord:GetSurfaceType()
-if surface==land.SurfaceType.WATER then
-return self
-end
-local cargotype=Cargo
-local cratesneeded=cargotype:GetCratesNeeded()
-local cratetemplate="Container"
-local cratename=cargotype:GetName()
-local cgotype=cargotype:GetType()
-local cgomass=cargotype:GetMass()
-local cratealias=string.format("%s-%s-%d",cratename,cratetemplate,math.random(1,100000))
-local isstatic=false
-if cgotype==CTLD_CARGO.Enum.STATIC then
-cratetemplate=cargotype:GetTemplates()
-isstatic=true
-end
-local basetype=self.basetype or"container_cargo"
-if isstatic then
-basetype=cratetemplate
-end
-self.CrateCounter=self.CrateCounter+1
-self.Spawned_Crates[self.CrateCounter]=SPAWNSTATIC:NewFromType(basetype,"Cargos",self.cratecountry)
-:InitCargoMass(cgomass)
-:InitCargo(self.enableslingload)
-:InitCoordinate(cratecoord)
-:Spawn(270,cratealias)
-local templ=cargotype:GetTemplates()
-local sorte=cargotype:GetType()
-self.CargoCounter=self.CargoCounter+1
-cargotype.Positionable=self.Spawned_Crates[self.CrateCounter]
-table.insert(self.Spawned_Cargo,cargotype)
-return self
-end
-function CTLD:InjectStaticFromTemplate(Zone,Template,Mass)
-self:T(self.lid.." InjectStaticFromTemplate")
-local cargotype=self:GetStaticsCargoFromTemplate(Template,Mass)
-self:InjectStatics(Zone,cargotype,true)
-return self
-end
-function CTLD:_ListCratesNearby(_group,_unit)
-self:T(self.lid.." _ListCratesNearby")
-local finddist=self.CrateDistance or 35
-local crates,number=self:_FindCratesNearby(_group,_unit,finddist,true)
-if number>0 then
-local text=REPORT:New("Crates Found Nearby:")
-text:Add("------------------------------------------------------------")
-for _,_entry in pairs(crates)do
-local entry=_entry
-local name=entry:GetName()
-local dropped=entry:WasDropped()
-if dropped then
-text:Add(string.format("Dropped crate for %s, %dkg",name,entry.PerCrateMass))
-else
-text:Add(string.format("Crate for %s, %dkg",name,entry.PerCrateMass))
-end
-end
-if text:GetCount()==1 then
-text:Add(" N O N E")
-end
-text:Add("------------------------------------------------------------")
-self:_SendMessage(text:Text(),30,true,_group)
-else
-self:_SendMessage(string.format("No (loadable) crates within %d meters!",finddist),10,false,_group)
-end
-return self
-end
-function CTLD:_RemoveCratesNearby(_group,_unit)
-self:T(self.lid.." _RemoveCratesNearby")
-local finddist=self.CrateDistance or 35
-local crates,number=self:_FindCratesNearby(_group,_unit,finddist,true)
-if number>0 then
-local text=REPORT:New("Removing Crates Found Nearby:")
-text:Add("------------------------------------------------------------")
-for _,_entry in pairs(crates)do
-local entry=_entry
-local name=entry:GetName()
-local dropped=entry:WasDropped()
-if dropped then
-text:Add(string.format("Crate for %s, %dkg removed",name,entry.PerCrateMass))
-else
-text:Add(string.format("Crate for %s, %dkg removed",name,entry.PerCrateMass))
-end
-entry:GetPositionable():Destroy(false)
-end
-if text:GetCount()==1 then
-text:Add(" N O N E")
-end
-text:Add("------------------------------------------------------------")
-self:_SendMessage(text:Text(),30,true,_group)
-else
-self:_SendMessage(string.format("No (loadable) crates within %d meters!",finddist),10,false,_group)
-end
-return self
-end
-function CTLD:_GetDistance(_point1,_point2)
-self:T(self.lid.." _GetDistance")
-if _point1 and _point2 then
-local distance1=_point1:Get2DDistance(_point2)
-local distance2=_point1:DistanceFromPointVec2(_point2)
-if distance1 and type(distance1)=="number"then
-return distance1
-elseif distance2 and type(distance2)=="number"then
-return distance2
-else
-self:E("*****Cannot calculate distance!")
-self:E({_point1,_point2})
-return-1
-end
-else
-self:E("******Cannot calculate distance!")
-self:E({_point1,_point2})
-return-1
-end
-end
-function CTLD:_FindCratesNearby(_group,_unit,_dist,_ignoreweight)
-self:T(self.lid.." _FindCratesNearby")
-local finddist=_dist
-local location=_group:GetCoordinate()
-local existingcrates=self.Spawned_Cargo
-local index=0
-local found={}
-local loadedmass=0
-local unittype="none"
-local capabilities={}
-local maxmass=2000
-local maxloadable=2000
-if not _ignoreweight then
-maxloadable=self:_GetMaxLoadableMass(_unit)
-end
-self:T(self.lid.." Max loadable mass: "..maxloadable)
-for _,_cargoobject in pairs(existingcrates)do
-local cargo=_cargoobject
-local static=cargo:GetPositionable()
-local staticid=cargo:GetID()
-local weight=cargo:GetMass()
-self:T(self.lid.." Found cargo mass: "..weight)
-if static and static:IsAlive()then
-local staticpos=static:GetCoordinate()
-local distance=self:_GetDistance(location,staticpos)
-if distance<=finddist and static and(weight<=maxloadable or _ignoreweight)then
-index=index+1
-table.insert(found,staticid,cargo)
-maxloadable=maxloadable-weight
-end
-end
-end
-return found,index
-end
-function CTLD:_LoadCratesNearby(Group,Unit)
-self:T(self.lid.." _LoadCratesNearby")
-local group=Group
-local unit=Unit
-local unitname=unit:GetName()
-local unittype=unit:GetTypeName()
-local capabilities=self:_GetUnitCapabilities(Unit)
-local cancrates=capabilities.crates
-local cratelimit=capabilities.cratelimit
-local grounded=not self:IsUnitInAir(Unit)
-local canhoverload=self:CanHoverLoad(Unit)
-if not cancrates then
-self:_SendMessage("Sorry this chopper cannot carry crates!",10,false,Group)
-elseif self.forcehoverload and not canhoverload then
-self:_SendMessage("Hover over the crates to pick them up!",10,false,Group)
-elseif not grounded and not canhoverload then
-self:_SendMessage("Land or hover over the crates to pick them up!",10,false,Group)
-else
-local numberonboard=0
-local massonboard=0
-local loaded={}
-if self.Loaded_Cargo[unitname]then
-loaded=self.Loaded_Cargo[unitname]
-numberonboard=loaded.Cratesloaded or 0
-massonboard=self:_GetUnitCargoMass(Unit)
-else
-loaded={}
-loaded.Troopsloaded=0
-loaded.Cratesloaded=0
-loaded.Cargo={}
-end
-local finddist=self.CrateDistance or 35
-local nearcrates,number=self:_FindCratesNearby(Group,Unit,finddist,false)
-self:T(self.lid.." Crates found: "..number)
-if number==0 and self.hoverautoloading then
-return self
-elseif number==0 then
-self:_SendMessage("Sorry no loadable crates nearby or max cargo weight reached!",10,false,Group)
-return self
-elseif numberonboard==cratelimit then
-self:_SendMessage("Sorry no fully loaded!",10,false,Group)
-return self
-else
-local capacity=cratelimit-numberonboard
-local crateidsloaded={}
-local loops=0
-while loaded.Cratesloadedcrateind and _crate.Positionable~=nil then
-crateind=_crate:GetID()
-end
-else
-if not _crate:HasMoved()and not _crate:WasDropped()and _crate:GetID()>crateind then
-crateind=_crate:GetID()
-end
-end
-end
-if crateind>0 then
-local crate=nearcrates[crateind]
-loaded.Cratesloaded=loaded.Cratesloaded+1
-crate:SetHasMoved(true)
-crate:SetWasDropped(false)
-table.insert(loaded.Cargo,crate)
-table.insert(crateidsloaded,crate:GetID())
-crate:GetPositionable():Destroy(false)
-crate.Positionable=nil
-self:_SendMessage(string.format("Crate ID %d for %s loaded!",crate:GetID(),crate:GetName()),10,false,Group)
-table.remove(nearcrates,crate:GetID())
-self:__CratesPickedUp(1,Group,Unit,crate)
-end
-end
-self.Loaded_Cargo[unitname]=loaded
-self:_UpdateUnitCargoMass(Unit)
-self:_CleanupTrackedCrates(crateidsloaded)
-end
-end
-return self
-end
-function CTLD:_CleanupTrackedCrates(crateIdsToRemove)
-local existingcrates=self.Spawned_Cargo
-local newexcrates={}
-for _,_crate in pairs(existingcrates)do
-local excrate=_crate
-local ID=excrate:GetID()
-local keep=true
-for _,_ID in pairs(crateIdsToRemove)do
-if ID==_ID then
-keep=false
-end
-end
-local static=_crate:GetPositionable()
-if not static or not static:IsAlive()then
-keep=false
-end
-if keep then
-table.insert(newexcrates,_crate)
-end
-end
-self.Spawned_Cargo=nil
-self.Spawned_Cargo=newexcrates
-return self
-end
-function CTLD:_GetUnitCargoMass(Unit)
-self:T(self.lid.." _GetUnitCargoMass")
-if not Unit then return 0 end
-local unitname=Unit:GetName()
-local loadedcargo=self.Loaded_Cargo[unitname]or{}
-local loadedmass=0
-if self.Loaded_Cargo[unitname]then
-local cargotable=loadedcargo.Cargo or{}
-for _,_cargo in pairs(cargotable)do
-local cargo=_cargo
-local type=cargo:GetType()
-if(type==CTLD_CARGO.Enum.TROOPS or type==CTLD_CARGO.Enum.ENGINEERS)and not cargo:WasDropped()then
-loadedmass=loadedmass+(cargo.PerCrateMass*cargo:GetCratesNeeded())
-end
-if type~=CTLD_CARGO.Enum.TROOPS and type~=CTLD_CARGO.Enum.ENGINEERS and not cargo:WasDropped()then
-loadedmass=loadedmass+cargo.PerCrateMass
-end
-end
-end
-return loadedmass
-end
-function CTLD:_GetMaxLoadableMass(Unit)
-self:T(self.lid.." _GetMaxLoadableMass")
-if not Unit then return 0 end
-local loadable=0
-local loadedmass=self:_GetUnitCargoMass(Unit)
-local capabilities=self:_GetUnitCapabilities(Unit)
-local maxmass=capabilities.cargoweightlimit or 2000
-loadable=maxmass-loadedmass
-return loadable
-end
-function CTLD:_UpdateUnitCargoMass(Unit)
-self:T(self.lid.." _UpdateUnitCargoMass")
-local calculatedMass=self:_GetUnitCargoMass(Unit)
-Unit:SetUnitInternalCargo(calculatedMass)
-return self
-end
-function CTLD:_ListCargo(Group,Unit)
-self:T(self.lid.." _ListCargo")
-local unitname=Unit:GetName()
-local unittype=Unit:GetTypeName()
-local capabilities=self:_GetUnitCapabilities(Unit)
-local trooplimit=capabilities.trooplimit
-local cratelimit=capabilities.cratelimit
-local loadedcargo=self.Loaded_Cargo[unitname]or{}
-local loadedmass=self:_GetUnitCargoMass(Unit)
-local maxloadable=self:_GetMaxLoadableMass(Unit)
-if self.Loaded_Cargo[unitname]then
-local no_troops=loadedcargo.Troopsloaded or 0
-local no_crates=loadedcargo.Cratesloaded or 0
-local cargotable=loadedcargo.Cargo or{}
-local report=REPORT:New("Transport Checkout Sheet")
-report:Add("------------------------------------------------------------")
-report:Add(string.format("Troops: %d(%d), Crates: %d(%d)",no_troops,trooplimit,no_crates,cratelimit))
-report:Add("------------------------------------------------------------")
-report:Add(" -- TROOPS --")
-for _,_cargo in pairs(cargotable)do
-local cargo=_cargo
-local type=cargo:GetType()
-if(type==CTLD_CARGO.Enum.TROOPS or type==CTLD_CARGO.Enum.ENGINEERS)and(not cargo:WasDropped()or self.allowcratepickupagain)then
-report:Add(string.format("Troop: %s size %d",cargo:GetName(),cargo:GetCratesNeeded()))
-end
-end
-if report:GetCount()==4 then
-report:Add(" N O N E")
-end
-report:Add("------------------------------------------------------------")
-report:Add(" -- CRATES --")
-local cratecount=0
-for _,_cargo in pairs(cargotable)do
-local cargo=_cargo
-local type=cargo:GetType()
-if(type~=CTLD_CARGO.Enum.TROOPS and type~=CTLD_CARGO.Enum.ENGINEERS)and(not cargo:WasDropped()or self.allowcratepickupagain)then
-report:Add(string.format("Crate: %s size 1",cargo:GetName()))
-cratecount=cratecount+1
-end
-end
-if cratecount==0 then
-report:Add(" N O N E")
-end
-report:Add("------------------------------------------------------------")
-report:Add("Total Mass: "..loadedmass.." kg. Loadable: "..maxloadable.." kg.")
-local text=report:Text()
-self:_SendMessage(text,30,true,Group)
-else
-self:_SendMessage(string.format("Nothing loaded!\nTroop limit: %d | Crate limit %d | Weight limit %d kgs",trooplimit,cratelimit,maxloadable),10,false,Group)
-end
-return self
-end
-function CTLD:_ListInventory(Group,Unit)
-self:T(self.lid.." _ListInventory")
-local unitname=Unit:GetName()
-local unittype=Unit:GetTypeName()
-local cgotypes=self.Cargo_Crates
-local trptypes=self.Cargo_Troops
-local stctypes=self.Cargo_Statics
-local function countcargo(cgotable)
-local counter=0
-for _,_cgo in pairs(cgotable)do
-counter=counter+1
-end
-return counter
-end
-local crateno=countcargo(cgotypes)
-local troopno=countcargo(trptypes)
-local staticno=countcargo(stctypes)
-if(crateno>0 or troopno>0 or staticno>0)then
-local report=REPORT:New("Inventory Sheet")
-report:Add("------------------------------------------------------------")
-report:Add(string.format("Troops: %d, Cratetypes: %d",troopno,crateno+staticno))
-report:Add("------------------------------------------------------------")
-report:Add(" -- TROOPS --")
-for _,_cargo in pairs(trptypes)do
-local cargo=_cargo
-local type=cargo:GetType()
-if(type==CTLD_CARGO.Enum.TROOPS or type==CTLD_CARGO.Enum.ENGINEERS)and not cargo:WasDropped()then
-local stockn=cargo:GetStock()
-local stock="none"
-if stockn==-1 then
-stock="unlimited"
-elseif stockn>0 then
-stock=tostring(stockn)
-end
-report:Add(string.format("Unit: %s | Soldiers: %d | Stock: %s",cargo:GetName(),cargo:GetCratesNeeded(),stock))
-end
-end
-if report:GetCount()==4 then
-report:Add(" N O N E")
-end
-report:Add("------------------------------------------------------------")
-report:Add(" -- CRATES --")
-local cratecount=0
-for _,_cargo in pairs(cgotypes)do
-local cargo=_cargo
-local type=cargo:GetType()
-if(type~=CTLD_CARGO.Enum.TROOPS and type~=CTLD_CARGO.Enum.ENGINEERS)and not cargo:WasDropped()then
-local stockn=cargo:GetStock()
-local stock="none"
-if stockn==-1 then
-stock="unlimited"
-elseif stockn>0 then
-stock=tostring(stockn)
-end
-report:Add(string.format("Type: %s | Crates per Set: %d | Stock: %s",cargo:GetName(),cargo:GetCratesNeeded(),stock))
-cratecount=cratecount+1
-end
-end
-for _,_cargo in pairs(stctypes)do
-local cargo=_cargo
-local type=cargo:GetType()
-if(type==CTLD_CARGO.Enum.STATIC)and not cargo:WasDropped()then
-local stockn=cargo:GetStock()
-local stock="none"
-if stockn==-1 then
-stock="unlimited"
-elseif stockn>0 then
-stock=tostring(stockn)
-end
-report:Add(string.format("Type: %s | Stock: %s",cargo:GetName(),stock))
-cratecount=cratecount+1
-end
-end
-if cratecount==0 then
-report:Add(" N O N E")
-end
-local text=report:Text()
-self:_SendMessage(text,30,true,Group)
-else
-self:_SendMessage(string.format("Nothing in stock!"),10,false,Group)
-end
-return self
-end
-function CTLD:IsHercules(Unit)
-if Unit:GetTypeName()=="Hercules"or string.find(Unit:GetTypeName(),"Bronco")then
-return true
-else
-return false
-end
-end
-function CTLD:_GetUnitPositions(Coordinate,Radius,Heading,Template)
-local Positions={}
-local template=_DATABASE:GetGroupTemplate(Template)
-UTILS.PrintTableToLog(template)
-local numbertroops=#template.units
-local newcenter=Coordinate:Translate(Radius,((Heading+270)%360))
-for i=1,360,math.floor(360/numbertroops)do
-local phead=((Heading+270+i)%360)
-local post=newcenter:Translate(Radius,phead)
-local pos1=post:GetVec2()
-local p1t={
-x=pos1.x,
-y=pos1.y,
-heading=phead,
-}
-table.insert(Positions,p1t)
-end
-UTILS.PrintTableToLog(Positions)
-return Positions
-end
-function CTLD:_UnloadTroops(Group,Unit)
-self:T(self.lid.." _UnloadTroops")
-local droppingatbase=false
-local canunload=true
-if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName())then
-self:_SendMessage("You need to open the door(s) to unload troops!",10,false,Group)
-if not self.debug then return self end
-end
-local inzone,zonename,zone,distance=self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD)
-if not inzone then
-inzone,zonename,zone,distance=self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP)
-end
-if inzone then
-droppingatbase=true
-end
-local hoverunload=self:IsCorrectHover(Unit)
-local IsHerc=self:IsHercules(Unit)
-if IsHerc then
-hoverunload=self:IsCorrectFlightParameters(Unit)
-end
-local grounded=not self:IsUnitInAir(Unit)
-local unitname=Unit:GetName()
-if self.Loaded_Cargo[unitname]and(grounded or hoverunload)then
-if not droppingatbase or self.debug then
-local loadedcargo=self.Loaded_Cargo[unitname]or{}
-local cargotable=loadedcargo.Cargo
-for _,_cargo in pairs(cargotable)do
-local cargo=_cargo
-local type=cargo:GetType()
-if(type==CTLD_CARGO.Enum.TROOPS or type==CTLD_CARGO.Enum.ENGINEERS)and not cargo:WasDropped()then
-local name=cargo:GetName()or"none"
-local temptable=cargo:GetTemplates()or{}
-local position=Group:GetCoordinate()
-local zoneradius=self.troopdropzoneradius or 100
-local factor=1
-if IsHerc then
-factor=cargo:GetCratesNeeded()or 1
-zoneradius=Unit:GetVelocityMPS()or 100
-end
-local zone=ZONE_GROUP:New(string.format("Unload zone-%s",unitname),Group,zoneradius*factor)
-local randomcoord=zone:GetRandomCoordinate(10,30*factor)
-local heading=Group:GetHeading()or 0
-if hoverunload or grounded then
-randomcoord=Group:GetCoordinate()
-local Angle=(heading+270)%360
-local offset=hoverunload and 1.5 or 5
-randomcoord:Translate(offset,Angle,nil,true)
-end
-local tempcount=0
-for _,_template in pairs(temptable)do
-self.TroopCounter=self.TroopCounter+1
-tempcount=tempcount+1
-local alias=string.format("%s-%d",_template,math.random(1,100000))
-local rad=2.5+tempcount
-local Positions=self:_GetUnitPositions(randomcoord,rad,heading,_template)
-self.DroppedTroops[self.TroopCounter]=SPAWN:NewWithAlias(_template,alias)
-:InitDelayOff()
-:InitSetUnitAbsolutePositions(Positions)
-:SpawnFromVec2(randomcoord:GetVec2())
-self:__TroopsDeployed(1,Group,Unit,self.DroppedTroops[self.TroopCounter],type)
-end
-cargo:SetWasDropped(true)
-if type==CTLD_CARGO.Enum.ENGINEERS then
-self.Engineers=self.Engineers+1
-local grpname=self.DroppedTroops[self.TroopCounter]:GetName()
-self.EngineersInField[self.Engineers]=CTLD_ENGINEERING:New(name,grpname)
-self:_SendMessage(string.format("Dropped Engineers %s into action!",name),10,false,Group)
-else
-self:_SendMessage(string.format("Dropped Troops %s into action!",name),10,false,Group)
-end
-end
-end
-else
-self:_SendMessage("Troops have returned to base!",10,false,Group)
-self:__TroopsRTB(1,Group,Unit)
-end
-local loaded={}
-loaded.Troopsloaded=0
-loaded.Cratesloaded=0
-loaded.Cargo={}
-local loadedcargo=self.Loaded_Cargo[unitname]or{}
-local cargotable=loadedcargo.Cargo or{}
-for _,_cargo in pairs(cargotable)do
-local cargo=_cargo
-local type=cargo:GetType()
-local dropped=cargo:WasDropped()
-if type~=CTLD_CARGO.Enum.TROOPS and type~=CTLD_CARGO.Enum.ENGINEERS and not dropped then
-table.insert(loaded.Cargo,_cargo)
-loaded.Cratesloaded=loaded.Cratesloaded+1
-else
-if(type==CTLD_CARGO.Enum.TROOPS or type==CTLD_CARGO.Enum.ENGINEERS)and droppingatbase then
-local name=cargo:GetName()
-local gentroops=self.Cargo_Troops
-for _id,_troop in pairs(gentroops)do
-if _troop.Name==name then
-local stock=_troop:GetStock()
-if stock and tonumber(stock)>=0 then _troop:AddStock()end
-end
-end
-end
-end
-end
-self.Loaded_Cargo[unitname]=nil
-self.Loaded_Cargo[unitname]=loaded
-self:_UpdateUnitCargoMass(Unit)
-else
-if IsHerc then
-self:_SendMessage("Nothing loaded or not within airdrop parameters!",10,false,Group)
-else
-self:_SendMessage("Nothing loaded or not hovering within parameters!",10,false,Group)
-end
-end
-return self
-end
-function CTLD:_UnloadCrates(Group,Unit)
-self:T(self.lid.." _UnloadCrates")
-if not self.dropcratesanywhere then
-local inzone,zonename,zone,distance=self:IsUnitInZone(Unit,CTLD.CargoZoneType.DROP)
-if not inzone then
-self:_SendMessage("You are not close enough to a drop zone!",10,false,Group)
-if not self.debug then
-return self
-end
-end
-end
-local hoverunload=self:IsCorrectHover(Unit)
-local IsHerc=self:IsHercules(Unit)
-if IsHerc then
-hoverunload=self:IsCorrectFlightParameters(Unit)
-end
-local grounded=not self:IsUnitInAir(Unit)
-local unitname=Unit:GetName()
-if self.Loaded_Cargo[unitname]and(grounded or hoverunload)then
-local loadedcargo=self.Loaded_Cargo[unitname]or{}
-local cargotable=loadedcargo.Cargo
-for _,_cargo in pairs(cargotable)do
-local cargo=_cargo
-local type=cargo:GetType()
-if type~=CTLD_CARGO.Enum.TROOPS and type~=CTLD_CARGO.Enum.ENGINEERS and(not cargo:WasDropped()or self.allowcratepickupagain)then
-self:_GetCrates(Group,Unit,cargo,1,true)
-cargo:SetWasDropped(true)
-cargo:SetHasMoved(true)
-end
-end
-local loaded={}
-loaded.Troopsloaded=0
-loaded.Cratesloaded=0
-loaded.Cargo={}
-for _,_cargo in pairs(cargotable)do
-local cargo=_cargo
-local type=cargo:GetType()
-local size=cargo:GetCratesNeeded()
-if type==CTLD_CARGO.Enum.TROOPS or type==CTLD_CARGO.Enum.ENGINEERS then
-table.insert(loaded.Cargo,_cargo)
-loaded.Troopsloaded=loaded.Troopsloaded+size
-end
-end
-self.Loaded_Cargo[unitname]=nil
-self.Loaded_Cargo[unitname]=loaded
-self:_UpdateUnitCargoMass(Unit)
-else
-if IsHerc then
-self:_SendMessage("Nothing loaded or not within airdrop parameters!",10,false,Group)
-else
-self:_SendMessage("Nothing loaded or not hovering within parameters!",10,false,Group)
-end
-end
-return self
-end
-function CTLD:_BuildCrates(Group,Unit,Engineering)
-self:T(self.lid.." _BuildCrates")
-if self:IsHercules(Unit)and self.enableHercules and not Engineering then
-local speed=Unit:GetVelocityKMH()
-if speed>1 then
-self:_SendMessage("You need to land / stop to build something, Pilot!",10,false,Group)
-return self
-end
-end
-if not Engineering and self.nobuildinloadzones then
-local inloadzone=self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD)
-if inloadzone then
-self:_SendMessage("You cannot build in a loading area, Pilot!",10,false,Group)
-return self
-end
-end
-local finddist=self.CrateDistance or 35
-local crates,number=self:_FindCratesNearby(Group,Unit,finddist,true)
-local buildables={}
-local foundbuilds=false
-local canbuild=false
-if number>0 then
-for _,_crate in pairs(crates)do
-local Crate=_crate
-if(Crate:WasDropped()or not self.movecratesbeforebuild)and not Crate:IsRepair()and not Crate:IsStatic()then
-local name=Crate:GetName()
-local required=Crate:GetCratesNeeded()
-local template=Crate:GetTemplates()
-local ctype=Crate:GetType()
-local ccoord=Crate:GetPositionable():GetCoordinate()
-if not buildables[name]then
-local object={}
-object.Name=name
-object.Required=required
-object.Found=1
-object.Template=template
-object.CanBuild=false
-object.Type=ctype
-object.Coord=ccoord:GetVec2()
-buildables[name]=object
-foundbuilds=true
-else
-buildables[name].Found=buildables[name].Found+1
-foundbuilds=true
-end
-if buildables[name].Found>=buildables[name].Required then
-buildables[name].CanBuild=true
-canbuild=true
-end
-self:T({buildables=buildables})
-end
-end
-local report=REPORT:New("Checklist Buildable Crates")
-report:Add("------------------------------------------------------------")
-for _,_build in pairs(buildables)do
-local build=_build
-local name=build.Name
-local needed=build.Required
-local found=build.Found
-local txtok="NO"
-if build.CanBuild then
-txtok="YES"
-end
-local text=string.format("Type: %s | Required %d | Found %d | Can Build %s",name,needed,found,txtok)
-report:Add(text)
-end
-if not foundbuilds then
-report:Add(" --- None found! ---")
-if self.movecratesbeforebuild then
-report:Add("*** Crates need to be moved before building!")
-end
-end
-report:Add("------------------------------------------------------------")
-local text=report:Text()
-if not Engineering then
-self:_SendMessage(text,30,true,Group)
-else
-self:T(text)
-end
-if canbuild then
-for _,_build in pairs(buildables)do
-local build=_build
-if build.CanBuild then
-self:_CleanUpCrates(crates,build,number)
-if self.buildtime and self.buildtime>0 then
-local buildtimer=TIMER:New(self._BuildObjectFromCrates,self,Group,Unit,build,false,Group:GetCoordinate())
-buildtimer:Start(self.buildtime)
-self:_SendMessage(string.format("Build started, ready in %d seconds!",self.buildtime),15,false,Group)
-self:__CratesBuildStarted(1,Group,Unit)
-else
-self:_BuildObjectFromCrates(Group,Unit,build)
-end
-end
-end
-end
-else
-if not Engineering then self:_SendMessage(string.format("No crates within %d meters!",finddist),10,false,Group)end
-end
-return self
-end
-function CTLD:_PackCratesNearby(Group,Unit)
-self:T(self.lid.." _PackCratesNearby")
-local location=Group:GetCoordinate()
-local nearestGroups=SET_GROUP:New():FilterCoalitions("blue"):FilterZones({ZONE_RADIUS:New("TempZone",location:GetVec2(),self.PackDistance,false)}):FilterOnce()
-for _,_Group in pairs(nearestGroups.Set)do
-for _,_Template in pairs(_DATABASE.Templates.Groups)do
-if(string.match(_Group:GetName(),_Template.GroupName))then
-for _,_entry in pairs(self.Cargo_Crates)do
-if(_entry.Templates[1]==_Template.GroupName)then
-_Group:Destroy()
-self:_GetCrates(Group,Unit,_entry,nil,false,true)
-return self
-end
-end
-end
-end
-end
-return self
-end
-function CTLD:_RepairCrates(Group,Unit,Engineering)
-self:T(self.lid.." _RepairCrates")
-local finddist=self.CrateDistance or 35
-local crates,number=self:_FindCratesNearby(Group,Unit,finddist,true)
-local buildables={}
-local foundbuilds=false
-local canbuild=false
-if number>0 then
-for _,_crate in pairs(crates)do
-local Crate=_crate
-if Crate:WasDropped()and Crate:IsRepair()and not Crate:IsStatic()then
-local name=Crate:GetName()
-local required=Crate:GetCratesNeeded()
-local template=Crate:GetTemplates()
-local ctype=Crate:GetType()
-if not buildables[name]then
-local object={}
-object.Name=name
-object.Required=required
-object.Found=1
-object.Template=template
-object.CanBuild=false
-object.Type=ctype
-buildables[name]=object
-foundbuilds=true
-else
-buildables[name].Found=buildables[name].Found+1
-foundbuilds=true
-end
-if buildables[name].Found>=buildables[name].Required then
-buildables[name].CanBuild=true
-canbuild=true
-end
-self:T({repair=buildables})
-end
-end
-local report=REPORT:New("Checklist Repairs")
-report:Add("------------------------------------------------------------")
-for _,_build in pairs(buildables)do
-local build=_build
-local name=build.Name
-local needed=build.Required
-local found=build.Found
-local txtok="NO"
-if build.CanBuild then
-txtok="YES"
-end
-local text=string.format("Type: %s | Required %d | Found %d | Can Repair %s",name,needed,found,txtok)
-report:Add(text)
-end
-if not foundbuilds then report:Add(" --- None Found ---")end
-report:Add("------------------------------------------------------------")
-local text=report:Text()
-if not Engineering then
-self:_SendMessage(text,30,true,Group)
-else
-self:T(text)
-end
-if canbuild then
-for _,_build in pairs(buildables)do
-local build=_build
-if build.CanBuild then
-self:_RepairObjectFromCrates(Group,Unit,crates,build,number,Engineering)
-end
-end
-end
-else
-if not Engineering then self:_SendMessage(string.format("No crates within %d meters!",finddist),10,false,Group)end
-end
-return self
-end
-function CTLD:_BuildObjectFromCrates(Group,Unit,Build,Repair,RepairLocation)
-self:T(self.lid.." _BuildObjectFromCrates")
-if Group and Group:IsAlive()or(RepairLocation and not Repair)then
-local name=Build.Name
-local ctype=Build.Type
-local canmove=false
-if ctype==CTLD_CARGO.Enum.VEHICLE then canmove=true end
-if ctype==CTLD_CARGO.Enum.STATIC then
-return self
-end
-local temptable=Build.Template or{}
-if type(temptable)=="string"then
-temptable={temptable}
-end
-local zone=nil
-if RepairLocation and not Repair then
-zone=ZONE_RADIUS:New(string.format("Build zone-%d",math.random(1,10000)),RepairLocation:GetVec2(),100)
-else
-zone=ZONE_GROUP:New(string.format("Unload zone-%d",math.random(1,10000)),Group,100)
-end
-local randomcoord=Build.Coord or zone:GetRandomCoordinate(35):GetVec2()
-if Repair then
-randomcoord=RepairLocation:GetVec2()
-end
-for _,_template in pairs(temptable)do
-self.TroopCounter=self.TroopCounter+1
-local alias=string.format("%s-%d",_template,math.random(1,100000))
-if canmove then
-self.DroppedTroops[self.TroopCounter]=SPAWN:NewWithAlias(_template,alias)
-:InitDelayOff()
-:SpawnFromVec2(randomcoord)
-else
-self.DroppedTroops[self.TroopCounter]=SPAWN:NewWithAlias(_template,alias)
-:InitDelayOff()
-:SpawnFromVec2(randomcoord)
-end
-if Repair then
-self:__CratesRepaired(1,Group,Unit,self.DroppedTroops[self.TroopCounter])
-else
-self:__CratesBuild(1,Group,Unit,self.DroppedTroops[self.TroopCounter])
-end
-end
-else
-self:T(self.lid.."Group KIA while building!")
-end
-return self
-end
-function CTLD:_MoveGroupToZone(Group)
-self:T(self.lid.." _MoveGroupToZone")
-local groupname=Group:GetName()or"none"
-local groupcoord=Group:GetCoordinate()
-local outcome,name,zone,distance=self:IsUnitInZone(Group,CTLD.CargoZoneType.MOVE)
-if(distance<=self.movetroopsdistance)and zone then
-local groupname=Group:GetName()
-local zonecoord=zone:GetRandomCoordinate(20,125)
-local coordinate=zonecoord:GetVec2()
-Group:SetAIOn()
-Group:OptionAlarmStateAuto()
-Group:OptionDisperseOnAttack(30)
-Group:OptionROEOpenFirePossible()
-Group:RouteToVec2(coordinate,5)
-end
-return self
-end
-function CTLD:_CleanUpCrates(Crates,Build,Number)
-self:T(self.lid.." _CleanUpCrates")
-local build=Build
-local existingcrates=self.Spawned_Cargo
-local newexcrates={}
-local numberdest=Build.Required
-local nametype=Build.Name
-local found=0
-local rounds=Number
-local destIDs={}
-for _,_crate in pairs(Crates)do
-local nowcrate=_crate
-local name=nowcrate:GetName()
-local thisID=nowcrate:GetID()
-if name==nametype then
-table.insert(destIDs,thisID)
-found=found+1
-nowcrate:GetPositionable():Destroy(false)
-nowcrate.Positionable=nil
-nowcrate.HasBeenDropped=false
-end
-if found==numberdest then break end
-end
-self:_CleanupTrackedCrates(destIDs)
-return self
-end
-function CTLD:_RefreshF10Menus()
-self:T(self.lid.." _RefreshF10Menus")
-local PlayerSet=self.PilotGroups
-local PlayerTable=PlayerSet:GetSetObjects()
-local _UnitList={}
-for _key,_group in pairs(PlayerTable)do
-local _unit=_group:GetUnit(1)
-if _unit then
-if _unit:IsAlive()and _unit:IsPlayer()then
-if _unit:IsHelicopter()or(self:IsHercules(_unit)and self.enableHercules)then
-local unitName=_unit:GetName()
-_UnitList[unitName]=unitName
-else
-local unitName=_unit:GetName()
-_UnitList[unitName]=nil
-end
-end
-end
-end
-self.CtldUnits=_UnitList
-if self.usesubcats then
-for _id,_cargo in pairs(self.Cargo_Crates)do
-local entry=_cargo
-if not self.subcats[entry.Subcategory]then
-self.subcats[entry.Subcategory]=entry.Subcategory
-end
-end
-for _id,_cargo in pairs(self.Cargo_Statics)do
-local entry=_cargo
-if not self.subcats[entry.Subcategory]then
-self.subcats[entry.Subcategory]=entry.Subcategory
-end
-end
-for _id,_cargo in pairs(self.Cargo_Troops)do
-local entry=_cargo
-if not self.subcatsTroop[entry.Subcategory]then
-self.subcatsTroop[entry.Subcategory]=entry.Subcategory
-end
-end
-end
-local menucount=0
-local menus={}
-for _,_unitName in pairs(self.CtldUnits)do
-if not self.MenusDone[_unitName]then
-local _unit=UNIT:FindByName(_unitName)
-if _unit then
-local _group=_unit:GetGroup()
-if _group then
-local unittype=_unit:GetTypeName()
-local capabilities=self:_GetUnitCapabilities(_unit)
-local cantroops=capabilities.troops
-local cancrates=capabilities.crates
-local topmenu=MENU_GROUP:New(_group,"CTLD",nil)
-local toptroops=nil
-local topcrates=nil
-if cantroops then
-toptroops=MENU_GROUP:New(_group,"Manage Troops",topmenu)
-end
-if cancrates then
-topcrates=MENU_GROUP:New(_group,"Manage Crates",topmenu)
-end
-local listmenu=MENU_GROUP_COMMAND:New(_group,"List boarded cargo",topmenu,self._ListCargo,self,_group,_unit)
-local invtry=MENU_GROUP_COMMAND:New(_group,"Inventory",topmenu,self._ListInventory,self,_group,_unit)
-local rbcns=MENU_GROUP_COMMAND:New(_group,"List active zone beacons",topmenu,self._ListRadioBeacons,self,_group,_unit)
-local smoketopmenu=MENU_GROUP:New(_group,"Smokes, Flares, Beacons",topmenu)
-local smokemenu=MENU_GROUP_COMMAND:New(_group,"Smoke zones nearby",smoketopmenu,self.SmokeZoneNearBy,self,_unit,false)
-local smokeself=MENU_GROUP:New(_group,"Drop smoke now",smoketopmenu)
-local smokeselfred=MENU_GROUP_COMMAND:New(_group,"Red smoke",smokeself,self.SmokePositionNow,self,_unit,false,SMOKECOLOR.Red)
-local smokeselfblue=MENU_GROUP_COMMAND:New(_group,"Blue smoke",smokeself,self.SmokePositionNow,self,_unit,false,SMOKECOLOR.Blue)
-local smokeselfgreen=MENU_GROUP_COMMAND:New(_group,"Green smoke",smokeself,self.SmokePositionNow,self,_unit,false,SMOKECOLOR.Green)
-local smokeselforange=MENU_GROUP_COMMAND:New(_group,"Orange smoke",smokeself,self.SmokePositionNow,self,_unit,false,SMOKECOLOR.Orange)
-local smokeselfwhite=MENU_GROUP_COMMAND:New(_group,"White smoke",smokeself,self.SmokePositionNow,self,_unit,false,SMOKECOLOR.White)
-local flaremenu=MENU_GROUP_COMMAND:New(_group,"Flare zones nearby",smoketopmenu,self.SmokeZoneNearBy,self,_unit,true)
-local flareself=MENU_GROUP_COMMAND:New(_group,"Fire flare now",smoketopmenu,self.SmokePositionNow,self,_unit,true)
-local beaconself=MENU_GROUP_COMMAND:New(_group,"Drop beacon now",smoketopmenu,self.DropBeaconNow,self,_unit):Refresh()
-if cantroops then
-local troopsmenu=MENU_GROUP:New(_group,"Load troops",toptroops)
-if self.usesubcats then
-local subcatmenus={}
-for _name,_entry in pairs(self.subcatsTroop)do
-subcatmenus[_name]=MENU_GROUP:New(_group,_name,troopsmenu)
-end
-for _,_entry in pairs(self.Cargo_Troops)do
-local entry=_entry
-local subcat=entry.Subcategory
-menucount=menucount+1
-menus[menucount]=MENU_GROUP_COMMAND:New(_group,entry.Name,subcatmenus[subcat],self._LoadTroops,self,_group,_unit,entry)
-end
-else
-for _,_entry in pairs(self.Cargo_Troops)do
-local entry=_entry
-menucount=menucount+1
-menus[menucount]=MENU_GROUP_COMMAND:New(_group,entry.Name,troopsmenu,self._LoadTroops,self,_group,_unit,entry)
-end
-end
-local unloadmenu1=MENU_GROUP_COMMAND:New(_group,"Drop troops",toptroops,self._UnloadTroops,self,_group,_unit):Refresh()
-local extractMenu1=MENU_GROUP_COMMAND:New(_group,"Extract troops",toptroops,self._ExtractTroops,self,_group,_unit):Refresh()
-end
-if cancrates then
-local loadmenu=MENU_GROUP_COMMAND:New(_group,"Load crates",topcrates,self._LoadCratesNearby,self,_group,_unit)
-local cratesmenu=MENU_GROUP:New(_group,"Get Crates",topcrates)
-local packmenu=MENU_GROUP_COMMAND:New(_group,"Pack crates",topcrates,self._PackCratesNearby,self,_group,_unit)
-local removecratesmenu=MENU_GROUP:New(_group,"Remove crates",topcrates)
-if self.usesubcats then
-local subcatmenus={}
-for _name,_entry in pairs(self.subcats)do
-subcatmenus[_name]=MENU_GROUP:New(_group,_name,cratesmenu)
-end
-for _,_entry in pairs(self.Cargo_Crates)do
-local entry=_entry
-local subcat=entry.Subcategory
-menucount=menucount+1
-local menutext=string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0)
-menus[menucount]=MENU_GROUP_COMMAND:New(_group,menutext,subcatmenus[subcat],self._GetCrates,self,_group,_unit,entry)
-end
-for _,_entry in pairs(self.Cargo_Statics)do
-local entry=_entry
-local subcat=entry.Subcategory
-menucount=menucount+1
-local menutext=string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0)
-menus[menucount]=MENU_GROUP_COMMAND:New(_group,menutext,subcatmenus[subcat],self._GetCrates,self,_group,_unit,entry)
-end
-else
-for _,_entry in pairs(self.Cargo_Crates)do
-local entry=_entry
-menucount=menucount+1
-local menutext=string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0)
-menus[menucount]=MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrates,self,_group,_unit,entry)
-end
-for _,_entry in pairs(self.Cargo_Statics)do
-local entry=_entry
-menucount=menucount+1
-local menutext=string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0)
-menus[menucount]=MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrates,self,_group,_unit,entry)
-end
-end
-listmenu=MENU_GROUP_COMMAND:New(_group,"List crates nearby",topcrates,self._ListCratesNearby,self,_group,_unit)
-removecrates=MENU_GROUP_COMMAND:New(_group,"Remove crates nearby",removecratesmenu,self._RemoveCratesNearby,self,_group,_unit)
-local unloadmenu=MENU_GROUP_COMMAND:New(_group,"Drop crates",topcrates,self._UnloadCrates,self,_group,_unit)
-if not self.nobuildmenu then
-local buildmenu=MENU_GROUP_COMMAND:New(_group,"Build crates",topcrates,self._BuildCrates,self,_group,_unit)
-local repairmenu=MENU_GROUP_COMMAND:New(_group,"Repair",topcrates,self._RepairCrates,self,_group,_unit):Refresh()
-else
-unloadmenu:Refresh()
-end
-end
-if self:IsHercules(_unit)then
-local hoverpars=MENU_GROUP_COMMAND:New(_group,"Show flight parameters",topmenu,self._ShowFlightParams,self,_group,_unit):Refresh()
-else
-local hoverpars=MENU_GROUP_COMMAND:New(_group,"Show hover parameters",topmenu,self._ShowHoverParams,self,_group,_unit):Refresh()
-end
-self.MenusDone[_unitName]=true
-end
-end
-else
-self:T(self.lid.." Menus already done for this group!")
-end
-end
-return self
-end
-function CTLD:_CheckTemplates(temptable)
-self:T(self.lid.." _CheckTemplates")
-local outcome=true
-if type(temptable)~="table"then
-temptable={temptable}
-end
-for _,_name in pairs(temptable)do
-if not _DATABASE.Templates.Groups[_name]then
-outcome=false
-self:E(self.lid.."ERROR: Template name ".._name.." is missing!")
-end
-end
-return outcome
-end
-function CTLD:AddTroopsCargo(Name,Templates,Type,NoTroops,PerTroopMass,Stock,SubCategory)
-self:T(self.lid.." AddTroopsCargo")
-self:T({Name,Templates,Type,NoTroops,PerTroopMass,Stock})
-if not self:_CheckTemplates(Templates)then
-self:E(self.lid.."Troops Cargo for "..Name.." has missing template(s)!")
-return self
-end
-self.CargoCounter=self.CargoCounter+1
-local cargo=CTLD_CARGO:New(self.CargoCounter,Name,Templates,Type,false,true,NoTroops,nil,nil,PerTroopMass,Stock,SubCategory)
-table.insert(self.Cargo_Troops,cargo)
-return self
-end
-function CTLD:AddCratesCargo(Name,Templates,Type,NoCrates,PerCrateMass,Stock,SubCategory)
-self:T(self.lid.." AddCratesCargo")
-if not self:_CheckTemplates(Templates)then
-self:E(self.lid.."Crates Cargo for "..Name.." has missing template(s)!")
-return self
-end
-self.CargoCounter=self.CargoCounter+1
-local cargo=CTLD_CARGO:New(self.CargoCounter,Name,Templates,Type,false,false,NoCrates,nil,nil,PerCrateMass,Stock,SubCategory)
-table.insert(self.Cargo_Crates,cargo)
-return self
-end
-function CTLD:AddStaticsCargo(Name,Mass,Stock,SubCategory)
-self:T(self.lid.." AddStaticsCargo")
-self.CargoCounter=self.CargoCounter+1
-local type=CTLD_CARGO.Enum.STATIC
-local template=STATIC:FindByName(Name,true):GetTypeName()
-local cargo=CTLD_CARGO:New(self.CargoCounter,Name,template,type,false,false,1,nil,nil,Mass,Stock,SubCategory)
-table.insert(self.Cargo_Statics,cargo)
-return self
-end
-function CTLD:GetStaticsCargoFromTemplate(Name,Mass)
-self:T(self.lid.." GetStaticsCargoFromTemplate")
-self.CargoCounter=self.CargoCounter+1
-local type=CTLD_CARGO.Enum.STATIC
-local template=STATIC:FindByName(Name,true):GetTypeName()
-local cargo=CTLD_CARGO:New(self.CargoCounter,Name,template,type,false,false,1,nil,nil,Mass,1)
-return cargo
-end
-function CTLD:AddCratesRepair(Name,Template,Type,NoCrates,PerCrateMass,Stock,SubCategory)
-self:T(self.lid.." AddCratesRepair")
-if not self:_CheckTemplates(Template)then
-self:E(self.lid.."Repair Cargo for "..Name.." has a missing template!")
-return self
-end
-self.CargoCounter=self.CargoCounter+1
-local cargo=CTLD_CARGO:New(self.CargoCounter,Name,Template,Type,false,false,NoCrates,nil,nil,PerCrateMass,Stock,SubCategory)
-table.insert(self.Cargo_Crates,cargo)
-return self
-end
-function CTLD:AddZone(Zone)
-self:T(self.lid.." AddZone")
-local zone=Zone
-if zone.type==CTLD.CargoZoneType.LOAD then
-table.insert(self.pickupZones,zone)
-elseif zone.type==CTLD.CargoZoneType.DROP then
-table.insert(self.dropOffZones,zone)
-elseif zone.type==CTLD.CargoZoneType.SHIP then
-table.insert(self.shipZones,zone)
-elseif zone.type==CTLD.CargoZoneType.BEACON then
-table.insert(self.droppedBeacons,zone)
-else
-table.insert(self.wpZones,zone)
-end
-return self
-end
-function CTLD:ActivateZone(Name,ZoneType,NewState)
-self:T(self.lid.." ActivateZone")
-local newstate=true
-if NewState~=nil then
-newstate=NewState
-end
-local table={}
-if ZoneType==CTLD.CargoZoneType.LOAD then
-table=self.pickupZones
-elseif ZoneType==CTLD.CargoZoneType.DROP then
-table=self.dropOffZones
-elseif ZoneType==CTLD.CargoZoneType.SHIP then
-table=self.shipZones
-else
-table=self.wpZones
-end
-for _,_zone in pairs(table)do
-local thiszone=_zone
-if thiszone.name==Name then
-thiszone.active=newstate
-break
-end
-end
-return self
-end
-function CTLD:DeactivateZone(Name,ZoneType)
-self:T(self.lid.." DeactivateZone")
-self:ActivateZone(Name,ZoneType,false)
-return self
-end
-function CTLD:_GetFMBeacon(Name)
-self:T(self.lid.." _GetFMBeacon")
-local beacon={}
-if#self.FreeFMFrequencies<=1 then
-self.FreeFMFrequencies=self.UsedFMFrequencies
-self.UsedFMFrequencies={}
-end
-local FM=table.remove(self.FreeFMFrequencies,math.random(#self.FreeFMFrequencies))
-table.insert(self.UsedFMFrequencies,FM)
-beacon.name=Name
-beacon.frequency=FM/1000000
-beacon.modulation=CTLD.RadioModulation.FM
-return beacon
-end
-function CTLD:_GetUHFBeacon(Name)
-self:T(self.lid.." _GetUHFBeacon")
-local beacon={}
-if#self.FreeUHFFrequencies<=1 then
-self.FreeUHFFrequencies=self.UsedUHFFrequencies
-self.UsedUHFFrequencies={}
-end
-local UHF=table.remove(self.FreeUHFFrequencies,math.random(#self.FreeUHFFrequencies))
-table.insert(self.UsedUHFFrequencies,UHF)
-beacon.name=Name
-beacon.frequency=UHF/1000000
-beacon.modulation=CTLD.RadioModulation.AM
-return beacon
-end
-function CTLD:_GetVHFBeacon(Name)
-self:T(self.lid.." _GetVHFBeacon")
-local beacon={}
-if#self.FreeVHFFrequencies<=3 then
-self.FreeVHFFrequencies=self.UsedVHFFrequencies
-self.UsedVHFFrequencies={}
-end
-local VHF=table.remove(self.FreeVHFFrequencies,math.random(#self.FreeVHFFrequencies))
-table.insert(self.UsedVHFFrequencies,VHF)
-beacon.name=Name
-beacon.frequency=VHF/1000000
-beacon.modulation=CTLD.RadioModulation.FM
-return beacon
-end
-function CTLD:AddCTLDZone(Name,Type,Color,Active,HasBeacon,Shiplength,Shipwidth)
-self:T(self.lid.." AddCTLDZone")
-local zone=ZONE:FindByName(Name)
-if not zone and Type~=CTLD.CargoZoneType.SHIP then
-self:E(self.lid.."**** Zone does not exist: "..Name)
-return self
-end
-if Type==CTLD.CargoZoneType.SHIP then
-local Ship=UNIT:FindByName(Name)
-if not Ship then
-self:E(self.lid.."**** Ship does not exist: "..Name)
-return self
-end
-end
-local ctldzone={}
-ctldzone.active=Active or false
-ctldzone.color=Color or SMOKECOLOR.Red
-ctldzone.name=Name or"NONE"
-ctldzone.type=Type or CTLD.CargoZoneType.MOVE
-ctldzone.hasbeacon=HasBeacon or false
-if Type==CTLD.CargoZoneType.BEACON then
-self.droppedbeaconref[ctldzone.name]=zone:GetCoordinate()
-ctldzone.timestamp=timer.getTime()
-end
-if HasBeacon then
-ctldzone.fmbeacon=self:_GetFMBeacon(Name)
-ctldzone.uhfbeacon=self:_GetUHFBeacon(Name)
-ctldzone.vhfbeacon=self:_GetVHFBeacon(Name)
-else
-ctldzone.fmbeacon=nil
-ctldzone.uhfbeacon=nil
-ctldzone.vhfbeacon=nil
-end
-if Type==CTLD.CargoZoneType.SHIP then
-ctldzone.shiplength=Shiplength or 100
-ctldzone.shipwidth=Shipwidth or 10
-end
-self:AddZone(ctldzone)
-return self
-end
-function CTLD:AddCTLDZoneFromAirbase(AirbaseName,Type,Color,Active,HasBeacon)
-self:T(self.lid.." AddCTLDZoneFromAirbase")
-local AFB=AIRBASE:FindByName(AirbaseName)
-local name=AFB:GetZone():GetName()
-self:T(self.lid.."AFB "..AirbaseName.." ZoneName "..name)
-self:AddCTLDZone(name,Type,Color,Active,HasBeacon)
-return self
-end
-function CTLD:DropBeaconNow(Unit)
-self:T(self.lid.." DropBeaconNow")
-local ctldzone={}
-ctldzone.active=true
-ctldzone.color=math.random(0,4)
-ctldzone.name="Beacon "..math.random(1,10000)
-ctldzone.type=CTLD.CargoZoneType.BEACON
-ctldzone.hasbeacon=true
-ctldzone.fmbeacon=self:_GetFMBeacon(ctldzone.name)
-ctldzone.uhfbeacon=self:_GetUHFBeacon(ctldzone.name)
-ctldzone.vhfbeacon=self:_GetVHFBeacon(ctldzone.name)
-ctldzone.timestamp=timer.getTime()
-self.droppedbeaconref[ctldzone.name]=Unit:GetCoordinate()
-self:AddZone(ctldzone)
-local FMbeacon=ctldzone.fmbeacon
-local VHFbeacon=ctldzone.vhfbeacon
-local UHFbeacon=ctldzone.uhfbeacon
-local Name=ctldzone.name
-local FM=FMbeacon.frequency
-local VHF=VHFbeacon.frequency*1000
-local UHF=UHFbeacon.frequency
-local text=string.format("Dropped %s | FM %s Mhz | VHF %s KHz | UHF %s Mhz ",Name,FM,VHF,UHF)
-self:_SendMessage(text,15,false,Unit:GetGroup())
-return self
-end
-function CTLD:CheckDroppedBeacons()
-self:T(self.lid.." CheckDroppedBeacons")
-local timeout=self.droppedbeacontimeout or 600
-local livebeacontable={}
-for _,_beacon in pairs(self.droppedBeacons)do
-local beacon=_beacon
-if not beacon.timestamp then beacon.timestamp=timer.getTime()+timeout end
-local T0=beacon.timestamp
-if timer.getTime()-T0>timeout then
-local name=beacon.name
-self.droppedbeaconref[name]=nil
-_beacon=nil
-else
-table.insert(livebeacontable,beacon)
-end
-end
-self.droppedBeacons=nil
-self.droppedBeacons=livebeacontable
-return self
-end
-function CTLD:_ListRadioBeacons(Group,Unit)
-self:T(self.lid.." _ListRadioBeacons")
-local report=REPORT:New("Active Zone Beacons")
-report:Add("------------------------------------------------------------")
-local zones={[1]=self.pickupZones,[2]=self.wpZones,[3]=self.dropOffZones,[4]=self.shipZones,[5]=self.droppedBeacons}
-for i=1,5 do
-for index,cargozone in pairs(zones[i])do
-local czone=cargozone
-if czone.active and czone.hasbeacon then
-local FMbeacon=czone.fmbeacon
-local VHFbeacon=czone.vhfbeacon
-local UHFbeacon=czone.uhfbeacon
-local Name=czone.name
-local FM=FMbeacon.frequency
-local VHF=VHFbeacon.frequency*1000
-local UHF=UHFbeacon.frequency
-report:AddIndent(string.format(" %s | FM %s Mhz | VHF %s KHz | UHF %s Mhz ",Name,FM,VHF,UHF),"|")
-end
-end
-end
-if report:GetCount()==1 then
-report:Add(" N O N E")
-end
-report:Add("------------------------------------------------------------")
-self:_SendMessage(report:Text(),30,true,Group)
-return self
-end
-function CTLD:_AddRadioBeacon(Name,Sound,Mhz,Modulation,IsShip,IsDropped)
-self:T(self.lid.." _AddRadioBeacon")
-local Zone=nil
-if IsShip then
-Zone=UNIT:FindByName(Name)
-elseif IsDropped then
-Zone=self.droppedbeaconref[Name]
-else
-Zone=ZONE:FindByName(Name)
-if not Zone then
-Zone=AIRBASE:FindByName(Name):GetZone()
-end
-end
-local Sound=Sound or"beacon.ogg"
-if Zone then
-if IsDropped then
-local ZoneCoord=Zone
-local ZoneVec3=ZoneCoord:GetVec3()or{x=0,y=0,z=0}
-local Frequency=Mhz*1000000
-local Sound=self.RadioPath..Sound
-trigger.action.radioTransmission(Sound,ZoneVec3,Modulation,false,Frequency,1000,Name..math.random(1,10000))
-self:T2(string.format("Beacon added | Name = %s | Sound = %s | Vec3 = %d %d %d | Freq = %f | Modulation = %d (0=AM/1=FM)",Name,Sound,ZoneVec3.x,ZoneVec3.y,ZoneVec3.z,Mhz,Modulation))
-else
-local ZoneCoord=Zone:GetCoordinate()
-local ZoneVec3=ZoneCoord:GetVec3()or{x=0,y=0,z=0}
-local Frequency=Mhz*1000000
-local Sound=self.RadioPath..Sound
-trigger.action.radioTransmission(Sound,ZoneVec3,Modulation,false,Frequency,1000,Name..math.random(1,10000))
-self:T2(string.format("Beacon added | Name = %s | Sound = %s | Vec3 = {x=%d, y=%d, z=%d} | Freq = %f | Modulation = %d (0=AM/1=FM)",Name,Sound,ZoneVec3.x,ZoneVec3.y,ZoneVec3.z,Mhz,Modulation))
-end
-else
-self:E(self.lid.."***** _AddRadioBeacon: Zone does not exist: "..Name)
-end
-return self
-end
-function CTLD:SetSoundfilesFolder(FolderPath)
-self:T(self.lid.." SetSoundfilesFolder")
-if FolderPath then
-local lastchar=string.sub(FolderPath,-1)
-if lastchar~="/"then
-FolderPath=FolderPath.."/"
-end
-end
-self.RadioPath=FolderPath
-self:I(self.lid..string.format("Setting sound files folder to: %s",self.RadioPath))
-return self
-end
-function CTLD:_RefreshRadioBeacons()
-self:T(self.lid.." _RefreshRadioBeacons")
-local zones={[1]=self.pickupZones,[2]=self.wpZones,[3]=self.dropOffZones,[4]=self.shipZones,[5]=self.droppedBeacons}
-for i=1,5 do
-local IsShip=false
-if i==4 then IsShip=true end
-local IsDropped=false
-if i==5 then IsDropped=true end
-for index,cargozone in pairs(zones[i])do
-local czone=cargozone
-local Sound=self.RadioSound
-local Silent=self.RadioSoundFC3 or self.RadioSound
-if czone.active and czone.hasbeacon then
-local FMbeacon=czone.fmbeacon
-local VHFbeacon=czone.vhfbeacon
-local UHFbeacon=czone.uhfbeacon
-local Name=czone.name
-local FM=FMbeacon.frequency
-local VHF=VHFbeacon.frequency
-local UHF=UHFbeacon.frequency
-self:_AddRadioBeacon(Name,Sound,FM,CTLD.RadioModulation.FM,IsShip,IsDropped)
-self:_AddRadioBeacon(Name,Sound,VHF,CTLD.RadioModulation.AM,IsShip,IsDropped)
-self:_AddRadioBeacon(Name,Silent,UHF,CTLD.RadioModulation.AM,IsShip,IsDropped)
-end
-end
-end
-return self
-end
-function CTLD:IsUnitInZone(Unit,Zonetype)
-self:T(self.lid.." IsUnitInZone")
-self:T(Zonetype)
-local unitname=Unit:GetName()
-local zonetable={}
-local outcome=false
-if Zonetype==CTLD.CargoZoneType.LOAD then
-zonetable=self.pickupZones
-elseif Zonetype==CTLD.CargoZoneType.DROP then
-zonetable=self.dropOffZones
-elseif Zonetype==CTLD.CargoZoneType.SHIP then
-zonetable=self.shipZones
-else
-zonetable=self.wpZones
-end
-local zonecoord=nil
-local colorret=nil
-local maxdist=1000000
-local zoneret=nil
-local zonewret=nil
-local zonenameret=nil
-local unitcoord=Unit:GetCoordinate()
-local unitVec2=unitcoord:GetVec2()
-for _,_cargozone in pairs(zonetable)do
-local czone=_cargozone
-local zonename=czone.name
-local active=czone.active
-local color=czone.color
-local zone=nil
-local zoneradius=100
-local zonewidth=20
-if Zonetype==CTLD.CargoZoneType.SHIP then
-self:T("Checking Type Ship: "..zonename)
-local ZoneUNIT=UNIT:FindByName(zonename)
-zonecoord=ZoneUNIT:GetCoordinate()
-zoneradius=czone.shiplength
-zonewidth=czone.shipwidth
-zone=ZONE_UNIT:New(ZoneUNIT:GetName(),ZoneUNIT,zoneradius/2)
-elseif ZONE:FindByName(zonename)then
-zone=ZONE:FindByName(zonename)
-self:T("Checking Zone: "..zonename)
-zonecoord=zone:GetCoordinate()
-zonewidth=zoneradius
-elseif AIRBASE:FindByName(zonename)then
-zone=AIRBASE:FindByName(zonename):GetZone()
-self:T("Checking Zone: "..zonename)
-zonecoord=zone:GetCoordinate()
-zoneradius=2000
-zonewidth=zoneradius
-end
-local distance=self:_GetDistance(zonecoord,unitcoord)
-if zone:IsVec2InZone(unitVec2)and active then
-outcome=true
-end
-if maxdist>distance then
-maxdist=distance
-zoneret=zone
-zonenameret=zonename
-zonewret=zonewidth
-colorret=color
-end
-end
-if Zonetype==CTLD.CargoZoneType.SHIP then
-return outcome,zonenameret,zoneret,maxdist,zonewret
-else
-return outcome,zonenameret,zoneret,maxdist
-end
-end
-function CTLD:SmokePositionNow(Unit,Flare,SmokeColor)
-self:T(self.lid.." SmokePositionNow")
-local Smokecolor=self.SmokeColor or SMOKECOLOR.Red
-if SmokeColor then
-Smokecolor=SmokeColor
-end
-local FlareColor=self.FlareColor or FLARECOLOR.Red
-local unitcoord=Unit:GetCoordinate()
-local Group=Unit:GetGroup()
-if Flare then
-unitcoord:Flare(FlareColor,90)
-else
-local height=unitcoord:GetLandHeight()+2
-unitcoord.y=height
-unitcoord:Smoke(Smokecolor)
-end
-return self
-end
-function CTLD:SmokeZoneNearBy(Unit,Flare)
-self:T(self.lid.." SmokeZoneNearBy")
-local unitcoord=Unit:GetCoordinate()
-local Group=Unit:GetGroup()
-local smokedistance=self.smokedistance
-local smoked=false
-local zones={[1]=self.pickupZones,[2]=self.wpZones,[3]=self.dropOffZones,[4]=self.shipZones}
-for i=1,4 do
-for index,cargozone in pairs(zones[i])do
-local CZone=cargozone
-local zonename=CZone.name
-local zone=nil
-if i==4 then
-zone=UNIT:FindByName(zonename)
-else
-zone=ZONE:FindByName(zonename)
-if not zone then
-zone=AIRBASE:FindByName(zonename):GetZone()
-end
-end
-local zonecoord=zone:GetCoordinate()
-local active=CZone.active
-local color=CZone.color
-local distance=self:_GetDistance(zonecoord,unitcoord)
-if distance=minh)then
-outcome=true
-end
-end
-return outcome
-end
-function CTLD:IsCorrectFlightParameters(Unit)
-self:T(self.lid.." IsCorrectFlightParameters")
-local outcome=false
-if self:IsUnitInAir(Unit)then
-local uspeed=Unit:GetVelocityMPS()
-local uheight=Unit:GetHeight()
-local ucoord=Unit:GetCoordinate()
-if not ucoord then
-return false
-end
-local gheight=ucoord:GetLandHeight()
-local aheight=uheight-gheight
-local minh=self.HercMinAngels
-local maxh=self.HercMaxAngels
-local maxspeed=self.HercMaxSpeed
-local kmspeed=uspeed*3.6
-local knspeed=kmspeed/1.86
-self:T(string.format("%s Unit parameters: at %dm AGL with %dmps | %dkph | %dkn",self.lid,aheight,uspeed,kmspeed,knspeed))
-if(aheight<=maxh)and(aheight>=minh)and(uspeed<=maxspeed)then
-outcome=true
-end
-end
-return outcome
-end
-function CTLD:_ShowHoverParams(Group,Unit)
-local inhover=self:IsCorrectHover(Unit)
-local htxt="true"
-if not inhover then htxt="false"end
-local text=""
-if _SETTINGS:IsMetric()then
-text=string.format("Hover parameters (autoload/drop):\n - Min height %dm \n - Max height %dm \n - Max speed 2mps \n - In parameter: %s",self.minimumHoverHeight,self.maximumHoverHeight,htxt)
-else
-local minheight=UTILS.MetersToFeet(self.minimumHoverHeight)
-local maxheight=UTILS.MetersToFeet(self.maximumHoverHeight)
-text=string.format("Hover parameters (autoload/drop):\n - Min height %dft \n - Max height %dft \n - Max speed 6ftps \n - In parameter: %s",minheight,maxheight,htxt)
-end
-self:_SendMessage(text,10,false,Group)
-return self
-end
-function CTLD:_ShowFlightParams(Group,Unit)
-local inhover=self:IsCorrectFlightParameters(Unit)
-local htxt="true"
-if not inhover then htxt="false"end
-local text=""
-if _SETTINGS:IsImperial()then
-local minheight=UTILS.MetersToFeet(self.HercMinAngels)
-local maxheight=UTILS.MetersToFeet(self.HercMaxAngels)
-text=string.format("Flight parameters (airdrop):\n - Min height %dft \n - Max height %dft \n - In parameter: %s",minheight,maxheight,htxt)
-else
-local minheight=self.HercMinAngels
-local maxheight=self.HercMaxAngels
-text=string.format("Flight parameters (airdrop):\n - Min height %dm \n - Max height %dm \n - In parameter: %s",minheight,maxheight,htxt)
-end
-self:_SendMessage(text,10,false,Group)
-return self
-end
-function CTLD:CanHoverLoad(Unit)
-self:T(self.lid.." CanHoverLoad")
-if self:IsHercules(Unit)then return false end
-local outcome=self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD)and self:IsCorrectHover(Unit)
-if not outcome then
-outcome=self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP)
-end
-return outcome
-end
-function CTLD:IsUnitInAir(Unit)
-local minheight=self.minimumHoverHeight
-if self.enableHercules and self:IsHercules(Unit)then
-minheight=5.1
-end
-local uheight=Unit:GetHeight()
-local ucoord=Unit:GetCoordinate()
-if not ucoord then
-return false
-end
-local gheight=ucoord:GetLandHeight()
-local aheight=uheight-gheight
-if aheight>=minheight then
-return true
-else
-return false
-end
-end
-function CTLD:AutoHoverLoad(Unit)
-self:T(self.lid.." AutoHoverLoad")
-local unittype=Unit:GetTypeName()
-local unitname=Unit:GetName()
-local Group=Unit:GetGroup()
-local capabilities=self:_GetUnitCapabilities(Unit)
-local cancrates=capabilities.crates
-local cratelimit=capabilities.cratelimit
-if cancrates then
-local numberonboard=0
-local loaded={}
-if self.Loaded_Cargo[unitname]then
-loaded=self.Loaded_Cargo[unitname]
-numberonboard=loaded.Cratesloaded or 0
-end
-local load=cratelimit-numberonboard
-local canload=self:CanHoverLoad(Unit)
-if canload and load>0 then
-self:_LoadCratesNearby(Group,Unit)
-end
-end
-return self
-end
-function CTLD:CheckAutoHoverload()
-if self.hoverautoloading then
-for _,_pilot in pairs(self.CtldUnits)do
-local Unit=UNIT:FindByName(_pilot)
-if self:CanHoverLoad(Unit)then self:AutoHoverLoad(Unit)end
-end
-end
-return self
-end
-function CTLD:CleanDroppedTroops()
-local troops=self.DroppedTroops
-local newtable={}
-for _index,_group in pairs(troops)do
-self:T({_group.ClassName})
-if _group and _group.ClassName=="GROUP"then
-if _group:IsAlive()then
-newtable[_index]=_group
-end
-end
-end
-self.DroppedTroops=newtable
-local engineers=self.EngineersInField
-local engtable={}
-for _index,_group in pairs(engineers)do
-self:T({_group.ClassName})
-if _group and _group:IsNotStatus("Stopped")then
-engtable[_index]=_group
-end
-end
-self.EngineersInField=engtable
-return self
-end
-function CTLD:AddStockTroops(Name,Number)
-local name=Name or"none"
-local number=Number or 1
-local gentroops=self.Cargo_Troops
-for _id,_troop in pairs(gentroops)do
-if _troop.Name==name then
-_troop:AddStock(number)
-end
-end
-return self
-end
-function CTLD:AddStockCrates(Name,Number)
-local name=Name or"none"
-local number=Number or 1
-local gentroops=self.Cargo_Crates
-for _id,_troop in pairs(gentroops)do
-if _troop.Name==name then
-_troop:AddStock(number)
-end
-end
-return self
-end
-function CTLD:AddStockStatics(Name,Number)
-local name=Name or"none"
-local number=Number or 1
-local gentroops=self.Cargo_Statics
-for _id,_troop in pairs(gentroops)do
-if _troop.Name==name then
-_troop:AddStock(number)
-end
-end
-return self
-end
-function CTLD:SetStockCrates(Name,Number)
-local name=Name or"none"
-local number=Number
-local gentroops=self.Cargo_Crates
-for _id,_troop in pairs(gentroops)do
-if _troop.Name==name then
-_troop:SetStock(number)
-end
-end
-return self
-end
-function CTLD:SetStockTroops(Name,Number)
-local name=Name or"none"
-local number=Number
-local gentroops=self.Cargo_Troops
-for _id,_troop in pairs(gentroops)do
-if _troop.Name==name then
-_troop:SetStock(number)
-end
-end
-return self
-end
-function CTLD:SetStockStatics(Name,Number)
-local name=Name or"none"
-local number=Number
-local gentroops=self.Cargo_Statics
-for _id,_troop in pairs(gentroops)do
-if _troop.Name==name then
-_troop:SetStock(number)
-end
-end
-return self
-end
-function CTLD:GetStockCrates()
-local Stock={}
-local gentroops=self.Cargo_Crates
-for _id,_troop in pairs(gentroops)do
-table.insert(Stock,_troop.Name,_troop.Stock or-1)
-end
-return Stock
-end
-function CTLD:GetStockTroops()
-local Stock={}
-local gentroops=self.Cargo_Troops
-for _id,_troop in pairs(gentroops)do
-table.insert(Stock,_troop.Name,_troop.Stock or-1)
-end
-return Stock
-end
-function CTLD:GetStockStatics()
-local Stock={}
-local gentroops=self.Cargo_Statics
-for _id,_troop in pairs(gentroops)do
-table.insert(Stock,_troop.Name,_troop.Stock or-1)
-end
-return Stock
-end
-function CTLD:RemoveStockTroops(Name,Number)
-local name=Name or"none"
-local number=Number or 1
-local gentroops=self.Cargo_Troops
-for _id,_troop in pairs(gentroops)do
-if _troop.Name==name then
-_troop:RemoveStock(number)
-end
-end
-return self
-end
-function CTLD:RemoveStockCrates(Name,Number)
-local name=Name or"none"
-local number=Number or 1
-local gentroops=self.Cargo_Crates
-for _id,_troop in pairs(gentroops)do
-if _troop.Name==name then
-_troop:RemoveStock(number)
-end
-end
-return self
-end
-function CTLD:RemoveStockStatics(Name,Number)
-local name=Name or"none"
-local number=Number or 1
-local gentroops=self.Cargo_Statics
-for _id,_troop in pairs(gentroops)do
-if _troop.Name==name then
-_troop:RemoveStock(number)
-end
-end
-return self
-end
-function CTLD:_CheckEngineers()
-self:T(self.lid.." CheckEngineers")
-local engtable=self.EngineersInField
-for _ind,_engineers in pairs(engtable)do
-local engineers=_engineers
-local wrenches=engineers.Group
-self:T(_engineers.lid.._engineers:GetStatus())
-if wrenches and wrenches:IsAlive()then
-if engineers:IsStatus("Running")or engineers:IsStatus("Searching")then
-local crates,number=self:_FindCratesNearby(wrenches,nil,self.EngineerSearch,true)
-engineers:Search(crates,number)
-elseif engineers:IsStatus("Moving")then
-engineers:Move()
-elseif engineers:IsStatus("Arrived")then
-engineers:Build()
-local unit=wrenches:GetUnit(1)
-self:_BuildCrates(wrenches,unit,true)
-self:_RepairCrates(wrenches,unit,true)
-engineers:Done()
-end
-else
-engineers:Stop()
-end
-end
-return self
-end
-function CTLD:InjectTroops(Zone,Cargo,Surfacetypes,PreciseLocation,Structure)
-self:T(self.lid.." InjectTroops")
-local cargo=Cargo
-local function IsTroopsMatch(cargo)
-local match=false
-local cgotbl=self.Cargo_Troops
-local name=cargo:GetName()
-for _,_cgo in pairs(cgotbl)do
-local cname=_cgo:GetName()
-if name==cname then
-match=true
-break
-end
-end
-return match
-end
-local function Cruncher(group,typename,anzahl)
-local units=group:GetUnits()
-local reduced=0
-for _,_unit in pairs(units)do
-local typo=_unit:GetTypeName()
-if typename==typo then
-_unit:Destroy(false)
-reduced=reduced+1
-if reduced==anzahl then break end
-end
-end
-end
-local function PostSpawn(args)
-local group=args[1]
-local structure=args[2]
-if structure then
-local loadedstructure={}
-local strcset=UTILS.Split(structure,";")
-for _,_data in pairs(strcset)do
-local datasplit=UTILS.Split(_data,"==")
-loadedstructure[datasplit[1]]=tonumber(datasplit[2])
-end
-local originalstructure=UTILS.GetCountPerTypeName(group)
-for _name,_number in pairs(originalstructure)do
-local loadednumber=0
-if loadedstructure[_name]then
-loadednumber=loadedstructure[_name]
-end
-local reduce=false
-if loadednumber<_number then reduce=true end
-if reduce then
-Cruncher(group,_name,_number-loadednumber)
-end
-end
-end
-end
-if not IsTroopsMatch(cargo)then
-self.CargoCounter=self.CargoCounter+1
-cargo.ID=self.CargoCounter
-cargo.Stock=1
-table.insert(self.Cargo_Troops,cargo)
-end
-local type=cargo:GetType()
-if(type==CTLD_CARGO.Enum.TROOPS or type==CTLD_CARGO.Enum.ENGINEERS)then
-local name=cargo:GetName()or"none"
-local temptable=cargo:GetTemplates()or{}
-local factor=1.5
-local zone=Zone
-local randomcoord=zone:GetRandomCoordinate(10,30*factor,Surfacetypes):GetVec2()
-if PreciseLocation then
-randomcoord=zone:GetCoordinate():GetVec2()
-end
-for _,_template in pairs(temptable)do
-self.TroopCounter=self.TroopCounter+1
-local alias=string.format("%s-%d",_template,math.random(1,100000))
-self.DroppedTroops[self.TroopCounter]=SPAWN:NewWithAlias(_template,alias)
-:InitRandomizeUnits(true,20,2)
-:InitDelayOff()
-:SpawnFromVec2(randomcoord)
-if self.movetroopstowpzone and type~=CTLD_CARGO.Enum.ENGINEERS then
-self:_MoveGroupToZone(self.DroppedTroops[self.TroopCounter])
-end
-end
-cargo:SetWasDropped(true)
-if type==CTLD_CARGO.Enum.ENGINEERS then
-self.Engineers=self.Engineers+1
-local grpname=self.DroppedTroops[self.TroopCounter]:GetName()
-self.EngineersInField[self.Engineers]=CTLD_ENGINEERING:New(name,grpname)
-end
-if Structure then
-BASE:ScheduleOnce(0.5,PostSpawn,{self.DroppedTroops[self.TroopCounter],Structure})
-end
-if self.eventoninject then
-self:__TroopsDeployed(1,nil,nil,self.DroppedTroops[self.TroopCounter],type)
-end
-end
-return self
-end
-function CTLD:InjectVehicles(Zone,Cargo,Surfacetypes,PreciseLocation,Structure)
-self:T(self.lid.." InjectVehicles")
-local cargo=Cargo
-local function IsVehicMatch(cargo)
-local match=false
-local cgotbl=self.Cargo_Crates
-local name=cargo:GetName()
-for _,_cgo in pairs(cgotbl)do
-local cname=_cgo:GetName()
-if name==cname then
-match=true
-break
-end
-end
-return match
-end
-local function Cruncher(group,typename,anzahl)
-local units=group:GetUnits()
-local reduced=0
-for _,_unit in pairs(units)do
-local typo=_unit:GetTypeName()
-if typename==typo then
-_unit:Destroy(false)
-reduced=reduced+1
-if reduced==anzahl then break end
-end
-end
-end
-local function PostSpawn(args)
-local group=args[1]
-local structure=args[2]
-if structure then
-local loadedstructure={}
-local strcset=UTILS.Split(structure,";")
-for _,_data in pairs(strcset)do
-local datasplit=UTILS.Split(_data,"==")
-loadedstructure[datasplit[1]]=tonumber(datasplit[2])
-end
-local originalstructure=UTILS.GetCountPerTypeName(group)
-for _name,_number in pairs(originalstructure)do
-local loadednumber=0
-if loadedstructure[_name]then
-loadednumber=loadedstructure[_name]
-end
-local reduce=false
-if loadednumber<_number then reduce=true end
-if reduce then
-Cruncher(group,_name,_number-loadednumber)
-end
-end
-end
-end
-if not IsVehicMatch(cargo)then
-self.CargoCounter=self.CargoCounter+1
-cargo.ID=self.CargoCounter
-cargo.Stock=1
-table.insert(self.Cargo_Crates,cargo)
-end
-local type=cargo:GetType()
-if(type==CTLD_CARGO.Enum.VEHICLE or type==CTLD_CARGO.Enum.FOB)then
-local name=cargo:GetName()or"none"
-local temptable=cargo:GetTemplates()or{}
-local factor=1.5
-local zone=Zone
-local randomcoord=zone:GetRandomCoordinate(10,30*factor,Surfacetypes):GetVec2()
-if PreciseLocation then
-randomcoord=zone:GetCoordinate():GetVec2()
-end
-cargo:SetWasDropped(true)
-local canmove=false
-if type==CTLD_CARGO.Enum.VEHICLE then canmove=true end
-for _,_template in pairs(temptable)do
-self.TroopCounter=self.TroopCounter+1
-local alias=string.format("%s-%d",_template,math.random(1,100000))
-if canmove then
-self.DroppedTroops[self.TroopCounter]=SPAWN:NewWithAlias(_template,alias)
-:InitRandomizeUnits(true,20,2)
-:InitDelayOff()
-:SpawnFromVec2(randomcoord)
-else
-self.DroppedTroops[self.TroopCounter]=SPAWN:NewWithAlias(_template,alias)
-:InitDelayOff()
-:SpawnFromVec2(randomcoord)
-end
-if Structure then
-BASE:ScheduleOnce(0.5,PostSpawn,{self.DroppedTroops[self.TroopCounter],Structure})
-end
-if self.eventoninject then
-self:__CratesBuild(1,nil,nil,self.DroppedTroops[self.TroopCounter])
-end
-end
-end
-return self
-end
-function CTLD:onafterStart(From,Event,To)
-self:T({From,Event,To})
-self:I(self.lid.."Started ("..self.version..")")
-if self.useprefix or self.enableHercules then
-local prefix=self.prefixes
-if self.enableHercules then
-self.PilotGroups=SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(prefix):FilterStart()
-else
-self.PilotGroups=SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(prefix):FilterCategories("helicopter"):FilterStart()
-end
-else
-self.PilotGroups=SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterCategories("helicopter"):FilterStart()
-end
-self:HandleEvent(EVENTS.PlayerEnterAircraft,self._EventHandler)
-self:HandleEvent(EVENTS.PlayerEnterUnit,self._EventHandler)
-self:HandleEvent(EVENTS.PlayerLeaveUnit,self._EventHandler)
-self:__Status(-5)
-if self.enableLoadSave then
-local interval=self.saveinterval
-local filename=self.filename
-local filepath=self.filepath
-self:__Save(interval,filepath,filename)
-end
-return self
-end
-function CTLD:onbeforeStatus(From,Event,To)
-self:T({From,Event,To})
-self:CleanDroppedTroops()
-self:_RefreshF10Menus()
-self:CheckDroppedBeacons()
-self:_RefreshRadioBeacons()
-self:CheckAutoHoverload()
-self:_CheckEngineers()
-return self
-end
-function CTLD:onafterStatus(From,Event,To)
-self:T({From,Event,To})
-local pilots=0
-for _,_pilot in pairs(self.CtldUnits)do
-pilots=pilots+1
-end
-local boxes=0
-for _,_pilot in pairs(self.Spawned_Cargo)do
-boxes=boxes+1
-end
-local cc=self.CargoCounter
-local tc=self.TroopCounter
-if self.debug or self.verbose>0 then
-local text=string.format("%s Pilots %d | Live Crates %d |\nCargo Counter %d | Troop Counter %d",self.lid,pilots,boxes,cc,tc)
-local m=MESSAGE:New(text,10,"CTLD"):ToAll()
-if self.verbose>0 then
-self:I(self.lid.."Cargo and Troops in Stock:")
-for _,_troop in pairs(self.Cargo_Crates)do
-local name=_troop:GetName()
-local stock=_troop:GetStock()
-self:I(string.format("-- %s \t\t\t %d",name,stock))
-end
-for _,_troop in pairs(self.Cargo_Statics)do
-local name=_troop:GetName()
-local stock=_troop:GetStock()
-self:I(string.format("-- %s \t\t\t %d",name,stock))
-end
-for _,_troop in pairs(self.Cargo_Troops)do
-local name=_troop:GetName()
-local stock=_troop:GetStock()
-self:I(string.format("-- %s \t\t %d",name,stock))
-end
-end
-end
-self:__Status(-30)
-return self
-end
-function CTLD:onafterStop(From,Event,To)
-self:T({From,Event,To})
-self:UnhandleEvent(EVENTS.PlayerEnterAircraft)
-self:UnhandleEvent(EVENTS.PlayerEnterUnit)
-self:UnhandleEvent(EVENTS.PlayerLeaveUnit)
-return self
-end
-function CTLD:onbeforeTroopsPickedUp(From,Event,To,Group,Unit,Cargo)
-self:T({From,Event,To})
-return self
-end
-function CTLD:onbeforeCratesPickedUp(From,Event,To,Group,Unit,Cargo)
-self:T({From,Event,To})
-return self
-end
-function CTLD:onbeforeTroopsExtracted(From,Event,To,Group,Unit,Troops)
-self:T({From,Event,To})
-return self
-end
-function CTLD:onbeforeTroopsDeployed(From,Event,To,Group,Unit,Troops)
-self:T({From,Event,To})
-if Unit and Unit:IsPlayer()and self.PlayerTaskQueue then
-local playername=Unit:GetPlayerName()
-local dropcoord=Troops:GetCoordinate()or COORDINATE:New(0,0,0)
-local dropvec2=dropcoord:GetVec2()
-self.PlayerTaskQueue:ForEach(
-function(Task)
-local task=Task
-local subtype=task:GetSubType()
-if Event==subtype and not task:IsDone()then
-local targetzone=task.Target:GetObject()
-if targetzone and targetzone.ClassName and string.match(targetzone.ClassName,"ZONE")and targetzone:IsVec2InZone(dropvec2)then
-if task.Clients:HasUniqueID(playername)then
-task:__Success(-1)
-end
-end
-end
-end
-)
-end
-return self
-end
-function CTLD:onafterTroopsDeployed(From,Event,To,Group,Unit,Troops,Type)
-self:T({From,Event,To})
-if self.movetroopstowpzone and Type~=CTLD_CARGO.Enum.ENGINEERS then
-self:_MoveGroupToZone(Troops)
-end
-return self
-end
-function CTLD:onbeforeCratesDropped(From,Event,To,Group,Unit,Cargotable)
-self:T({From,Event,To})
-return self
-end
-function CTLD:onbeforeCratesBuild(From,Event,To,Group,Unit,Vehicle)
-self:T({From,Event,To})
-if Unit and Unit:IsPlayer()and self.PlayerTaskQueue then
-local playername=Unit:GetPlayerName()
-local dropcoord=Vehicle:GetCoordinate()or COORDINATE:New(0,0,0)
-local dropvec2=dropcoord:GetVec2()
-self.PlayerTaskQueue:ForEach(
-function(Task)
-local task=Task
-local subtype=task:GetSubType()
-if Event==subtype and not task:IsDone()then
-local targetzone=task.Target:GetObject()
-if targetzone and targetzone.ClassName and string.match(targetzone.ClassName,"ZONE")and targetzone:IsVec2InZone(dropvec2)then
-if task.Clients:HasUniqueID(playername)then
-task:__Success(-1)
-end
-end
-end
-end
-)
-end
-return self
-end
-function CTLD:onafterCratesBuild(From,Event,To,Group,Unit,Vehicle)
-self:T({From,Event,To})
-if self.movetroopstowpzone then
-self:_MoveGroupToZone(Vehicle)
-end
-return self
-end
-function CTLD:onbeforeTroopsRTB(From,Event,To,Group,Unit)
-self:T({From,Event,To})
-return self
-end
-function CTLD:onbeforeSave(From,Event,To,path,filename)
-self:T({From,Event,To,path,filename})
-if not self.enableLoadSave then
-return self
-end
-if not io then
-self:E(self.lid.."ERROR: io not desanitized. Can't save current state.")
-return false
-end
-if path==nil and not lfs then
-self:E(self.lid.."WARNING: lfs not desanitized. State will be saved in DCS installation root directory rather than your \"Saved Games\\DCS\" folder.")
-end
-return true
-end
-function CTLD:onafterSave(From,Event,To,path,filename)
-self:T({From,Event,To,path,filename})
-if not self.enableLoadSave then
-return self
-end
-local function _savefile(filename,data)
-local f=assert(io.open(filename,"wb"))
-f:write(data)
-f:close()
-end
-if lfs then
-path=self.filepath or lfs.writedir()
-end
-filename=filename or self.filename
-if path~=nil then
-filename=path.."\\"..filename
-end
-local grouptable=self.DroppedTroops
-local cgovehic=self.Cargo_Crates
-local cgotable=self.Cargo_Troops
-local stcstable=self.Spawned_Cargo
-local statics=nil
-local statics={}
-self:T(self.lid.."Bulding Statics Table for Saving")
-for _,_cargo in pairs(stcstable)do
-local cargo=_cargo
-local object=cargo:GetPositionable()
-if object and object:IsAlive()and(cargo:WasDropped()or not cargo:HasMoved())then
-statics[#statics+1]=cargo
-end
-end
-local function FindCargoType(name,table)
-local match=false
-local cargo=nil
-name=string.gsub(name,"-"," ")
-for _ind,_cargo in pairs(table)do
-local thiscargo=_cargo
-local template=thiscargo:GetTemplates()
-if type(template)=="string"then
-template={template}
-end
-for _,_name in pairs(template)do
-_name=string.gsub(_name,"-"," ")
-if string.find(name,_name)and _cargo:GetType()~=CTLD_CARGO.Enum.REPAIR then
-match=true
-cargo=thiscargo
-end
-end
-if match then break end
-end
-return match,cargo
-end
-local data="Group,x,y,z,CargoName,CargoTemplates,CargoType,CratesNeeded,CrateMass,Structure\n"
-local n=0
-for _,_grp in pairs(grouptable)do
-local group=_grp
-if group and group:IsAlive()then
-local name=group:GetName()
-local template=name
-if string.find(template,"#")then
-template=string.gsub(name,"#(%d+)$","")
-end
-local template=string.gsub(name,"-(%d+)$","")
-local match,cargo=FindCargoType(template,cgotable)
-if not match then
-match,cargo=FindCargoType(template,cgovehic)
-end
-if match then
-n=n+1
-local cargo=cargo
-local cgoname=cargo.Name
-local cgotemp=cargo.Templates
-local cgotype=cargo.CargoType
-local cgoneed=cargo.CratesNeeded
-local cgomass=cargo.PerCrateMass
-local structure=UTILS.GetCountPerTypeName(group)
-local strucdata=""
-for typen,anzahl in pairs(structure)do
-strucdata=strucdata..typen.."=="..anzahl..";"
-end
-if type(cgotemp)=="table"then
-local templates="{"
-for _,_tmpl in pairs(cgotemp)do
-templates=templates.._tmpl..";"
-end
-templates=templates.."}"
-cgotemp=templates
-end
-local location=group:GetVec3()
-local txt=string.format("%s,%d,%d,%d,%s,%s,%s,%d,%d,%s\n"
-,template,location.x,location.y,location.z,cgoname,cgotemp,cgotype,cgoneed,cgomass,strucdata)
-data=data..txt
-end
-end
-end
-for _,_cgo in pairs(statics)do
-local object=_cgo
-local cgoname=object.Name
-local cgotemp=object.Templates
-if type(cgotemp)=="table"then
-local templates="{"
-for _,_tmpl in pairs(cgotemp)do
-templates=templates.._tmpl..";"
-end
-templates=templates.."}"
-cgotemp=templates
-end
-local cgotype=object.CargoType
-local cgoneed=object.CratesNeeded
-local cgomass=object.PerCrateMass
-local crateobj=object.Positionable
-local location=crateobj:GetVec3()
-local txt=string.format("%s,%d,%d,%d,%s,%s,%s,%d,%d\n"
-,"STATIC",location.x,location.y,location.z,cgoname,cgotemp,cgotype,cgoneed,cgomass)
-data=data..txt
-end
-_savefile(filename,data)
-if self.enableLoadSave then
-local interval=self.saveinterval
-local filename=self.filename
-local filepath=self.filepath
-self:__Save(interval,filepath,filename)
-end
-return self
-end
-function CTLD:onbeforeLoad(From,Event,To,path,filename)
-self:T({From,Event,To,path,filename})
-if not self.enableLoadSave then
-return self
-end
-local function _fileexists(name)
-local f=io.open(name,"r")
-if f~=nil then
-io.close(f)
-return true
-else
-return false
-end
-end
-filename=filename or self.filename
-path=path or self.filepath
-if not io then
-self:E(self.lid.."WARNING: io not desanitized. Cannot load file.")
-return false
-end
-if path==nil and not lfs then
-self:E(self.lid.."WARNING: lfs not desanitized. State will be saved in DCS installation root directory rather than your \"Saved Games\\DCS\" folder.")
-end
-if lfs then
-path=path or lfs.writedir()
-end
-if path~=nil then
-filename=path.."\\"..filename
-end
-local exists=_fileexists(filename)
-if exists then
-return true
-else
-self:E(self.lid..string.format("WARNING: State file %s might not exist.",filename))
-return false
-end
-end
-function CTLD:onafterLoad(From,Event,To,path,filename)
-self:T({From,Event,To,path,filename})
-if not self.enableLoadSave then
-return self
-end
-local function _loadfile(filename)
-local f=assert(io.open(filename,"rb"))
-local data=f:read("*all")
-f:close()
-return data
-end
-filename=filename or self.filename
-path=path or self.filepath
-if lfs then
-path=path or lfs.writedir()
-end
-if path~=nil then
-filename=path.."\\"..filename
-end
-local text=string.format("Loading CTLD state from file %s",filename)
-MESSAGE:New(text,10):ToAllIf(self.Debug)
-self:I(self.lid..text)
-local file=assert(io.open(filename,"rb"))
-local loadeddata={}
-for line in file:lines()do
-loadeddata[#loadeddata+1]=line
-end
-file:close()
-table.remove(loadeddata,1)
-for _id,_entry in pairs(loadeddata)do
-local dataset=UTILS.Split(_entry,",")
-local groupname=dataset[1]
-local vec2={}
-vec2.x=tonumber(dataset[2])
-vec2.y=tonumber(dataset[4])
-local cargoname=dataset[5]
-local cargotype=dataset[7]
-if type(groupname)=="string"and groupname~="STATIC"then
-local cargotemplates=dataset[6]
-cargotemplates=string.gsub(cargotemplates,"{","")
-cargotemplates=string.gsub(cargotemplates,"}","")
-cargotemplates=UTILS.Split(cargotemplates,";")
-local size=tonumber(dataset[8])
-local mass=tonumber(dataset[9])
-local structure=nil
-if dataset[10]then
-structure=dataset[10]
-structure=string.gsub(structure,",","")
-end
-local dropzone=ZONE_RADIUS:New("DropZone",vec2,20)
-if cargotype==CTLD_CARGO.Enum.VEHICLE or cargotype==CTLD_CARGO.Enum.FOB then
-local injectvehicle=CTLD_CARGO:New(nil,cargoname,cargotemplates,cargotype,true,true,size,nil,true,mass)
-self:InjectVehicles(dropzone,injectvehicle,self.surfacetypes,self.useprecisecoordloads,structure)
-elseif cargotype==CTLD_CARGO.Enum.TROOPS or cargotype==CTLD_CARGO.Enum.ENGINEERS then
-local injecttroops=CTLD_CARGO:New(nil,cargoname,cargotemplates,cargotype,true,true,size,nil,true,mass)
-self:InjectTroops(dropzone,injecttroops,self.surfacetypes,self.useprecisecoordloads,structure)
-end
-elseif(type(groupname)=="string"and groupname=="STATIC")or cargotype==CTLD_CARGO.Enum.REPAIR then
-local cargotemplates=dataset[6]
-local size=tonumber(dataset[8])
-local mass=tonumber(dataset[9])
-local dropzone=ZONE_RADIUS:New("DropZone",vec2,20)
-local injectstatic=nil
-if cargotype==CTLD_CARGO.Enum.VEHICLE or cargotype==CTLD_CARGO.Enum.FOB then
-cargotemplates=string.gsub(cargotemplates,"{","")
-cargotemplates=string.gsub(cargotemplates,"}","")
-cargotemplates=UTILS.Split(cargotemplates,";")
-injectstatic=CTLD_CARGO:New(nil,cargoname,cargotemplates,cargotype,true,true,size,nil,true,mass)
-elseif cargotype==CTLD_CARGO.Enum.STATIC or cargotype==CTLD_CARGO.Enum.REPAIR then
-injectstatic=CTLD_CARGO:New(nil,cargoname,cargotemplates,cargotype,true,true,size,nil,true,mass)
-end
-if injectstatic then
-self:InjectStatics(dropzone,injectstatic)
-end
-end
-end
-return self
-end
-end
-do
-CTLD_HERCULES={
-ClassName="CTLD_HERCULES",
-lid="",
-Name="",
-Version="0.0.3",
-}
-CTLD_HERCULES.Types={
-["ATGM M1045 HMMWV TOW Air [7183lb]"]={['name']="M1045 HMMWV TOW",['container']=true},
-["ATGM M1045 HMMWV TOW Skid [7073lb]"]={['name']="M1045 HMMWV TOW",['container']=false},
-["APC M1043 HMMWV Armament Air [7023lb]"]={['name']="M1043 HMMWV Armament",['container']=true},
-["APC M1043 HMMWV Armament Skid [6912lb]"]={['name']="M1043 HMMWV Armament",['container']=false},
-["SAM Avenger M1097 Air [7200lb]"]={['name']="M1097 Avenger",['container']=true},
-["SAM Avenger M1097 Skid [7090lb]"]={['name']="M1097 Avenger",['container']=false},
-["APC Cobra Air [10912lb]"]={['name']="Cobra",['container']=true},
-["APC Cobra Skid [10802lb]"]={['name']="Cobra",['container']=false},
-["APC M113 Air [21624lb]"]={['name']="M-113",['container']=true},
-["APC M113 Skid [21494lb]"]={['name']="M-113",['container']=false},
-["Tanker M978 HEMTT [34000lb]"]={['name']="M978 HEMTT Tanker",['container']=false},
-["HEMTT TFFT [34400lb]"]={['name']="HEMTT TFFT",['container']=false},
-["SPG M1128 Stryker MGS [33036lb]"]={['name']="M1128 Stryker MGS",['container']=false},
-["AAA Vulcan M163 Air [21666lb]"]={['name']="Vulcan",['container']=true},
-["AAA Vulcan M163 Skid [21577lb]"]={['name']="Vulcan",['container']=false},
-["APC M1126 Stryker ICV [29542lb]"]={['name']="M1126 Stryker ICV",['container']=false},
-["ATGM M1134 Stryker [30337lb]"]={['name']="M1134 Stryker ATGM",['container']=false},
-["APC LAV-25 Air [22520lb]"]={['name']="LAV-25",['container']=true},
-["APC LAV-25 Skid [22514lb]"]={['name']="LAV-25",['container']=false},
-["M1025 HMMWV Air [6160lb]"]={['name']="Hummer",['container']=true},
-["M1025 HMMWV Skid [6050lb]"]={['name']="Hummer",['container']=false},
-["IFV M2A2 Bradley [34720lb]"]={['name']="M-2 Bradley",['container']=false},
-["IFV MCV-80 [34720lb]"]={['name']="MCV-80",['container']=false},
-["IFV BMP-1 [23232lb]"]={['name']="BMP-1",['container']=false},
-["IFV BMP-2 [25168lb]"]={['name']="BMP-2",['container']=false},
-["IFV BMP-3 [32912lb]"]={['name']="BMP-3",['container']=false},
-["ARV BRDM-2 Air [12320lb]"]={['name']="BRDM-2",['container']=true},
-["ARV BRDM-2 Skid [12210lb]"]={['name']="BRDM-2",['container']=false},
-["APC BTR-80 Air [23936lb]"]={['name']="BTR-80",['container']=true},
-["APC BTR-80 Skid [23826lb]"]={['name']="BTR-80",['container']=false},
-["APC BTR-82A Air [24998lb]"]={['name']="BTR-82A",['container']=true},
-["APC BTR-82A Skid [24888lb]"]={['name']="BTR-82A",['container']=false},
-["SAM ROLAND ADS [34720lb]"]={['name']="Roland Radar",['container']=false},
-["SAM ROLAND LN [34720b]"]={['name']="Roland ADS",['container']=false},
-["SAM SA-13 STRELA [21624lb]"]={['name']="Strela-10M3",['container']=false},
-["AAA ZSU-23-4 Shilka [32912lb]"]={['name']="ZSU-23-4 Shilka",['container']=false},
-["SAM SA-19 Tunguska 2S6 [34720lb]"]={['name']="2S6 Tunguska",['container']=false},
-["Transport UAZ-469 Air [3747lb]"]={['name']="UAZ-469",['container']=true},
-["Transport UAZ-469 Skid [3630lb]"]={['name']="UAZ-469",['container']=false},
-["AAA GEPARD [34720lb]"]={['name']="Gepard",['container']=false},
-["SAM CHAPARRAL Air [21624lb]"]={['name']="M48 Chaparral",['container']=true},
-["SAM CHAPARRAL Skid [21516lb]"]={['name']="M48 Chaparral",['container']=false},
-["SAM LINEBACKER [34720lb]"]={['name']="M6 Linebacker",['container']=false},
-["Transport URAL-375 [14815lb]"]={['name']="Ural-375",['container']=false},
-["Transport M818 [16000lb]"]={['name']="M 818",['container']=false},
-["IFV MARDER [34720lb]"]={['name']="Marder",['container']=false},
-["Transport Tigr Air [15900lb]"]={['name']="Tigr_233036",['container']=true},
-["Transport Tigr Skid [15730lb]"]={['name']="Tigr_233036",['container']=false},
-["IFV TPZ FUCH [33440lb]"]={['name']="TPZ",['container']=false},
-["IFV BMD-1 Air [18040lb]"]={['name']="BMD-1",['container']=true},
-["IFV BMD-1 Skid [17930lb]"]={['name']="BMD-1",['container']=false},
-["IFV BTR-D Air [18040lb]"]={['name']="BTR_D",['container']=true},
-["IFV BTR-D Skid [17930lb]"]={['name']="BTR_D",['container']=false},
-["EWR SBORKA Air [21624lb]"]={['name']="Dog Ear radar",['container']=true},
-["EWR SBORKA Skid [21624lb]"]={['name']="Dog Ear radar",['container']=false},
-["ART 2S9 NONA Air [19140lb]"]={['name']="SAU 2-C9",['container']=true},
-["ART 2S9 NONA Skid [19030lb]"]={['name']="SAU 2-C9",['container']=false},
-["ART GVOZDIKA [34720lb]"]={['name']="SAU Gvozdika",['container']=false},
-["APC MTLB Air [26400lb]"]={['name']="MTLB",['container']=true},
-["APC MTLB Skid [26290lb]"]={['name']="MTLB",['container']=false},
-}
-function CTLD_HERCULES:New(Coalition,Alias,CtldObject)
-local self=BASE:Inherit(self,FSM:New())
-if Coalition and type(Coalition)=="string"then
-if Coalition=="blue"then
-self.coalition=coalition.side.BLUE
-self.coalitiontxt=Coalition
-elseif Coalition=="red"then
-self.coalition=coalition.side.RED
-self.coalitiontxt=Coalition
-elseif Coalition=="neutral"then
-self.coalition=coalition.side.NEUTRAL
-self.coalitiontxt=Coalition
-else
-self:E("ERROR: Unknown coalition in CTLD!")
-end
-else
-self.coalition=Coalition
-self.coalitiontxt=string.lower(UTILS.GetCoalitionName(self.coalition))
-end
-if Alias then
-self.alias=tostring(Alias)
-else
-self.alias="UNHCR"
-if self.coalition then
-if self.coalition==coalition.side.RED then
-self.alias="Red CTLD Hercules"
-elseif self.coalition==coalition.side.BLUE then
-self.alias="Blue CTLD Hercules"
-end
-end
-end
-self.lid=string.format("%s (%s) | ",self.alias,self.coalitiontxt)
-self.infantrytemplate="Infantry"
-self.CTLD=CtldObject
-self.verbose=true
-self.j=0
-self.carrierGroups={}
-self.Cargo={}
-self.ParatrooperCount={}
-self.ObjectTracker={}
-self.lid=string.format("%s (%s) | ",self.alias,self.coalition and UTILS.GetCoalitionName(self.coalition)or"unknown")
-self:HandleEvent(EVENTS.Shot,self._HandleShot)
-self:I(self.lid.."Started")
-self:CheckTemplates()
-return self
-end
-function CTLD_HERCULES:CheckTemplates()
-self:T(self.lid..'CheckTemplates')
-self.Types["Paratroopers 10"]={
-name=self.infantrytemplate,
-container=false,
-available=false,
-}
-local missing={}
-local nomissing=0
-local found={}
-local nofound=0
-for _index,_tab in pairs(self.Types)do
-local outcometxt="MISSING"
-if _DATABASE.Templates.Groups[_tab.name]then
-outcometxt="OK"
-self.Types[_index].available=true
-found[_tab.name]=true
-else
-self.Types[_index].available=false
-missing[_tab.name]=true
-end
-if self.verbose then
-self:I(string.format(self.lid.."Checking template for %s (%s) ... %s",_index,_tab.name,outcometxt))
-end
-end
-for _,_name in pairs(found)do
-nofound=nofound+1
-end
-for _,_name in pairs(missing)do
-nomissing=nomissing+1
-end
-self:I(string.format(self.lid.."Template Check Summary: Found %d, Missing %d, Total %d",nofound,nomissing,nofound+nomissing))
-return self
-end
-function CTLD_HERCULES:Soldier_SpawnGroup(Cargo_Drop_initiator,Cargo_Drop_Position,Cargo_Type_name,CargoHeading,Cargo_Country,GroupSpacing)
-self:T(self.lid..'Soldier_SpawnGroup')
-self:T(Cargo_Drop_Position)
-local InjectTroopsType=CTLD_CARGO:New(nil,self.infantrytemplate,{self.infantrytemplate},CTLD_CARGO.Enum.TROOPS,true,true,10,nil,false,80)
-local position=Cargo_Drop_Position:GetVec2()
-local dropzone=ZONE_RADIUS:New("Infantry "..math.random(1,10000),position,100)
-self.CTLD:InjectTroops(dropzone,InjectTroopsType)
-return self
-end
-function CTLD_HERCULES:Cargo_SpawnGroup(Cargo_Drop_initiator,Cargo_Drop_Position,Cargo_Type_name,CargoHeading,Cargo_Country)
-self:T(self.lid.."Cargo_SpawnGroup")
-self:T(Cargo_Type_name)
-if Cargo_Type_name~='Container red 1'then
-local InjectVehicleType=CTLD_CARGO:New(nil,Cargo_Type_name,{Cargo_Type_name},CTLD_CARGO.Enum.VEHICLE,true,true,1,nil,false,1000)
-local position=Cargo_Drop_Position:GetVec2()
-local dropzone=ZONE_RADIUS:New("Vehicle "..math.random(1,10000),position,100)
-self.CTLD:InjectVehicles(dropzone,InjectVehicleType)
-end
-return self
-end
-function CTLD_HERCULES:Cargo_SpawnStatic(Cargo_Drop_initiator,Cargo_Drop_Position,Cargo_Type_name,CargoHeading,dead,Cargo_Country)
-self:T(self.lid.."Cargo_SpawnStatic")
-self:T("Static "..Cargo_Type_name.." Dead "..tostring(dead))
-local position=Cargo_Drop_Position:GetVec2()
-local Zone=ZONE_RADIUS:New("Cargo Static "..math.random(1,10000),position,100)
-if not dead then
-local injectstatic=CTLD_CARGO:New(nil,"Cargo Static Group "..math.random(1,10000),"iso_container",CTLD_CARGO.Enum.STATIC,true,false,1,nil,true,4500,1)
-self.CTLD:InjectStatics(Zone,injectstatic,true)
-end
-return self
-end
-function CTLD_HERCULES:Cargo_SpawnDroppedAsCargo(_name,_pos)
-local theCargo=self.CTLD:_FindCratesCargoObject(_name)
-if theCargo then
-self.CTLD.CrateCounter=self.CTLD.CrateCounter+1
-self.CTLD.CargoCounter=self.CTLD.CargoCounter+1
-local basetype=self.CTLD.basetype or"container_cargo"
-local theStatic=SPAWNSTATIC:NewFromType(basetype,"Cargos",self.cratecountry)
-:InitCargoMass(theCargo.PerCrateMass)
-:InitCargo(self.CTLD.enableslingload)
-:InitCoordinate(_pos)
-:Spawn(270,_name.."-Container-"..math.random(1,100000))
-self.CTLD.Spawned_Crates[self.CTLD.CrateCounter]=theStatic
-local newCargo=CTLD_CARGO:New(self.CTLD.CargoCounter,theCargo.Name,theCargo.Templates,theCargo.CargoType,true,false,theCargo.CratesNeeded,self.CTLD.Spawned_Crates[self.CTLD.CrateCounter],true,theCargo.PerCrateMass,nil,theCargo.Subcategory)
-table.insert(self.CTLD.Spawned_Cargo,newCargo)
-newCargo:SetWasDropped(true)
-newCargo:SetHasMoved(true)
-end
-return self
-end
-function CTLD_HERCULES:Cargo_SpawnObjects(Cargo_Drop_initiator,Cargo_Drop_Direction,Cargo_Content_position,Cargo_Type_name,Cargo_over_water,Container_Enclosed,ParatrooperGroupSpawn,offload_cargo,all_cargo_survive_to_the_ground,all_cargo_gets_destroyed,destroy_cargo_dropped_without_parachute,Cargo_Country)
-self:T(self.lid..'Cargo_SpawnObjects')
-local CargoHeading=self.CargoHeading
-if offload_cargo==true or ParatrooperGroupSpawn==true then
-if ParatrooperGroupSpawn==true then
-self:Soldier_SpawnGroup(Cargo_Drop_initiator,Cargo_Content_position,Cargo_Type_name,CargoHeading,Cargo_Country,10)
-else
-self:Cargo_SpawnGroup(Cargo_Drop_initiator,Cargo_Content_position,Cargo_Type_name,CargoHeading,Cargo_Country)
-end
-else
-if all_cargo_gets_destroyed==true or Cargo_over_water==true then
-else
-if all_cargo_survive_to_the_ground==true then
-if ParatrooperGroupSpawn==true then
-self:Cargo_SpawnStatic(Cargo_Drop_initiator,Cargo_Content_position,Cargo_Type_name,CargoHeading,true,Cargo_Country)
-else
-self:Cargo_SpawnGroup(Cargo_Drop_initiator,Cargo_Content_position,Cargo_Type_name,CargoHeading,Cargo_Country)
-end
-if Container_Enclosed==true then
-if ParatrooperGroupSpawn==false then
-self:Cargo_SpawnStatic(Cargo_Drop_initiator,Cargo_Content_position,"Hercules_Container_Parachute_Static",CargoHeading,false,Cargo_Country)
-end
-end
-end
-if destroy_cargo_dropped_without_parachute==true then
-if Container_Enclosed==true then
-if ParatrooperGroupSpawn==true then
-self:Soldier_SpawnGroup(Cargo_Drop_initiator,Cargo_Content_position,Cargo_Type_name,CargoHeading,Cargo_Country,0)
-else
-if self.CTLD.dropAsCargoCrate then
-self:Cargo_SpawnDroppedAsCargo(Cargo_Type_name,Cargo_Content_position)
-else
-self:Cargo_SpawnGroup(Cargo_Drop_initiator,Cargo_Content_position,Cargo_Type_name,CargoHeading,Cargo_Country)
-self:Cargo_SpawnStatic(Cargo_Drop_initiator,Cargo_Content_position,"Hercules_Container_Parachute_Static",CargoHeading,false,Cargo_Country)
-end
-end
-else
-self:Cargo_SpawnStatic(Cargo_Drop_initiator,Cargo_Content_position,Cargo_Type_name,CargoHeading,true,Cargo_Country)
-end
-end
-end
-end
-return self
-end
-function CTLD_HERCULES:Calculate_Object_Height_AGL(group)
-self:T(self.lid.."Calculate_Object_Height_AGL")
-if group.ClassName and group.ClassName=="GROUP"then
-local gcoord=group:GetCoordinate()
-local height=group:GetHeight()
-local lheight=gcoord:GetLandHeight()
-self:T(self.lid.."Height "..height-lheight)
-return height-lheight
-else
-if group:isExist()then
-local dcsposition=group:getPosition().p
-local dcsvec2={x=dcsposition.x,y=dcsposition.z}
-local height=math.floor(group:getPosition().p.y-land.getHeight(dcsvec2))
-self.ObjectTracker[group.id_]=dcsposition
-self:T(self.lid.."Height "..height)
-return height
-else
-return 0
-end
-end
-end
-function CTLD_HERCULES:Check_SurfaceType(object)
-self:T(self.lid.."Check_SurfaceType")
-if object:isExist()then
-return land.getSurfaceType({x=object:getPosition().p.x,y=object:getPosition().p.z})
-else
-return 1
-end
-end
-function CTLD_HERCULES:Cargo_Track(cargo,initiator)
-self:T(self.lid.."Cargo_Track")
-local Cargo_Drop_initiator=initiator
-if cargo.Cargo_Contents~=nil then
-if self:Calculate_Object_Height_AGL(cargo.Cargo_Contents)<10 then
-if self:Check_SurfaceType(cargo.Cargo_Contents)==2 or self:Check_SurfaceType(cargo.Cargo_Contents)==3 then
-cargo.Cargo_over_water=true
-end
-local dcsvec3=self.ObjectTracker[cargo.Cargo_Contents.id_]or initiator:GetVec3()
-self:T("SPAWNPOSITION: ")
-self:T({dcsvec3})
-local Vec2={
-x=dcsvec3.x,
-y=dcsvec3.z,
-}
-local vec3=COORDINATE:NewFromVec2(Vec2)
-self.ObjectTracker[cargo.Cargo_Contents.id_]=nil
-self:Cargo_SpawnObjects(Cargo_Drop_initiator,cargo.Cargo_Drop_Direction,vec3,cargo.Cargo_Type_name,cargo.Cargo_over_water,cargo.Container_Enclosed,cargo.ParatrooperGroupSpawn,cargo.offload_cargo,cargo.all_cargo_survive_to_the_ground,cargo.all_cargo_gets_destroyed,cargo.destroy_cargo_dropped_without_parachute,cargo.Cargo_Country)
-if cargo.Cargo_Contents:isExist()then
-cargo.Cargo_Contents:destroy()
-end
-cargo.scheduleFunctionID:Stop()
-cargo={}
-end
-end
-return self
-end
-function CTLD_HERCULES:Calculate_Cargo_Drop_initiator_NorthCorrection(point)
-self:T(self.lid.."Calculate_Cargo_Drop_initiator_NorthCorrection")
-if not point.z then
-point.z=point.y
-point.y=0
-end
-local lat,lon=coord.LOtoLL(point)
-local north_posit=coord.LLtoLO(lat+1,lon)
-return math.atan2(north_posit.z-point.z,north_posit.x-point.x)
-end
-function CTLD_HERCULES:Calculate_Cargo_Drop_initiator_Heading(Cargo_Drop_initiator)
-self:T(self.lid.."Calculate_Cargo_Drop_initiator_Heading")
-local Heading=Cargo_Drop_initiator:GetHeading()
-Heading=Heading+self:Calculate_Cargo_Drop_initiator_NorthCorrection(Cargo_Drop_initiator:GetVec3())
-if Heading<0 then
-Heading=Heading+(2*math.pi)
-end
-return Heading+0.06
-end
-function CTLD_HERCULES:Cargo_Initialize(Initiator,Cargo_Contents,Cargo_Type_name,Container_Enclosed,SoldierGroup,ParatrooperGroupSpawnInit)
-self:T(self.lid.."Cargo_Initialize")
-local Cargo_Drop_initiator=Initiator:GetName()
-if Cargo_Drop_initiator~=nil then
-if ParatrooperGroupSpawnInit==true then
-self:T("Paratrooper Drop")
-if not self.ParatrooperCount[Cargo_Drop_initiator]then
-self.ParatrooperCount[Cargo_Drop_initiator]=1
-else
-self.ParatrooperCount[Cargo_Drop_initiator]=self.ParatrooperCount[Cargo_Drop_initiator]+1
-end
-local Paratroopers=self.ParatrooperCount[Cargo_Drop_initiator]
-self:T("Paratrooper Drop Number "..self.ParatrooperCount[Cargo_Drop_initiator])
-local SpawnParas=false
-if math.fmod(Paratroopers,10)==0 then
-SpawnParas=true
-end
-self.j=self.j+1
-self.Cargo[self.j]={}
-self.Cargo[self.j].Cargo_Drop_Direction=self:Calculate_Cargo_Drop_initiator_Heading(Initiator)
-self.Cargo[self.j].Cargo_Contents=Cargo_Contents
-self.Cargo[self.j].Cargo_Type_name=Cargo_Type_name
-self.Cargo[self.j].Container_Enclosed=Container_Enclosed
-self.Cargo[self.j].ParatrooperGroupSpawn=SpawnParas
-self.Cargo[self.j].Cargo_Country=Initiator:GetCountry()
-if self:Calculate_Object_Height_AGL(Initiator)<5.0 then
-self.Cargo[self.j].offload_cargo=true
-elseif self:Calculate_Object_Height_AGL(Initiator)<10.0 then
-self.Cargo[self.j].all_cargo_survive_to_the_ground=true
-elseif self:Calculate_Object_Height_AGL(Initiator)<100.0 then
-self.Cargo[self.j].all_cargo_gets_destroyed=true
-else
-self.Cargo[self.j].all_cargo_gets_destroyed=false
-end
-local timer=TIMER:New(self.Cargo_Track,self,self.Cargo[self.j],Initiator)
-self.Cargo[self.j].scheduleFunctionID=timer
-timer:Start(1,1,600)
-else
-self.j=self.j+1
-self.Cargo[self.j]={}
-self.Cargo[self.j].Cargo_Drop_Direction=self:Calculate_Cargo_Drop_initiator_Heading(Initiator)
-self.Cargo[self.j].Cargo_Contents=Cargo_Contents
-self.Cargo[self.j].Cargo_Type_name=Cargo_Type_name
-self.Cargo[self.j].Container_Enclosed=Container_Enclosed
-self.Cargo[self.j].ParatrooperGroupSpawn=false
-self.Cargo[self.j].Cargo_Country=Initiator:GetCountry()
-if self:Calculate_Object_Height_AGL(Initiator)<5.0 then
-self.Cargo[self.j].offload_cargo=true
-elseif self:Calculate_Object_Height_AGL(Initiator)<10.0 then
-self.Cargo[self.j].all_cargo_survive_to_the_ground=true
-elseif self:Calculate_Object_Height_AGL(Initiator)<100.0 then
-self.Cargo[self.j].all_cargo_gets_destroyed=true
-else
-self.Cargo[self.j].destroy_cargo_dropped_without_parachute=true
-end
-local timer=TIMER:New(self.Cargo_Track,self,self.Cargo[self.j],Initiator)
-self.Cargo[self.j].scheduleFunctionID=timer
-timer:Start(1,1,600)
-end
-end
-return self
-end
-function CTLD_HERCULES:SetType(key,cargoType,cargoNum)
-self:T(self.lid.."SetType")
-self.carrierGroups[key]['cargoType']=cargoType
-self.carrierGroups[key]['cargoNum']=cargoNum
-return self
-end
-function CTLD_HERCULES:_HandleShot(Cargo_Drop_Event)
-self:T(self.lid.."Shot Event ID:"..Cargo_Drop_Event.id)
-if Cargo_Drop_Event.id==EVENTS.Shot then
-local GT_Name=""
-local SoldierGroup=false
-local ParatrooperGroupSpawnInit=false
-local GT_DisplayName=Weapon.getDesc(Cargo_Drop_Event.weapon).typeName:sub(15,-1)
-self:T(string.format("%sCargo_Drop_Event: %s",self.lid,Weapon.getDesc(Cargo_Drop_Event.weapon).typeName))
-if(GT_DisplayName=="Squad 30 x Soldier [7950lb]")then
-self:Cargo_Initialize(Cargo_Drop_Event.IniGroup,Cargo_Drop_Event.weapon,"Soldier M4 GRG",false,true,true)
-end
-if self.Types[GT_DisplayName]then
-local GT_Name=self.Types[GT_DisplayName]['name']
-local Cargo_Container_Enclosed=self.Types[GT_DisplayName]['container']
-self:Cargo_Initialize(Cargo_Drop_Event.IniGroup,Cargo_Drop_Event.weapon,GT_Name,Cargo_Container_Enclosed)
-end
-end
-return self
-end
-function CTLD_HERCULES:_HandleBirth(event)
-self:T(self.lid.."Birth Event ID:"..event.id)
-return self
-end
-end
-FLEET={
-ClassName="FLEET",
-verbose=0,
-pathfinding=false,
-}
-FLEET.version="0.0.1"
-function FLEET:New(WarehouseName,FleetName)
-local self=BASE:Inherit(self,LEGION:New(WarehouseName,FleetName))
-if not self then
-BASE:E(string.format("ERROR: Could not find warehouse %s!",WarehouseName))
-return nil
-end
-self.lid=string.format("FLEET %s | ",self.alias)
-self:SetRetreatZones()
-if self:IsShip()then
-local wh=self.warehouse
-local group=wh:GetGroup()
-self.warehouseOpsGroup=NAVYGROUP:New(group)
-self.warehouseOpsElement=self.warehouseOpsGroup:GetElementByName(wh:GetName())
-end
-self:AddTransition("*","NavyOnMission","*")
-return self
-end
-function FLEET:AddFlotilla(Flotilla)
-table.insert(self.cohorts,Flotilla)
-self:AddAssetToFlotilla(Flotilla,Flotilla.Ngroups)
-Flotilla:SetFleet(self)
-if Flotilla:IsStopped()then
-Flotilla:Start()
-end
-return self
-end
-function FLEET:AddAssetToFlotilla(Flotilla,Nassets)
-if Flotilla then
-local Group=GROUP:FindByName(Flotilla.templatename)
-if Group then
-local text=string.format("Adding asset %s to flotilla %s",Group:GetName(),Flotilla.name)
-self:T(self.lid..text)
-self:AddAsset(Group,Nassets,nil,nil,nil,nil,Flotilla.skill,Flotilla.livery,Flotilla.name)
-else
-self:E(self.lid.."ERROR: Group does not exist!")
-end
-else
-self:E(self.lid.."ERROR: Flotilla does not exit!")
-end
-return self
-end
-function FLEET:SetPathfinding(Switch)
-self.pathfinding=Switch
-return self
-end
-function FLEET:SetRetreatZones(RetreatZoneSet)
-self.retreatZones=RetreatZoneSet or SET_ZONE:New()
-return self
-end
-function FLEET:AddRetreatZone(RetreatZone)
-self.retreatZones:AddZone(RetreatZone)
-return self
-end
-function FLEET:GetRetreatZones()
-return self.retreatZones
-end
-function FLEET:GetFlotilla(FlotillaName)
-local flotilla=self:_GetCohort(FlotillaName)
-return flotilla
-end
-function FLEET:GetFlotillaOfAsset(Asset)
-local flotilla=self:GetFlotilla(Asset.squadname)
-return flotilla
-end
-function FLEET:RemoveAssetFromFlotilla(Asset)
-local flotilla=self:GetFlotillaOfAsset(Asset)
-if flotilla then
-flotilla:DelAsset(Asset)
-end
-end
-function FLEET:onafterStart(From,Event,To)
-self:GetParent(self,FLEET).onafterStart(self,From,Event,To)
-self:I(self.lid..string.format("Starting FLEET v%s",FLEET.version))
-end
-function FLEET:onafterStatus(From,Event,To)
-self:GetParent(self).onafterStatus(self,From,Event,To)
-local fsmstate=self:GetState()
-self:CheckTransportQueue()
-self:CheckMissionQueue()
-if self.verbose>=1 then
-local Nmissions=self:CountMissionsInQueue()
-local Npq,Np,Nq=self:CountAssetsOnMission()
-local assets=string.format("%d [OnMission: Total=%d, Active=%d, Queued=%d]",self:CountAssets(),Npq,Np,Nq)
-local text=string.format("%s: Missions=%d, Flotillas=%d, Assets=%s",fsmstate,Nmissions,#self.cohorts,assets)
-self:I(self.lid..text)
-end
-if self.verbose>=2 then
-local text=string.format("Missions Total=%d:",#self.missionqueue)
-for i,_mission in pairs(self.missionqueue)do
-local mission=_mission
-local prio=string.format("%d/%s",mission.prio,tostring(mission.importance));if mission.urgent then prio=prio.." (!)"end
-local assets=string.format("%d/%d",mission:CountOpsGroups(),mission.Nassets or 0)
-local target=string.format("%d/%d Damage=%.1f",mission:CountMissionTargets(),mission:GetTargetInitialNumber(),mission:GetTargetDamage())
-text=text..string.format("\n[%d] %s %s: Status=%s, Prio=%s, Assets=%s, Targets=%s",i,mission.name,mission.type,mission.status,prio,assets,target)
-end
-self:I(self.lid..text)
-end
-if self.verbose>=2 then
-local text=string.format("Transports Total=%d:",#self.transportqueue)
-for i,_transport in pairs(self.transportqueue)do
-local transport=_transport
-local prio=string.format("%d/%s",transport.prio,tostring(transport.importance));if transport.urgent then prio=prio.." (!)"end
-local carriers=string.format("Ncargo=%d/%d, Ncarriers=%d",transport.Ncargo,transport.Ndelivered,transport.Ncarrier)
-text=text..string.format("\n[%d] UID=%d: Status=%s, Prio=%s, Cargo: %s",i,transport.uid,transport:GetState(),prio,carriers)
-end
-self:I(self.lid..text)
-end
-if self.verbose>=3 then
-local text="Flotillas:"
-for i,_flotilla in pairs(self.cohorts)do
-local flotilla=_flotilla
-local callsign=flotilla.callsignName and UTILS.GetCallsignName(flotilla.callsignName)or"N/A"
-local modex=flotilla.modex and flotilla.modex or-1
-local skill=flotilla.skill and tostring(flotilla.skill)or"N/A"
-text=text..string.format("\n* %s %s: %s*%d/%d, Callsign=%s, Modex=%d, Skill=%s",flotilla.name,flotilla:GetState(),flotilla.aircrafttype,flotilla:CountAssets(true),#flotilla.assets,callsign,modex,skill)
-end
-self:I(self.lid..text)
-end
-end
-function FLEET:onafterNavyOnMission(From,Event,To,NavyGroup,Mission)
-self:T(self.lid..string.format("Group %s on %s mission %s",NavyGroup:GetName(),Mission:GetType(),Mission:GetName()))
-end
-FLIGHTCONTROL={
-ClassName="FLIGHTCONTROL",
-verbose=0,
-lid=nil,
-theatre=nil,
-airbasename=nil,
-airbase=nil,
-airbasetype=nil,
-zoneAirbase=nil,
-parking={},
-runways={},
-flights={},
-clients={},
-atis=nil,
-Nlanding=nil,
-dTlanding=nil,
-Nparkingspots=nil,
-holdingpatterns={},
-hpcounter=0,
-nosubs=false,
-}
-FLIGHTCONTROL.FlightStatus={
-UNKNOWN="Unknown",
-PARKING="Parking",
-READYTX="Ready To Taxi",
-TAXIOUT="Taxi To Runway",
-READYTO="Ready For Takeoff",
-TAKEOFF="Takeoff",
-INBOUND="Inbound",
-HOLDING="Holding",
-LANDING="Landing",
-TAXIINB="Taxi To Parking",
-ARRIVED="Arrived",
-}
-FLIGHTCONTROL.version="0.7.5"
-function FLIGHTCONTROL:New(AirbaseName,Frequency,Modulation,PathToSRS,Port,GoogleKey)
-local self=BASE:Inherit(self,FSM:New())
-self.airbase=AIRBASE:FindByName(AirbaseName)
-self.airbasename=AirbaseName
-self.lid=string.format("FLIGHTCONTROL %s | ",AirbaseName)
-if not self.airbase then
-self:E(string.format("ERROR: Could not find airbase %s!",tostring(AirbaseName)))
-return nil
-end
-if self.airbase:GetAirbaseCategory()~=Airbase.Category.AIRDROME then
-self:E(string.format("ERROR: Airbase %s is not an AIRDROME! Script does not handle FARPS or ships.",tostring(AirbaseName)))
-return nil
-end
-self.airbasetype=self.airbase:GetAirbaseCategory()
-self.theatre=env.mission.theatre
-self.zoneAirbase=ZONE_RADIUS:New("FC",self:GetCoordinate():GetVec2(),UTILS.NMToMeters(5))
-self:_AddHoldingPatternBackup()
-self.alias=self.airbasename.." Tower"
-self:SetLimitLanding(2,0)
-self:SetLimitTaxi(2,false,0)
-self:SetLandingInterval()
-self:SetFrequency(Frequency,Modulation)
-self:SetMarkHoldingPattern(true)
-self:SetRunwayRepairtime()
-self.nosubs=false
-self:SetSRSPort(Port or 5002)
-self:SetCallSignOptions(true,true)
-self.msrsqueue=MSRSQUEUE:New(self.alias)
-self.msrsTower=MSRS:New(PathToSRS,Frequency,Modulation)
-self.msrsTower:SetPort(self.Port)
-self.msrsTower:SetGoogle(GoogleKey)
-self.msrsTower:SetCoordinate(self:GetCoordinate())
-self:SetSRSTower()
-self.msrsPilot=MSRS:New(PathToSRS,Frequency,Modulation)
-self.msrsPilot:SetPort(self.Port)
-self.msrsPilot:SetGoogle(GoogleKey)
-self.msrsTower:SetCoordinate(self:GetCoordinate())
-self:SetSRSPilot()
-self.dTmessage=10
-self:SetStartState("Stopped")
-self:AddTransition("Stopped","Start","Running")
-self:AddTransition("*","StatusUpdate","*")
-self:AddTransition("*","PlayerKilledGuard","*")
-self:AddTransition("*","PlayerSpeeding","*")
-self:AddTransition("*","RunwayDestroyed","*")
-self:AddTransition("*","RunwayRepaired","*")
-self:AddTransition("*","Stop","Stopped")
-_DATABASE:AddFlightControl(self)
-return self
-end
-function FLIGHTCONTROL:SetVerbosity(VerbosityLevel)
-self.verbose=VerbosityLevel or 0
-return self
-end
-function FLIGHTCONTROL:SwitchSubtitlesOn()
-self.nosubs=false
-return self
-end
-function FLIGHTCONTROL:SwitchSubtitlesOff()
-self.nosubs=true
-return self
-end
-function FLIGHTCONTROL:SetFrequency(Frequency,Modulation)
-self.frequency=Frequency or 305
-self.modulation=Modulation or radio.modulation.AM
-if self.msrsPilot then
-self.msrsPilot:SetFrequencies(Frequency)
-self.msrsPilot:SetModulations(Modulation)
-end
-if self.msrsTower then
-self.msrsTower:SetFrequencies(Frequency)
-self.msrsTower:SetModulations(Modulation)
-end
-return self
-end
-function FLIGHTCONTROL:SetSRSPort(Port)
-self.Port=Port or 5002
-return self
-end
-function FLIGHTCONTROL:_SetSRSOptions(msrs,Gender,Culture,Voice,Volume,Label,PathToGoogleCredentials,Port)
-Gender=Gender or"female"
-Culture=Culture or"en-GB"
-Volume=Volume or 1.0
-if msrs then
-msrs:SetGender(Gender)
-msrs:SetCulture(Culture)
-msrs:SetVoice(Voice)
-msrs:SetVolume(Volume)
-msrs:SetLabel(Label)
-msrs:SetGoogle(PathToGoogleCredentials)
-msrs:SetCoalition(self:GetCoalition())
-msrs:SetPort(Port or self.Port or 5002)
-end
-return self
-end
-function FLIGHTCONTROL:SetSRSTower(Gender,Culture,Voice,Volume,Label,PathToGoogleCredentials)
-if self.msrsTower then
-self:_SetSRSOptions(self.msrsTower,Gender or"female",Culture or"en-GB",Voice,Volume,Label or self.alias,PathToGoogleCredentials)
-end
-return self
-end
-function FLIGHTCONTROL:SetSRSPilot(Gender,Culture,Voice,Volume,Label,PathToGoogleCredentials)
-if self.msrsPilot then
-self:_SetSRSOptions(self.msrsPilot,Gender or"male",Culture or"en-US",Voice,Volume,Label or"Pilot",PathToGoogleCredentials)
-end
-return self
-end
-function FLIGHTCONTROL:SetLimitLanding(Nlanding,Ntakeoff)
-self.NlandingTot=Nlanding or 2
-self.NlandingTakeoff=Ntakeoff or 0
-return self
-end
-function FLIGHTCONTROL:SetLandingInterval(dt)
-self.dTlanding=dt or 180
-return self
-end
-function FLIGHTCONTROL:SetLimitTaxi(Ntaxi,IncludeInbound,Nlanding)
-self.NtaxiTot=Ntaxi or 2
-self.NtaxiInbound=IncludeInbound
-self.NtaxiLanding=Nlanding or 0
-return self
-end
-function FLIGHTCONTROL:AddHoldingPattern(ArrivalZone,Heading,Length,FlightlevelMin,FlightlevelMax,Prio)
-if type(ArrivalZone)=="string"then
-ArrivalZone=ZONE:New(ArrivalZone)
-end
-self.hpcounter=self.hpcounter+1
-local hp={}
-hp.uid=self.hpcounter
-hp.arrivalzone=ArrivalZone
-hp.name=string.format("%s-%d",ArrivalZone:GetName(),hp.uid)
-hp.pos0=ArrivalZone:GetCoordinate()
-hp.pos1=hp.pos0:Translate(UTILS.NMToMeters(Length or 15),Heading)
-hp.angelsmin=FlightlevelMin or 5
-hp.angelsmax=FlightlevelMax or 15
-hp.prio=Prio or 50
-hp.stacks={}
-for i=hp.angelsmin,hp.angelsmax do
-local stack={}
-stack.angels=i
-stack.flightgroup=nil
-stack.pos0=UTILS.DeepCopy(hp.pos0)
-stack.pos0:SetAltitude(UTILS.FeetToMeters(i*1000))
-stack.pos1=UTILS.DeepCopy(hp.pos1)
-stack.pos1:SetAltitude(UTILS.FeetToMeters(i*1000))
-stack.heading=Heading
-table.insert(hp.stacks,stack)
-end
-table.insert(self.holdingpatterns,hp)
-local function _sort(a,b)
-return a.prio0 then
-local text=string.format("Still got %d messages in the radio queue. Will call status again in %.1f sec",#self.msrsqueue,Tqueue)
-self:T(self.lid..text)
-self:__StatusUpdate(-Tqueue)
-return false
-end
-return true
-end
-function FLIGHTCONTROL:onafterStatusUpdate()
-self:T2(self.lid.."Status update")
-self:_CheckMarkHoldingPatterns()
-if self:IsRunwayOperational()==false then
-local Trepair=self:GetRunwayRepairtime()
-self:I(self.lid..string.format("Runway still destroyed! Will be repaired in %d sec",Trepair))
-if Trepair==0 then
-self:RunwayRepaired()
-end
-end
-self:_CheckFlights()
-self:_CheckQueues()
-local rwyLanding=self:GetActiveRunwayText()
-local rwyTakeoff=self:GetActiveRunwayText(true)
-local Nflights=self:CountFlights()
-local NQparking=self:CountFlights(FLIGHTCONTROL.FlightStatus.PARKING)
-local NQreadytx=self:CountFlights(FLIGHTCONTROL.FlightStatus.READYTX)
-local NQtaxiout=self:CountFlights(FLIGHTCONTROL.FlightStatus.TAXIOUT)
-local NQreadyto=self:CountFlights(FLIGHTCONTROL.FlightStatus.READYTO)
-local NQtakeoff=self:CountFlights(FLIGHTCONTROL.FlightStatus.TAKEOFF)
-local NQinbound=self:CountFlights(FLIGHTCONTROL.FlightStatus.INBOUND)
-local NQholding=self:CountFlights(FLIGHTCONTROL.FlightStatus.HOLDING)
-local NQlanding=self:CountFlights(FLIGHTCONTROL.FlightStatus.LANDING)
-local NQtaxiinb=self:CountFlights(FLIGHTCONTROL.FlightStatus.TAXIINB)
-local NQarrived=self:CountFlights(FLIGHTCONTROL.FlightStatus.ARRIVED)
-local Nqueues=(NQparking+NQreadytx+NQtaxiout+NQreadyto+NQtakeoff)+(NQinbound+NQholding+NQlanding+NQtaxiinb+NQarrived)
-local nfree=self.Nparkingspots-NQarrived-NQparking
-local Nfree=self:CountParking(AIRBASE.SpotStatus.FREE)
-local Noccu=self:CountParking(AIRBASE.SpotStatus.OCCUPIED)
-local Nresv=self:CountParking(AIRBASE.SpotStatus.RESERVED)
-if Nfree+Noccu+Nresv~=self.Nparkingspots then
-self:E(self.lid..string.format("WARNING: Number of parking spots does not match! Nfree=%d, Noccu=%d, Nreserved=%d != %d total",Nfree,Noccu,Nresv,self.Nparkingspots))
-end
-if self.verbose>=1 then
-local text=string.format("State %s - Runway Landing=%s, Takeoff=%s - Parking F=%d/O=%d/R=%d of %d - Flights=%s: Qpark=%d Qtxout=%d Qready=%d Qto=%d | Qinbound=%d Qhold=%d Qland=%d Qtxinb=%d Qarr=%d",
-self:GetState(),rwyLanding,rwyTakeoff,Nfree,Noccu,Nresv,self.Nparkingspots,Nflights,NQparking,NQtaxiout,NQreadyto,NQtakeoff,NQinbound,NQholding,NQlanding,NQtaxiinb,NQarrived)
-self:I(self.lid..text)
-end
-if Nflights==Nqueues then
-else
-self:E(string.format("WARNING: Number of total flights %d!=%d number of flights in all queues!",Nflights,Nqueues))
-end
-if self.verbose>=2 then
-local text="Holding Patterns:"
-for i,_pattern in pairs(self.holdingpatterns)do
-local pattern=_pattern
-text=text..string.format("\n[%d] Pattern %s [Prio=%d, UID=%d]: Stacks=%d, Angels %d - %d",i,pattern.name,pattern.prio,pattern.uid,#pattern.stacks,pattern.angelsmin,pattern.angelsmax)
-if self.verbose>=4 then
-for _,_stack in pairs(pattern.stacks)do
-local stack=_stack
-local text=string.format("",stack.angels,stack)
-end
-end
-end
-self:I(self.lid..text)
-end
-self:__StatusUpdate(-30)
-end
-function FLIGHTCONTROL:onafterStop()
-self:UnHandleEvent(EVENTS.Birth)
-self:UnHandleEvent(EVENTS.EngineStartup)
-self:UnHandleEvent(EVENTS.Takeoff)
-self:UnHandleEvent(EVENTS.Land)
-self:UnHandleEvent(EVENTS.EngineShutdown)
-self:UnHandleEvent(EVENTS.Crash)
-self:UnHandleEvent(EVENTS.Kill)
-end
-function FLIGHTCONTROL:OnEventBirth(EventData)
-self:F3({EvendData=EventData})
-if EventData and EventData.IniGroupName and EventData.IniUnit then
-self:T3(self.lid..string.format("BIRTH: unit = %s",tostring(EventData.IniUnitName)))
-self:T3(self.lid..string.format("BIRTH: group = %s",tostring(EventData.IniGroupName)))
-local unit=EventData.IniUnit
-if unit:IsAir()then
-local bornhere=EventData.Place and EventData.Place:GetName()==self.airbasename or false
-local playerunit,playername=self:_GetPlayerUnitAndName(EventData.IniUnitName)
-if playername or bornhere then
-self:ScheduleOnce(0.5,self._CreateFlightGroup,self,EventData.IniGroup)
-end
-if bornhere then
-self:SpawnParkingGuard(unit)
-end
-end
-end
-end
-function FLIGHTCONTROL:OnEventCrashOrDead(EventData)
-if EventData then
-if EventData.IniUnitName then
-if self.airbase and self.airbasename and self.airbasename==EventData.IniUnitName then
-self:RunwayDestroyed()
-end
-end
-end
-end
-function FLIGHTCONTROL:OnEventLand(EventData)
-self:F3({EvendData=EventData})
-self:T2(self.lid..string.format("LAND: unit = %s",tostring(EventData.IniUnitName)))
-self:T3(self.lid..string.format("LAND: group = %s",tostring(EventData.IniGroupName)))
-end
-function FLIGHTCONTROL:OnEventTakeoff(EventData)
-self:F3({EvendData=EventData})
-self:T2(self.lid..string.format("TAKEOFF: unit = %s",tostring(EventData.IniUnitName)))
-self:T3(self.lid..string.format("TAKEOFF: group = %s",tostring(EventData.IniGroupName)))
-local airbase=EventData.Place
-local unit=EventData.IniUnit
-if not(airbase or unit)then
-self:E(self.lid.."WARNING: Airbase or IniUnit is nil in takeoff event!")
-return
-end
-end
-function FLIGHTCONTROL:OnEventEngineStartup(EventData)
-self:F3({EvendData=EventData})
-self:T2(self.lid..string.format("ENGINESTARTUP: unit = %s",tostring(EventData.IniUnitName)))
-self:T3(self.lid..string.format("ENGINESTARTUP: group = %s",tostring(EventData.IniGroupName)))
-end
-function FLIGHTCONTROL:OnEventEngineShutdown(EventData)
-self:F3({EvendData=EventData})
-self:T2(self.lid..string.format("ENGINESHUTDOWN: unit = %s",tostring(EventData.IniUnitName)))
-self:T3(self.lid..string.format("ENGINESHUTDOWN: group = %s",tostring(EventData.IniGroupName)))
-end
-function FLIGHTCONTROL:OnEventKill(EventData)
-self:F3({EvendData=EventData})
-self:T2(self.lid..string.format("KILL: ini unit = %s",tostring(EventData.IniUnitName)))
-self:T3(self.lid..string.format("KILL: ini group = %s",tostring(EventData.IniGroupName)))
-self:T2(self.lid..string.format("KILL: tgt unit = %s",tostring(EventData.TgtUnitName)))
-self:T3(self.lid..string.format("KILL: tgt group = %s",tostring(EventData.TgtGroupName)))
-local guardPrefix=string.format("Parking Guard %s",self.airbasename)
-local victimName=EventData.IniUnitName
-local killerName=EventData.TgtUnitName
-if victimName and victimName:find(guardPrefix)then
-env.info(string.format("Parking guard %s killed!",victimName))
-for _,_flight in pairs(self.flights)do
-local flight=_flight
-local element=flight:GetElementByName(killerName)
-if element then
-env.info(string.format("Parking guard %s killed by %s!",victimName,killerName))
-return
-end
-end
-end
-end
-function FLIGHTCONTROL:onafterRunwayDestroyed(From,Event,To)
-self:T(self.lid..string.format("Runway destoyed!"))
-self.runwaydestroyed=timer.getAbsTime()
-self:TransmissionTower("All flights, our runway was destroyed. All operations are suspended for one hour.",Flight,Delay)
-end
-function FLIGHTCONTROL:onafterRunwayRepaired(From,Event,To)
-self:T(self.lid..string.format("Runway repaired!"))
-self.runwaydestroyed=nil
-end
-function FLIGHTCONTROL:_CheckQueues()
-if self.verbose>=2 then
-self:_PrintQueue(self.flights,"All flights")
-end
-local flight,isholding,parking=self:_GetNextFlight()
-if flight then
-if isholding then
-if self:_CheckFlightLanding(flight)then
-local dTlanding=99999
-if self.Tlanding then
-dTlanding=timer.getAbsTime()-self.Tlanding
-end
-if parking and dTlanding>=self.dTlanding then
-local callsign=self:_GetCallsignName(flight)
-local runway=self:GetActiveRunwayText()
-local text=string.format("%s, %s, you are cleared to land, runway %s",callsign,self.alias,runway)
-self:TransmissionTower(text,flight)
-if flight.isAI then
-local text=string.format("Runway %s, cleared to land, %s",runway,callsign)
-self:TransmissionPilot(text,flight,10)
-self:_LandAI(flight,parking)
-else
-self:SetFlightStatus(flight,FLIGHTCONTROL.FlightStatus.LANDING)
-end
-self.Tlanding=timer.getAbsTime()
-end
-else
-self:T3(self.lid..string.format("FYI: Landing clearance for flight %s denied",flight.groupname))
-end
-else
-if self:_CheckFlightTakeoff(flight)then
-local callsign=self:_GetCallsignName(flight)
-local runway=self:GetActiveRunwayText(true)
-local text=string.format("%s, %s, taxi to runway %s, hold short",callsign,self.alias,runway)
-if self:GetFlightStatus(flight)==FLIGHTCONTROL.FlightStatus.READYTO then
-text=string.format("%s, %s, cleared for take-off, runway %s",callsign,self.alias,runway)
-end
-self:TransmissionTower(text,flight)
-if flight.isAI then
-local text="Wilco, "
-if flight:IsUncontrolled()then
-text=text..string.format("starting engines, ")
-flight:StartUncontrolled()
-end
-text=text..string.format("runway %s, %s",runway,callsign)
-self:TransmissionPilot(text,flight,10)
-for _,_element in pairs(flight.elements)do
-local element=_element
-if element and element.parking then
-local spot=self:GetParkingSpotByID(element.parking.TerminalID)
-self:RemoveParkingGuard(spot)
-end
-end
-self:SetFlightStatus(flight,FLIGHTCONTROL.FlightStatus.TAKEOFF)
-else
-if self:GetFlightStatus(flight)==FLIGHTCONTROL.FlightStatus.READYTO then
-self:SetFlightStatus(flight,FLIGHTCONTROL.FlightStatus.TAKEOFF)
-else
-for _,_element in pairs(flight.elements)do
-local element=_element
-if element.parking then
-local spot=self:GetParkingSpotByID(element.parking.TerminalID)
-if element.ai then
-self:RemoveParkingGuard(spot,15)
-else
-self:RemoveParkingGuard(spot,10)
-end
-end
-end
-end
-end
-else
-self:T3(self.lid..string.format("FYI: Take off for flight %s denied",flight.groupname))
-end
-end
-else
-self:T2(self.lid..string.format("FYI: No flight in queue for takeoff or landing"))
-end
-end
-function FLIGHTCONTROL:_CheckFlightTakeoff(flight)
-local nlanding=self:CountFlights(FLIGHTCONTROL.FlightStatus.LANDING)
-local ntakeoff=self:CountFlights(FLIGHTCONTROL.FlightStatus.TAKEOFF,nil,true)
-local status=self:GetFlightStatus(flight)
-if flight.isAI then
-if nlanding>self.NtaxiLanding then
-self:T(self.lid..string.format("AI flight %s [status=%s] NOT cleared for taxi/takeoff as %d>%d flight(s) landing",flight.groupname,status,nlanding,self.NtaxiLanding))
-return false
-end
-local ninbound=0
-if self.NtaxiInbound then
-ninbound=self:CountFlights(FLIGHTCONTROL.FlightStatus.TAXIINB,nil,true)
-end
-if ntakeoff+ninbound>=self.NtaxiTot then
-self:T(self.lid..string.format("AI flight %s [status=%s] NOT cleared for taxi/takeoff as %d>=%d flight(s) taxi/takeoff",flight.groupname,status,ntakeoff,self.NtaxiTot))
-return false
-end
-self:T(self.lid..string.format("AI flight %s [status=%s] cleared for taxi/takeoff! nLanding=%d, nTakeoff=%d",flight.groupname,status,nlanding,ntakeoff))
-return true
-else
-if status==FLIGHTCONTROL.FlightStatus.READYTO then
-if nlanding>self.NtaxiLanding then
-self:T(self.lid..string.format("Player flight %s [status=%s] not cleared for taxi/takeoff as %d>%d flight(s) landing",flight.groupname,status,nlanding,self.NtaxiLanding))
-return false
-end
-end
-self:T(self.lid..string.format("Player flight %s [status=%s] cleared for taxi/takeoff",flight.groupname,status))
-return true
-end
-end
-function FLIGHTCONTROL:_CheckFlightLanding(flight)
-local nlanding=self:CountFlights(FLIGHTCONTROL.FlightStatus.LANDING)
-local ntakeoff=self:CountFlights(FLIGHTCONTROL.FlightStatus.TAKEOFF,nil,true)
-local status=self:GetFlightStatus(flight)
-if flight.isAi then
-if ntakeoff<=self.NlandingTakeoff and nlandingTholdingMin then
-return fg
-end
-end
-local function _sortByFuel(a,b)
-local flightA=a
-local flightB=b
-local fuelA=flightA.group:GetFuelMin()
-local fuelB=flightB.group:GetFuelMin()
-return fuelATholdingMin then
-return fg
-end
-return nil
-end
-function FLIGHTCONTROL:_GetNextFightParking()
-local OnlyAI=nil
-if self:IsRunwayDestroyed()then
-OnlyAI=false
-end
-local QreadyTO=self:GetFlights(FLIGHTCONTROL.FlightStatus.READYTO,OPSGROUP.GroupStatus.TAXIING,OnlyAI)
-if#QreadyTO>0 then
-return QreadyTO[1]
-end
-local QreadyTX=self:GetFlights(FLIGHTCONTROL.FlightStatus.READYTX,OPSGROUP.GroupStatus.PARKING,OnlyAI)
-if#QreadyTX>0 then
-return QreadyTX[1]
-end
-if self:IsRunwayDestroyed()then
-return nil
-end
-local Qparking=self:GetFlights(FLIGHTCONTROL.FlightStatus.PARKING,nil,true)
-local Nparking=#Qparking
-if Nparking==0 then
-return nil
-end
-local function _sortByTparking(a,b)
-local flightA=a
-local flightB=b
-return flightA.Tparking=2 then
-local text="Parking flights:"
-for i,_flight in pairs(Qparking)do
-local flight=_flight
-text=text..string.format("\n[%d] %s [%s], state=%s [%s]: Tparking=%.1f sec",i,flight.groupname,flight.actype,flight:GetState(),self:GetFlightStatus(flight),flight:GetParkingTime())
-end
-self:I(self.lid..text)
-end
-for i,_flight in pairs(Qparking)do
-local flight=_flight
-if flight.isAI and flight.isReadyTO then
-return flight
-end
-end
-return nil
-end
-function FLIGHTCONTROL:_PrintQueue(queue,name)
-local text=string.format("%s Queue N=%d:",name,#queue)
-if#queue==0 then
-text=text.." empty."
-else
-local time=timer.getAbsTime()
-for i,_flight in ipairs(queue)do
-local flight=_flight
-local fuel=flight.group:GetFuelMin()*100
-local ai=tostring(flight.isAI)
-local actype=tostring(flight.actype)
-local holding=flight.Tholding and UTILS.SecondsToClock(time-flight.Tholding,true)or"X"
-local parking=flight.Tparking and UTILS.SecondsToClock(time-flight.Tparking,true)or"X"
-local holding=flight:GetHoldingTime()
-if holding>=0 then
-holding=UTILS.SecondsToClock(holding,true)
-else
-holding="X"
-end
-local parking=flight:GetParkingTime()
-if parking>=0 then
-parking=UTILS.SecondsToClock(parking,true)
-else
-parking="X"
-end
-local nunits=flight:CountElements()
-local state=flight:GetState()
-local status=self:GetFlightStatus(flight)
-text=text..string.format("\n[%d] %s (%s*%d): status=%s | %s, ai=%s, fuel=%d, holding=%s, parking=%s",
-i,flight.groupname,actype,nunits,state,status,ai,fuel,holding,parking)
-for j,_element in pairs(flight.elements)do
-local element=_element
-local life=element.unit:GetLife()
-local life0=element.unit:GetLife0()
-local park=element.parking and tostring(element.parking.TerminalID)or"N/A"
-text=text..string.format("\n (%d) %s (%s): status=%s, ai=%s, airborne=%s life=%d/%d spot=%s",
-j,tostring(element.modex),element.name,tostring(element.status),tostring(element.ai),tostring(element.unit:InAir()),life,life0,park)
-end
-end
-end
-self:I(self.lid..text)
-return text
-end
-function FLIGHTCONTROL:SetFlightStatus(flight,status)
-self:T(self.lid..string.format("New status %s-->%s for flight %s",flight.controlstatus or"unknown",status,flight:GetName()))
-if flight.controlstatus~=status and not flight.isAI then
-self:T(self.lid.."Updating menu in 0.2 sec after flight status change")
-flight:_UpdateMenu(0.2)
-end
-flight.controlstatus=status
-end
-function FLIGHTCONTROL:GetFlightStatus(flight)
-if flight then
-return flight.controlstatus or"unkonwn"
-end
-return"unknown"
-end
-function FLIGHTCONTROL:IsControlling(flight)
-local is=flight.flightcontrol and flight.flightcontrol.airbasename==self.airbasename or false
-return is
-end
-function FLIGHTCONTROL:_InQueue(queue,group)
-local name=group:GetName()
-for _,_flight in pairs(queue)do
-local flight=_flight
-if name==flight.groupname then
-return true
-end
-end
-return false
-end
-function FLIGHTCONTROL:GetFlights(Status,GroupStatus,AI)
-if Status~=nil or GroupStatus~=nil or AI~=nil then
-local flights={}
-for _,_flight in pairs(self.flights)do
-local flight=_flight
-local status=self:GetFlightStatus(flight,Status)
-if status==Status then
-if AI==nil or AI==flight.isAI then
-if GroupStatus==nil or GroupStatus==flight:GetState()then
-table.insert(flights,flight)
-end
-end
-end
-end
-return flights
-else
-return self.flights
-end
-end
-function FLIGHTCONTROL:CountFlights(Status,GroupStatus,AI)
-if Status~=nil or GroupStatus~=nil or AI~=nil then
-local flights=self:GetFlights(Status,GroupStatus,AI)
-return#flights
-else
-return#self.flights
-end
-end
-function FLIGHTCONTROL:GetActiveRunway()
-local rwy=self.airbase:GetActiveRunway()
-return rwy
-end
-function FLIGHTCONTROL:GetActiveRunwayLanding()
-local rwy=self.airbase:GetActiveRunwayLanding()
-return rwy
-end
-function FLIGHTCONTROL:GetActiveRunwayTakeoff()
-local rwy=self.airbase:GetActiveRunwayTakeoff()
-return rwy
-end
-function FLIGHTCONTROL:GetActiveRunwayText(Takeoff)
-local runway
-if Takeoff then
-runway=self:GetActiveRunwayTakeoff()
-else
-runway=self:GetActiveRunwayLanding()
-end
-local name=self.airbase:GetRunwayName(runway,true)
-return name or"XX"
-end
-function FLIGHTCONTROL:_InitParkingSpots()
-local parkingdata=self.airbase:GetParkingSpotsTable()
-self.parking={}
-self.Nparkingspots=0
-for _,_spot in pairs(parkingdata)do
-local spot=_spot
-local text=string.format("Parking ID=%d, Terminal=%d: Free=%s, Client=%s, Dist=%.1f",spot.TerminalID,spot.TerminalType,tostring(spot.Free),tostring(spot.ClientName),spot.DistToRwy)
-self:T3(self.lid..text)
-self.parking[spot.TerminalID]=spot
-if spot.Free then
-self:SetParkingFree(spot)
-else
-local unit=spot.Coordinate:FindClosestUnit(20)
-if unit then
-local unitname=unit and unit:GetName()or"unknown"
-local isalive=unit:IsAlive()
-if isalive then
-self:SetParkingOccupied(spot,unitname)
-self:SpawnParkingGuard(unit)
-else
-self:SetParkingFree(spot)
-end
-else
-self:E(self.lid..string.format("ERROR: Parking spot is NOT FREE but no unit could be found there!"))
-end
-end
-self.Nparkingspots=self.Nparkingspots+1
-end
-end
-function FLIGHTCONTROL:GetParkingSpotByID(TerminalID)
-return self.parking[TerminalID]
-end
-function FLIGHTCONTROL:_UpdateSpotStatus(spot,status,unitname)
-self:T2(self.lid..string.format("Updating parking spot %d status: %s --> %s (unit=%s)",spot.TerminalID,tostring(spot.Status),status,tostring(unitname)))
-spot.Status=status
-end
-function FLIGHTCONTROL:SetParkingFree(spot)
-local spot=self:GetParkingSpotByID(spot.TerminalID)
-self:_UpdateSpotStatus(spot,AIRBASE.SpotStatus.FREE,spot.OccupiedBy or spot.ReservedBy)
-spot.OccupiedBy=nil
-spot.ReservedBy=nil
-self:RemoveParkingGuard(spot)
-self:UpdateParkingMarker(spot)
-end
-function FLIGHTCONTROL:SetParkingReserved(spot,unitname)
-local spot=self:GetParkingSpotByID(spot.TerminalID)
-self:_UpdateSpotStatus(spot,AIRBASE.SpotStatus.RESERVED,unitname)
-spot.ReservedBy=unitname or"unknown"
-self:UpdateParkingMarker(spot)
-end
-function FLIGHTCONTROL:SetParkingOccupied(spot,unitname)
-local spot=self:GetParkingSpotByID(spot.TerminalID)
-self:_UpdateSpotStatus(spot,AIRBASE.SpotStatus.OCCUPIED,unitname)
-spot.OccupiedBy=unitname or"unknown"
-self:UpdateParkingMarker(spot)
-end
-function FLIGHTCONTROL:UpdateParkingMarker(spot)
-if self.markerParking then
-local spot=self:GetParkingSpotByID(spot.TerminalID)
-if spot.Status==AIRBASE.SpotStatus.FREE then
-if spot.Marker then
-spot.Marker:Remove()
-end
-else
-local text=string.format("Spot %d (type %d): %s",spot.TerminalID,spot.TerminalType,spot.Status:upper())
-if spot.OccupiedBy then
-text=text..string.format("\nOccupied by %s",tostring(spot.OccupiedBy))
-end
-if spot.ReservedBy then
-text=text..string.format("\nReserved for %s",tostring(spot.ReservedBy))
-end
-if spot.ClientSpot then
-text=text..string.format("\nClient %s",tostring(spot.ClientName))
-end
-if spot.Marker then
-if text~=spot.Marker.text or not spot.Marker.shown then
-spot.Marker:UpdateText(text)
-end
-else
-spot.Marker=MARKER:New(spot.Coordinate,text):ToAll()
-end
-end
-end
-end
-function FLIGHTCONTROL:IsParkingFree(spot)
-return spot.Status==AIRBASE.SpotStatus.FREE
-end
-function FLIGHTCONTROL:IsParkingOccupied(spot)
-if spot.Status==AIRBASE.SpotStatus.OCCUPIED then
-return tostring(spot.OccupiedBy)
-else
-return false
-end
-end
-function FLIGHTCONTROL:IsParkingReserved(spot)
-if spot.Status==AIRBASE.SpotStatus.RESERVED then
-return tostring(spot.ReservedBy)
-else
-return false
-end
-end
-function FLIGHTCONTROL:_GetFreeParkingSpots(terminal)
-local freespots={}
-local n=0
-for _,_parking in pairs(self.parking)do
-local parking=_parking
-if self:IsParkingFree(parking)then
-if terminal==nil or terminal==parking.terminal then
-n=n+1
-table.insert(freespots,parking)
-end
-end
-end
-return n,freespots
-end
-function FLIGHTCONTROL:GetClosestParkingSpot(Coordinate,TerminalType,Status)
-local distmin=math.huge
-local spotmin=nil
-for TerminalID,Spot in pairs(self.parking)do
-local spot=Spot
-if(Status==nil or Status==spot.Status)and AIRBASE._CheckTerminalType(spot.TerminalType,TerminalType)then
-local dist=Coordinate:Get2DDistance(spot.Coordinate)
-if dist0 then
-text=text..string.format("\n- Parking %d",NQparking)
-end
-if NQreadytx>0 then
-text=text..string.format("\n- Ready to taxi %d",NQreadytx)
-end
-if NQtaxiout>0 then
-text=text..string.format("\n- Taxi to runway %d",NQtaxiout)
-end
-if NQreadyto>0 then
-text=text..string.format("\n- Ready for takeoff %d",NQreadyto)
-end
-if NQtakeoff>0 then
-text=text..string.format("\n- Taking off %d",NQtakeoff)
-end
-if NQinbound>0 then
-text=text..string.format("\n- Inbound %d",NQinbound)
-end
-if NQholding>0 then
-text=text..string.format("\n- Holding pattern %d",NQholding)
-end
-if NQlanding>0 then
-text=text..string.format("\n- Landing %d",NQlanding)
-end
-if NQtaxiinb>0 then
-text=text..string.format("\n- Taxi to parking %d",NQtaxiinb)
-end
-if NQarrived>0 then
-text=text..string.format("\n- Arrived at parking %d",NQarrived)
-end
-self:TextMessageToFlight(text,flight,15,true)
-else
-self:E(self.lid..string.format("Cannot find flight group %s.",tostring(groupname)))
-end
-end
-function FLIGHTCONTROL:_PlayerRequestInbound(groupname)
-local flight=_DATABASE:GetOpsGroup(groupname)
-if flight then
-if flight:IsAirborne()then
-local callsign=self:_GetCallsignName(flight)
-local player=flight:GetPlayerElement()
-local text=string.format("%s, %s, inbound for landing",self.alias,callsign)
-self:TransmissionPilot(text,flight)
-local flightcoord=flight:GetCoordinate(nil,player.name)
-local dist=flightcoord:Get2DDistance(self:GetCoordinate())
-if distself.NlandingTakeoff then
-local text=string.format("%s, negative! We have currently traffic taking off!",callsign)
-self:TransmissionTower(text,flight,10)
-else
-local runway=self:GetActiveRunwayText()
-local text=string.format("%s, affirmative, runway %s. Confirm approach!",callsign,runway)
-self:TransmissionTower(text,flight,10)
-self:SetFlightStatus(flight,FLIGHTCONTROL.FlightStatus.LANDING)
-end
-else
-local text=string.format("Negative, you must be INBOUND and CONTROLLED by us!")
-self:TextMessageToFlight(text,flight,10)
-end
-else
-self:E(self.lid..string.format("Cannot find flight group %s.",tostring(groupname)))
-end
-end
-function FLIGHTCONTROL:_PlayerRequestTaxi(groupname)
-local flight=_DATABASE:GetOpsGroup(groupname)
-if flight then
-local callsign=self:_GetCallsignName(flight)
-local text=string.format("%s, %s, request taxi to runway.",self.alias,callsign)
-self:TransmissionPilot(text,flight)
-if flight:IsParking()then
-local text=string.format("%s, %s, hold position until further notice.",callsign,self.alias)
-self:TransmissionTower(text,flight,10)
-self:SetFlightStatus(flight,FLIGHTCONTROL.FlightStatus.READYTX)
-elseif flight:IsTaxiing()then
-local runway=self:GetActiveRunwayText(true)
-local text=string.format("%s, %s, taxi to runway %s, hold short.",callsign,self.alias,runway)
-self:TransmissionTower(text,flight,10)
-self:SetFlightStatus(flight,FLIGHTCONTROL.FlightStatus.TAXIOUT)
-local playerElement=flight:GetPlayerElement()
-if playerElement and playerElement.parking then
-self:SetParkingFree(playerElement.parking)
-end
-else
-self:TextMessageToFlight(string.format("Negative, you must be PARKING to request TAXI!"),flight)
-end
-else
-self:E(self.lid..string.format("Cannot find flight group %s.",tostring(groupname)))
-end
-end
-function FLIGHTCONTROL:_PlayerAbortTaxi(groupname)
-local flight=_DATABASE:GetOpsGroup(groupname)
-if flight then
-local callsign=self:_GetCallsignName(flight)
-local text=string.format("%s, %s, cancel my taxi request.",self.alias,callsign)
-self:TransmissionPilot(text,flight)
-if flight:IsParking()then
-local text=string.format("%s, %s, roger, remain on your parking position.",callsign,self.alias)
-self:TransmissionTower(text,flight,10)
-self:SetFlightStatus(flight,FLIGHTCONTROL.FlightStatus.PARKING)
-local playerElement=flight:GetPlayerElement()
-if playerElement then
-self:SpawnParkingGuard(playerElement.unit)
-end
-elseif flight:IsTaxiing()then
-local text=string.format("%s, %s, roger, return to your parking position.",callsign,self.alias)
-self:TransmissionTower(text,flight,10)
-self:SetFlightStatus(flight,FLIGHTCONTROL.FlightStatus.TAXIINB)
-else
-self:TextMessageToFlight(string.format("Negative, you must be PARKING or TAXIING to abort TAXI!"),flight)
-end
-else
-self:E(self.lid..string.format("Cannot find flight group %s.",tostring(groupname)))
-end
-end
-function FLIGHTCONTROL:_PlayerRequestTakeoff(groupname)
-local flight=_DATABASE:GetOpsGroup(groupname)
-if flight then
-if flight:IsTaxiing()then
-local callsign=self:_GetCallsignName(flight)
-local text=string.format("%s, %s, ready for departure. Request takeoff.",self.alias,callsign)
-self:TransmissionPilot(text,flight)
-local Nlanding=self:CountFlights(FLIGHTCONTROL.FlightStatus.LANDING)
-local Ntakeoff=self:CountFlights(FLIGHTCONTROL.FlightStatus.TAKEOFF)
-local text=string.format("%s, %s, ",callsign,self.alias)
-if Nlanding==0 then
-text=text.."no current traffic. You are cleared for takeoff."
-self:SetFlightStatus(flight,FLIGHTCONTROL.FlightStatus.TAKEOFF)
-elseif Nlanding>0 then
-if Nlanding==1 then
-text=text..string.format("negative, we got %d flight inbound before it's your turn. Hold position until futher notice.",Nlanding)
-else
-text=text..string.format("negative, we got %d flights inbound. Hold positon until futher notice.",Nlanding)
-end
-end
-self:TransmissionTower(text,flight,10)
-else
-self:TextMessageToFlight(string.format("Negative, you must request TAXI before you can request TAKEOFF!"),flight)
-end
-else
-self:E(self.lid..string.format("Cannot find flight group %s.",tostring(groupname)))
-end
-end
-function FLIGHTCONTROL:_PlayerAbortTakeoff(groupname)
-local flight=_DATABASE:GetOpsGroup(groupname)
-if flight then
-local status=self:GetFlightStatus(flight)
-if status==FLIGHTCONTROL.FlightStatus.TAKEOFF or status==FLIGHTCONTROL.FlightStatus.READYTO then
-local callsign=self:_GetCallsignName(flight)
-local text=string.format("%s, %s, abort takeoff.",self.alias,callsign)
-self:TransmissionPilot(text,flight)
-if flight:IsParking()then
-text=string.format("%s, %s, affirm, remain on your parking position.",callsign,self.alias)
-self:SetFlightStatus(flight,FLIGHTCONTROL.FlightStatus.PARKING)
-local playerElement=flight:GetPlayerElement()
-if playerElement then
-self:SpawnParkingGuard(playerElement.unit)
-end
-elseif flight:IsTaxiing()then
-text=string.format("%s, %s, roger, report whether you want to taxi back or takeoff later.",callsign,self.alias)
-self:SetFlightStatus(flight,FLIGHTCONTROL.FlightStatus.TAXIOUT)
-else
-env.info(self.lid.."ERROR")
-end
-self:TransmissionTower(text,flight,10)
-else
-self:TextMessageToFlight("Negative, You are NOT in the takeoff queue",flight)
-end
-else
-self:E(self.lid..string.format("Cannot find flight group %s.",tostring(groupname)))
-end
-end
-function FLIGHTCONTROL:_PlayerRequestParking(groupname)
-local flight=_DATABASE:GetOpsGroup(groupname)
-if flight then
-local callsign=self:_GetCallsignName(flight)
-local player=flight:GetPlayerElement()
-local TerminalType=AIRBASE.TerminalType.FighterAircraft
-if flight.isHelo then
-TerminalType=AIRBASE.TerminalType.HelicopterUsable
-end
-local coord=flight:GetCoordinate(nil,player.name)
-local spot=self:_GetPlayerSpot(player.name)
-if not spot then
-spot=self:GetClosestParkingSpot(coord,TerminalType,AIRBASE.SpotStatus.FREE)
-end
-if spot then
-local text=string.format("%s, your assigned parking position is terminal ID %d.",callsign,spot.TerminalID)
-self:TransmissionTower(text,flight)
-if player.parking then
-self:SetParkingFree(player.parking)
-end
-player.parking=spot
-self:SetParkingReserved(spot,player.name)
-flight:_UpdateMenu(0.2)
-else
-local text=string.format("%s, no free parking spot available. Try again later.",callsign)
-self:TransmissionTower(text,flight)
-end
-else
-self:E(self.lid..string.format("Cannot find flight group %s.",tostring(groupname)))
-end
-end
-function FLIGHTCONTROL:_PlayerCancelParking(groupname)
-local flight=_DATABASE:GetOpsGroup(groupname)
-if flight then
-local callsign=self:_GetCallsignName(flight)
-local player=flight:GetPlayerElement()
-if player.parking then
-self:SetParkingFree(player.parking)
-player.parking=nil
-self:TextMessageToFlight(string.format("%s, your parking spot reservation at terminal ID %d was cancelled.",callsign,player.parking.TerminalID),flight)
-else
-self:TextMessageToFlight("You did not have a valid parking spot reservation.",flight)
-end
-flight:_UpdateMenu(0.2)
-else
-self:E(self.lid..string.format("Cannot find flight group %s.",tostring(groupname)))
-end
-end
-function FLIGHTCONTROL:_PlayerArrived(groupname)
-local flight=_DATABASE:GetOpsGroup(groupname)
-if flight then
-local player=flight:GetPlayerElement()
-local coord=flight:GetCoordinate(nil,player.name)
-local spot=self:_GetPlayerSpot(player.name)
-if player.parking then
-spot=self:GetParkingSpotByID(player.parking.TerminalID)
-else
-if not spot then
-spot=self:GetClosestParkingSpot(coord)
-end
-end
-if spot then
-local callsign=self:_GetCallsignName(flight)
-local dist=coord:Get2DDistance(spot.Coordinate)
-if dist<12 then
-local text=string.format("%s, %s, arrived at parking position. Terminal ID %d.",self.alias,callsign,spot.TerminalID)
-self:TransmissionPilot(text,flight)
-local text=""
-if spot.ReservedBy and spot.ReservedBy~=player.name then
-text=string.format("%s, this spot is already reserved for %s. Find yourself a different parking position.",callsign,self.alias,spot.ReservedBy)
-else
-text=string.format("%s, %s, roger. Enjoy a cool bevarage in the officers' club.",callsign,self.alias)
-flight:ElementParking(player,spot)
-self:SetFlightStatus(flight,FLIGHTCONTROL.FlightStatus.PARKING)
-if player then
-self:SpawnParkingGuard(player.unit)
-end
-end
-self:TransmissionTower(text,flight,10)
-else
-local text=string.format("%s, %s, arrived at parking position.",self.alias,callsign)
-self:TransmissionPilot(text,flight)
-local text=""
-if spot.ReservedBy then
-if spot.ReservedBy==player.name then
-text=string.format("%s, %s, you are still %d meters away from your reserved parking position at terminal ID %d. Continue taxiing!",callsign,self.alias,dist,spot.TerminalID)
-else
-text=string.format("%s, %s, the closest parking spot is already reserved. Continue taxiing to a free spot!",callsign,self.alias)
-end
-else
-text=string.format("%s, %s, you are still %d meters away from the closest parking position. Continue taxiing to a proper spot!",callsign,self.alias,dist)
-end
-self:TransmissionTower(text,flight,10)
-end
-else
-end
-else
-self:E(self.lid..string.format("Cannot find flight group %s.",tostring(groupname)))
-end
-end
-function FLIGHTCONTROL:_CreateFlightGroup(group)
-if self:_InQueue(self.flights,group)then
-self:E(self.lid..string.format("WARNING: Flight group %s does already exist!",group:GetName()))
-return
-end
-self:T(self.lid..string.format("Creating new flight for group %s of aircraft type %s.",group:GetName(),group:GetTypeName()))
-local flight=_DATABASE:GetOpsGroup(group:GetName())
-if not flight then
-flight=FLIGHTGROUP:New(group:GetName())
-end
-if flight.homebase and flight.homebase:GetName()==self.airbasename then
-flight:SetFlightControl(self)
-end
-return flight
-end
-function FLIGHTCONTROL:_RemoveFlight(Flight)
-for i,_flight in pairs(self.flights)do
-local flight=_flight
-if flight.groupname==Flight.groupname then
-self:T(self.lid..string.format("Removing flight group %s",flight.groupname))
-table.remove(self.flights,i)
-Flight.flightcontrol=nil
-self:SetFlightStatus(Flight,FLIGHTCONTROL.FlightStatus.UNKNOWN)
-return true
-end
-end
-self:E(self.lid..string.format("WARNING: Could NOT remove flight group %s",Flight.groupname))
-end
-function FLIGHTCONTROL:_GetFlightFromGroup(group)
-if group then
-local name=group:GetName()
-for i,_flight in pairs(self.flights)do
-local flight=_flight
-if flight.groupname==name then
-return flight,i
-end
-end
-self:T2(self.lid..string.format("WARNING: Flight group %s could not be found in queue.",name))
-end
-self:T2(self.lid..string.format("WARNING: Flight group could not be found in queue. Group is nil!"))
-return nil,nil
-end
-function FLIGHTCONTROL:_GetFlightElement(unitname)
-local unit=UNIT:FindByName(unitname)
-if unit then
-local flight=self:_GetFlightFromGroup(unit:GetGroup())
-if flight then
-for i,_element in pairs(flight.elements)do
-local element=_element
-if element.unit:GetName()==unitname then
-return element,i,flight
-end
-end
-self:T2(self.lid..string.format("WARNING: Flight element %s could not be found in flight group.",unitname,flight.groupname))
-end
-end
-return nil,nil,nil
-end
-function FLIGHTCONTROL:_CheckFlights()
-for i=#self.flights,1,-1 do
-local flight=self.flights[i]
-if flight:IsDead()then
-self:T(self.lid..string.format("Removing DEAD flight %s",tostring(flight.groupname)))
-self:_RemoveFlight(flight)
-end
-end
-if self.speedLimitTaxi then
-for _,_flight in pairs(self.flights)do
-local flight=_flight
-if not flight.isAI then
-local playerElement=flight:GetPlayerElement()
-local flightstatus=self:GetFlightStatus(flight)
-if playerElement then
-if(flightstatus==FLIGHTCONTROL.FlightStatus.TAXIINB or flightstatus==FLIGHTCONTROL.FlightStatus.TAXIOUT)and self.speedLimitTaxi then
-local speed=playerElement.unit:GetVelocityMPS()
-local coord=playerElement.unit:GetCoord()
-local onRunway=self:IsCoordinateRunway(coord)
-self:T(self.lid..string.format("Player %s speed %.1f knots (max=%.1f) onRunway=%s",playerElement.playerName,UTILS.MpsToKnots(speed),UTILS.MpsToKnots(self.speedLimitTaxi),tostring(onRunway)))
-if speed and speed>self.speedLimitTaxi and not onRunway then
-local callsign=self:_GetCallsignName(flight)
-local text=string.format("%s, slow down, you are taxiing too fast!",callsign)
-self:TransmissionTower(text,flight)
-local PlayerData=flight:_GetPlayerData()
-self:PlayerSpeeding(PlayerData)
-end
-end
-end
-end
-end
-end
-end
-function FLIGHTCONTROL:_CheckParking()
-for TerminalID,_spot in pairs(self.parking)do
-local spot=_spot
-if spot.Reserved then
-if spot.MarkerID then
-spot.Coordinate:RemoveMark(spot.MarkerID)
-end
-spot.MarkerID=spot.Coordinate:MarkToCoalition(string.format("Parking reserved for %s",tostring(spot.Reserved)),self:GetCoalition())
-end
-for i=1,#self.flights do
-local flight=self.flights[i]
-for _,_element in pairs(flight.elements)do
-local element=_element
-if element.parking and element.parking.TerminalID==TerminalID then
-if spot.MarkerID then
-spot.Coordinate:RemoveMark(spot.MarkerID)
-end
-spot.MarkerID=spot.Coordinate:MarkToCoalition(string.format("Parking spot occupied by %s",tostring(element.name)),self:GetCoalition())
-end
-end
-end
-end
-end
-function FLIGHTCONTROL:_LandAI(flight,parking)
-self:T(self.lid..string.format("Landing AI flight %s.",flight.groupname))
-local respawn=false
-if respawn then
-local Template=flight.group:GetTemplate()
-Template.route.points=wp
-for i,unit in pairs(Template.units)do
-local spot=parking[i]
-local element=flight:GetElementByName(unit.name)
-if element then
-unit.parking_landing=spot.TerminalID
-local text=string.format("Reserving parking spot %d for unit %s",spot.TerminalID,tostring(unit.name))
-self:T(self.lid..text)
-self:SetParkingReserved(spot,element.name)
-else
-env.info("FF error could not get element to assign parking!")
-end
-end
-self:TextMessageToFlight(string.format("Respawning group %s",flight.groupname),flight)
-flight:Respawn(Template)
-else
-flight:ClearToLand()
-end
-end
-function FLIGHTCONTROL:_GetHoldingStack(flight)
-self:T(self.lid..string.format("Getting holding point for flight %s",flight:GetName()))
-for i,_hp in pairs(self.holdingpatterns)do
-local holdingpattern=_hp
-self:T(self.lid..string.format("Checking holding point %s",holdingpattern.name))
-for j,_stack in pairs(holdingpattern.stacks)do
-local stack=_stack
-local name=stack.flightgroup and stack.flightgroup:GetName()or"empty"
-self:T(self.lid..string.format("Stack %d: %s",j,name))
-if not stack.flightgroup then
-return stack
-end
-end
-end
-return nil
-end
-function FLIGHTCONTROL:_CountFlightsInPattern(Pattern)
-local N=0
-for _,_stack in pairs(Pattern.stacks)do
-local stack=_stack
-if stack.flightgroup then
-N=N+1
-end
-end
-return N
-end
-function FLIGHTCONTROL:_FlightOnFinal(flight)
-local callsign=self:_GetCallsignName(flight)
-local text=string.format("%s, final",callsign)
-self:TransmissionPilot(text,flight)
-return self
-end
-function FLIGHTCONTROL:TransmissionTower(Text,Flight,Delay)
-local text=self:_GetTextForSpeech(Text)
-local subgroups=nil
-if Flight and not Flight.isAI then
-local playerData=Flight:_GetPlayerData()
-if playerData.subtitles and(not self.nosubs)then
-subgroups=subgroups or{}
-table.insert(subgroups,Flight.group)
-end
-end
-local transmission=self.msrsqueue:NewTransmission(text,nil,self.msrsTower,nil,1,subgroups,Text)
-self.Tlastmessage=timer.getAbsTime()+(Delay or 0)
-self:T(self.lid..string.format("Radio Tower: %s",Text))
-end
-function FLIGHTCONTROL:TransmissionPilot(Text,Flight,Delay)
-local playerData=Flight:_GetPlayerData()
-if playerData==nil or playerData.myvoice then
-local text=self:_GetTextForSpeech(Text)
-local msrs=self.msrsPilot
-if Flight.useSRS and Flight.msrs then
-msrs=Flight.msrs
-end
-local subgroups=nil
-if Flight and not Flight.isAI then
-local playerData=Flight:_GetPlayerData()
-if playerData.subtitles and(not self.nosubs)then
-subgroups=subgroups or{}
-table.insert(subgroups,Flight.group)
-end
-end
-local coordinate=Flight:GetCoordinate(true)
-msrs:SetCoordinate()
-self.msrsqueue:NewTransmission(text,nil,msrs,nil,1,subgroups,Text,nil,self.frequency,self.modulation)
-end
-self.Tlastmessage=timer.getAbsTime()+(Delay or 0)
-self:T(self.lid..string.format("Radio Pilot: %s",Text))
-end
-function FLIGHTCONTROL:TextMessageToFlight(Text,Flight,Duration,Clear,Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,FLIGHTCONTROL.TextMessageToFlight,self,Text,Flight,Duration,Clear,0)
-else
-if Flight and Flight.group and Flight.group:IsAlive()then
-local gid=Flight.group:GetID()
-trigger.action.outTextForGroup(gid,self:_CleanText(Text),Duration or 5,Clear)
-end
-end
-end
-function FLIGHTCONTROL:_CleanText(Text)
-local text=Text:gsub("\n$",""):gsub("\n$","")
-return text
-end
-function FLIGHTCONTROL:_SpawnParkingGuard(unit)
-local coordinate=unit:GetCoordinate()
-local spot=self:GetClosestParkingSpot(coordinate)
-if not spot.ParkingGuard then
-local heading=unit:GetHeading()
-local size,x,y,z=unit:GetObjectSize()
-local xdiff=3
-if AIRBASE._CheckTerminalType(spot.TerminalType,AIRBASE.TerminalType.Shelter)then
-xdiff=27-(x*0.5)
-end
-if(AIRBASE._CheckTerminalType(spot.TerminalType,AIRBASE.TerminalType.OpenMed)or AIRBASE._CheckTerminalType(spot.TerminalType,AIRBASE.TerminalType.Shelter))and self.airbasename==AIRBASE.Sinai.Ramon_Airbase then
-xdiff=12
-end
-self:T2(self.lid..string.format("Parking guard for %s: heading=%d, length x=%.1f m, xdiff=%.1f m",unit:GetName(),heading,x,xdiff))
-local Coordinate=coordinate:Translate(x*0.5+xdiff,heading)
-local lookat=heading-180
-self.parkingGuard:InitHeading(lookat)
-if self.parkingGuard:IsInstanceOf("SPAWN")then
-end
-spot.ParkingGuard=self.parkingGuard:SpawnFromCoordinate(Coordinate)
-else
-self:E(self.lid.."ERROR: Parking Guard already exists!")
-end
-end
-function FLIGHTCONTROL:SpawnParkingGuard(unit)
-if unit and self.parkingGuard then
-self:ScheduleOnce(1,FLIGHTCONTROL._SpawnParkingGuard,self,unit)
-end
-end
-function FLIGHTCONTROL:RemoveParkingGuard(spot,delay)
-if delay and delay>0 then
-self:ScheduleOnce(delay,FLIGHTCONTROL.RemoveParkingGuard,self,spot)
-else
-if spot.ParkingGuard then
-spot.ParkingGuard:Destroy()
-spot.ParkingGuard=nil
-end
-end
-end
-function FLIGHTCONTROL:_IsFlightOnRunway(flight)
-for _,_runway in pairs(self.airbase.runways)do
-local runway=_runway
-local inzone=flight:IsInZone(runway.zone)
-if inzone then
-return runway
-end
-end
-return nil
-end
-function FLIGHTCONTROL:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTranslations)
-if not ShortCallsign or ShortCallsign==false then
-self.ShortCallsign=false
-else
-self.ShortCallsign=true
-end
-self.Keepnumber=Keepnumber or false
-self.CallsignTranslations=CallsignTranslations
-return self
-end
-function FLIGHTCONTROL:_GetCallsignName(flight)
-local callsign=flight:GetCallsignName(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
-return callsign
-end
-function FLIGHTCONTROL:_GetTextForSpeech(text)
-local function space(text)
-local res=""
-for i=1,#text do
-local char=text:sub(i,i)
-res=res..char.." "
-end
-return res
-end
-local t=text:gsub("(%d+)",space)
-return t
-end
-function FLIGHTCONTROL:_GetPlayerUnitAndName(unitName)
-if unitName then
-local DCSunit=Unit.getByName(unitName)
-if DCSunit then
-local playername=DCSunit:getPlayerName()
-local unit=UNIT:Find(DCSunit)
-if DCSunit and unit and playername then
-self:T(self.lid..string.format("Found DCS unit %s with player %s",tostring(unitName),tostring(playername)))
-return unit,playername
-end
-end
-end
-return nil,nil
-end
-function FLIGHTCONTROL:_CheckMarkHoldingPatterns()
-for _,pattern in pairs(self.holdingpatterns)do
-local Pattern=pattern
-if self.markPatterns then
-self:_MarkHoldingPattern(Pattern)
-else
-self:_UnMarkHoldingPattern(Pattern)
-end
-end
-end
-function FLIGHTCONTROL:_MarkHoldingPattern(Pattern)
-if not Pattern.markArrow then
-Pattern.markArrow=Pattern.pos0:ArrowToAll(Pattern.pos1,nil,{1,0,0},1,{1,1,0},0.5,2,true)
-end
-if not Pattern.markArrival then
-Pattern.markArrival=Pattern.arrivalzone:DrawZone()
-end
-end
-function FLIGHTCONTROL:_UnMarkHoldingPattern(Pattern)
-if Pattern.markArrow then
-UTILS.RemoveMark(Pattern.markArrow)
-Pattern.markArrow=nil
-end
-if Pattern.markArrival then
-UTILS.RemoveMark(Pattern.markArrival)
-Pattern.markArrival=nil
-end
-end
-function FLIGHTCONTROL:_AddHoldingPatternBackup()
-local runway=self:GetActiveRunway()
-local heading=runway.heading
-local vec2=self.airbase:GetVec2()
-local Vec2=UTILS.Vec2Translate(vec2,UTILS.NMToMeters(5),heading+90)
-local ArrivalZone=ZONE_RADIUS:New("Arrival Zone",Vec2,5000)
-self.holdingBackup=self:AddHoldingPattern(ArrivalZone,heading,15,5,25,999)
-return self
-end
-FLIGHTGROUP={
-ClassName="FLIGHTGROUP",
-homebase=nil,
-destbase=nil,
-homezone=nil,
-destzone=nil,
-actype=nil,
-speedMax=nil,
-rangemax=nil,
-ceiling=nil,
-fuellow=false,
-fuellowthresh=nil,
-fuellowrtb=nil,
-fuelcritical=nil,
-fuelcriticalthresh=nil,
-fuelcriticalrtb=false,
-outofAAMrtb=false,
-outofAGMrtb=false,
-flightcontrol=nil,
-flaghold=nil,
-Tholding=nil,
-Tparking=nil,
-Twaiting=nil,
-menu=nil,
-isHelo=nil,
-RTBRecallCount=0,
-playerSettings={},
-playerWarnings={},
-prohibitAB=false,
-jettisonEmptyTanks=true,
-jettisonWeapons=true,
-}
-FLIGHTGROUP.Attribute={
-TRANSPORTPLANE="TransportPlane",
-AWACS="AWACS",
-FIGHTER="Fighter",
-BOMBER="Bomber",
-TANKER="Tanker",
-TRANSPORTHELO="TransportHelo",
-ATTACKHELO="AttackHelo",
-UAV="UAV",
-OTHER="Other",
-}
-FLIGHTGROUP.RadioMessage={
-AIRBORNE={normal="Airborn",enhanced="Airborn"},
-TAXIING={normal="Taxiing",enhanced="Taxiing"},
-}
-FLIGHTGROUP.PlayerSkill={
-STUDENT="Student",
-AVIATOR="Aviator",
-GRADUATE="Graduate",
-INSTRUCTOR="Instructor",
-}
-FLIGHTGROUP.Players={}
-FLIGHTGROUP.version="1.0.2"
-function FLIGHTGROUP:New(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
-local self=BASE:Inherit(self,OPSGROUP:New(group))
-self.lid=string.format("FLIGHTGROUP %s | ",self.groupname)
-self:SetDefaultROE()
-self:SetDefaultROT()
-self:SetDefaultEPLRS(self.isEPLRS)
-self:SetDetection()
-self:SetFuelLowThreshold()
-self:SetFuelLowRTB()
-self:SetFuelCriticalThreshold()
-self:SetFuelCriticalRTB()
-self.flaghold=USERFLAG:New(string.format("%s_FlagHold",self.groupname))
-self.flaghold:Set(0)
-self:AddTransition("*","LandAtAirbase","Inbound")
-self:AddTransition("*","RTB","Inbound")
-self:AddTransition("*","RTZ","Inbound")
-self:AddTransition("Inbound","Holding","Holding")
-self:AddTransition("*","Refuel","Going4Fuel")
-self:AddTransition("Going4Fuel","Refueled","Cruising")
-self:AddTransition("*","LandAt","LandingAt")
-self:AddTransition("LandingAt","LandedAt","LandedAt")
-self:AddTransition("*","FuelLow","*")
-self:AddTransition("*","FuelCritical","*")
-self:AddTransition("Cruising","EngageTarget","Engaging")
-self:AddTransition("Engaging","Disengage","Cruising")
-self:AddTransition("*","ElementParking","*")
-self:AddTransition("*","ElementEngineOn","*")
-self:AddTransition("*","ElementTaxiing","*")
-self:AddTransition("*","ElementTakeoff","*")
-self:AddTransition("*","ElementAirborne","*")
-self:AddTransition("*","ElementLanded","*")
-self:AddTransition("*","ElementArrived","*")
-self:AddTransition("*","ElementOutOfAmmo","*")
-self:AddTransition("*","Parking","Parking")
-self:AddTransition("*","Taxiing","Taxiing")
-self:AddTransition("*","Takeoff","Airborne")
-self:AddTransition("*","Airborne","Airborne")
-self:AddTransition("*","Cruise","Cruising")
-self:AddTransition("*","Landing","Landing")
-self:AddTransition("*","Landed","Landed")
-self:AddTransition("*","Arrived","Arrived")
-self:HandleEvent(EVENTS.Birth,self.OnEventBirth)
-self:HandleEvent(EVENTS.EngineStartup,self.OnEventEngineStartup)
-self:HandleEvent(EVENTS.Takeoff,self.OnEventTakeOff)
-self:HandleEvent(EVENTS.Land,self.OnEventLanding)
-self:HandleEvent(EVENTS.EngineShutdown,self.OnEventEngineShutdown)
-self:HandleEvent(EVENTS.PilotDead,self.OnEventPilotDead)
-self:HandleEvent(EVENTS.Ejection,self.OnEventEjection)
-self:HandleEvent(EVENTS.Crash,self.OnEventCrash)
-self:HandleEvent(EVENTS.RemoveUnit,self.OnEventRemoveUnit)
-self:HandleEvent(EVENTS.UnitLost,self.OnEventUnitLost)
-self:HandleEvent(EVENTS.Kill,self.OnEventKill)
-self:HandleEvent(EVENTS.PlayerLeaveUnit,self.OnEventPlayerLeaveUnit)
-self:_InitGroup()
-self:_InitWaypoints()
-self.timerStatus=TIMER:New(self.Status,self):Start(1,30)
-self.timerQueueUpdate=TIMER:New(self._QueueUpdate,self):Start(2,5)
-self.timerCheckZone=TIMER:New(self._CheckInZones,self):Start(3,10)
-_DATABASE:AddOpsGroup(self)
-return self
-end
-function FLIGHTGROUP:AddTaskEnrouteEngageTargetsInZone(ZoneRadius,TargetTypes,Priority)
-local Task=self.group:EnRouteTaskEngageTargetsInZone(ZoneRadius:GetVec2(),ZoneRadius:GetRadius(),TargetTypes,Priority)
-self:AddTaskEnroute(Task)
-end
-function FLIGHTGROUP:GetAirwing()
-return self.legion
-end
-function FLIGHTGROUP:GetAirwingName()
-local name=self.legion and self.legion.alias or"None"
-return name
-end
-function FLIGHTGROUP:GetSquadron()
-return self.cohort
-end
-function FLIGHTGROUP:GetSquadronName()
-local name=self.cohort and self.cohort:GetName()or"None"
-return name
-end
-function FLIGHTGROUP:SetVTOL()
-self.isVTOL=true
-return self
-end
-function FLIGHTGROUP:SetProhibitAfterburner()
-self.prohibitAB=true
-if self:GetGroup():IsAlive()then
-self:GetGroup():SetOption(AI.Option.Air.id.PROHIBIT_AB,true)
-end
-return self
-end
-function FLIGHTGROUP:SetAllowAfterburner()
-self.prohibitAB=false
-if self:GetGroup():IsAlive()then
-self:GetGroup():SetOption(AI.Option.Air.id.PROHIBIT_AB,false)
-end
-return self
-end
-function FLIGHTGROUP:SetJettisonEmptyTanks(Switch)
-self.jettisonEmptyTanks=Switch
-if self:GetGroup():IsAlive()then
-self:GetGroup():SetOption(AI.Option.Air.id.JETT_TANKS_IF_EMPTY,Switch)
-end
-return self
-end
-function FLIGHTGROUP:SetJettisonWeapons(Switch)
-self.jettisonWeapons=not Switch
-if self:GetGroup():IsAlive()then
-self:GetGroup():SetOption(AI.Option.Air.id.PROHIBIT_JETT,not Switch)
-end
-return self
-end
-function FLIGHTGROUP:SetReadyForTakeoff(ReadyTO,Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,FLIGHTGROUP.SetReadyForTakeoff,self,ReadyTO,0)
-else
-self.isReadyTO=ReadyTO
-end
-return self
-end
-function FLIGHTGROUP:SetFlightControl(flightcontrol)
-if self.flightcontrol then
-if self.flightcontrol:IsControlling(self)then
-return
-else
-self.flightcontrol:_RemoveFlight(self)
-end
-end
-self:T(self.lid..string.format("Setting FLIGHTCONTROL to airbase %s",flightcontrol.airbasename))
-self.flightcontrol=flightcontrol
-if not flightcontrol:IsFlight(self)then
-table.insert(flightcontrol.flights,self)
-end
-return self
-end
-function FLIGHTGROUP:GetFlightControl()
-return self.flightcontrol
-end
-function FLIGHTGROUP:SetHomebase(HomeAirbase)
-if type(HomeAirbase)=="string"then
-HomeAirbase=AIRBASE:FindByName(HomeAirbase)
-end
-self.homebase=HomeAirbase
-return self
-end
-function FLIGHTGROUP:SetDestinationbase(DestinationAirbase)
-if type(DestinationAirbase)=="string"then
-DestinationAirbase=AIRBASE:FindByName(DestinationAirbase)
-end
-self.destbase=DestinationAirbase
-return self
-end
-function FLIGHTGROUP:SetAirboss(airboss)
-self.airboss=airboss
-return self
-end
-function FLIGHTGROUP:SetFuelLowThreshold(threshold)
-self.fuellowthresh=threshold or 25
-return self
-end
-function FLIGHTGROUP:SetFuelLowRTB(switch)
-if switch==false then
-self.fuellowrtb=false
-else
-self.fuellowrtb=true
-end
-return self
-end
-function FLIGHTGROUP:SetOutOfAAMRTB(switch)
-if switch==false then
-self.outofAAMrtb=false
-else
-self.outofAAMrtb=true
-end
-return self
-end
-function FLIGHTGROUP:SetOutOfAGMRTB(switch)
-if switch==false then
-self.outofAGMrtb=false
-else
-self.outofAGMrtb=true
-end
-return self
-end
-function FLIGHTGROUP:SetFuelLowRefuel(switch)
-if switch==false then
-self.fuellowrefuel=false
-else
-self.fuellowrefuel=true
-end
-return self
-end
-function FLIGHTGROUP:SetFuelCriticalThreshold(threshold)
-self.fuelcriticalthresh=threshold or 10
-return self
-end
-function FLIGHTGROUP:SetFuelCriticalRTB(switch)
-if switch==false then
-self.fuelcriticalrtb=false
-else
-self.fuelcriticalrtb=true
-end
-return self
-end
-function FLIGHTGROUP:SetDespawnAfterLanding()
-self.despawnAfterLanding=true
-return self
-end
-function FLIGHTGROUP:SetDespawnAfterHolding()
-self.despawnAfterHolding=true
-return self
-end
-function FLIGHTGROUP:IsParking(Element)
-local is=self:Is("Parking")
-if Element then
-is=Element.status==OPSGROUP.ElementStatus.PARKING
-end
-return is
-end
-function FLIGHTGROUP:IsTaxiing(Element)
-local is=self:Is("Taxiing")
-if Element then
-is=Element.status==OPSGROUP.ElementStatus.TAXIING
-end
-return is
-end
-function FLIGHTGROUP:IsAirborne(Element)
-local is=self:Is("Airborne")or self:Is("Cruising")
-if Element then
-is=Element.status==OPSGROUP.ElementStatus.AIRBORNE
-end
-return is
-end
-function FLIGHTGROUP:IsCruising()
-local is=self:Is("Cruising")
-return is
-end
-function FLIGHTGROUP:IsLanding(Element)
-local is=self:Is("Landing")
-if Element then
-is=Element.status==OPSGROUP.ElementStatus.LANDING
-end
-return is
-end
-function FLIGHTGROUP:IsLanded(Element)
-local is=self:Is("Landed")
-if Element then
-is=Element.status==OPSGROUP.ElementStatus.LANDED
-end
-return is
-end
-function FLIGHTGROUP:IsArrived(Element)
-local is=self:Is("Arrived")
-if Element then
-is=Element.status==OPSGROUP.ElementStatus.ARRIVED
-end
-return is
-end
-function FLIGHTGROUP:IsInbound()
-local is=self:Is("Inbound")
-return is
-end
-function FLIGHTGROUP:IsHolding()
-local is=self:Is("Holding")
-return is
-end
-function FLIGHTGROUP:IsGoing4Fuel()
-local is=self:Is("Going4Fuel")
-return is
-end
-function FLIGHTGROUP:IsLandingAt()
-local is=self:Is("LandingAt")
-return is
-end
-function FLIGHTGROUP:IsLandedAt()
-local is=self:Is("LandedAt")
-return is
-end
-function FLIGHTGROUP:IsFuelLow()
-return self.fuellow
-end
-function FLIGHTGROUP:IsFuelCritical()
-return self.fuelcritical
-end
-function FLIGHTGROUP:IsFuelGood()
-local isgood=not(self.fuellow or self.fuelcritical)
-return isgood
-end
-function FLIGHTGROUP:CanAirToGround(ExcludeGuns)
-local ammo=self:GetAmmoTot()
-if ExcludeGuns then
-return ammo.MissilesAG+ammo.Rockets+ammo.Bombs>0
-else
-return ammo.MissilesAG+ammo.Rockets+ammo.Bombs+ammo.Guns>0
-end
-end
-function FLIGHTGROUP:CanAirToAir(ExcludeGuns)
-local ammo=self:GetAmmoTot()
-if ExcludeGuns then
-return ammo.MissilesAA>0
-else
-return ammo.MissilesAA+ammo.Guns>0
-end
-end
-function FLIGHTGROUP:StartUncontrolled(delay)
-if delay and delay>0 then
-self:T2(self.lid..string.format("Starting uncontrolled group in %d seconds",delay))
-self:ScheduleOnce(delay,FLIGHTGROUP.StartUncontrolled,self)
-else
-local alive=self:IsAlive()
-if alive~=nil then
-local _delay=0
-if alive==false then
-self:Activate()
-_delay=1
-end
-self:T(self.lid.."Starting uncontrolled group")
-self.group:StartUncontrolled(_delay)
-self.isUncontrolled=false
-else
-self:T(self.lid.."ERROR: Could not start uncontrolled group as it is NOT alive!")
-end
-end
-return self
-end
-function FLIGHTGROUP:ClearToLand(Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,FLIGHTGROUP.ClearToLand,self)
-else
-if self:IsHolding()then
-self:T(self.lid..string.format("Clear to land ==> setting holding flag to 1 (true)"))
-self.flaghold:Set(1)
-self.Tholding=nil
-if self.stack then
-self.stack.flightgroup=nil
-self.stack=nil
-end
-end
-end
-return self
-end
-function FLIGHTGROUP:GetFuelMin()
-local fuelmin=math.huge
-for i,_element in pairs(self.elements)do
-local element=_element
-local unit=element.unit
-local life=unit:GetLife()
-if unit and unit:IsAlive()and life>1 then
-local fuel=unit:GetFuel()
-if fuelself.Twaiting+self.dTwait then
-end
-end
-end
-if mission and mission.updateDCSTask then
-if(mission:GetType()==AUFTRAG.Type.ORBIT or mission:GetType()==AUFTRAG.Type.RECOVERYTANKER or mission:GetType()==AUFTRAG.Type.CAP)and mission.orbitVec2 then
-local vec2=mission:GetTargetVec2()
-local hdg=mission:GetTargetHeading()
-local hdgchange=false
-if mission.orbitLeg then
-if UTILS.HdgDiff(hdg,mission.targetHeading)>0 then
-hdgchange=true
-end
-end
-local dist=UTILS.VecDist2D(vec2,mission.orbitVec2)
-local distchange=dist>mission.orbitDeltaR
-self:T3(self.lid..string.format("Checking orbit mission dist=%d meters",dist))
-if distchange or hdgchange then
-self:T3(self.lid..string.format("Updating orbit!"))
-local DCSTask=mission:GetDCSMissionTask()
-local Task=mission:GetGroupWaypointTask(self)
-self.controller:resetTask()
-self:_SandwitchDCSTask(DCSTask,Task,false,1)
-end
-elseif mission.type==AUFTRAG.Type.CAPTUREZONE then
-local Task=mission:GetGroupWaypointTask(self)
-if mission:GetGroupStatus(self)==AUFTRAG.GroupStatus.EXECUTING or mission:GetGroupStatus(self)==AUFTRAG.GroupStatus.STARTED then
-self:_UpdateTask(Task,mission)
-end
-end
-end
-if self:IsParking()then
-for _,_element in pairs(self.elements)do
-local element=_element
-if element.parking then
-local dist=self:_GetDistToParking(element.parking,element.unit:GetCoord())
-self:T(self.lid..string.format("Distance to parking spot %d = %.1f meters",element.parking.TerminalID,dist))
-if dist>12 and element.engineOn then
-self:ElementTaxiing(element)
-end
-else
-end
-end
-end
-else
-self:_CheckDamage()
-end
-if self.verbose>=1 then
-local nelem=self:CountElements()
-local Nelem=#self.elements
-local nTaskTot,nTaskSched,nTaskWP=self:CountRemainingTasks()
-local nMissions=self:CountRemainingMissison()
-local roe=self:GetROE()or-1
-local rot=self:GetROT()or-1
-local wpidxCurr=self.currentwp
-local wpuidCurr=self:GetWaypointUIDFromIndex(wpidxCurr)or 0
-local wpidxNext=self:GetWaypointIndexNext()or 0
-local wpuidNext=self:GetWaypointUIDFromIndex(wpidxNext)or 0
-local wpN=#self.waypoints or 0
-local wpF=tostring(self.passedfinalwp)
-local speed=UTILS.MpsToKnots(self.velocity or 0)
-local speedEx=UTILS.MpsToKnots(self:GetExpectedSpeed())
-local alt=self.position and self.position.y or 0
-local hdg=self.heading or 0
-local formation=self.option.Formation or"unknown"
-local life=self.life or 0
-local ammo=self:GetAmmoTot().Total
-local ndetected=self.detectionOn and tostring(self.detectedunits:Count())or"Off"
-local cargo=0
-for _,_element in pairs(self.elements)do
-local element=_element
-cargo=cargo+element.weightCargo
-end
-local home=self.homebase and self.homebase:GetName()or"unknown"
-local dest=self.destbase and self.destbase:GetName()or"unknown"
-local curr=self.currbase and self.currbase:GetName()or"N/A"
-local text=string.format("%s [%d/%d]: ROE/ROT=%d/%d | T/M=%d/%d | Wp=%d[%d]-->%d[%d]/%d [%s] | Life=%.1f | v=%.1f (%d) | Hdg=%03d | Ammo=%d | Detect=%s | Cargo=%.1f | Base=%s [%s-->%s]",
-fsmstate,nelem,Nelem,roe,rot,nTaskTot,nMissions,wpidxCurr,wpuidCurr,wpidxNext,wpuidNext,wpN,wpF,life,speed,speedEx,hdg,ammo,ndetected,cargo,curr,home,dest)
-self:I(self.lid..text)
-end
-if self.verbose>=2 then
-local text="Elements:"
-for i,_element in pairs(self.elements)do
-local element=_element
-local name=element.name
-local status=element.status
-local unit=element.unit
-local fuel=unit:GetFuel()or 0
-local life=unit:GetLifeRelative()or 0
-local lp=unit:GetLife()
-local lp0=unit:GetLife0()
-local parking=element.parking and tostring(element.parking.TerminalID)or"X"
-local ammo=self:GetAmmoElement(element)
-text=text..string.format("\n[%d] %s: status=%s, fuel=%.1f, life=%.1f [%.1f/%.1f], guns=%d, rockets=%d, bombs=%d, missiles=%d (AA=%d, AG=%d, AS=%s), parking=%s",
-i,name,status,fuel*100,life*100,lp,lp0,ammo.Guns,ammo.Rockets,ammo.Bombs,ammo.Missiles,ammo.MissilesAA,ammo.MissilesAG,ammo.MissilesAS,parking)
-end
-if#self.elements==0 then
-text=text.." none!"
-end
-self:I(self.lid..text)
-end
-if self.verbose>=4 and alive then
-local ds=self.travelds
-local dt=self.dTpositionUpdate
-local v=ds/dt
-local TmaxFuel=math.huge
-for _,_element in pairs(self.elements)do
-local element=_element
-local fuel=element.unit:GetFuel()or 0
-local dFrel=element.fuelrel-fuel
-local dFreldt=dFrel/dt
-local Tfuel=fuel/dFreldt
-if Tfuel Tfuel=%.1f min",element.name,fuel*100,dFrel*100,dFreldt*100*60,Tfuel/60))
-element.fuelrel=fuel
-end
-self:T(self.lid..string.format("Travelled ds=%.1f km dt=%.1f s ==> v=%.1f knots. Fuel left for %.1f min",self.traveldist/1000,dt,UTILS.MpsToKnots(v),TmaxFuel/60))
-end
-if false then
-for _,_element in pairs(self.elements)do
-local element=_element
-local unit=element.unit
-if unit and unit:IsAlive()then
-local vec3=unit:GetVec3()
-if vec3 and element.pos then
-local id=UTILS.GetMarkID()
-trigger.action.lineToAll(-1,id,vec3,element.pos,{1,1,1,0.5},1)
-end
-element.pos=vec3
-end
-end
-end
-if alive and self.group:IsAirborne(true)then
-local fuelmin=self:GetFuelMin()
-self:T2(self.lid..string.format("Fuel state=%d",fuelmin))
-if fuelmin>=self.fuellowthresh then
-self.fuellow=false
-end
-if fuelmin>=self.fuelcriticalthresh then
-self.fuelcritical=false
-end
-if fuelmin taxiing (if AI)",element.name))
-self:ElementEngineOn(element)
-element.engineOn=true
-end
-end
-end
-end
-function FLIGHTGROUP:OnEventTakeOff(EventData)
-self:T3(self.lid.."EVENT: TakeOff")
-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
-local element=self:GetElementByName(unitname)
-if element then
-self:T2(self.lid..string.format("EVENT: Element %s took off ==> airborne",element.name))
-self:ElementTakeoff(element,EventData.Place)
-end
-end
-end
-function FLIGHTGROUP:OnEventLanding(EventData)
-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
-local element=self:GetElementByName(unitname)
-local airbase=EventData.Place
-local airbasename="unknown"
-if airbase then
-airbasename=tostring(airbase:GetName())
-end
-if element then
-self:T3(self.lid..string.format("EVENT: Element %s landed at %s ==> landed",element.name,airbasename))
-self:ElementLanded(element,airbase)
-end
-end
-end
-function FLIGHTGROUP:OnEventEngineShutdown(EventData)
-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
-local element=self:GetElementByName(unitname)
-if element then
-element.engineOn=false
-if element.unit and element.unit:IsAlive()then
-local airbase=self:GetClosestAirbase()
-local parking=self:GetParkingSpot(element,100,airbase)
-if airbase and parking then
-self:ElementArrived(element,airbase,parking)
-self:T3(self.lid..string.format("EVENT: Element %s shut down engines ==> arrived",element.name))
-else
-self:T3(self.lid..string.format("EVENT: Element %s shut down engines but is not parking. Is it dead?",element.name))
-end
-else
-end
-end
-end
-end
-function FLIGHTGROUP:OnEventCrash(EventData)
-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
-local element=self:GetElementByName(unitname)
-if element and element.status~=OPSGROUP.ElementStatus.DEAD then
-self:T(self.lid..string.format("EVENT: Element %s crashed ==> destroyed",element.name))
-self:ElementDestroyed(element)
-end
-end
-end
-function FLIGHTGROUP:OnEventUnitLost(EventData)
-if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then
-self:T2(self.lid..string.format("EVENT: Unit %s lost at t=%.3f",EventData.IniUnitName,timer.getTime()))
-local unit=EventData.IniUnit
-local group=EventData.IniGroup
-local unitname=EventData.IniUnitName
-local element=self:GetElementByName(unitname)
-if element and element.status~=OPSGROUP.ElementStatus.DEAD then
-self:T(self.lid..string.format("EVENT: Element %s unit lost ==> destroyed t=%.3f",element.name,timer.getTime()))
-self:ElementDestroyed(element)
-end
-end
-end
-function FLIGHTGROUP:onafterElementSpawned(From,Event,To,Element)
-self:T(self.lid..string.format("Element spawned %s",Element.name))
-if Element.playerName then
-self:_InitPlayerData(Element.playerName)
-end
-self:_UpdateStatus(Element,OPSGROUP.ElementStatus.SPAWNED)
-if Element.unit:InAir(not self.isHelo)then
-self:__ElementAirborne(0.11,Element)
-else
-local spot=self:GetParkingSpot(Element,10)
-if spot then
-self:__ElementParking(0.11,Element,spot)
-else
-self:T(self.lid..string.format("Element spawned not in air but not on any parking spot."))
-self:__ElementParking(0.11,Element)
-end
-end
-end
-function FLIGHTGROUP:onafterElementParking(From,Event,To,Element,Spot)
-if Spot then
-self:_SetElementParkingAt(Element,Spot)
-end
-self:T(self.lid..string.format("Element parking %s at spot %s",Element.name,Element.parking and tostring(Element.parking.TerminalID)or"N/A"))
-self:_UpdateStatus(Element,OPSGROUP.ElementStatus.PARKING)
-if self:IsTakeoffCold()then
-elseif self:IsTakeoffHot()then
-self:__ElementEngineOn(0.5,Element)
-Element.engineOn=true
-elseif self:IsTakeoffRunway()then
-self:__ElementEngineOn(0.5,Element)
-Element.engineOn=true
-end
-end
-function FLIGHTGROUP:onafterElementEngineOn(From,Event,To,Element)
-self:T(self.lid..string.format("Element %s started engines",Element.name))
-self:_UpdateStatus(Element,OPSGROUP.ElementStatus.ENGINEON)
-end
-function FLIGHTGROUP:onafterElementTaxiing(From,Event,To,Element)
-local TerminalID=Element.parking and tostring(Element.parking.TerminalID)or"N/A"
-self:T(self.lid..string.format("Element taxiing %s. Parking spot %s is now free",Element.name,TerminalID))
-self:_SetElementParkingFree(Element)
-self:_UpdateStatus(Element,OPSGROUP.ElementStatus.TAXIING)
-end
-function FLIGHTGROUP:onafterElementTakeoff(From,Event,To,Element,airbase)
-self:T(self.lid..string.format("Element takeoff %s at %s airbase.",Element.name,airbase and airbase:GetName()or"unknown"))
-if Element.parking then
-self:_SetElementParkingFree(Element)
-end
-self:_UpdateStatus(Element,OPSGROUP.ElementStatus.TAKEOFF,airbase)
-self:__ElementAirborne(0.01,Element)
-end
-function FLIGHTGROUP:onafterElementAirborne(From,Event,To,Element)
-self:T2(self.lid..string.format("Element airborne %s",Element.name))
-self:_UpdateStatus(Element,OPSGROUP.ElementStatus.AIRBORNE)
-end
-function FLIGHTGROUP:onafterElementLanded(From,Event,To,Element,airbase)
-self:T2(self.lid..string.format("Element landed %s at %s airbase",Element.name,airbase and airbase:GetName()or"unknown"))
-self:_UpdateStatus(Element,OPSGROUP.ElementStatus.LANDED,airbase)
-if self.isHelo then
-local Spot=self:GetParkingSpot(Element,10,airbase)
-if Spot then
-self:_SetElementParkingAt(Element,Spot)
-self:_UpdateStatus(Element,OPSGROUP.ElementStatus.ARRIVED)
-end
-end
-if self.despawnAfterLanding then
-if self.legion then
-if airbase and self.legion.airbase and airbase.AirbaseName==self.legion.airbase.AirbaseName then
-if self:IsLanded()then
-self:ReturnToLegion()
-else
-self:DespawnElement(Element)
-end
-end
-else
-self:DespawnElement(Element)
-end
-end
-end
-function FLIGHTGROUP:onafterElementArrived(From,Event,To,Element,airbase,Parking)
-self:T(self.lid..string.format("Element arrived %s at %s airbase using parking spot %d",Element.name,airbase and airbase:GetName()or"unknown",Parking and Parking.TerminalID or-99))
-self:_SetElementParkingAt(Element,Parking)
-self:_UpdateStatus(Element,OPSGROUP.ElementStatus.ARRIVED)
-end
-function FLIGHTGROUP:onafterElementDestroyed(From,Event,To,Element)
-self:GetParent(self).onafterElementDestroyed(self,From,Event,To,Element)
-end
-function FLIGHTGROUP:onafterElementDead(From,Event,To,Element)
-if self.flightcontrol and Element.parking then
-self.flightcontrol:SetParkingFree(Element.parking)
-end
-self:GetParent(self).onafterElementDead(self,From,Event,To,Element)
-Element.parking=nil
-end
-function FLIGHTGROUP:onafterSpawned(From,Event,To)
-self:T(self.lid..string.format("Flight spawned"))
-if self.verbose>=1 then
-local text=string.format("Initialized Flight 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("Range max = %.1f km\n",self.rangemax/1000)
-text=text..string.format("Ceiling = %.1f feet\n",UTILS.MetersToFeet(self.ceiling))
-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("Tanker type = %s\n",tostring(self.tankertype))
-text=text..string.format("Refuel type = %s\n",tostring(self.refueltype))
-text=text..string.format("AI = %s\n",tostring(self.isAI))
-text=text..string.format("Has EPLRS = %s\n",tostring(self.isEPLRS))
-text=text..string.format("Helicopter = %s\n",tostring(self.isHelo))
-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/B=%d/M=%d)\n",self.ammo.Total,self.ammo.Guns,self.ammo.Rockets,self.ammo.Bombs,self.ammo.Missiles)
-text=text..string.format("FSM state = %s\n",self:GetState())
-text=text..string.format("Is alive = %s\n",tostring(self.group:IsAlive()))
-text=text..string.format("LateActivate = %s\n",tostring(self:IsLateActivated()))
-text=text..string.format("Uncontrolled = %s\n",tostring(self:IsUncontrolled()))
-text=text..string.format("Start Air = %s\n",tostring(self:IsTakeoffAir()))
-text=text..string.format("Start Cold = %s\n",tostring(self:IsTakeoffCold()))
-text=text..string.format("Start Hot = %s\n",tostring(self:IsTakeoffHot()))
-text=text..string.format("Start Rwy = %s\n",tostring(self:IsTakeoffRunway()))
-text=text..string.format("Elements:")
-for i,_element in pairs(self.elements)do
-local element=_element
-text=text..string.format("\n[%d] %s: callsign=%s, modex=%s, player=%s",i,element.name,tostring(element.callsign),tostring(element.modex),tostring(element.playerName))
-end
-self:I(self.lid..text)
-end
-self:_UpdatePosition()
-self.isDead=false
-self.isDestroyed=false
-if self.isAI then
-self:SwitchROE(self.option.ROE)
-self:SwitchROT(self.option.ROT)
-self:SwitchEPLRS(self.option.EPLRS)
-self:SwitchInvisible(self.option.Invisible)
-self:SwitchImmortal(self.option.Immortal)
-self:SwitchFormation(self.option.Formation)
-self:_SwitchTACAN()
-if self.radioDefault then
-self:SwitchRadio()
-else
-self:SetDefaultRadio(self.radio.Freq,self.radio.Modu,self.radio.On)
-end
-if self.callsignDefault then
-self:SwitchCallsign(self.callsignDefault.NumberSquad,self.callsignDefault.NumberGroup)
-else
-self:SetDefaultCallsign(self.callsign.NumberSquad,self.callsign.NumberGroup)
-end
-self:GetGroup():SetOption(AI.Option.Air.id.PROHIBIT_JETT,self.jettisonWeapons)
-self:GetGroup():SetOption(AI.Option.Air.id.PROHIBIT_AB,self.prohibitAB)
-self:GetGroup():SetOption(AI.Option.Air.id.RTB_ON_BINGO,false)
-self:GetGroup():SetOption(AI.Option.Air.id.JETT_TANKS_IF_EMPTY,self.jettisonEmptyTanks)
-self:__UpdateRoute(-0.5)
-else
-if self.currbase then
-local flightcontrol=_DATABASE:GetFlightControl(self.currbase:GetName())
-if flightcontrol then
-self:SetFlightControl(flightcontrol)
-else
-self:_UpdateMenu(0.5)
-end
-else
-self:_UpdateMenu(0.5)
-end
-end
-end
-function FLIGHTGROUP:onafterParking(From,Event,To)
-local airbase=self:GetClosestAirbase()
-local airbasename=airbase:GetName()or"unknown"
-self:T(self.lid..string.format("Flight is parking at airbase %s",airbasename))
-self.currbase=airbase
-if not self.homebase then
-self.homebase=airbase
-end
-self.Tparking=timer.getAbsTime()
-local flightcontrol=_DATABASE:GetFlightControl(airbasename)
-if flightcontrol then
-self:SetFlightControl(flightcontrol)
-if self.flightcontrol then
-self.flightcontrol:SetFlightStatus(self,FLIGHTCONTROL.FlightStatus.PARKING)
-end
-else
-self:T3(self.lid.."INFO: No flight control in onAfterParking!")
-end
-end
-function FLIGHTGROUP:onafterTaxiing(From,Event,To)
-self:T(self.lid..string.format("Flight is taxiing"))
-self.Tparking=nil
-if self.flightcontrol and self.flightcontrol:IsControlling(self)then
-if self.isAI then
-self.flightcontrol:SetFlightStatus(self,FLIGHTCONTROL.FlightStatus.TAKEOFF)
-else
-self.flightcontrol:SetFlightStatus(self,FLIGHTCONTROL.FlightStatus.TAXIOUT)
-end
-end
-end
-function FLIGHTGROUP:onafterTakeoff(From,Event,To,airbase)
-self:T(self.lid..string.format("Flight takeoff from %s",airbase and airbase:GetName()or"unknown airbase"))
-if self.flightcontrol and airbase and self.flightcontrol.airbasename==airbase:GetName()then
-self.flightcontrol:_RemoveFlight(self)
-self.flightcontrol=nil
-end
-end
-function FLIGHTGROUP:onafterAirborne(From,Event,To)
-self:T(self.lid..string.format("Flight airborne"))
-self.currbase=nil
-self:__Cruise(-0.01)
-end
-function FLIGHTGROUP:onafterCruise(From,Event,To)
-self:T(self.lid..string.format("Flight cruising"))
-self.Twaiting=nil
-self.dTwait=nil
-if self.isAI then
-self:_CheckGroupDone(nil,120)
-else
-end
-end
-function FLIGHTGROUP:onafterLanding(From,Event,To)
-self:T(self.lid..string.format("Flight is landing"))
-self:_SetElementStatusAll(OPSGROUP.ElementStatus.LANDING)
-if self.flightcontrol and self.flightcontrol:IsControlling(self)then
-self.flightcontrol:SetFlightStatus(self,FLIGHTCONTROL.FlightStatus.LANDING)
-end
-self.Tholding=nil
-if self.stack then
-self.stack.flightgroup=nil
-self.stack=nil
-end
-end
-function FLIGHTGROUP:onafterLanded(From,Event,To,airbase)
-self:T(self.lid..string.format("Flight landed at %s",airbase and airbase:GetName()or"unknown place"))
-if self.flightcontrol and self.flightcontrol:IsControlling(self)then
-self.flightcontrol:SetFlightStatus(self,FLIGHTCONTROL.FlightStatus.TAXIINB)
-end
-end
-function FLIGHTGROUP:onafterLandedAt(From,Event,To)
-self:T(self.lid..string.format("Flight landed at"))
-if self:IsPickingup()then
-self:__Loading(-1)
-elseif self:IsTransporting()then
-self:__Unloading(-1)
-end
-end
-function FLIGHTGROUP:onafterArrived(From,Event,To)
-self:T(self.lid..string.format("Flight arrived"))
-if self.flightcontrol then
-self.flightcontrol:SetFlightStatus(self,FLIGHTCONTROL.FlightStatus.ARRIVED)
-end
-if not self.isAI then
-return
-end
-local airwing=self:GetAirwing()
-if airwing and not(self:IsPickingup()or self:IsTransporting())then
-self:T(self.lid..string.format("Airwing asset group %s arrived ==> Adding asset back to stock of airwing %s",self.groupname,airwing.alias))
-self:ReturnToLegion(1)
-elseif self.isLandingAtAirbase then
-local Template=UTILS.DeepCopy(self.template)
-self.isLateActivated=false
-Template.lateActivation=self.isLateActivated
-self.isUncontrolled=true
-Template.uncontrolled=self.isUncontrolled
-local SpawnPoint=Template.route.points[1]
-SpawnPoint.linkUnit=nil
-SpawnPoint.helipadId=nil
-SpawnPoint.airdromeId=nil
-local airbase=self.isLandingAtAirbase
-local AirbaseID=airbase:GetID()
-if airbase:IsShip()then
-SpawnPoint.linkUnit=AirbaseID
-SpawnPoint.helipadId=AirbaseID
-elseif airbase:IsHelipad()then
-SpawnPoint.linkUnit=AirbaseID
-SpawnPoint.helipadId=AirbaseID
-elseif airbase:IsAirdrome()then
-SpawnPoint.airdromeId=AirbaseID
-end
-SpawnPoint.alt=0
-SpawnPoint.type=COORDINATE.WaypointType.TakeOffParking
-SpawnPoint.action=COORDINATE.WaypointAction.FromParkingArea
-local units=Template.units
-for i=#units,1,-1 do
-local unit=units[i]
-local element=self:GetElementByName(unit.name)
-if element and element.status~=OPSGROUP.ElementStatus.DEAD then
-unit.parking=element.parking and element.parking.TerminalID or nil
-unit.parking_id=nil
-local vec3=element.unit:GetVec3()
-local heading=element.unit:GetHeading()
-unit.x=vec3.x
-unit.y=vec3.z
-unit.alt=vec3.y
-unit.heading=math.rad(heading)
-unit.psi=-unit.heading
-else
-table.remove(units,i)
-end
-end
-self:_Respawn(0,Template)
-self.isLandingAtAirbase=nil
-if self:IsPickingup()then
-self:__Loading(-1)
-elseif self:IsTransporting()then
-self:__Unloading(-1)
-end
-else
-self:T(self.lid..string.format("Despawning group in 5 minutes after arrival!"))
-self:Despawn(5*60)
-end
-end
-function FLIGHTGROUP:onafterDead(From,Event,To)
-if self.flightcontrol then
-self.flightcontrol:_RemoveFlight(self)
-self.flightcontrol=nil
-end
-self:GetParent(self).onafterDead(self,From,Event,To)
-end
-function FLIGHTGROUP:onbeforeUpdateRoute(From,Event,To,n,N)
-local allowed=true
-local trepeat=nil
-if self:IsAlive()then
-self:T3(self.lid.."Update route possible. Group is ALIVE")
-elseif self:IsDead()then
-self:T(self.lid.."Update route denied. Group is DEAD!")
-allowed=false
-elseif self:IsInUtero()then
-self:T(self.lid.."Update route denied. Group is INUTERO!")
-allowed=false
-else
-self:T(self.lid.."Update route denied ==> checking back in 5 sec")
-trepeat=-5
-allowed=false
-end
-if allowed and self:IsUncontrolled()then
-self:T(self.lid.."Update route denied. Group is UNCONTROLLED!")
-local mission=self:GetMissionCurrent()
-if mission and mission.type==AUFTRAG.Type.ALERT5 then
-trepeat=nil
-else
-trepeat=-5
-end
-allowed=false
-end
-if n and n<1 then
-self:T(self.lid.."Update route denied because waypoint n<1!")
-allowed=false
-end
-if not self.currentwp then
-self:T(self.lid.."Update route denied because self.currentwp=nil!")
-allowed=false
-end
-local Nn=n or self.currentwp+1
-if not Nn or Nn<1 then
-self:T(self.lid.."Update route denied because N=nil or N<1")
-trepeat=-5
-allowed=false
-end
-if self.taskcurrent>0 then
-local task=self:GetTaskByID(self.taskcurrent)
-if task then
-if task.dcstask.id==AUFTRAG.SpecialTask.PATROLZONE then
-self:T2(self.lid.."Allowing update route for Task: PatrolZone")
-elseif task.dcstask.id==AUFTRAG.SpecialTask.CAPTUREZONE then
-self:T2(self.lid.."Allowing update route for Task: CaptureZone")
-elseif task.dcstask.id==AUFTRAG.SpecialTask.RECON then
-self:T2(self.lid.."Allowing update route for Task: ReconMission")
-elseif task.dcstask.id==AUFTRAG.SpecialTask.PATROLRACETRACK then
-self:T2(self.lid.."Allowing update route for Task: Patrol Race Track")
-elseif task.dcstask.id==AUFTRAG.SpecialTask.HOVER then
-self:T2(self.lid.."Allowing update route for Task: Hover")
-elseif task.dcstask.id==AUFTRAG.SpecialTask.RELOCATECOHORT then
-self:T2(self.lid.."Allowing update route for Task: Relocate Cohort")
-elseif task.description and task.description=="Task_Land_At"then
-self:T2(self.lid.."Allowing update route for Task: Task_Land_At")
-else
-local taskname=task and task.description or"No description"
-self:T(self.lid..string.format("WARNING: Update route denied because taskcurrent=%d>0! Task description = %s",self.taskcurrent,tostring(taskname)))
-allowed=false
-end
-else
-self:T(self.lid..string.format("WARNING: before update route taskcurrent=%d (>0!) but no task?!",self.taskcurrent))
-allowed=false
-end
-end
-if not self.isAI then
-allowed=false
-end
-self:T2(self.lid..string.format("Onbefore Updateroute in state %s: allowed=%s (repeat in %s)",self:GetState(),tostring(allowed),tostring(trepeat)))
-if trepeat then
-self:__UpdateRoute(trepeat,n)
-end
-return allowed
-end
-function FLIGHTGROUP:onafterUpdateRoute(From,Event,To,n,N)
-n=n or self.currentwp+1
-N=N or#self.waypoints
-N=math.min(N,#self.waypoints)
-local wp={}
-local speed=self.group and self.group:GetVelocityKMH()or 100
-local waypointType=COORDINATE.WaypointType.TurningPoint
-local waypointAction=COORDINATE.WaypointAction.TurningPoint
-if self:IsLanded()or self:IsLandedAt()or self:IsAirborne()==false then
-waypointType=COORDINATE.WaypointType.TakeOff
-end
-local current=self:GetCoordinate():WaypointAir(COORDINATE.WaypointAltType.BARO,waypointType,waypointAction,speed,true,nil,{},"Current")
-table.insert(wp,current)
-for i=n,N do
-table.insert(wp,self.waypoints[i])
-end
-if wp[2]then
-self.speedWp=wp[2].speed
-end
-local hb=self.homebase and self.homebase:GetName()or"unknown"
-local db=self.destbase and self.destbase:GetName()or"unknown"
-self:T(self.lid..string.format("Updating route for WP #%d-%d [%s], homebase=%s destination=%s",n,#wp,self:GetState(),hb,db))
-if#wp>1 then
-self:Route(wp)
-else
-if self:IsAirborne()then
-self:T(self.lid.."No waypoints left ==> CheckGroupDone")
-self:_CheckGroupDone()
-end
-end
-end
-function FLIGHTGROUP:onafterOutOfMissilesAA(From,Event,To)
-self:T(self.lid.."Group is out of AA Missiles!")
-if self.outofAAMrtb then
-local airbase=self.destbase or self.homebase
-self:__RTB(-5,airbase)
-end
-end
-function FLIGHTGROUP:onafterOutOfMissilesAG(From,Event,To)
-self:T(self.lid.."Group is out of AG Missiles!")
-if self.outofAGMrtb then
-local airbase=self.destbase or self.homebase
-self:__RTB(-5,airbase)
-end
-end
-function FLIGHTGROUP:_CheckGroupDone(delay,waittime)
-local fsmstate=self:GetState()
-if self:IsAlive()and self.isAI then
-if delay and delay>0 then
-self:T(self.lid..string.format("Check FLIGHTGROUP [state=%s] done in %.3f seconds... (t=%.4f)",fsmstate,delay,timer.getTime()))
-self:ScheduleOnce(delay,FLIGHTGROUP._CheckGroupDone,self)
-else
-self:T(self.lid..string.format("Check FLIGHTGROUP [state=%s] done? (t=%.4f)",fsmstate,timer.getTime()))
-if self:IsEngaging()then
-self:T(self.lid.."Engaging! Group NOT done...")
-return
-end
-local nTasks=self:CountRemainingTasks()
-local nMissions=self:CountRemainingMissison()
-local nTransports=self:CountRemainingTransports()
-local nPaused=self:_CountPausedMissions()
-if nPaused>0 and nPaused==nMissions then
-local missionpaused=self:_GetPausedMission()
-self:T(self.lid..string.format("Found paused mission %s [%s]. Unpausing mission...",missionpaused.name,missionpaused.type))
-self:UnpauseMission()
-return
-end
-if self.isLandingAtAirbase then
-self:T(self.lid..string.format("Landing at airbase %s! Group NOT done...",self.isLandingAtAirbase:GetName()))
-return
-end
-if self:IsWaiting()then
-self:T(self.lid.."Waiting! Group NOT done...")
-return
-end
-self:T(self.lid..string.format("Remaining (final=%s): missions=%d, tasks=%d, transports=%d",tostring(self.passedfinalwp),nMissions,nTasks,nTransports))
-if self:HasPassedFinalWaypoint()or self:GetWaypointIndexNext()==1 then
-if self.currentmission==nil and self.taskcurrent==0 and(self.cargoTransport==nil or self.cargoTransport:GetCarrierTransportStatus(self)==OPSTRANSPORT.Status.DELIVERED)then
-if nTasks==0 and nMissions==0 and nTransports==0 then
-local destbase=self.destbase or self.homebase
-local destzone=self.destzone or self.homezone
-if waittime then
-self:T(self.lid..string.format("Passed Final WP and No current and/or future missions/tasks/transports. Waittime given ==> Waiting for %d sec!",waittime))
-self:Wait(waittime)
-elseif destbase then
-if self.currbase and self.currbase.AirbaseName==destbase.AirbaseName and self:IsParking()then
-self:T(self.lid.."Passed Final WP and No current and/or future missions/tasks/transports AND parking at destination airbase ==> Arrived!")
-self:Arrived()
-else
-self:T(self.lid.."Passed Final WP and No current and/or future missions/tasks/transports ==> RTB!")
-self:__RTB(-0.1,destbase)
-end
-elseif destzone then
-self:T(self.lid.."Passed Final WP and No current and/or future missions/tasks/transports ==> RTZ!")
-self:__RTZ(-0.1,destzone)
-else
-self:T(self.lid.."Passed Final WP and NO Tasks/Missions left. No DestBase or DestZone ==> Wait!")
-self:__Wait(-1)
-end
-else
-if not self:IsParking()then
-self:T(self.lid..string.format("Passed Final WP but Tasks=%d or Missions=%d left in the queue. Wait!",nTasks,nMissions))
-self:__Wait(-1)
-end
-end
-else
-self:T(self.lid..string.format("Passed Final WP but still have current Task (#%s) or Mission (#%s) left to do",tostring(self.taskcurrent),tostring(self.currentmission)))
-end
-else
-self:T(self.lid..string.format("Flight (status=%s) did NOT pass the final waypoint yet ==> update route in -0.01 sec",self:GetState()))
-self:__UpdateRoute(-0.01)
-end
-end
-end
-end
-function FLIGHTGROUP:onbeforeRTB(From,Event,To,airbase,SpeedTo,SpeedHold)
-self:T(self.lid..string.format("RTB: before event=%s: %s --> %s to %s",Event,From,To,airbase and airbase:GetName()or"None"))
-if self:IsAlive()then
-local allowed=true
-local Tsuspend=nil
-if airbase==nil then
-self:T(self.lid.."ERROR: Airbase is nil in RTB() call!")
-allowed=false
-end
-if airbase and airbase:GetCoalition()~=self.group:GetCoalition()and airbase:GetCoalition()>0 then
-self:T(self.lid..string.format("ERROR: Wrong airbase coalition %d in RTB() call! We allow only same as group %d or neutral airbases 0",airbase:GetCoalition(),self.group:GetCoalition()))
-return false
-end
-if self.currbase and self.currbase:GetName()==airbase:GetName()then
-self:T(self.lid.."WARNING: Currbase is already same as RTB airbase. RTB canceled!")
-return false
-end
-if self:IsLanded()then
-self:T(self.lid.."WARNING: Flight has already landed. RTB canceled!")
-return false
-end
-if not self.group:IsAirborne(true)then
-self:T(self.lid..string.format("WARNING: Group [%s] is not AIRBORNE ==> RTB event is suspended for 20 sec",self:GetState()))
-allowed=false
-Tsuspend=-20
-local groupspeed=self.group:GetVelocityMPS()
-if groupspeed<=1 and not self:IsParking()then
-self.RTBRecallCount=self.RTBRecallCount+1
-end
-if self.RTBRecallCount>6 then
-self:T(self.lid..string.format("WARNING: Group [%s] is not moving and was called RTB %d times. Assuming a problem and despawning!",self:GetState(),self.RTBRecallCount))
-self.RTBRecallCount=0
-self:Despawn(5)
-return
-end
-end
-if self:IsFuelGood()then
-local Ntot,Nsched,Nwp=self:CountRemainingTasks()
-if self.taskcurrent>0 then
-self:T(self.lid..string.format("WARNING: Got current task ==> RTB event is suspended for 10 sec"))
-Tsuspend=-10
-allowed=false
-end
-if Nsched>0 then
-self:T(self.lid..string.format("WARNING: Still got %d SCHEDULED tasks in the queue ==> RTB event is suspended for 10 sec",Nsched))
-Tsuspend=-10
-allowed=false
-end
-if Nwp>0 then
-self:T(self.lid..string.format("WARNING: Still got %d WAYPOINT tasks in the queue ==> RTB event is suspended for 10 sec",Nwp))
-Tsuspend=-10
-allowed=false
-end
-if self.Twaiting and self.dTwait then
-self:T(self.lid..string.format("WARNING: Group is Waiting for a specific duration ==> RTB event is canceled",Nwp))
-allowed=false
-end
-end
-if Tsuspend and not allowed then
-self:__RTB(Tsuspend,airbase,SpeedTo,SpeedHold)
-end
-return allowed
-else
-self:T(self.lid.."WARNING: Group is not alive! RTB call not allowed.")
-return false
-end
-end
-function FLIGHTGROUP:onafterRTB(From,Event,To,airbase,SpeedTo,SpeedHold,SpeedLand)
-self:T(self.lid..string.format("RTB: event=%s: %s --> %s to %s",Event,From,To,airbase:GetName()))
-self.destbase=airbase
-self:CancelAllMissions()
-self:_LandAtAirbase(airbase,SpeedTo,SpeedHold,SpeedLand)
-end
-function FLIGHTGROUP:onbeforeLandAtAirbase(From,Event,To,airbase)
-if self:IsAlive()then
-local allowed=true
-local Tsuspend=nil
-if airbase==nil then
-self:T(self.lid.."ERROR: Airbase is nil in LandAtAirase() call!")
-allowed=false
-end
-if airbase and airbase:GetCoalition()~=self.group:GetCoalition()and airbase:GetCoalition()>0 then
-self:T(self.lid..string.format("ERROR: Wrong airbase coalition %d in LandAtAirbase() call! We allow only same as group %d or neutral airbases 0",airbase:GetCoalition(),self.group:GetCoalition()))
-return false
-end
-if self.currbase and self.currbase:GetName()==airbase:GetName()then
-self:T(self.lid.."WARNING: Currbase is already same as LandAtAirbase airbase. LandAtAirbase canceled!")
-return false
-end
-if self:IsLanded()then
-self:T(self.lid.."WARNING: Flight has already landed. LandAtAirbase canceled!")
-return false
-end
-if self:IsParking()then
-allowed=false
-Tsuspend=-30
-self:T(self.lid.."WARNING: Flight is parking. LandAtAirbase call delayed by 30 sec")
-elseif self:IsTaxiing()then
-allowed=false
-Tsuspend=-1
-self:T(self.lid.."WARNING: Flight is parking. LandAtAirbase call delayed by 1 sec")
-end
-if Tsuspend and not allowed then
-self:__LandAtAirbase(Tsuspend,airbase)
-end
-return allowed
-else
-self:T(self.lid.."WARNING: Group is not alive! LandAtAirbase call not allowed")
-return false
-end
-end
-function FLIGHTGROUP:onafterLandAtAirbase(From,Event,To,airbase)
-self.isLandingAtAirbase=airbase
-self:_LandAtAirbase(airbase)
-end
-function FLIGHTGROUP:_LandAtAirbase(airbase,SpeedTo,SpeedHold,SpeedLand)
-self.currbase=airbase
-self:_PassedFinalWaypoint(true,"_LandAtAirbase")
-self.Twaiting=nil
-self.dTwait=nil
-SpeedTo=SpeedTo or UTILS.KmphToKnots(self.speedCruise)
-SpeedHold=SpeedHold or(self.isHelo and 80 or 250)
-SpeedLand=SpeedLand or(self.isHelo and 40 or 170)
-self.Tholding=nil
-local text=string.format("Flight group set to hold at airbase %s. SpeedTo=%d, SpeedHold=%d, SpeedLand=%d",airbase:GetName(),SpeedTo,SpeedHold,SpeedLand)
-self:T(self.lid..text)
-local althold=self.isHelo and 1000+math.random(10)*100 or math.random(4,10)*1000
-local c0=self:GetCoordinate()
-local p0=airbase:GetZone():GetRandomCoordinate():SetAltitude(UTILS.FeetToMeters(althold))
-local p1=nil
-local wpap=nil
-local fc=_DATABASE:GetFlightControl(airbase:GetName())
-if fc and self.isAI then
-local stack=fc:_GetHoldingStack(self)
-if stack then
-stack.flightgroup=self
-self.stack=stack
-p0=stack.pos0
-p1=stack.pos1
-if false then
-p0:MarkToAll(string.format("%s: Holding stack P0, alt=%d meters",self:GetName(),p0.y))
-p1:MarkToAll(string.format("%s: Holding stack P1, alt=%d meters",self:GetName(),p0.y))
-end
-else
-end
-self:SetFlightControl(fc)
-self.flightcontrol:SetFlightStatus(self,FLIGHTCONTROL.FlightStatus.INBOUND)
-local callsign=self:GetCallsignName()
-local text=string.format("%s, %s, inbound for landing",fc.alias,callsign)
-fc:TransmissionPilot(text,self)
-local text=string.format("%s, %s, roger, hold at angels %d. Report entering the pattern.",callsign,fc.alias,stack.angels)
-fc:TransmissionTower(text,self,10)
-end
-local c1=c0:GetIntermediateCoordinate(p0,0.25):SetAltitude(self.altitudeCruise,true)
-local c2=c0:GetIntermediateCoordinate(p0,0.75):SetAltitude(self.altitudeCruise,true)
-local x1=self.isHelo and UTILS.NMToMeters(2.0)or UTILS.NMToMeters(10)
-local x2=self.isHelo and UTILS.NMToMeters(1.0)or UTILS.NMToMeters(5)
-local alpha=math.rad(3)
-local h1=x1*math.tan(alpha)
-local h2=x2*math.tan(alpha)
-local runway=airbase:GetActiveRunwayLanding()
-self.flaghold:Set(0)
-local holdtime=2*60
-if fc or self.airboss then
-holdtime=nil
-end
-local TaskArrived=self.group:TaskFunction("FLIGHTGROUP._ReachedHolding",self)
-local TaskOrbit=self.group:TaskOrbit(p0,nil,UTILS.KnotsToMps(SpeedHold),p1)
-local TaskLand=self.group:TaskCondition(nil,self.flaghold.UserFlagName,1,nil,holdtime)
-local TaskHold=self.group:TaskControlled(TaskOrbit,TaskLand)
-local TaskKlar=self.group:TaskFunction("FLIGHTGROUP._ClearedToLand",self)
-local wp={}
-wp[#wp+1]=c1:WaypointAir("BARO",COORDINATE.WaypointType.TurningPoint,COORDINATE.WaypointAction.TurningPoint,UTILS.KnotsToKmph(SpeedTo),true,nil,{},"Climb")
-wp[#wp+1]=c2:WaypointAir("BARO",COORDINATE.WaypointType.TurningPoint,COORDINATE.WaypointAction.TurningPoint,UTILS.KnotsToKmph(SpeedTo),true,nil,{},"Descent")
-wp[#wp+1]=p0:WaypointAir("BARO",COORDINATE.WaypointType.TurningPoint,COORDINATE.WaypointAction.TurningPoint,UTILS.KnotsToKmph(SpeedTo),true,nil,{TaskArrived,TaskHold,TaskKlar},"Holding Point")
-if airbase:IsAirdrome()then
-local TaskFinal=self.group:TaskFunction("FLIGHTGROUP._OnFinal",self)
-local papp=airbase:GetCoordinate():Translate(x1,runway.heading-180):SetAltitude(h1)
-wp[#wp+1]=papp:WaypointAirTurningPoint("BARO",UTILS.KnotsToKmph(SpeedLand),{TaskFinal},"Final Approach")
-local pland=airbase:GetCoordinate():Translate(x2,runway.heading-180):SetAltitude(h2)
-wp[#wp+1]=pland:WaypointAirLanding(UTILS.KnotsToKmph(SpeedLand),airbase,{},"Landing")
-elseif airbase:IsShip()or airbase:IsHelipad()then
-local pland=airbase:GetCoordinate()
-wp[#wp+1]=pland:WaypointAirLanding(UTILS.KnotsToKmph(SpeedLand),airbase,{},"Landing")
-end
-if self.isAI then
-self:Route(wp,1.0)
-end
-end
-function FLIGHTGROUP:onbeforeWait(From,Event,To,Duration,Altitude,Speed)
-local allowed=true
-local Tsuspend=nil
-if self.taskcurrent>0 and not self:IsLandedAt()then
-self:T(self.lid..string.format("WARNING: Got current task ==> WAIT event is suspended for 30 sec!"))
-Tsuspend=-30
-allowed=false
-end
-if self.cargoTransport and not self:IsLandedAt()then
-end
-if Tsuspend and not allowed then
-self:__Wait(Tsuspend,Duration,Altitude,Speed)
-end
-return allowed
-end
-function FLIGHTGROUP:onafterWait(From,Event,To,Duration,Altitude,Speed)
-local Coord=self:GetCoordinate()
-if Altitude then
-Altitude=UTILS.FeetToMeters(Altitude)
-else
-Altitude=self.altitudeCruise
-end
-Speed=Speed or(self.isHelo and 20 or 250)
-local text=string.format("Group set to wait/orbit at altitude %d m and speed %.1f km/h for %s seconds",Altitude,Speed,tostring(Duration))
-self:T(self.lid..text)
-self.flaghold:Set(0)
-local TaskOrbit=self.group:TaskOrbit(Coord,Altitude,UTILS.KnotsToMps(Speed))
-local TaskStop=self.group:TaskCondition(nil,self.flaghold.UserFlagName,1,nil,Duration)
-local TaskCntr=self.group:TaskControlled(TaskOrbit,TaskStop)
-local TaskOver=self.group:TaskFunction("FLIGHTGROUP._FinishedWaiting",self)
-local DCSTasks
-if Duration or true then
-DCSTasks=self.group:TaskCombo({TaskCntr,TaskOver})
-else
-DCSTasks=self.group:TaskCombo({TaskOrbit,TaskOver})
-end
-self:PushTask(DCSTasks)
-self.Twaiting=timer.getAbsTime()
-self.dTwait=Duration
-end
-function FLIGHTGROUP:onafterRefuel(From,Event,To,Coordinate)
-local text=string.format("Flight group set to refuel at the nearest tanker")
-self:T(self.lid..text)
-self:PauseMission()
-local TaskRefuel=self.group:TaskRefueling()
-local TaskFunction=self.group:TaskFunction("FLIGHTGROUP._FinishedRefuelling",self)
-local DCSTasks={TaskRefuel,TaskFunction}
-local Speed=self.speedCruise
-local coordinate=self:GetCoordinate()
-Coordinate=Coordinate or coordinate:Translate(UTILS.NMToMeters(5),self.group:GetHeading(),true)
-local wp0=coordinate:WaypointAir("BARO",COORDINATE.WaypointType.TurningPoint,COORDINATE.WaypointAction.TurningPoint,Speed,true)
-local wp9=Coordinate:WaypointAir("BARO",COORDINATE.WaypointType.TurningPoint,COORDINATE.WaypointAction.TurningPoint,Speed,true,nil,DCSTasks,"Refuel")
-self:Route({wp0,wp9},1)
-end
-function FLIGHTGROUP:onafterRefueled(From,Event,To)
-local text=string.format("Flight group finished refuelling")
-self:T(self.lid..text)
-self:_CheckGroupDone(1)
-end
-function FLIGHTGROUP:onafterHolding(From,Event,To)
-self.flaghold:Set(0)
-if self.despawnAfterHolding then
-if self.legion then
-self:ReturnToLegion(1)
-else
-self:Despawn(1)
-end
-return
-end
-self.Tholding=timer.getAbsTime()
-local text=string.format("Flight group %s is HOLDING now",self.groupname)
-self:T(self.lid..text)
-if self.flightcontrol then
-self.flightcontrol:SetFlightStatus(self,FLIGHTCONTROL.FlightStatus.HOLDING)
-if self.isAI then
-local callsign=self:GetCallsignName()
-local text=string.format("%s, %s, arrived at holding pattern",self.flightcontrol.alias,callsign)
-if self.stack then
-text=text..string.format(", angels %d.",self.stack.angels)
-end
-self.flightcontrol:TransmissionPilot(text,self)
-local text=string.format("%s, roger, fly heading %d and wait for landing clearance",callsign,self.stack.heading)
-self.flightcontrol:TransmissionTower(text,self,10)
-end
-elseif self.airboss then
-if self.isHelo then
-local carrierpos=self.airboss:GetCoordinate()
-local carrierheading=self.airboss:GetHeading()
-local Distance=UTILS.NMToMeters(5)
-local Angle=carrierheading+90
-local altitude=math.random(12,25)*100
-local oc=carrierpos:Translate(Distance,Angle):SetAltitude(altitude,true)
-local TaskOrbit=self.group:TaskOrbit(oc,nil,UTILS.KnotsToMps(50))
-local TaskLand=self.group:TaskCondition(nil,self.flaghold.UserFlagName,1)
-local TaskHold=self.group:TaskControlled(TaskOrbit,TaskLand)
-local TaskKlar=self.group:TaskFunction("FLIGHTGROUP._ClearedToLand",self)
-local DCSTask=self.group:TaskCombo({TaskOrbit,TaskHold,TaskKlar})
-self:SetTask(DCSTask)
-end
-end
-end
-function FLIGHTGROUP:onafterEngageTarget(From,Event,To,Target)
-local DCStask=nil
-if Target:IsInstanceOf("UNIT")or Target:IsInstanceOf("STATIC")then
-DCStask=self:GetGroup():TaskAttackUnit(Target,true)
-elseif Target:IsInstanceOf("GROUP")then
-DCStask=self:GetGroup():TaskAttackGroup(Target,nil,nil,nil,nil,nil,nil,true)
-elseif Target:IsInstanceOf("SET_UNIT")then
-local DCSTasks={}
-for _,_unit in pairs(Target:GetSet())do
-local unit=_unit
-local task=self:GetGroup():TaskAttackUnit(unit,true)
-table.insert(DCSTasks)
-end
-DCStask=self:GetGroup():TaskCombo(DCSTasks)
-elseif Target:IsInstanceOf("SET_GROUP")then
-local DCSTasks={}
-for _,_unit in pairs(Target:GetSet())do
-local unit=_unit
-local task=self:GetGroup():TaskAttackGroup(Target,nil,nil,nil,nil,nil,nil,true)
-table.insert(DCSTasks)
-end
-DCStask=self:GetGroup():TaskCombo(DCSTasks)
-else
-self:T("ERROR: unknown Target in EngageTarget! Needs to be a UNIT, STATIC, GROUP, SET_UNIT or SET_GROUP")
-return
-end
-local Task=self:NewTaskScheduled(DCStask,1,"Engage_Target",0)
-Task.backupROE=self:GetROE()
-self:SwitchROE(ENUMS.ROE.OpenFire)
-local mission=self:GetMissionCurrent()
-if mission then
-self:PauseMission()
-end
-self:TaskExecute(Task)
-end
-function FLIGHTGROUP:onafterDisengage(From,Event,To)
-self:T(self.lid.."Disengage target")
-end
-function FLIGHTGROUP:onbeforeLandAt(From,Event,To,Coordinate,Duration)
-return self.isHelo
-end
-function FLIGHTGROUP:onafterLandAt(From,Event,To,Coordinate,Duration)
-self:T(self.lid..string.format("Landing at Coordinate for %s seconds",tostring(Duration)))
-Coordinate=Coordinate or self:GetCoordinate()
-local DCStask=self.group:TaskLandAtVec2(Coordinate:GetVec2(),Duration)
-local Task=self:NewTaskScheduled(DCStask,1,"Task_Land_At",0)
-self:TaskExecute(Task)
-end
-function FLIGHTGROUP:onafterFuelLow(From,Event,To)
-local fuel=self:GetFuelMin()or 0
-local text=string.format("Low fuel %d for flight group %s",fuel,self.groupname)
-self:T(self.lid..text)
-self.fuellow=true
-local airbase=self.destbase or self.homebase
-if self.fuellowrefuel and self.refueltype then
-local tanker=self:FindNearestTanker(50)
-if tanker then
-self:T(self.lid..string.format("Send to refuel at tanker %s",tanker:GetName()))
-local coordinate=self:GetCoordinate():GetIntermediateCoordinate(tanker:GetCoordinate(),0.75)
-self:Refuel(coordinate)
-return
-end
-end
-if airbase and self.fuellowrtb then
-self:RTB(airbase)
-end
-end
-function FLIGHTGROUP:onafterFuelCritical(From,Event,To)
-local text=string.format("Critical fuel for flight group %s",self.groupname)
-self:T(self.lid..text)
-self.fuelcritical=true
-local airbase=self.destbase or self.homebase
-if airbase and self.fuelcriticalrtb and not self:IsGoing4Fuel()then
-self:RTB(airbase)
-end
-end
-function FLIGHTGROUP._ReachedHolding(group,flightgroup)
-flightgroup:T2(flightgroup.lid..string.format("Group reached holding point"))
-flightgroup:__Holding(-1)
-end
-function FLIGHTGROUP._ClearedToLand(group,flightgroup)
-flightgroup:T2(flightgroup.lid..string.format("Group was cleared to land"))
-flightgroup:__Landing(-1)
-end
-function FLIGHTGROUP._OnFinal(group,flightgroup)
-flightgroup:T2(flightgroup.lid..string.format("Group on final approach"))
-local fc=flightgroup.flightcontrol
-if fc and fc:IsControlling(flightgroup)then
-fc:_FlightOnFinal(flightgroup)
-end
-end
-function FLIGHTGROUP._FinishedRefuelling(group,flightgroup)
-flightgroup:T2(flightgroup.lid..string.format("Group finished refueling"))
-flightgroup:__Refueled(-1)
-end
-function FLIGHTGROUP._FinishedWaiting(group,flightgroup)
-flightgroup:T(flightgroup.lid..string.format("Group finished waiting"))
-flightgroup.Twaiting=nil
-flightgroup.dTwait=nil
-flightgroup:_CheckGroupDone(0.1)
-end
-function FLIGHTGROUP:_InitGroup(Template)
-if self.groupinitialized then
-self:T(self.lid.."WARNING: Group was already initialized! Will NOT do it again!")
-return
-end
-local group=self.group
-local template=Template or self:_GetTemplate()
-self.isHelo=group:IsHelicopter()
-self.isUncontrolled=template.uncontrolled
-self.isLateActivated=template.lateActivation
-self.speedMax=group:GetSpeedMax()
-if self.speedMax>3.6 then
-self.isMobile=true
-else
-self.isMobile=false
-end
-local speedCruiseLimit=self.isHelo and UTILS.KnotsToKmph(110)or UTILS.KnotsToKmph(380)
-self.speedCruise=math.min(self.speedMax*0.7,speedCruiseLimit)
-self.ammo=self:GetAmmoTot()
-self.radio.Freq=tonumber(template.frequency)
-self.radio.Modu=tonumber(template.modulation)
-self.radio.On=template.communication
-local callsign=template.units[1].callsign
-if type(callsign)=="number"then
-local cs=tostring(callsign)
-callsign={}
-callsign[1]=cs:sub(1,1)
-callsign[2]=cs:sub(2,2)
-callsign[3]=cs:sub(3,3)
-end
-self.callsign.NumberSquad=tonumber(callsign[1])
-self.callsign.NumberGroup=tonumber(callsign[2])
-self.callsign.NameSquad=UTILS.GetCallsignName(self.callsign.NumberSquad)
-if self.isHelo then
-self.optionDefault.Formation=ENUMS.Formation.RotaryWing.EchelonLeft.D300
-else
-self.optionDefault.Formation=ENUMS.Formation.FixedWing.EchelonLeft.Group
-end
-self:SetDefaultTACAN(nil,nil,nil,nil,true)
-self.tacan=UTILS.DeepCopy(self.tacanDefault)
-self.isAI=not self:_IsHuman(group)
-if not self.isAI then
-self.menu=self.menu or{}
-self.menu.atc=self.menu.atc or{}
-self.menu.atc.root=self.menu.atc.root or MENU_GROUP:New(self.group,"ATC")
-self.menu.atc.help=self.menu.atc.help or MENU_GROUP:New(self.group,"Help",self.menu.atc.root)
-end
-local units=self.group:GetUnits()
-local dcsgroup=Group.getByName(self.groupname)
-local size0=dcsgroup:getInitialSize()
-if#units~=size0 then
-self:T(self.lid..string.format("ERROR: Got #units=%d but group consists of %d units!",#units,size0))
-end
-for _,unit in pairs(units)do
-self:_AddElementByName(unit:GetName())
-end
-self.groupinitialized=true
-return self
-end
-function FLIGHTGROUP:GetHomebaseFromWaypoints()
-local wp=self.waypoints0 and self.waypoints0[1]or nil
-if wp then
-if wp and wp.action and wp.action==COORDINATE.WaypointAction.FromParkingArea
-or wp.action==COORDINATE.WaypointAction.FromParkingAreaHot
-or wp.action==COORDINATE.WaypointAction.FromRunway then
-local airbaseID=nil
-if wp.airdromeId then
-airbaseID=wp.airdromeId
-else
-airbaseID=-wp.helipadId
-end
-local airbase=AIRBASE:FindByID(airbaseID)
-return airbase
-end
-end
-return nil
-end
-function FLIGHTGROUP:FindNearestAirbase(Radius)
-local coord=self:GetCoordinate()
-local dmin=math.huge
-local airbase=nil
-for _,_airbase in pairs(AIRBASE.GetAllAirbases())do
-local ab=_airbase
-local coalitionAB=ab:GetCoalition()
-if coalitionAB==self:GetCoalition()or coalitionAB==coalition.side.NEUTRAL then
-if airbase then
-local d=ab:GetCoordinate():Get2DDistance(coord)
-if d1 then
-table.remove(self.waypoints,#self.waypoints)
-else
-self.destbase=self.homebase
-end
-self:T(self.lid..string.format("Initializing %d waypoints. Homebase %s ==> %s Destination",#self.waypoints,self.homebase and self.homebase:GetName()or"unknown",self.destbase and self.destbase:GetName()or"uknown"))
-if#self.waypoints>0 then
-if#self.waypoints==1 then
-self:_PassedFinalWaypoint(true,"FLIGHTGROUP:InitWaypoints #self.waypoints==1")
-end
-end
-return self
-end
-function FLIGHTGROUP:AddWaypoint(Coordinate,Speed,AfterWaypointWithID,Altitude,Updateroute)
-local coordinate=self:_CoordinateFromObject(Coordinate)
-local wpnumber=self:GetWaypointIndexAfterID(AfterWaypointWithID)
-Speed=Speed or self:GetSpeedCruise()
-self:T3(self.lid..string.format("Waypoint Speed=%.1f knots",Speed))
-local alttype=COORDINATE.WaypointAltType.BARO
-if self.isHelo then
-alttype=COORDINATE.WaypointAltType.RADIO
-end
-local wp=coordinate:WaypointAir(alttype,COORDINATE.WaypointType.TurningPoint,COORDINATE.WaypointAction.TurningPoint,UTILS.KnotsToKmph(Speed),true,nil,{})
-local waypoint=self:_CreateWaypoint(wp)
-if Altitude then
-waypoint.alt=UTILS.FeetToMeters(Altitude)
-end
-self:_AddWaypoint(waypoint,wpnumber)
-self:T(self.lid..string.format("Adding AIR waypoint #%d, speed=%.1f knots. Last waypoint passed was #%s. Total waypoints #%d",wpnumber,Speed,self.currentwp,#self.waypoints))
-if Updateroute==nil or Updateroute==true then
-self:__UpdateRoute(-0.01)
-end
-return waypoint
-end
-function FLIGHTGROUP:AddWaypointLanding(Airbase,Speed,AfterWaypointWithID,Altitude,Updateroute)
-local wpnumber=self:GetWaypointIndexAfterID(AfterWaypointWithID)
-if wpnumber>self.currentwp then
-self:_PassedFinalWaypoint(false,"AddWaypointLanding")
-end
-Speed=Speed or self.speedCruise
-local Coordinate=Airbase:GetCoordinate()
-local wp=Coordinate:WaypointAir(COORDINATE.WaypointAltType.BARO,COORDINATE.WaypointType.Land,COORDINATE.WaypointAction.Landing,Speed,nil,Airbase,{},"Landing Temp",nil)
-local waypoint=self:_CreateWaypoint(wp)
-if Altitude then
-waypoint.alt=UTILS.FeetToMeters(Altitude)
-end
-self:_AddWaypoint(waypoint,wpnumber)
-self:T(self.lid..string.format("Adding AIR waypoint #%d, speed=%.1f knots. Last waypoint passed was #%s. Total waypoints #%d",wpnumber,Speed,self.currentwp,#self.waypoints))
-if Updateroute==nil or Updateroute==true then
-self:__UpdateRoute(-1)
-end
-return waypoint
-end
-function FLIGHTGROUP:GetPlayerElement()
-for _,_element in pairs(self.elements)do
-local element=_element
-if not element.ai then
-return element
-end
-end
-return nil
-end
-function FLIGHTGROUP:GetPlayerName()
-local playerElement=self:GetPlayerElement()
-if playerElement then
-return playerElement.playerName
-end
-return nil
-end
-function FLIGHTGROUP:_SetElementParkingAt(Element,Spot)
-Element.parking=Spot
-if Spot then
-self:T(self.lid..string.format("Element %s is parking on spot %d",Element.name,Spot.TerminalID))
-local fc=_DATABASE:GetFlightControl(Spot.AirbaseName)
-if fc and not self.flightcontrol then
-self:SetFlightControl(fc)
-end
-if self.flightcontrol then
-self.flightcontrol:SetParkingOccupied(Element.parking,Element.name)
-end
-end
-end
-function FLIGHTGROUP:_SetElementParkingFree(Element)
-if Element.parking then
-if self.flightcontrol then
-self.flightcontrol:SetParkingFree(Element.parking)
-end
-Element.parking=nil
-end
-end
-function FLIGHTGROUP:_GetOnboardNumber(unitname)
-local group=UNIT:FindByName(unitname):GetGroup()
-local units=group:GetTemplate().units
-local numbers={}
-for _,unit in pairs(units)do
-if unitname==unit.name then
-return tostring(unit.onboard_num)
-end
-end
-return nil
-end
-function FLIGHTGROUP:_IsHumanUnit(unit)
-local playerunit=self:_GetPlayerUnitAndName(unit:GetName())
-if playerunit then
-return true
-else
-return false
-end
-end
-function FLIGHTGROUP:_IsHuman(group)
-local units=group:GetUnits()
-for _,_unit in pairs(units)do
-local human=self:_IsHumanUnit(_unit)
-if human then
-return true
-end
-end
-return false
-end
-function FLIGHTGROUP:_GetPlayerUnitAndName(_unitName)
-self:F2(_unitName)
-if _unitName~=nil then
-local DCSunit=Unit.getByName(_unitName)
-if DCSunit then
-local playername=DCSunit:getPlayerName()
-local unit=UNIT:Find(DCSunit)
-if DCSunit and unit and playername then
-return unit,playername
-end
-end
-end
-return nil,nil
-end
-function FLIGHTGROUP:GetParkingSpot(element,maxdist,airbase)
-local coord=element.unit:GetCoordinate()
-airbase=airbase or self:GetClosestAirbase()
-local parking=airbase.parking
-if airbase and airbase:IsShip()then
-if#parking>1 then
-coord=airbase:GetRelativeCoordinate(coord.x,coord.y,coord.z)
-else
-coord.x=0
-coord.z=0
-maxdist=500
-end
-end
-local spot=nil
-local dist=nil
-local distmin=math.huge
-for _,_parking in pairs(parking)do
-local parking=_parking
-dist=coord:Get2DDistance(parking.Coordinate)
-if distsafedist)
-return safe
-end
-local function _clients()
-local clients=_DATABASE.CLIENTS
-local coords={}
-for clientname,client in pairs(clients)do
-local template=_DATABASE:GetGroupTemplateFromUnitName(clientname)
-local units=template.units
-for i,unit in pairs(units)do
-local coord=COORDINATE:New(unit.x,unit.alt,unit.y)
-coords[unit.name]=coord
-end
-end
-return coords
-end
-local airbasecategory=airbase:GetAirbaseCategory()
-local parkingdata=airbase:GetParkingSpotsTable()
-local obstacles={}
-for _,_parkingspot in pairs(parkingdata)do
-local parkingspot=_parkingspot
-local _,_,_,_units,_statics,_sceneries=parkingspot.Coordinate:ScanObjects(scanradius,scanunits,scanstatics,scanscenery)
-for _,_unit in pairs(_units)do
-local unit=_unit
-local _coord=unit:GetCoordinate()
-local _size=self:_GetObjectSize(unit:GetDCSObject())
-local _name=unit:GetName()
-table.insert(obstacles,{coord=_coord,size=_size,name=_name,type="unit"})
-end
-local clientcoords=_clients()
-for clientname,_coord in pairs(clientcoords)do
-table.insert(obstacles,{coord=_coord,size=15,name=clientname,type="client"})
-end
-for _,static in pairs(_statics)do
-local _vec3=static:getPoint()
-local _coord=COORDINATE:NewFromVec3(_vec3)
-local _name=static:getName()
-local _size=self:_GetObjectSize(static)
-table.insert(obstacles,{coord=_coord,size=_size,name=_name,type="static"})
-end
-for _,scenery in pairs(_sceneries)do
-local _vec3=scenery:getPoint()
-local _coord=COORDINATE:NewFromVec3(_vec3)
-local _name=scenery:getTypeName()
-local _size=self:_GetObjectSize(scenery)
-table.insert(obstacles,{coord=_coord,size=_size,name=_name,type="scenery"})
-end
-end
-local parking={}
-local terminaltype=self:_GetTerminal(self.attribute,airbase:GetAirbaseCategory())
-for i,_element in pairs(self.elements)do
-local element=_element
-local gotit=false
-for _,_parkingspot in pairs(parkingdata)do
-local parkingspot=_parkingspot
-if AIRBASE._CheckTerminalType(parkingspot.TerminalType,terminaltype)then
-local free=true
-local problem=nil
-if verysafe and parkingspot.TOAC then
-free=false
-self:T2(self.lid..string.format("Parking spot %d is occupied by other aircraft taking off (TOAC).",parkingspot.TerminalID))
-end
-for _,obstacle in pairs(obstacles)do
-local dist=parkingspot.Coordinate:Get2DDistance(obstacle.coord)
-local safe=_overlap(element.size,obstacle.size,dist)
-if not safe then
-free=false
-problem=obstacle
-problem.dist=dist
-break
-end
-end
-if self.flightcontrol and self.flightcontrol.airbasename==airbase:GetName()then
-local problem=self.flightcontrol:IsParkingReserved(parkingspot)or self.flightcontrol:IsParkingOccupied(parkingspot)
-if problem then
-free=false
-end
-end
-if free then
-table.insert(parking,parkingspot)
-self:T2(self.lid..string.format("Parking spot %d is free for element %s!",parkingspot.TerminalID,element.name))
-table.insert(obstacles,{coord=parkingspot.Coordinate,size=element.size,name=element.name,type="element"})
-gotit=true
-break
-else
-self:T2(self.lid..string.format("Parking spot %d is occupied or not big enough!",parkingspot.TerminalID))
-end
-end
-end
-if not gotit then
-self:T(self.lid..string.format("WARNING: No free parking spot for element %s",element.name))
-return nil
-end
-end
-return parking
-end
-function FLIGHTGROUP:_GetObjectSize(DCSobject)
-local DCSdesc=DCSobject:getDesc()
-if DCSdesc.box then
-local x=DCSdesc.box.max.x+math.abs(DCSdesc.box.min.x)
-local y=DCSdesc.box.max.y+math.abs(DCSdesc.box.min.y)
-local z=DCSdesc.box.max.z+math.abs(DCSdesc.box.min.z)
-return math.max(x,z),x,y,z
-end
-return 0,0,0,0
-end
-function FLIGHTGROUP:_GetAttribute()
-local attribute=FLIGHTGROUP.Attribute.OTHER
-local group=self.group
-if group then
-local transportplane=group:HasAttribute("Transports")and group:HasAttribute("Planes")
-local awacs=group:HasAttribute("AWACS")
-local fighter=group:HasAttribute("Fighters")or group:HasAttribute("Interceptors")or group:HasAttribute("Multirole fighters")or(group:HasAttribute("Bombers")and not group:HasAttribute("Strategic bombers"))
-local bomber=group:HasAttribute("Strategic bombers")
-local tanker=group:HasAttribute("Tankers")
-local uav=group:HasAttribute("UAVs")
-local transporthelo=group:HasAttribute("Transport helicopters")
-local attackhelicopter=group:HasAttribute("Attack helicopters")
-if transportplane then
-attribute=FLIGHTGROUP.Attribute.AIR_TRANSPORTPLANE
-elseif awacs then
-attribute=FLIGHTGROUP.Attribute.AIR_AWACS
-elseif fighter then
-attribute=FLIGHTGROUP.Attribute.AIR_FIGHTER
-elseif bomber then
-attribute=FLIGHTGROUP.Attribute.AIR_BOMBER
-elseif tanker then
-attribute=FLIGHTGROUP.Attribute.AIR_TANKER
-elseif transporthelo then
-attribute=FLIGHTGROUP.Attribute.AIR_TRANSPORTHELO
-elseif attackhelicopter then
-attribute=FLIGHTGROUP.Attribute.AIR_ATTACKHELO
-elseif uav then
-attribute=FLIGHTGROUP.Attribute.AIR_UAV
-end
-end
-return attribute
-end
-function FLIGHTGROUP:_GetTerminal(_attribute,_category)
-local _terminal=AIRBASE.TerminalType.OpenBig
-if _attribute==FLIGHTGROUP.Attribute.AIR_FIGHTER or _attribute==FLIGHTGROUP.Attribute.AIR_UAV then
-_terminal=AIRBASE.TerminalType.FighterAircraft
-elseif _attribute==FLIGHTGROUP.Attribute.AIR_BOMBER or _attribute==FLIGHTGROUP.Attribute.AIR_TRANSPORTPLANE or _attribute==FLIGHTGROUP.Attribute.AIR_TANKER or _attribute==FLIGHTGROUP.Attribute.AIR_AWACS then
-_terminal=AIRBASE.TerminalType.OpenBig
-elseif _attribute==FLIGHTGROUP.Attribute.AIR_TRANSPORTHELO or _attribute==FLIGHTGROUP.Attribute.AIR_ATTACKHELO then
-_terminal=AIRBASE.TerminalType.HelicopterUsable
-else
-end
-if _category==Airbase.Category.SHIP then
-if not(_attribute==FLIGHTGROUP.Attribute.AIR_TRANSPORTHELO or _attribute==FLIGHTGROUP.Attribute.AIR_ATTACKHELO)then
-_terminal=AIRBASE.TerminalType.OpenMedOrBig
-end
-end
-return _terminal
-end
-function FLIGHTGROUP:_UpdateMenu(delay)
-if delay and delay>0 then
-self:ScheduleOnce(delay,FLIGHTGROUP._UpdateMenu,self)
-else
-local player=self:GetPlayerElement()
-if player and player.status~=OPSGROUP.ElementStatus.DEAD then
-if self.verbose>=2 then
-local text=string.format("Updating MENU: State=%s, ATC=%s [%s]",self:GetState(),
-self.flightcontrol and self.flightcontrol.airbasename or"None",self.flightcontrol and self.flightcontrol:GetFlightStatus(self)or"Unknown")
-MESSAGE:New(text,5):ToGroup(self.group)
-self:I(self.lid..text)
-end
-local position=self:GetCoordinate(nil,player.name)
-local fc={}
-for airbasename,_flightcontrol in pairs(_DATABASE.FLIGHTCONTROLS)do
-local flightcontrol=_flightcontrol
-local coord=flightcontrol:GetCoordinate()
-local dist=coord:Get2DDistance(position)
-table.insert(fc,{airbasename=airbasename,dist=dist})
-end
-local function _sort(a,b)
-return a.dist=1 then
-local fsmstate=self:GetState()
-local callsign=self.callsignName and UTILS.GetCallsignName(self.callsignName)or"N/A"
-local skill=self.skill and tostring(self.skill)or"N/A"
-local NassetsTot=#self.assets
-local NassetsInS=self:CountAssets(true)
-local NassetsQP=0;local NassetsP=0;local NassetsQ=0
-if self.legion then
-NassetsQP,NassetsP,NassetsQ=self.legion:CountAssetsOnMission(nil,self)
-end
-local text=string.format("%s [Type=%s, Call=%s, Skill=%s]: Assets Total=%d, Stock=%d, Mission=%d [Active=%d, Queue=%d]",
-fsmstate,self.aircrafttype,callsign,skill,NassetsTot,NassetsInS,NassetsQP,NassetsP,NassetsQ)
-self:T(self.lid..text)
-if self.verbose>=3 and self.weaponData then
-local text="Weapon Data:"
-for bit,_weapondata in pairs(self.weaponData)do
-local weapondata=_weapondata
-text=text..string.format("\n- Bit=%s: Rmin=%.1f km, Rmax=%.1f km",bit,weapondata.RangeMin/1000,weapondata.RangeMax/1000)
-end
-self:I(self.lid..text)
-end
-self:_CheckAssetStatus()
-end
-if not self:IsStopped()then
-self:__Status(-60)
-end
-end
-INTEL={
-ClassName="INTEL",
-verbose=0,
-lid=nil,
-alias=nil,
-filterCategory={},
-detectionset=nil,
-Contacts={},
-ContactsLost={},
-ContactsUnknown={},
-Clusters={},
-clustercounter=1,
-clusterradius=15000,
-clusteranalysis=true,
-clustermarkers=false,
-clusterarrows=false,
-prediction=300,
-detectStatics=false,
-}
-INTEL.Ctype={
-GROUND="Ground",
-NAVAL="Naval",
-AIRCRAFT="Aircraft",
-STRUCTURE="Structure"
-}
-INTEL.version="0.3.6"
-function INTEL:New(DetectionSet,Coalition,Alias)
-local self=BASE:Inherit(self,FSM:New())
-self.detectionset=DetectionSet or SET_GROUP:New()
-if Coalition and type(Coalition)=="string"then
-if Coalition=="blue"then
-Coalition=coalition.side.BLUE
-elseif Coalition=="red"then
-Coalition=coalition.side.RED
-elseif Coalition=="neutral"then
-Coalition=coalition.side.NEUTRAL
-else
-self:E("ERROR: Unknown coalition in INTEL!")
-end
-end
-self.coalition=Coalition or DetectionSet:CountAlive()>0 and DetectionSet:GetFirst():GetCoalition()or nil
-if self.coalition then
-local coalitionname=UTILS.GetCoalitionName(self.coalition):lower()
-self.detectionset:FilterCoalitions(coalitionname)
-end
-self.detectionset:FilterOnce()
-if Alias then
-self.alias=tostring(Alias)
-else
-self.alias="INTEL SPECTRE"
-if self.coalition then
-if self.coalition==coalition.side.RED then
-self.alias="INTEL KGB"
-elseif self.coalition==coalition.side.BLUE then
-self.alias="INTEL CIA"
-end
-end
-end
-self.DetectVisual=true
-self.DetectOptical=true
-self.DetectRadar=true
-self.DetectIRST=true
-self.DetectRWR=true
-self.DetectDLINK=true
-self.statusupdate=-60
-self.lid=string.format("%s (%s) | ",self.alias,self.coalition and UTILS.GetCoalitionName(self.coalition)or"unknown")
-self:SetStartState("Stopped")
-self:AddTransition("Stopped","Start","Running")
-self:AddTransition("*","Status","*")
-self:AddTransition("*","Stop","Stopped")
-self:AddTransition("*","Detect","*")
-self:AddTransition("*","NewContact","*")
-self:AddTransition("*","LostContact","*")
-self:AddTransition("*","NewCluster","*")
-self:AddTransition("*","LostCluster","*")
-self:SetForgetTime()
-self:SetAcceptZones()
-self:SetRejectZones()
-self:SetConflictZones()
-return self
-end
-function INTEL:SetAcceptZones(AcceptZoneSet)
-self.acceptzoneset=AcceptZoneSet or SET_ZONE:New()
-return self
-end
-function INTEL:AddAcceptZone(AcceptZone)
-self.acceptzoneset:AddZone(AcceptZone)
-return self
-end
-function INTEL:RemoveAcceptZone(AcceptZone)
-self.acceptzoneset:Remove(AcceptZone:GetName(),true)
-return self
-end
-function INTEL:SetRejectZones(RejectZoneSet)
-self.rejectzoneset=RejectZoneSet or SET_ZONE:New()
-return self
-end
-function INTEL:AddRejectZone(RejectZone)
-self.rejectzoneset:AddZone(RejectZone)
-return self
-end
-function INTEL:RemoveRejectZone(RejectZone)
-self.rejectzoneset:Remove(RejectZone:GetName(),true)
-return self
-end
-function INTEL:SetConflictZones(ConflictZoneSet)
-self.conflictzoneset=ConflictZoneSet or SET_ZONE:New()
-return self
-end
-function INTEL:AddConflictZone(ConflictZone)
-self.conflictzoneset:AddZone(ConflictZone)
-return self
-end
-function INTEL:RemoveConflictZone(ConflictZone)
-self.conflictzoneset:Remove(ConflictZone:GetName(),true)
-return self
-end
-function INTEL:SetForgetTime(TimeInterval)
-return self
-end
-function INTEL:SetFilterCategory(Categories)
-if type(Categories)~="table"then
-Categories={Categories}
-end
-self.filterCategory=Categories
-local text="Filter categories: "
-for _,category in pairs(self.filterCategory)do
-text=text..string.format("%d,",category)
-end
-self:T(self.lid..text)
-return self
-end
-function INTEL:SetRadarBlur(minheight,thresheight,thresblur,closing)
-self.RadarBlur=true
-self.RadarBlurMinHeight=minheight or 250
-self.RadarBlurThresHeight=thresheight or 90
-self.RadarBlurThresBlur=thresblur or 85
-self.RadarBlurClosing=closing or 20
-self.RadarBlurClosingSquare=self.RadarBlurClosing*self.RadarBlurClosing
-return self
-end
-function INTEL:SetAcceptRange(Range)
-self.RadarAcceptRange=true
-self.RadarAcceptRangeKilometers=Range or 75
-return self
-end
-function INTEL:FilterCategoryGroup(GroupCategories)
-if type(GroupCategories)~="table"then
-GroupCategories={GroupCategories}
-end
-self.filterCategoryGroup=GroupCategories
-local text="Filter group categories: "
-for _,category in pairs(self.filterCategoryGroup)do
-text=text..string.format("%d,",category)
-end
-self:T(self.lid..text)
-return self
-end
-function INTEL:AddAgent(AgentGroup)
-if AgentGroup:IsInstanceOf("OPSGROUP")then
-AgentGroup=AgentGroup:GetGroup()
-end
-self.detectionset:AddGroup(AgentGroup)
-return self
-end
-function INTEL:SetClusterAnalysis(Switch,Markers,Arrows)
-self.clusteranalysis=Switch
-self.clustermarkers=Markers
-self.clusterarrows=Arrows
-return self
-end
-function INTEL:SetDetectStatics(Switch)
-if Switch and Switch==true then
-self.detectStatics=true
-else
-self.detectStatics=false
-end
-return self
-end
-function INTEL:SetVerbosity(Verbosity)
-self.verbose=Verbosity or 2
-return self
-end
-function INTEL:AddMissionToContact(Contact,Mission)
-if Mission and Contact then
-Contact.mission=Mission
-end
-return self
-end
-function INTEL:AddMissionToCluster(Cluster,Mission)
-if Mission and Cluster then
-Cluster.mission=Mission
-end
-return self
-end
-function INTEL:SetClusterRadius(radius)
-self.clusterradius=(radius or 15)*1000
-return self
-end
-function INTEL:SetDetectionTypes(DetectVisual,DetectOptical,DetectRadar,DetectIRST,DetectRWR,DetectDLINK)
-self.DetectVisual=DetectVisual and true
-self.DetectOptical=DetectOptical and true
-self.DetectRadar=DetectRadar and true
-self.DetectIRST=DetectIRST and true
-self.DetectRWR=DetectRWR and true
-self.DetectDLINK=DetectDLINK and true
-return self
-end
-function INTEL:GetContactTable()
-if self:Is("Running")then
-return self.Contacts
-else
-return nil
-end
-end
-function INTEL:GetClusterTable()
-if self:Is("Running")and self.clusteranalysis then
-return self.Clusters
-else
-return nil
-end
-end
-function INTEL:GetContactName(Contact)
-return Contact.groupname
-end
-function INTEL:GetContactGroup(Contact)
-return Contact.group
-end
-function INTEL:GetContactThreatlevel(Contact)
-return Contact.threatlevel
-end
-function INTEL:GetContactTypeName(Contact)
-return Contact.typename
-end
-function INTEL:GetContactCategoryName(Contact)
-return Contact.categoryname
-end
-function INTEL:GetContactCoordinate(Contact)
-return Contact.position
-end
-function INTEL:onafterStart(From,Event,To)
-local text=string.format("Starting INTEL v%s",self.version)
-self:I(self.lid..text)
-self:__Status(-math.random(10))
-return self
-end
-function INTEL:onafterStatus(From,Event,To)
-local fsmstate=self:GetState()
-self.ContactsLost={}
-self.ContactsUnknown={}
-self:UpdateIntel()
-local Ncontacts=#self.Contacts
-local Nclusters=#self.Clusters
-if self.verbose>=1 then
-local text=string.format("Status %s [Agents=%s]: Contacts=%d, Clusters=%d, New=%d, Lost=%d",fsmstate,self.detectionset:CountAlive(),Ncontacts,Nclusters,#self.ContactsUnknown,#self.ContactsLost)
-self:I(self.lid..text)
-end
-if self.verbose>=2 and Ncontacts>0 then
-local text="Detected Contacts:"
-for _,_contact in pairs(self.Contacts)do
-local contact=_contact
-local dT=timer.getAbsTime()-contact.Tdetected
-text=text..string.format("\n- %s (%s): %s, units=%d, T=%d sec",contact.categoryname,contact.attribute,contact.groupname,contact.isStatic and 1 or contact.group:CountAliveUnits(),dT)
-if contact.mission then
-local mission=contact.mission
-text=text..string.format(" mission name=%s type=%s target=%s",mission.name,mission.type,mission:GetTargetName()or"unknown")
-end
-end
-self:I(self.lid..text)
-end
-self:__Status(self.statusupdate)
-return self
-end
-function INTEL:UpdateIntel()
-local DetectedUnits={}
-local RecceDetecting={}
-for _,_group in pairs(self.detectionset.Set or{})do
-local group=_group
-if group and group:IsAlive()then
-for _,_recce in pairs(group:GetUnits())do
-local recce=_recce
-self:GetDetectedUnits(recce,DetectedUnits,RecceDetecting,self.DetectVisual,self.DetectOptical,self.DetectRadar,self.DetectIRST,self.DetectRWR,self.DetectDLINK)
-end
-end
-end
-local remove={}
-for unitname,_unit in pairs(DetectedUnits)do
-local unit=_unit
-local inconflictzone=false
-if self.conflictzoneset:Count()>0 then
-for _,_zone in pairs(self.conflictzoneset.Set)do
-local zone=_zone
-if unit:IsInZone(zone)then
-inconflictzone=true
-break
-end
-end
-end
-if self.acceptzoneset:Count()>0 then
-local inzone=false
-for _,_zone in pairs(self.acceptzoneset.Set)do
-local zone=_zone
-if unit:IsInZone(zone)then
-inzone=true
-break
-end
-end
-if(not inzone)and(not inconflictzone)then
-table.insert(remove,unitname)
-end
-end
-if self.rejectzoneset:Count()>0 then
-local inzone=false
-for _,_zone in pairs(self.rejectzoneset.Set)do
-local zone=_zone
-if unit:IsInZone(zone)then
-inzone=true
-break
-end
-end
-if inzone and(not inconflictzone)then
-table.insert(remove,unitname)
-end
-end
-if#self.filterCategory>0 and unit:IsInstanceOf("UNIT")then
-local unitcategory=unit:GetUnitCategory()
-local keepit=false
-for _,filtercategory in pairs(self.filterCategory)do
-if unitcategory==filtercategory then
-keepit=true
-break
-end
-end
-if not keepit then
-self:T(self.lid..string.format("Removing unit %s category=%d",unitname,unit:GetCategory()))
-table.insert(remove,unitname)
-end
-end
-end
-for _,unitname in pairs(remove)do
-DetectedUnits[unitname]=nil
-end
-local DetectedGroups={}
-local DetectedStatics={}
-local RecceGroups={}
-for unitname,_unit in pairs(DetectedUnits)do
-local unit=_unit
-if unit:IsInstanceOf("UNIT")then
-local group=unit:GetGroup()
-if group then
-local groupname=group:GetName()
-DetectedGroups[groupname]=group
-RecceGroups[groupname]=RecceDetecting[unitname]
-end
-else
-if self.detectStatics then
-DetectedStatics[unitname]=unit
-RecceGroups[unitname]=RecceDetecting[unitname]
-end
-end
-end
-self:CreateDetectedItems(DetectedGroups,DetectedStatics,RecceGroups)
-if self.clusteranalysis then
-self:PaintPicture()
-end
-return self
-end
-function INTEL:_UpdateContact(Contact)
-if Contact.isStatic then
-else
-if Contact.group and Contact.group:IsAlive()then
-Contact.Tdetected=timer.getAbsTime()
-Contact.position=Contact.group:GetCoordinate()
-Contact.velocity=Contact.group:GetVelocityVec3()
-Contact.speed=Contact.group:GetVelocityMPS()
-if Contact.group:IsAir()then
-Contact.altitude=Contact.group:GetAltitude()
-local oldheading=Contact.heading or 1
-local newheading=Contact.group:GetHeading()
-if newheading==0 then newheading=1 end
-local changeh=math.abs(((oldheading-newheading)+360)%360)
-Contact.heading=newheading
-if changeh>10 then
-Contact.maneuvering=true
-else
-Contact.maneuvering=false
-end
-end
-end
-end
-return self
-end
-function INTEL:_CreateContact(Positionable,RecceName)
-if Positionable and Positionable:IsAlive()then
-local item={}
-if Positionable:IsInstanceOf("GROUP")then
-local group=Positionable
-item.groupname=group:GetName()
-item.group=group
-item.Tdetected=timer.getAbsTime()
-item.typename=group:GetTypeName()
-item.attribute=group:GetAttribute()
-item.category=group:GetCategory()
-item.categoryname=group:GetCategoryName()
-item.threatlevel=group:GetThreatLevel()
-item.position=group:GetCoordinate()
-item.velocity=group:GetVelocityVec3()
-item.speed=group:GetVelocityMPS()
-item.recce=RecceName
-item.isground=group:IsGround()or false
-item.isship=group:IsShip()or false
-item.isStatic=false
-if group:IsAir()then
-item.platform=group:GetNatoReportingName()
-item.heading=group:GetHeading()
-item.maneuvering=false
-item.altitude=group:GetAltitude()
-else
-item.platform="Unknown"
-item.altitude=group:GetAltitude(true)
-end
-if item.category==Group.Category.AIRPLANE or item.category==Group.Category.HELICOPTER then
-item.ctype=INTEL.Ctype.AIRCRAFT
-elseif item.category==Group.Category.GROUND or item.category==Group.Category.TRAIN then
-item.ctype=INTEL.Ctype.GROUND
-elseif item.category==Group.Category.SHIP then
-item.ctype=INTEL.Ctype.NAVAL
-end
-return item
-elseif Positionable:IsInstanceOf("STATIC")then
-local static=Positionable
-item.groupname=static:GetName()
-item.group=static
-item.Tdetected=timer.getAbsTime()
-item.typename=static:GetTypeName()or"Unknown"
-item.attribute="Static"
-item.category=3
-item.categoryname=static:GetCategoryName()or"Unknown"
-item.threatlevel=static:GetThreatLevel()or 0
-item.position=static:GetCoordinate()
-item.velocity=static:GetVelocityVec3()
-item.speed=0
-item.recce=RecceName
-item.isground=true
-item.isship=false
-item.isStatic=true
-item.ctype=INTEL.Ctype.STRUCTURE
-return item
-else
-self:E(self.lid..string.format("ERROR: object needs to be a GROUP or STATIC!"))
-end
-end
-return nil
-end
-function INTEL:CreateDetectedItems(DetectedGroups,DetectedStatics,RecceDetecting)
-self:F({RecceDetecting=RecceDetecting})
-local Tnow=timer.getAbsTime()
-for groupname,_group in pairs(DetectedGroups)do
-local group=_group
-self:KnowObject(group,RecceDetecting[groupname])
-end
-for staticname,_static in pairs(DetectedStatics)do
-local static=_static
-self:KnowObject(static,RecceDetecting[staticname])
-end
-for i=#self.Contacts,1,-1 do
-local item=self.Contacts[i]
-if self:_CheckContactLost(item)then
-self:LostContact(item)
-self:RemoveContact(item)
-end
-end
-return self
-end
-function INTEL:GetDetectedUnits(Unit,DetectedUnits,RecceDetecting,DetectVisual,DetectOptical,DetectRadar,DetectIRST,DetectRWR,DetectDLINK)
-local reccename=Unit:GetName()
-local detectedtargets=Unit:GetDetectedTargets(DetectVisual,DetectOptical,DetectRadar,DetectIRST,DetectRWR,DetectDLINK)
-for DetectionObjectID,Detection in pairs(detectedtargets or{})do
-local DetectedObject=Detection.object
-if DetectedObject and DetectedObject:isExist()and DetectedObject.id_<50000000 then
-local status,name=pcall(
-function()
-local name=DetectedObject:getName()
-return name
-end)
-if status then
-local unit=UNIT:FindByName(name)
-if unit and unit:IsAlive()then
-local DetectionAccepted=true
-if self.RadarAcceptRange then
-local reccecoord=Unit:GetCoordinate()
-local coord=unit:GetCoordinate()
-local dist=math.floor(coord:Get2DDistance(reccecoord)/1000)
-if dist>self.RadarAcceptRangeKilometers then DetectionAccepted=false end
-end
-if self.RadarBlur then
-local reccecoord=Unit:GetCoordinate()
-local coord=unit:GetCoordinate()
-local dist=math.floor(coord:Get2DDistance(reccecoord)/1000)
-local AGL=unit:GetAltitude(true)
-local minheight=self.RadarBlurMinHeight or 250
-local thresheight=self.RadarBlurThresHeight or 90
-local thresblur=self.RadarBlurThresBlur or 85
-if dist<=self.RadarBlurClosing then
-thresheight=(((dist*dist)/self.RadarBlurClosingSquare)*thresheight)
-thresblur=(((dist*dist)/self.RadarBlurClosingSquare)*thresblur)
-end
-local fheight=math.floor(math.random(1,10000)/100)
-local fblur=math.floor(math.random(1,10000)/100)
-if fblur>thresblur then DetectionAccepted=false end
-if AGL<=minheight and fheight1 then
-MESSAGE:New("Radar Blur",10):ToLogIf(self.debug):ToAllIf(self.verbose>1)
-MESSAGE:New("Unit "..name.." is at "..math.floor(AGL).."m. Distance "..math.floor(dist).."km.",10):ToLogIf(self.debug):ToAllIf(self.verbose>1)
-MESSAGE:New(string.format("fheight = %d/%d | fblur = %d/%d",fheight,thresheight,fblur,thresblur),10):ToLogIf(self.debug):ToAllIf(self.verbose>1)
-MESSAGE:New("Detection Accepted = "..tostring(DetectionAccepted),10):ToLogIf(self.debug):ToAllIf(self.verbose>1)
-end
-end
-if DetectionAccepted then
-DetectedUnits[name]=unit
-RecceDetecting[name]=reccename
-self:T(string.format("Unit %s detect by %s",name,reccename))
-end
-else
-if self.detectStatics then
-local static=STATIC:FindByName(name,false)
-if static then
-DetectedUnits[name]=static
-RecceDetecting[name]=reccename
-end
-end
-end
-else
-self:T(self.lid..string.format("WARNING: Could not get name of detected object ID=%s! Detected by %s",DetectedObject.id_,reccename))
-end
-end
-end
-end
-function INTEL:onafterNewContact(From,Event,To,Contact)
-self:F(self.lid..string.format("NEW contact %s",Contact.groupname))
-table.insert(self.ContactsUnknown,Contact)
-return self
-end
-function INTEL:onafterLostContact(From,Event,To,Contact)
-self:F(self.lid..string.format("LOST contact %s",Contact.groupname))
-table.insert(self.ContactsLost,Contact)
-return self
-end
-function INTEL:onafterNewCluster(From,Event,To,Cluster)
-self:F(self.lid..string.format("NEW cluster #%d [%s] of size %d",Cluster.index,Cluster.ctype,Cluster.size))
-self:_AddCluster(Cluster)
-return self
-end
-function INTEL:onafterLostCluster(From,Event,To,Cluster,Mission)
-local text=self.lid..string.format("LOST cluster #%d [%s]",Cluster.index,Cluster.ctype)
-if Mission then
-local mission=Mission
-text=text..string.format(" mission name=%s type=%s target=%s",mission.name,mission.type,mission:GetTargetName()or"unknown")
-end
-self:T(text)
-return self
-end
-function INTEL:KnowObject(Positionable,RecceName,Tdetected)
-local Tnow=timer.getAbsTime()
-Tdetected=Tdetected or Tnow
-if Positionable and Positionable:IsAlive()then
-if Tdetected>Tnow then
-self:ScheduleOnce(Tdetected-Tnow,self.KnowObject,self,Positionable,RecceName)
-else
-local name=Positionable:GetName()
-local contact=self:GetContactByName(name)
-if contact then
-self:_UpdateContact(contact)
-else
-contact=self:_CreateContact(Positionable,RecceName)
-if contact then
-self:T(string.format("%s contact detected by %s",contact.groupname,RecceName or"unknown"))
-self:AddContact(contact)
-self:NewContact(contact)
-end
-end
-end
-end
-return self
-end
-function INTEL:GetContactByName(groupname)
-for i,_contact in pairs(self.Contacts)do
-local contact=_contact
-if contact.groupname==groupname then
-return contact
-end
-end
-return nil
-end
-function INTEL:_IsContactKnown(Contact)
-for i,_contact in pairs(self.Contacts)do
-local contact=_contact
-if contact.groupname==Contact.groupname then
-return true
-end
-end
-return false
-end
-function INTEL:AddContact(Contact)
-if self:_IsContactKnown(Contact)then
-self:E(self.lid..string.format("WARNING: Contact %s is already in the contact table!",tostring(Contact.groupname)))
-else
-self:T(self.lid..string.format("Adding new Contact %s to table",tostring(Contact.groupname)))
-table.insert(self.Contacts,Contact)
-end
-return self
-end
-function INTEL:RemoveContact(Contact)
-for i,_contact in pairs(self.Contacts)do
-local contact=_contact
-if contact.groupname==Contact.groupname then
-table.remove(self.Contacts,i)
-end
-end
-return self
-end
-function INTEL:_CheckContactLost(Contact)
-if Contact.group==nil or not Contact.group:IsAlive()then
-return true
-end
-if Contact.isStatic then
-return false
-end
-local dT=timer.getAbsTime()-Contact.Tdetected
-local dTforget=nil
-if Contact.category==Group.Category.GROUND then
-dTforget=60*60*2
-elseif Contact.category==Group.Category.AIRPLANE then
-dTforget=60*10
-elseif Contact.category==Group.Category.HELICOPTER then
-dTforget=60*20
-elseif Contact.category==Group.Category.SHIP then
-dTforget=60*60
-elseif Contact.category==Group.Category.TRAIN then
-dTforget=60*60
-end
-if dT>dTforget then
-return true
-else
-return false
-end
-end
-function INTEL:PaintPicture()
-self:F(self.lid.."Painting Picture!")
-for _,_contact in pairs(self.ContactsLost)do
-local contact=_contact
-local cluster=self:GetClusterOfContact(contact)
-if cluster then
-self:RemoveContactFromCluster(contact,cluster)
-end
-end
-local ClusterSet={}
-for _i,_cluster in pairs(self.Clusters)do
-local cluster=_cluster
-if cluster.size>0 and self:ClusterCountUnits(cluster)>0 then
-table.insert(ClusterSet,_cluster)
-else
-if cluster.marker then
-cluster.marker:Remove()
-end
-if cluster.markerID then
-COORDINATE:RemoveMark(cluster.markerID)
-end
-self:LostCluster(cluster,cluster.mission)
-end
-end
-self.Clusters=ClusterSet
-self:_UpdateClusterPositions()
-for _,_contact in pairs(self.Contacts)do
-local contact=_contact
-self:T(string.format("Paint Picture: checking for %s",contact.groupname))
-local currentcluster=self:GetClusterOfContact(contact)
-if currentcluster then
-local isconnected=self:IsContactConnectedToCluster(contact,currentcluster)
-if isconnected then
-else
-self:RemoveContactFromCluster(contact,currentcluster)
-local cluster=self:_GetClosestClusterOfContact(contact)
-if cluster then
-self:AddContactToCluster(contact,cluster)
-else
-local newcluster=self:_CreateClusterFromContact(contact)
-self:NewCluster(newcluster)
-end
-end
-else
-self:T(self.lid..string.format("Paint Picture: contact %s has NO current cluster",contact.groupname))
-local cluster=self:_GetClosestClusterOfContact(contact)
-if cluster then
-self:T(self.lid..string.format("Paint Picture: contact %s has closest cluster #%d",contact.groupname,cluster.index))
-self:AddContactToCluster(contact,cluster)
-else
-self:T(self.lid..string.format("Paint Picture: contact %s has no closest cluster ==> Create new cluster",contact.groupname))
-local newcluster=self:_CreateClusterFromContact(contact)
-self:NewCluster(newcluster)
-end
-end
-end
-self:_UpdateClusterPositions()
-if self.clustermarkers then
-for _,_cluster in pairs(self.Clusters)do
-local cluster=_cluster
-if self.verbose>=1 then
-BASE:I("Updating cluster marker and future position")
-end
-self:UpdateClusterMarker(cluster)
-self:CalcClusterFuturePosition(cluster,300)
-end
-end
-return self
-end
-function INTEL:_CreateCluster()
-local cluster={}
-cluster.index=self.clustercounter
-cluster.coordinate=COORDINATE:New(0,0,0)
-cluster.threatlevelSum=0
-cluster.threatlevelMax=0
-cluster.size=0
-cluster.Contacts={}
-cluster.altitude=0
-self.clustercounter=self.clustercounter+1
-return cluster
-end
-function INTEL:_CreateClusterFromContact(Contact)
-local cluster=self:_CreateCluster()
-self:T(self.lid..string.format("Created NEW cluster #%d with first contact %s",cluster.index,Contact.groupname))
-cluster.coordinate:UpdateFromCoordinate(Contact.position)
-cluster.ctype=Contact.ctype
-self:AddContactToCluster(Contact,cluster)
-return cluster
-end
-function INTEL:_AddCluster(Cluster)
-table.insert(self.Clusters,Cluster)
-return self
-end
-function INTEL:AddContactToCluster(contact,cluster)
-if contact and cluster then
-table.insert(cluster.Contacts,contact)
-cluster.threatlevelSum=cluster.threatlevelSum+contact.threatlevel
-cluster.size=cluster.size+1
-self:GetClusterAltitude(cluster,true)
-self:T(self.lid..string.format("Adding contact %s to cluster #%d [%s] ==> New size=%d",contact.groupname,cluster.index,cluster.ctype,cluster.size))
-end
-return self
-end
-function INTEL:RemoveContactFromCluster(contact,cluster)
-if contact and cluster then
-for i=#cluster.Contacts,1,-1 do
-local Contact=cluster.Contacts[i]
-if Contact.groupname==contact.groupname then
-cluster.threatlevelSum=cluster.threatlevelSum-contact.threatlevel
-cluster.size=cluster.size-1
-table.remove(cluster.Contacts,i)
-self:T(self.lid..string.format("Removing contact %s from cluster #%d ==> New cluster size=%d",contact.groupname,cluster.index,cluster.size))
-return self
-end
-end
-end
-return self
-end
-function INTEL:CalcClusterThreatlevelSum(cluster)
-local threatlevel=0
-for _,_contact in pairs(cluster.Contacts)do
-local contact=_contact
-threatlevel=threatlevel+contact.threatlevel
-end
-cluster.threatlevelSum=threatlevel
-return threatlevel
-end
-function INTEL:CalcClusterThreatlevelAverage(cluster)
-local threatlevel=self:CalcClusterThreatlevelSum(cluster)
-threatlevel=threatlevel/cluster.size
-cluster.threatlevelAve=threatlevel
-return threatlevel
-end
-function INTEL:CalcClusterThreatlevelMax(cluster)
-local threatlevel=0
-for _,_contact in pairs(cluster.Contacts)do
-local contact=_contact
-if contact.threatlevel>threatlevel then
-threatlevel=contact.threatlevel
-end
-end
-cluster.threatlevelMax=threatlevel
-return threatlevel
-end
-function INTEL:CalcClusterDirection(cluster)
-local direction=0
-local speedsum=0
-local n=0
-for _,_contact in pairs(cluster.Contacts)do
-local contact=_contact
-if(not contact.isStatic)and contact.group:IsAlive()then
-local speed=contact.group:GetVelocityKNOTS()
-direction=direction+(contact.group:GetHeading()*speed)
-n=n+1
-speedsum=speedsum+speed
-end
-end
-if n==0 then
-return 0
-else
-return math.floor(direction/(speedsum*n))
-end
-end
-function INTEL:CalcClusterSpeed(cluster)
-local velocity=0;local n=0
-for _,_contact in pairs(cluster.Contacts)do
-local contact=_contact
-if(not contact.isStatic)and contact.group:IsAlive()then
-velocity=velocity+contact.group:GetVelocityMPS()
-n=n+1
-end
-end
-if n==0 then
-return 0
-else
-return math.floor(velocity/n)
-end
-end
-function INTEL:CalcClusterVelocityVec3(cluster)
-local v={x=0,y=0,z=0}
-for _,_contact in pairs(cluster.Contacts)do
-local contact=_contact
-if(not contact.isStatic)and contact.group:IsAlive()then
-local vec=contact.group:GetVelocityVec3()
-v.x=v.x+vec.x
-v.y=v.y+vec.y
-v.z=v.y+vec.z
-end
-end
-return v
-end
-function INTEL:CalcClusterFuturePosition(cluster,seconds)
-local p=self:GetClusterCoordinate(cluster)
-local v=self:CalcClusterVelocityVec3(cluster)
-local t=seconds or self.prediction
-local Vec3={x=p.x+v.x*t,y=p.y+v.y*t,z=p.z+v.z*t}
-local futureposition=COORDINATE:NewFromVec3(Vec3)
-if self.clustermarkers and self.clusterarrows then
-if cluster.markerID then
-COORDINATE:RemoveMark(cluster.markerID)
-end
-cluster.markerID=p:ArrowToAll(futureposition,self.coalition,{1,0,0},1,{1,1,0},0.5,2,true,"Position Calc")
-end
-return futureposition
-end
-function INTEL:CheckContactInClusters(contact)
-for _,_cluster in pairs(self.Clusters)do
-local cluster=_cluster
-for _,_contact in pairs(cluster.Contacts)do
-local Contact=_contact
-if Contact.groupname==contact.groupname then
-return true
-end
-end
-end
-return false
-end
-function INTEL:IsContactConnectedToCluster(contact,cluster)
-if contact.ctype~=cluster.ctype then
-return false,math.huge
-end
-for _,_contact in pairs(cluster.Contacts)do
-local Contact=_contact
-if Contact.groupname~=contact.groupname or cluster.size==1 then
-local dist=Contact.position:DistanceFromPointVec2(contact.position)
-local airprox=true
-if contact.ctype==INTEL.Ctype.AIRCRAFT then
-self:T(string.format("Cluster Alt=%d | Contact Alt=%d",cluster.altitude,contact.altitude))
-local adist=math.abs(cluster.altitude-contact.altitude)
-if adist>UTILS.FeetToMeters(10000)then
-airprox=false
-end
-end
-if distUTILS.FeetToMeters(10000)then
-airprox=false
-end
-end
-if dist0 then
-avgalt=newalt/n
-end
-Cluster.altitude=avgalt
-self:T(string.format("Updating Cluster Altitude: %d",Cluster.altitude))
-return Cluster.altitude
-end
-function INTEL:GetClusterCoordinate(Cluster,Update)
-local x=0;local y=0;local z=0;local n=0
-for _,_contact in pairs(Cluster.Contacts)do
-local contact=_contact
-local vec3=nil
-if Update and contact.group and contact.group:IsAlive()then
-vec3=contact.group:GetVec3()
-end
-if not vec3 then
-vec3=contact.position
-end
-if vec3 then
-x=x+vec3.x
-y=y+vec3.y
-z=z+vec3.z
-n=n+1
-end
-end
-if n>0 then
-local Vec3={x=x/n,y=y/n,z=z/n}
-Cluster.coordinate:UpdateFromVec3(Vec3)
-end
-return Cluster.coordinate
-end
-function INTEL:_CheckClusterCoordinateChanged(Cluster,Coordinate,Threshold)
-Threshold=Threshold or 100
-Coordinate=Coordinate or Cluster.coordinate
-local a=Coordinate:GetVec3()
-local b=self:GetClusterCoordinate(Cluster,true):GetVec3()
-local dist=UTILS.VecDist3D(a,b)
-if dist>Threshold then
-return true
-else
-return false
-end
-end
-function INTEL:_UpdateClusterPositions()
-for _,_cluster in pairs(self.Clusters)do
-local cluster=_cluster
-local coord=self:GetClusterCoordinate(cluster,true)
-local alt=self:GetClusterAltitude(cluster,true)
-self:T(self.lid..string.format("Updating Cluster position size: %s",cluster.size))
-end
-return self
-end
-function INTEL:ContactCountUnits(Contact)
-if Contact.isStatic then
-if Contact.group and Contact.group:IsAlive()then
-return 1
-else
-return 0
-end
-else
-if Contact.group then
-local n=Contact.group:CountAliveUnits()
-return n
-else
-return 0
-end
-end
-end
-function INTEL:ClusterCountUnits(Cluster)
-local unitcount=0
-for _,_contact in pairs(Cluster.Contacts)do
-local contact=_contact
-unitcount=unitcount+self:ContactCountUnits(contact)
-end
-return unitcount
-end
-function INTEL:UpdateClusterMarker(cluster)
-local unitcount=self:ClusterCountUnits(cluster)
-local text=string.format("Cluster #%d: %s\nSize %d\nUnits %d\nTLsum=%d",cluster.index,cluster.ctype,cluster.size,unitcount,cluster.threatlevelSum)
-if not cluster.marker then
-cluster.marker=MARKER:New(cluster.coordinate,text):ToCoalition(self.coalition)
-else
-local refresh=false
-if cluster.marker.text~=text then
-cluster.marker.text=text
-refresh=true
-end
-local coordchange=self:_CheckClusterCoordinateChanged(cluster,cluster.marker.coordinate)
-if coordchange then
-cluster.marker.coordinate:UpdateFromCoordinate(cluster.coordinate)
-refresh=true
-end
-if refresh then
-cluster.marker:Refresh()
-end
-end
-return self
-end
-function INTEL:GetHighestThreatContact(Cluster)
-local threatlevel=-1
-local rcontact=nil
-for _,_contact in pairs(Cluster.Contacts)do
-local contact=_contact
-if contact.threatlevel>threatlevel then
-threatlevel=contact.threatlevel
-rcontact=contact
-end
-end
-return rcontact
-end
-INTEL_DLINK={
-ClassName="INTEL_DLINK",
-verbose=0,
-lid=nil,
-alias=nil,
-cachetime=300,
-interval=20,
-contacts={},
-clusters={},
-contactcoords={},
-}
-INTEL_DLINK.version="0.0.1"
-function INTEL_DLINK:New(Intels,Alias,Interval,Cachetime)
-local self=BASE:Inherit(self,FSM:New())
-self.intels=Intels or{}
-self.contacts={}
-self.clusters={}
-self.contactcoords={}
-if Alias then
-self.alias=tostring(Alias)
-else
-self.alias="SPECTRE"
-end
-self.cachetime=Cachetime or 300
-self.interval=Interval or 20
-self.lid=string.format("INTEL_DLINK %s | ",self.alias)
-self:SetStartState("Stopped")
-self:AddTransition("Stopped","Start","Running")
-self:AddTransition("*","Collect","*")
-self:AddTransition("*","Collected","*")
-self:AddTransition("*","Stop","Stopped")
-return self
-end
-function INTEL_DLINK:AddIntel(Intel)
-self:T(self.lid.."AddIntel")
-if Intel then
-table.insert(self.intels,Intel)
-end
-return self
-end
-function INTEL_DLINK:onafterStart(From,Event,To)
-self:T({From,Event,To})
-local text=string.format("Version %s started.",self.version)
-self:I(self.lid..text)
-self:__Collect(-math.random(1,10))
-return self
-end
-function INTEL_DLINK:onbeforeCollect(From,Event,To)
-self:T({From,Event,To})
-self:T("Contacts Data Gathering")
-local newcontacts={}
-local intels=self.intels
-for _,_intel in pairs(intels)do
-_intel=_intel
-if _intel:Is("Running")then
-local ctable=_intel:GetContactTable()or{}
-for _,_contact in pairs(ctable)do
-local _ID=string.format("%s-%d",_contact.groupname,_contact.Tdetected)
-self:T(string.format("Adding %s",_ID))
-newcontacts[_ID]=_contact
-end
-end
-end
-self:T("Cleanup")
-local contacttable={}
-local coordtable={}
-local TNow=timer.getAbsTime()
-local Tcache=self.cachetime
-for _ind,_contact in pairs(newcontacts)do
-if TNow-_contact.Tdetected0 then
-self:ScheduleOnce(Delay,LEGION.RelocateCohort,self,Cohort,Legion,0,NcarriersMin,NcarriersMax,TransportLegions)
-else
-if Legion:IsCohort(Cohort.name)then
-self:E(self.lid..string.format("ERROR: Cohort %s is already part of new legion %s ==> CANNOT Relocate!",Cohort.name,Legion.alias))
-return self
-else
-table.insert(Legion.cohorts,Cohort)
-end
-if not self:IsCohort(Cohort.name)then
-self:E(self.lid..string.format("ERROR: Cohort %s is NOT part of this legion %s ==> CANNOT Relocate!",Cohort.name,self.alias))
-return self
-end
-if self.alias==Legion.alias then
-self:E(self.lid..string.format("ERROR: old legion %s is same as new legion %s ==> CANNOT Relocate!",self.alias,Legion.alias))
-return self
-end
-Cohort:Relocate()
-local mission=AUFTRAG:_NewRELOCATECOHORT(Legion,Cohort)
-if false then
-mission:_AddAssets(Cohort.assets)
-self:I(self.lid..string.format("Relocating Cohort %s [nassets=%d] to legion %s",Cohort.name,#Cohort.assets,Legion.alias))
-self:MissionAssign(mission,{self})
-else
-mission:AssignCohort(Cohort)
-mission:SetRequiredAssets(#Cohort.assets)
-if NcarriersMin and NcarriersMin>0 then
-mission:SetRequiredTransport(Legion.spawnzone,NcarriersMin,NcarriersMax)
-end
-if TransportLegions then
-for _,legion in pairs(TransportLegions)do
-mission:AssignTransportLegion(legion)
-end
-end
-mission:SetMissionRange(10000)
-self:AddMission(mission)
-end
-end
-return self
-end
-function LEGION:_GetCohort(CohortName)
-for _,_cohort in pairs(self.cohorts)do
-local cohort=_cohort
-if cohort.name==CohortName then
-return cohort
-end
-end
-return nil
-end
-function LEGION:IsCohort(CohortName)
-for _,_cohort in pairs(self.cohorts)do
-local cohort=_cohort
-if cohort.name==CohortName then
-return true
-end
-end
-return false
-end
-function LEGION:GetName()
-return self.alias
-end
-function LEGION:_GetCohortOfAsset(Asset)
-local cohort=self:_GetCohort(Asset.squadname)
-return cohort
-end
-function LEGION:IsBrigade()
-local is=self.ClassName==BRIGADE.ClassName
-return is
-end
-function LEGION:IsAirwing()
-local is=self.ClassName==AIRWING.ClassName
-return is
-end
-function LEGION:IsFleet()
-local is=self.ClassName==FLEET.ClassName
-return is
-end
-function LEGION:onafterStart(From,Event,To)
-self:GetParent(self,LEGION).onafterStart(self,From,Event,To)
-self:T3(self.lid..string.format("Starting LEGION v%s",LEGION.version))
-end
-function LEGION:CheckMissionQueue()
-local Nmissions=#self.missionqueue
-if Nmissions==0 then
-return nil
-end
-for _,_mission in pairs(self.missionqueue)do
-local mission=_mission
-if mission:IsNotOver()and mission:IsReadyToCancel()then
-mission:Cancel()
-end
-end
-if self:IsAirwing()then
-if self:IsRunwayOperational()==false then
-return nil
-end
-local airboss=self.airboss
-if airboss then
-if not airboss:IsIdle()then
-return nil
-end
-end
-end
-local function _sort(a,b)
-local taskA=a
-local taskB=b
-return(taskA.prio0 then
-local N=mission.Nassigned-mission.Ndead
-if N Reinforce=%s",
-mission.reinforce,N,mission.Nassigned,mission.Ndead,mission.NassetsMin,tostring(reinforce)))
-end
-if(mission:IsQueued(self)or reinforce)and mission:IsReadyToGo()and(mission.importance==nil or mission.importance<=vip)then
-local recruited,assets,legions=self:RecruitAssetsForMission(mission)
-if recruited then
-local EscortAvail=self:RecruitAssetsForEscort(mission,assets)
-local TransportAvail=true
-if EscortAvail then
-local Transport=nil
-if mission.NcarriersMin then
-local Legions=mission.transportLegions or{self}
-TransportAvail,Transport=self:AssignAssetsForTransport(Legions,assets,mission.NcarriersMin,mission.NcarriersMax,mission.transportDeployZone,mission.transportDisembarkZone,mission.carrierCategories,mission.carrierAttributes,mission.carrierProperties)
-end
-if TransportAvail and Transport then
-mission.opstransport=Transport
-end
-end
-if EscortAvail and TransportAvail then
-self:MissionRequest(mission,assets)
-if reinforce then
-mission.reinforce=mission.reinforce-#assets
-self:I(self.lid..string.format("Reinforced with N=%d Nreinforce=%d",#assets,mission.reinforce))
-end
-return true
-else
-LEGION.UnRecruitAssets(assets,mission)
-end
-end
-end
-end
-return nil
-end
-function LEGION:CheckTransportQueue()
-local Ntransports=#self.transportqueue
-if Ntransports==0 then
-return nil
-end
-local function _sort(a,b)
-local taskA=a
-local taskB=b
-return(taskA.prio0 then
-for i,_asset in pairs(Assetlist)do
-local asset=_asset
-asset.requested=true
-if asset.spawned then
-asset.requested=false
-end
-asset.isReserved=false
-if Mission.missionTask then
-asset.missionTask=Mission.missionTask
-end
-if Mission.type==AUFTRAG.Type.ALERT5 then
-asset.takeoffType=COORDINATE.WaypointType.TakeOffParking
-end
-Mission:AddAsset(asset)
-end
-local assignment=string.format("Mission-%d",Mission.auftragsnummer)
-local request=self:_AddRequest(WAREHOUSE.Descriptor.ASSETLIST,Assetlist,#Assetlist,Mission.prio,assignment)
-self:T(self.lid..string.format("Added request=%d for Nasssets=%d",request.uid,#Assetlist))
-Mission:_SetRequestID(self,self.queueid)
-self:T(self.lid..string.format("Mission %s [%s] got Request ID=%d",Mission:GetName(),Mission:GetType(),self.queueid))
-if request then
-if self:IsShip()then
-self:T(self.lid.."Warehouse physical structure is SHIP. Requestes assets will be late activated!")
-request.lateActivation=true
-end
-end
-end
-end
-function LEGION:onafterTransportAssign(From,Event,To,Transport,Legions)
-for _,_Legion in pairs(Legions)do
-local Legion=_Legion
-self:T(self.lid..string.format("Assigning transport %d to legion %s",Transport.uid,Legion.alias))
-Legion:AddOpsTransport(Transport)
-Legion:TransportRequest(Transport)
-end
-end
-function LEGION:onafterTransportRequest(From,Event,To,OpsTransport)
-local AssetList={}
-for i,_asset in pairs(OpsTransport.assets)do
-local asset=_asset
-if asset.wid==self.uid then
-asset.requested=true
-asset.isReserved=false
-asset.missionTask=ENUMS.MissionTask.TRANSPORT
-table.insert(AssetList,asset)
-end
-end
-if#AssetList>0 then
-OpsTransport:Requested()
-OpsTransport:SetLegionStatus(self,OPSTRANSPORT.Status.REQUESTED)
-local assignment=string.format("Transport-%d",OpsTransport.uid)
-self:_AddRequest(WAREHOUSE.Descriptor.ASSETLIST,AssetList,#AssetList,OpsTransport.prio,assignment)
-OpsTransport.requestID[self.alias]=self.queueid
-end
-end
-function LEGION:onafterTransportCancel(From,Event,To,Transport)
-self:T(self.lid..string.format("Cancel transport UID=%d",Transport.uid))
-Transport:SetLegionStatus(self,OPSTRANSPORT.Status.CANCELLED)
-for i=#Transport.assets,1,-1 do
-local asset=Transport.assets[i]
-if asset.wid==self.uid then
-local opsgroup=asset.flightgroup
-if opsgroup then
-opsgroup:TransportCancel(Transport)
-end
-local cargos=Transport:GetCargoOpsGroups(false)
-for _,_cargo in pairs(cargos)do
-local cargo=_cargo
-cargo:_DelMyLift(Transport)
-local legion=cargo.legion
-if legion then
-legion:T(self.lid..string.format("Adding cargo group %s back to legion",cargo:GetName()))
-legion:__AddAsset(0.1,cargo.group,1)
-end
-end
-Transport:DelAsset(asset)
-asset.requested=nil
-asset.isReserved=nil
-end
-end
-if Transport.requestID[self.alias]then
-self:_DeleteQueueItemByID(Transport.requestID[self.alias],self.queue)
-end
-end
-function LEGION:onafterMissionCancel(From,Event,To,Mission)
-self:T(self.lid..string.format("Cancel mission %s",Mission.name))
-Mission:SetLegionStatus(self,AUFTRAG.Status.CANCELLED)
-for i=#Mission.assets,1,-1 do
-local asset=Mission.assets[i]
-if asset.wid==self.uid then
-local opsgroup=asset.flightgroup
-if opsgroup then
-opsgroup:MissionCancel(Mission)
-end
-Mission:DelAsset(asset)
-asset.requested=nil
-asset.isReserved=nil
-end
-end
-local requestID=Mission:_GetRequestID(self)
-if requestID then
-self:_DeleteQueueItemByID(requestID,self.queue)
-end
-end
-function LEGION:onafterOpsOnMission(From,Event,To,OpsGroup,Mission)
-self:T2(self.lid..string.format("Group %s on mission %s [%s]",OpsGroup:GetName(),Mission:GetName(),Mission:GetType()))
-if self:IsAirwing()then
-self:FlightOnMission(OpsGroup,Mission)
-elseif self:IsBrigade()then
-self:ArmyOnMission(OpsGroup,Mission)
-else
-self:NavyOnMission(OpsGroup,Mission)
-end
-if self:IsBrigade()and self:IsShip()then
-OpsGroup:PauseMission()
-self.warehouseOpsGroup:Load(OpsGroup,self.warehouseOpsElement)
-end
-if self.chief then
-self.chief:OpsOnMission(OpsGroup,Mission)
-end
-if self.commander then
-self.commander:OpsOnMission(OpsGroup,Mission)
-end
-end
-function LEGION:onafterNewAsset(From,Event,To,asset,assignment)
-self:GetParent(self,LEGION).onafterNewAsset(self,From,Event,To,asset,assignment)
-local text=string.format("New asset %s with assignment %s and request assignment %s",asset.spawngroupname,tostring(asset.assignment),tostring(assignment))
-self:T(self.lid..text)
-local cohort=self:_GetCohort(asset.assignment)
-if cohort then
-if asset.assignment==assignment then
-local nunits=#asset.template.units
-local text=string.format("Adding asset to cohort %s: assignment=%s, type=%s, attribute=%s, nunits=%d ngroup=%s",cohort.name,assignment,asset.unittype,asset.attribute,nunits,tostring(cohort.ngrouping))
-self:T(self.lid..text)
-if cohort.ngrouping then
-local template=asset.template
-local N=math.max(#template.units,cohort.ngrouping)
-asset.weight=0
-asset.cargobaytot=0
-for i=1,N do
-local unit=template.units[i]
-if i>nunits then
-table.insert(template.units,UTILS.DeepCopy(template.units[1]))
-asset.cargobaytot=asset.cargobaytot+asset.cargobay[1]
-asset.weight=asset.weight+asset.weights[1]
-template.units[i].x=template.units[1].x+5*(i-nunits)
-template.units[i].y=template.units[1].y+5*(i-nunits)
-else
-if i<=cohort.ngrouping then
-asset.weight=asset.weight+asset.weights[i]
-asset.cargobaytot=asset.cargobaytot+asset.cargobay[i]
-end
-end
-if i>cohort.ngrouping then
-template.units[i]=nil
-end
-end
-asset.nunits=cohort.ngrouping
-self:T(self.lid..string.format("After regrouping: Nunits=%d, weight=%.1f cargobaytot=%.1f kg",#asset.template.units,asset.weight,asset.cargobaytot))
-end
-asset.takeoffType=cohort.takeoffType~=nil and cohort.takeoffType or self.takeoffType
-asset.parkingIDs=cohort.parkingIDs
-cohort:GetCallsign(asset)
-cohort:GetModex(asset)
-asset.spawngroupname=string.format("%s_AID-%d",cohort.name,asset.uid)
-cohort:AddAsset(asset)
-else
-self:T(self.lid..string.format("Asset returned to legion ==> calling LegionAssetReturned event"))
-asset.takeoffType=cohort.takeoffType
-self:LegionAssetReturned(cohort,asset)
-end
-end
-end
-function LEGION:onafterLegionAssetReturned(From,Event,To,Cohort,Asset)
-self:T(self.lid..string.format("Asset %s from Cohort %s returned! asset.assignment=\"%s\"",Asset.spawngroupname,Cohort.name,tostring(Asset.assignment)))
-if Asset.flightgroup and not Asset.flightgroup:IsStopped()then
-Asset.flightgroup:Stop()
-end
-if Asset.flightgroup:IsFlightgroup()then
-self:ReturnPayloadFromAsset(Asset)
-end
-if Asset.tacan then
-Cohort:ReturnTacan(Asset.tacan)
-end
-Asset.Treturned=timer.getAbsTime()
-end
-function LEGION:onafterAssetSpawned(From,Event,To,group,asset,request)
-self:T({From,Event,To,group:GetName(),asset.assignment,request.assignment})
-self:GetParent(self,LEGION).onafterAssetSpawned(self,From,Event,To,group,asset,request)
-local cohort=self:_GetCohortOfAsset(asset)
-if cohort then
-self:T(self.lid..string.format("Cohort asset spawned %s",asset.spawngroupname))
-local flightgroup=self:_CreateFlightGroup(asset)
-asset.flightgroup=flightgroup
-asset.requested=nil
-asset.Treturned=nil
-local Tacan=cohort:FetchTacan()
-if Tacan then
-asset.tacan=Tacan
-flightgroup:SwitchTACAN(Tacan,Morse,UnitName,Band)
-end
-local radioFreq,radioModu=cohort:GetRadio()
-if radioFreq then
-flightgroup:SwitchRadio(radioFreq,radioModu)
-end
-if cohort.fuellow then
-flightgroup:SetFuelLowThreshold(cohort.fuellow)
-end
-if cohort.fuellowRefuel then
-flightgroup:SetFuelLowRefuel(cohort.fuellowRefuel)
-end
-local assignment=request.assignment
-if self:IsFleet()then
-flightgroup:SetPathfinding(self.pathfinding)
-end
-if string.find(assignment,"Mission-")then
-local uid=UTILS.Split(assignment,"-")[2]
-local mission=self:GetMissionByID(uid)
-local despawnLanding=cohort.despawnAfterLanding~=nil and cohort.despawnAfterLanding or self.despawnAfterLanding
-if despawnLanding then
-flightgroup:SetDespawnAfterLanding()
-end
-local despawnHolding=cohort.despawnAfterHolding~=nil and cohort.despawnAfterHolding or self.despawnAfterHolding
-if despawnHolding then
-flightgroup:SetDespawnAfterHolding()
-end
-if mission then
-if Tacan then
-end
-flightgroup:AddMission(mission)
-if self:IsBrigade()or self:IsFleet()then
-flightgroup:SetReturnOnOutOfAmmo()
-end
-self:__OpsOnMission(5,flightgroup,mission)
-else
-if Tacan then
-end
-end
-local chief=self.chief or(self.commander and self.commander.chief or nil)
-if chief then
-self:T(self.lid..string.format("Adding group %s to agents of CHIEF",group:GetName()))
-chief.detectionset:AddGroup(asset.flightgroup.group)
-end
-elseif string.find(assignment,"Transport-")then
-local uid=UTILS.Split(assignment,"-")[2]
-local transport=self:GetTransportByID(uid)
-if transport then
-flightgroup:AddOpsTransport(transport)
-end
-end
-end
-end
-function LEGION:onafterAssetDead(From,Event,To,asset,request)
-self:GetParent(self,LEGION).onafterAssetDead(self,From,Event,To,asset,request)
-if self.commander and self.commander.chief then
-self.commander.chief.detectionset:RemoveGroupsByName({asset.spawngroupname})
-end
-end
-function LEGION:onafterDestroyed(From,Event,To)
-self:T(self.lid.."Legion warehouse destroyed!")
-for _,_mission in pairs(self.missionqueue)do
-local mission=_mission
-mission:Cancel()
-end
-for _,_cohort in pairs(self.cohorts)do
-local cohort=_cohort
-cohort:Stop()
-end
-self:GetParent(self,LEGION).onafterDestroyed(self,From,Event,To)
-end
-function LEGION:onafterRequest(From,Event,To,Request)
-if Request.toself then
-local assets=Request.cargoassets
-local Mission=self:GetMissionByID(Request.assignment)
-if Mission and assets then
-for _,_asset in pairs(assets)do
-local asset=_asset
-end
-end
-end
-self:GetParent(self,LEGION).onafterRequest(self,From,Event,To,Request)
-end
-function LEGION:onafterSelfRequest(From,Event,To,groupset,request)
-self:GetParent(self,LEGION).onafterSelfRequest(self,From,Event,To,groupset,request)
-local mission=self:GetMissionByID(request.assignment)
-for _,_asset in pairs(request.assets)do
-local asset=_asset
-end
-for _,_group in pairs(groupset:GetSet())do
-local group=_group
-end
-end
-function LEGION:onafterRequestSpawned(From,Event,To,Request,CargoGroupSet,TransportGroupSet)
-self:GetParent(self,LEGION).onafterRequestSpawned(self,From,Event,To,Request,CargoGroupSet,TransportGroupSet)
-end
-function LEGION:onafterCaptured(From,Event,To,Coalition,Country)
-self:GetParent(self,LEGION).onafterCaptured(self,From,Event,To,Coalition,Country)
-if self.chief then
-self.chief.commander:LegionLost(self,Coalition,Country)
-self.chief:LegionLost(self,Coalition,Country)
-self.chief:RemoveLegion(self)
-elseif self.commander then
-self.commander:LegionLost(self,Coalition,Country)
-self.commander:RemoveLegion(self)
-end
-end
-function LEGION:_CreateFlightGroup(asset)
-local opsgroup=nil
-if self:IsAirwing()then
-opsgroup=FLIGHTGROUP:New(asset.spawngroupname)
-elseif self:IsBrigade()then
-opsgroup=ARMYGROUP:New(asset.spawngroupname)
-elseif self:IsFleet()then
-opsgroup=NAVYGROUP:New(asset.spawngroupname)
-else
-self:E(self.lid.."ERROR: not airwing or brigade!")
-end
-opsgroup:_SetLegion(self)
-opsgroup.cohort=self:_GetCohortOfAsset(asset)
-opsgroup.homebase=self.airbase
-opsgroup.homezone=self.spawnzone
-if opsgroup.cohort.weaponData then
-local text="Weapon data for group:"
-opsgroup.weaponData=opsgroup.weaponData or{}
-for bittype,_weapondata in pairs(opsgroup.cohort.weaponData)do
-local weapondata=_weapondata
-opsgroup.weaponData[bittype]=UTILS.DeepCopy(weapondata)
-text=text..string.format("\n- Bit=%s: Rmin=%.1f km, Rmax=%.1f km",bittype,weapondata.RangeMin/1000,weapondata.RangeMax/1000)
-end
-self:T3(self.lid..text)
-end
-return opsgroup
-end
-function LEGION:IsAssetOnMission(asset,MissionTypes)
-if MissionTypes then
-if type(MissionTypes)~="table"then
-MissionTypes={MissionTypes}
-end
-else
-MissionTypes=AUFTRAG.Type
-end
-if asset.flightgroup and asset.flightgroup:IsAlive()then
-for _,_mission in pairs(asset.flightgroup.missionqueue or{})do
-local mission=_mission
-if mission:IsNotOver()then
-local status=mission:GetGroupStatus(asset.flightgroup)
-if(status==AUFTRAG.GroupStatus.STARTED or status==AUFTRAG.GroupStatus.EXECUTING)and AUFTRAG.CheckMissionType(mission.type,MissionTypes)then
-return true
-end
-end
-end
-end
-return false
-end
-function LEGION:GetAssetCurrentMission(asset)
-if asset.flightgroup then
-return asset.flightgroup:GetMissionCurrent()
-end
-return nil
-end
-function LEGION:CountPayloadsInStock(MissionTypes,UnitTypes,Payloads)
-if MissionTypes then
-if type(MissionTypes)=="string"then
-MissionTypes={MissionTypes}
-end
-end
-if UnitTypes then
-if type(UnitTypes)=="string"then
-UnitTypes={UnitTypes}
-end
-end
-local function _checkUnitTypes(payload)
-if UnitTypes then
-for _,unittype in pairs(UnitTypes)do
-if unittype==payload.aircrafttype then
-return true
-end
-end
-else
-return true
-end
-return false
-end
-local function _checkPayloads(payload)
-if Payloads then
-for _,Payload in pairs(Payloads)do
-if Payload.uid==payload.uid then
-return true
-end
-end
-else
-return nil
-end
-return false
-end
-local n=0
-for _,_payload in pairs(self.payloads or{})do
-local payload=_payload
-for _,MissionType in pairs(MissionTypes)do
-local specialpayload=_checkPayloads(payload)
-local compatible=AUFTRAG.CheckMissionCapability(MissionType,payload.capabilities)
-local goforit=specialpayload or(specialpayload==nil and compatible)
-if goforit and _checkUnitTypes(payload)then
-if payload.unlimited then
-return 999
-else
-n=n+payload.navail
-end
-end
-end
-end
-return n
-end
-function LEGION:CountMissionsInQueue(MissionTypes)
-MissionTypes=MissionTypes or AUFTRAG.Type
-local N=0
-for _,_mission in pairs(self.missionqueue)do
-local mission=_mission
-if mission:IsNotOver()and AUFTRAG.CheckMissionType(mission.type,MissionTypes)then
-N=N+1
-end
-end
-return N
-end
-function LEGION:CountAssets(InStock,MissionTypes,Attributes)
-local N=0
-for _,_cohort in pairs(self.cohorts)do
-local cohort=_cohort
-N=N+cohort:CountAssets(InStock,MissionTypes,Attributes)
-end
-return N
-end
-function LEGION:GetOpsGroups(MissionTypes,Attributes)
-local setLegion=SET_OPSGROUP:New()
-for _,_cohort in pairs(self.cohorts)do
-local cohort=_cohort
-local setCohort=cohort:GetOpsGroups(MissionTypes,Attributes)
-self:T2(self.lid..string.format("Found %d opsgroups of cohort %s",setCohort:Count(),cohort.name))
-setLegion:AddSet(setCohort)
-end
-return setLegion
-end
-function LEGION:CountAssetsWithPayloadsInStock(Payloads,MissionTypes,Attributes)
-local N=0
-local Npayloads={}
-for _,_cohort in pairs(self.cohorts)do
-local cohort=_cohort
-if Npayloads[cohort.aircrafttype]==nil then
-Npayloads[cohort.aircrafttype]=self:CountPayloadsInStock(MissionTypes,cohort.aircrafttype,Payloads)
-self:T3(self.lid..string.format("Got Npayloads=%d for type=%s",Npayloads[cohort.aircrafttype],cohort.aircrafttype))
-end
-end
-for _,_cohort in pairs(self.cohorts)do
-local cohort=_cohort
-local n=cohort:CountAssets(true,MissionTypes,Attributes)
-local p=Npayloads[cohort.aircrafttype]or 0
-local m=math.min(n,p)
-N=N+m
-Npayloads[cohort.aircrafttype]=Npayloads[cohort.aircrafttype]-m
-end
-return N
-end
-function LEGION:CountAssetsOnMission(MissionTypes,Cohort)
-local Nq=0
-local Np=0
-for _,_mission in pairs(self.missionqueue)do
-local mission=_mission
-if AUFTRAG.CheckMissionType(mission.type,MissionTypes or AUFTRAG.Type)then
-for _,_asset in pairs(mission.assets or{})do
-local asset=_asset
-if asset.wid==self.uid then
-if Cohort==nil or Cohort.name==asset.squadname then
-local request,isqueued=self:GetRequestByID(mission.requestID[self.alias])
-if isqueued then
-Nq=Nq+1
-else
-Np=Np+1
-end
-end
-end
-end
-end
-end
-return Np+Nq,Np,Nq
-end
-function LEGION:GetAssetsOnMission(MissionTypes)
-local assets={}
-local Np=0
-for _,_mission in pairs(self.missionqueue)do
-local mission=_mission
-if AUFTRAG.CheckMissionType(mission.type,MissionTypes)then
-for _,_asset in pairs(mission.assets or{})do
-local asset=_asset
-if asset.wid==self.uid then
-table.insert(assets,asset)
-end
-end
-end
-end
-return assets
-end
-function LEGION:GetAircraftTypes(onlyactive,cohorts)
-local unittypes={}
-for _,_cohort in pairs(cohorts or self.cohorts)do
-local cohort=_cohort
-if(not onlyactive)or cohort:IsOnDuty()then
-local gotit=false
-for _,unittype in pairs(unittypes)do
-if cohort.aircrafttype==unittype then
-gotit=true
-break
-end
-end
-if not gotit then
-table.insert(unittypes,cohort.aircrafttype)
-end
-end
-end
-return unittypes
-end
-function LEGION:_CountPayloads(MissionType,Cohorts,Payloads)
-local Npayloads={}
-for _,_cohort in pairs(Cohorts)do
-local cohort=_cohort
-if Npayloads[cohort.aircrafttype]==nil then
-Npayloads[cohort.aircrafttype]=cohort.legion:IsAirwing()and self:CountPayloadsInStock(MissionType,cohort.aircrafttype,Payloads)or 999
-self:T2(self.lid..string.format("Got N=%d payloads for mission type=%s and unit type=%s",Npayloads[cohort.aircrafttype],MissionType,cohort.aircrafttype))
-end
-end
-return Npayloads
-end
-function LEGION:RecruitAssetsForMission(Mission)
-local NreqMin,NreqMax=Mission:GetRequiredAssets()
-local TargetVec2=Mission:GetTargetVec2()
-local Payloads=Mission.payloads
-local MaxWeight=nil
-if Mission.NcarriersMin then
-local legions={self}
-local cohorts=self.cohorts
-if Mission.transportLegions or Mission.transportCohorts then
-legions=Mission.transportLegions
-cohorts=Mission.transportCohorts
-end
-local Cohorts=LEGION._GetCohorts(legions,cohorts)
-local transportcohorts={}
-for _,_cohort in pairs(Cohorts)do
-local cohort=_cohort
-local can=LEGION._CohortCan(cohort,AUFTRAG.Type.OPSTRANSPORT,Mission.carrierCategories,Mission.carrierAttributes,Mission.carrierProperties,nil,TargetVec2)
-if can and(MaxWeight==nil or cohort.cargobayLimit>MaxWeight)then
-MaxWeight=cohort.cargobayLimit
-end
-end
-self:T(self.lid..string.format("Largest cargo bay available=%.1f",MaxWeight))
-end
-local legions={self}
-local cohorts=self.cohorts
-if Mission.specialLegions or Mission.specialCohorts then
-legions=Mission.specialLegions
-cohorts=Mission.specialCohorts
-end
-local Cohorts=LEGION._GetCohorts(legions,cohorts,Operation,OpsQueue)
-local recruited,assets,legions=LEGION.RecruitCohortAssets(Cohorts,Mission.type,Mission.alert5MissionType,NreqMin,NreqMax,TargetVec2,Payloads,
-Mission.engageRange,Mission.refuelSystem,nil,nil,MaxWeight,nil,Mission.attributes,Mission.properties,{Mission.engageWeaponType})
-return recruited,assets,legions
-end
-function LEGION:RecruitAssetsForTransport(Transport)
-local cargoOpsGroups=Transport:GetCargoOpsGroups(false)
-local weightGroup=0
-local TotalWeight=nil
-if#cargoOpsGroups>0 then
-TotalWeight=0
-for _,_opsgroup in pairs(cargoOpsGroups)do
-local opsgroup=_opsgroup
-local weight=opsgroup:GetWeightTotal()
-if weight>weightGroup then
-weightGroup=weight
-end
-TotalWeight=TotalWeight+weight
-end
-else
-return false
-end
-local TargetVec2=Transport:GetDeployZone():GetVec2()
-local NreqMin,NreqMax=Transport:GetRequiredCarriers()
-local recruited,assets,legions=LEGION.RecruitCohortAssets(self.cohorts,AUFTRAG.Type.OPSTRANSPORT,nil,NreqMin,NreqMax,TargetVec2,nil,nil,nil,weightGroup,TotalWeight)
-return recruited,assets,legions
-end
-function LEGION:RecruitAssetsForEscort(Mission,Assets)
-if Mission.NescortMin and Mission.NescortMax and(Mission.NescortMin>0 or Mission.NescortMax>0)then
-self:T(self.lid..string.format("Requested escort for mission %s [%s]. Required assets=%d-%d",Mission:GetName(),Mission:GetType(),Mission.NescortMin,Mission.NescortMax))
-local Cohorts={}
-for _,_legion in pairs(Mission.escortLegions or{})do
-local legion=_legion
-for _,_cohort in pairs(legion.cohorts)do
-local cohort=_cohort
-table.insert(Cohorts,cohort)
-end
-end
-for _,_cohort in pairs(Mission.escortCohorts or{})do
-local cohort=_cohort
-table.insert(Cohorts,cohort)
-end
-if#Cohorts==0 then
-Cohorts=self.cohorts
-end
-local assigned=LEGION.AssignAssetsForEscort(self,Cohorts,Assets,Mission.NescortMin,Mission.NescortMax,Mission.escortMissionType,Mission.escortTargetTypes)
-return assigned
-end
-return true
-end
-function LEGION._GetCohorts(Legions,Cohorts,Operation,OpsQueue)
-OpsQueue=OpsQueue or{}
-local function CheckOperation(LegionOrCohort)
-if#OpsQueue==0 then
-return true
-end
-local isAvail=true
-if Operation then
-isAvail=false
-end
-for _,_operation in pairs(OpsQueue)do
-local operation=_operation
-local isOps=operation:IsAssignedCohortOrLegion(LegionOrCohort)
-if isOps and operation:IsRunning()then
-isAvail=false
-if Operation==nil then
-return false
-else
-if Operation.uid==operation.uid then
-return true
-end
-end
-end
-end
-return isAvail
-end
-local cohorts={}
-if(Legions and#Legions>0)or(Cohorts and#Cohorts>0)then
-for _,_legion in pairs(Legions or{})do
-local legion=_legion
-local Runway=legion:IsAirwing()and legion:IsRunwayOperational()or true
-if legion:IsRunning()and Runway then
-for _,_cohort in pairs(legion.cohorts)do
-local cohort=_cohort
-if(CheckOperation(cohort.legion)or CheckOperation(cohort))and not UTILS.IsInTable(cohorts,cohort,"name")then
-table.insert(cohorts,cohort)
-end
-end
-end
-end
-for _,_cohort in pairs(Cohorts or{})do
-local cohort=_cohort
-if CheckOperation(cohort)and not UTILS.IsInTable(cohorts,cohort,"name")then
-table.insert(cohorts,cohort)
-end
-end
-end
-return cohorts
-end
-function LEGION._CohortCan(Cohort,MissionType,Categories,Attributes,Properties,WeaponTypes,TargetVec2,RangeMax,RefuelSystem,CargoWeight,MaxWeight)
-local function CheckCategory(_cohort)
-local cohort=_cohort
-if Categories and#Categories>0 then
-for _,category in pairs(Categories)do
-if category==cohort.category then
-return true
-end
-end
-else
-return true
-end
-end
-local function CheckAttribute(_cohort)
-local cohort=_cohort
-if Attributes and#Attributes>0 then
-for _,attribute in pairs(Attributes)do
-if attribute==cohort.attribute then
-return true
-end
-end
-else
-return true
-end
-end
-local function CheckProperty(_cohort)
-local cohort=_cohort
-if Properties and#Properties>0 then
-for _,Property in pairs(Properties)do
-for property,value in pairs(cohort.properties)do
-if Property==property then
-return true
-end
-end
-end
-else
-return true
-end
-end
-local function CheckWeapon(_cohort)
-local cohort=_cohort
-if WeaponTypes and#WeaponTypes>0 then
-for _,WeaponType in pairs(WeaponTypes)do
-if WeaponType==ENUMS.WeaponFlag.Auto then
-return true
-else
-for _,_weaponData in pairs(cohort.weaponData or{})do
-local weaponData=_weaponData
-if weaponData.BitType==WeaponType then
-return true
-end
-end
-end
-end
-return false
-else
-return true
-end
-end
-local function CheckRange(_cohort)
-local cohort=_cohort
-local TargetDistance=TargetVec2 and UTILS.VecDist2D(TargetVec2,cohort.legion:GetVec2())or 0
-local Rmax=cohort:GetMissionRange(WeaponTypes)
-local RangeMax=RangeMax or 0
-local InRange=(RangeMax and math.max(RangeMax,Rmax)or Rmax)>=TargetDistance
-return InRange
-end
-local function CheckRefueling(_cohort)
-local cohort=_cohort
-if RefuelSystem then
-if cohort.tankerSystem then
-return RefuelSystem==cohort.tankerSystem
-else
-return false
-end
-else
-return true
-end
-end
-local function CheckCargoWeight(_cohort)
-local cohort=_cohort
-if CargoWeight~=nil then
-return cohort.cargobayLimit>=CargoWeight
-else
-return true
-end
-end
-local function CheckMaxWeight(_cohort)
-local cohort=_cohort
-if MaxWeight~=nil then
-cohort:T(string.format("Cohort weight=%.1f | max weight=%.1f",cohort.weightAsset,MaxWeight))
-return cohort.weightAsset<=MaxWeight
-else
-return true
-end
-end
-local can=AUFTRAG.CheckMissionCapability(MissionType,Cohort.missiontypes)
-if can then
-can=CheckCategory(Cohort)
-else
-Cohort:T(Cohort.lid..string.format("Cohort %s cannot because of mission types",Cohort.name))
-return false
-end
-if can then
-if MissionType==AUFTRAG.Type.RELOCATECOHORT then
-can=Cohort:IsRelocating()
-else
-can=Cohort:IsOnDuty()
-end
-else
-Cohort:T(Cohort.lid..string.format("Cohort %s cannot because of category",Cohort.name))
-return false
-end
-if can then
-can=CheckAttribute(Cohort)
-else
-Cohort:T(Cohort.lid..string.format("Cohort %s cannot because of readyiness",Cohort.name))
-return false
-end
-if can then
-can=CheckProperty(Cohort)
-else
-Cohort:T(Cohort.lid..string.format("Cohort %s cannot because of attribute",Cohort.name))
-return false
-end
-if can then
-can=CheckWeapon(Cohort)
-else
-Cohort:T(Cohort.lid..string.format("Cohort %s cannot because of property",Cohort.name))
-return false
-end
-if can then
-can=CheckRange(Cohort)
-else
-Cohort:T(Cohort.lid..string.format("Cohort %s cannot because of weapon type",Cohort.name))
-return false
-end
-if can then
-can=CheckRefueling(Cohort)
-else
-Cohort:T(Cohort.lid..string.format("Cohort %s cannot because of range",Cohort.name))
-return false
-end
-if can then
-can=CheckCargoWeight(Cohort)
-else
-Cohort:T(Cohort.lid..string.format("Cohort %s cannot because of refueling system",Cohort.name))
-return false
-end
-if can then
-can=CheckMaxWeight(Cohort)
-else
-Cohort:T(Cohort.lid..string.format("Cohort %s cannot because of cargo weight",Cohort.name))
-return false
-end
-if can then
-return true
-else
-Cohort:T(Cohort.lid..string.format("Cohort %s cannot because of max weight",Cohort.name))
-return false
-end
-return nil
-end
-function LEGION.RecruitCohortAssets(Cohorts,MissionTypeRecruit,MissionTypeOpt,NreqMin,NreqMax,TargetVec2,Payloads,RangeMax,RefuelSystem,CargoWeight,TotalWeight,MaxWeight,Categories,Attributes,Properties,WeaponTypes)
-local Assets={}
-local Legions={}
-if MissionTypeOpt==nil then
-MissionTypeOpt=MissionTypeRecruit
-end
-for _,_cohort in pairs(Cohorts)do
-local cohort=_cohort
-local can=LEGION._CohortCan(cohort,MissionTypeRecruit,Categories,Attributes,Properties,WeaponTypes,TargetVec2,RangeMax,RefuelSystem,CargoWeight,MaxWeight)
-if can then
-local assets,npayloads=cohort:RecruitAssets(MissionTypeRecruit,999)
-for _,asset in pairs(assets)do
-table.insert(Assets,asset)
-end
-end
-end
-LEGION._OptimizeAssetSelection(Assets,MissionTypeOpt,TargetVec2,false)
-for _,_asset in pairs(Assets)do
-local asset=_asset
-if asset.legion:IsAirwing()and not asset.payload then
-asset.payload=asset.legion:FetchPayloadFromStock(asset.unittype,MissionTypeOpt,Payloads)
-end
-end
-for i=#Assets,1,-1 do
-local asset=Assets[i]
-if asset.legion:IsAirwing()and not asset.payload then
-table.remove(Assets,i)
-end
-end
-LEGION._OptimizeAssetSelection(Assets,MissionTypeOpt,TargetVec2,true)
-local Nassets=math.min(#Assets,NreqMax)
-if#Assets>=NreqMin then
-local cargobay=0
-for i=1,Nassets do
-local asset=Assets[i]
-asset.isReserved=true
-Legions[asset.legion.alias]=asset.legion
-if TotalWeight then
-local N=math.floor(asset.cargobaytot/asset.nunits/CargoWeight)*asset.nunits
-cargobay=cargobay+N*CargoWeight
-if cargobay>=TotalWeight then
-Nassets=i
-break
-end
-end
-end
-for i=#Assets,Nassets+1,-1 do
-local asset=Assets[i]
-if asset.legion:IsAirwing()and not asset.spawned then
-asset.legion:T2(asset.legion.lid..string.format("Returning payload from asset %s",asset.spawngroupname))
-asset.legion:ReturnPayloadFromAsset(asset)
-end
-table.remove(Assets,i)
-end
-return true,Assets,Legions
-else
-for i=1,#Assets do
-local asset=Assets[i]
-if asset.legion:IsAirwing()and not asset.spawned then
-asset.legion:T2(asset.legion.lid..string.format("Returning payload from asset %s",asset.spawngroupname))
-asset.legion:ReturnPayloadFromAsset(asset)
-end
-end
-return false,{},{}
-end
-return false,{},{}
-end
-function LEGION.UnRecruitAssets(Assets,Mission)
-for i=1,#Assets do
-local asset=Assets[i]
-asset.isReserved=false
-if asset.legion:IsAirwing()and not asset.spawned then
-asset.legion:T2(asset.legion.lid..string.format("Returning payload from asset %s",asset.spawngroupname))
-asset.legion:ReturnPayloadFromAsset(asset)
-end
-if Mission then
-Mission:DelAsset(asset)
-end
-end
-end
-function LEGION:AssignAssetsForEscort(Cohorts,Assets,NescortMin,NescortMax,MissionType,TargetTypes,EngageRange)
-if NescortMin and NescortMax and(NescortMin>0 or NescortMax>0)then
-self:T(self.lid..string.format("Requested escort for %d assets from %d cohorts. Required escort assets=%d-%d",#Assets,#Cohorts,NescortMin,NescortMax))
-local Escorts={}
-local EscortAvail=true
-for _,_asset in pairs(Assets)do
-local asset=_asset
-local TargetVec2=asset.legion:GetVec2()
-local Categories={Group.Category.HELICOPTER}
-local targetTypes={"Ground Units"}
-if asset.category==Group.Category.AIRPLANE then
-Categories={Group.Category.AIRPLANE}
-targetTypes={"Air"}
-end
-TargetTypes=TargetTypes or targetTypes
-local Erecruited,eassets,elegions=LEGION.RecruitCohortAssets(Cohorts,AUFTRAG.Type.ESCORT,MissionType,NescortMin,NescortMax,TargetVec2,nil,nil,nil,nil,nil,nil,Categories)
-if Erecruited then
-Escorts[asset.spawngroupname]={EscortLegions=elegions,EscortAssets=eassets,ecategory=asset.category}
-else
-EscortAvail=false
-break
-end
-end
-if EscortAvail then
-local N=0
-for groupname,value in pairs(Escorts)do
-local Elegions=value.EscortLegions
-local Eassets=value.EscortAssets
-local ecategory=value.ecategory
-for _,_legion in pairs(Elegions)do
-local legion=_legion
-local OffsetVector=nil
-if ecategory==Group.Category.GROUND then
-OffsetVector={}
-OffsetVector.x=0
-OffsetVector.y=UTILS.FeetToMeters(1000)
-OffsetVector.z=0
-elseif MissionType==AUFTRAG.Type.SEAD then
-OffsetVector={}
-OffsetVector.x=-100
-OffsetVector.y=500
-OffsetVector.z=500
-end
-local escort=AUFTRAG:NewESCORT(groupname,OffsetVector,EngageRange,TargetTypes)
-if MissionType==AUFTRAG.Type.SEAD then
-escort.missionTask=ENUMS.MissionTask.SEAD
-local DCStask=CONTROLLABLE.EnRouteTaskSEAD(nil)
-table.insert(escort.enrouteTasks,DCStask)
-end
-for _,_asset in pairs(Eassets)do
-local asset=_asset
-escort:AddAsset(asset)
-N=N+1
-end
-self:MissionAssign(escort,{legion})
-end
-end
-self:T(self.lid..string.format("Recruited %d escort assets",N))
-return true
-else
-self:T(self.lid..string.format("Could not get at least one escort!"))
-for groupname,value in pairs(Escorts)do
-local Eassets=value.EscortAssets
-LEGION.UnRecruitAssets(Eassets)
-end
-return false
-end
-else
-self:T(self.lid..string.format("No escort required! NescortMin=%s, NescortMax=%s",tostring(NescortMin),tostring(NescortMax)))
-return true
-end
-end
-function LEGION:AssignAssetsForTransport(Legions,CargoAssets,NcarriersMin,NcarriersMax,DeployZone,DisembarkZone,Categories,Attributes,Properties)
-if NcarriersMin and NcarriersMax and(NcarriersMin>0 or NcarriersMax>0)then
-local Cohorts=LEGION._GetCohorts(Legions)
-local CargoLegions={};local CargoWeight=nil;local TotalWeight=0
-for _,_asset in pairs(CargoAssets)do
-local asset=_asset
-CargoLegions[asset.legion.alias]=asset.legion
-if CargoWeight==nil or asset.weight>CargoWeight then
-CargoWeight=asset.weight
-end
-TotalWeight=TotalWeight+asset.weight
-end
-self:T(self.lid..string.format("Cargo weight=%.1f",CargoWeight))
-self:T(self.lid..string.format("Total weight=%.1f",TotalWeight))
-local TargetVec2=DeployZone:GetVec2()
-local TransportAvail,CarrierAssets,CarrierLegions=
-LEGION.RecruitCohortAssets(Cohorts,AUFTRAG.Type.OPSTRANSPORT,nil,NcarriersMin,NcarriersMax,TargetVec2,nil,nil,nil,CargoWeight,TotalWeight,nil,Categories,Attributes,Properties)
-if TransportAvail then
-local Transport=OPSTRANSPORT:New(nil,nil,DeployZone)
-if DisembarkZone then
-Transport:SetDisembarkZone(DisembarkZone)
-end
-self:T(self.lid..string.format("Transport available with %d carrier assets",#CarrierAssets))
-for _,_legion in pairs(CargoLegions)do
-local legion=_legion
-local pickupzone=legion.spawnzone
-local tpz=Transport:AddTransportZoneCombo(nil,pickupzone,Transport:GetDeployZone())
-tpz.PickupAirbase=legion:IsRunwayOperational()and legion.airbase or nil
-Transport:SetEmbarkZone(legion.spawnzone,tpz)
-for _,_asset in pairs(CargoAssets)do
-local asset=_asset
-if asset.legion.alias==legion.alias then
-Transport:AddAssetCargo(asset,tpz)
-end
-end
-end
-for _,_asset in pairs(CarrierAssets)do
-local asset=_asset
-Transport:AddAsset(asset)
-end
-self:TransportAssign(Transport,CarrierLegions)
-return true,Transport
-else
-self:T(self.lid..string.format("Transport assets could not be allocated ==> Unrecruiting assets"))
-LEGION.UnRecruitAssets(CarrierAssets)
-return false,nil
-end
-return nil,nil
-end
-return true,nil
-end
-function LEGION.CalculateAssetMissionScore(asset,MissionType,TargetVec2,IncludePayload)
-local score=0
-if asset.skill==AI.Skill.AVERAGE then
-score=score+0
-elseif asset.skill==AI.Skill.GOOD then
-score=score+10
-elseif asset.skill==AI.Skill.HIGH then
-score=score+20
-elseif asset.skill==AI.Skill.EXCELLENT then
-score=score+30
-end
-score=score+asset.cohort:GetMissionPeformance(MissionType)
-local function scorePayload(Payload,MissionType)
-for _,Capability in pairs(Payload.capabilities)do
-local capability=Capability
-if capability.MissionType==MissionType then
-return capability.Performance
-end
-end
-return 0
-end
-if IncludePayload and asset.payload then
-score=score+scorePayload(asset.payload,MissionType)
-end
-local OrigVec2=asset.flightgroup and asset.flightgroup:GetVec2()or asset.legion:GetVec2()
-local distance=0
-if TargetVec2 and OrigVec2 then
-distance=UTILS.MetersToNM(UTILS.VecDist2D(OrigVec2,TargetVec2))
-if asset.category==Group.Category.AIRPLANE or asset.category==Group.Category.HELICOPTER then
-distance=UTILS.Round(distance/10,0)
-else
-distance=UTILS.Round(distance,0)
-end
-end
-score=score-distance
-if asset.spawned and asset.flightgroup and asset.flightgroup:IsAlive()then
-local currmission=asset.flightgroup:GetMissionCurrent()
-if currmission then
-if currmission.type==AUFTRAG.Type.ALERT5 and currmission.alert5MissionType==MissionType then
-score=score+25
-elseif(currmission.type==AUFTRAG.Type.GCICAP or currmission.type==AUFTRAG.Type.PATROLRACETRACK)and MissionType==AUFTRAG.Type.INTERCEPT then
-score=score+35
-elseif(currmission.type==AUFTRAG.Type.ONGUARD or currmission.type==AUFTRAG.Type.PATROLZONE)and(MissionType==AUFTRAG.Type.ARTY or MissionType==AUFTRAG.Type.GROUNDATTACK)then
-score=score+25
-elseif currmission.type==AUFTRAG.Type.NOTHING then
-score=score+25
-end
-end
-if MissionType==AUFTRAG.Type.OPSTRANSPORT or MissionType==AUFTRAG.Type.AMMOSUPPLY or MissionType==AUFTRAG.Type.AWACS or MissionType==AUFTRAG.Type.FUELSUPPLY or MissionType==AUFTRAG.Type.TANKER then
-score=score-10
-else
-if asset.flightgroup:IsOutOfAmmo()then
-score=score-1000
-end
-end
-end
-if MissionType==AUFTRAG.Type.OPSTRANSPORT then
-score=score+UTILS.Round(asset.cargobaymax/10,0)
-end
-if asset.legion and asset.legion.verbose>=2 then
-asset.legion:I(asset.legion.lid..string.format("Asset %s [spawned=%s] score=%d",asset.spawngroupname,tostring(asset.spawned),score))
-end
-return score
-end
-function LEGION._OptimizeAssetSelection(assets,MissionType,TargetVec2,IncludePayload)
-for _,_asset in pairs(assets)do
-local asset=_asset
-asset.score=LEGION.CalculateAssetMissionScore(asset,MissionType,TargetVec2,IncludePayload)
-if IncludePayload then
-local RandomScoreMax=asset.legion and asset.legion.RandomAssetScore or LEGION.RandomAssetScore
-local RandomScore=math.random(0,RandomScoreMax)
-asset.score=asset.score+RandomScore
-end
-end
-local function optimize(a,b)
-local assetA=a
-local assetB=b
-return(assetA.score>assetB.score)
-end
-table.sort(assets,optimize)
-if LEGION.verbose>0 then
-local text=string.format("Optimized %d assets for %s mission/transport (payload=%s):",#assets,MissionType,tostring(IncludePayload))
-for i,Asset in pairs(assets)do
-local asset=Asset
-text=text..string.format("\n%d. %s [%s]: score=%d",i,asset.spawngroupname,asset.squadname,asset.score or-1)
-asset.score=nil
-end
-env.info(text)
-end
-end
-function LEGION:GetMissionByID(mid)
-for _,_mission in pairs(self.missionqueue)do
-local mission=_mission
-if mission.auftragsnummer==tonumber(mid)then
-return mission
-end
-end
-return nil
-end
-function LEGION:GetTransportByID(uid)
-for _,_transport in pairs(self.transportqueue)do
-local transport=_transport
-if transport.uid==tonumber(uid)then
-return transport
-end
-end
-return nil
-end
-function LEGION:GetMissionFromRequestID(RequestID)
-for _,_mission in pairs(self.missionqueue)do
-local mission=_mission
-local mid=mission.requestID[self.alias]
-if mid and mid==RequestID then
-return mission
-end
-end
-return nil
-end
-function LEGION:GetMissionFromRequest(Request)
-return self:GetMissionFromRequestID(Request.uid)
-end
-function LEGION:FetchPayloadFromStock(UnitType,MissionType,Payloads)
-return nil
-end
-function LEGION:ReturnPayloadFromAsset(asset)
-return nil
-end
-NAVYGROUP={
-ClassName="NAVYGROUP",
-turning=false,
-intowind=nil,
-intowindcounter=0,
-Qintowind={},
-pathCorridor=400,
-engage={},
-}
-NAVYGROUP.version="1.0.2"
-function NAVYGROUP:New(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
-local self=BASE:Inherit(self,OPSGROUP:New(group))
-self.lid=string.format("NAVYGROUP %s | ",self.groupname)
-self:SetDefaultROE()
-self:SetDefaultAlarmstate()
-self:SetDefaultEPLRS(self.isEPLRS)
-self:SetDefaultEmission()
-self:SetDetection()
-self:SetPatrolAdInfinitum(true)
-self:SetPathfinding(false)
-self:AddTransition("*","FullStop","Holding")
-self:AddTransition("*","Cruise","Cruising")
-self:AddTransition("*","RTZ","Returning")
-self:AddTransition("Returning","Returned","Returned")
-self:AddTransition("*","Detour","Cruising")
-self:AddTransition("*","DetourReached","*")
-self:AddTransition("*","Retreat","Retreating")
-self:AddTransition("Retreating","Retreated","Retreated")
-self:AddTransition("Cruising","EngageTarget","Engaging")
-self:AddTransition("Holding","EngageTarget","Engaging")
-self:AddTransition("OnDetour","EngageTarget","Engaging")
-self:AddTransition("Engaging","Disengage","Cruising")
-self:AddTransition("*","TurnIntoWind","Cruising")
-self:AddTransition("*","TurnedIntoWind","*")
-self:AddTransition("*","TurnIntoWindStop","*")
-self:AddTransition("*","TurnIntoWindOver","*")
-self:AddTransition("*","TurningStarted","*")
-self:AddTransition("*","TurningStopped","*")
-self:AddTransition("*","CollisionWarning","*")
-self:AddTransition("*","ClearAhead","*")
-self:AddTransition("Cruising","Dive","Cruising")
-self:AddTransition("Engaging","Dive","Engaging")
-self:AddTransition("Cruising","Surface","Cruising")
-self:AddTransition("Engaging","Surface","Engaging")
-self:_InitWaypoints()
-self:_InitGroup()
-self:HandleEvent(EVENTS.Birth,self.OnEventBirth)
-self:HandleEvent(EVENTS.Dead,self.OnEventDead)
-self:HandleEvent(EVENTS.RemoveUnit,self.OnEventRemoveUnit)
-self.timerStatus=TIMER:New(self.Status,self):Start(1,30)
-self.timerQueueUpdate=TIMER:New(self._QueueUpdate,self):Start(2,5)
-self.timerCheckZone=TIMER:New(self._CheckInZones,self):Start(2,60)
-_DATABASE:AddOpsGroup(self)
-return self
-end
-function NAVYGROUP:SetPatrolAdInfinitum(switch)
-if switch==false then
-self.adinfinitum=false
-else
-self.adinfinitum=true
-end
-return self
-end
-function NAVYGROUP:SetPathfinding(Switch,CorridorWidth)
-self.pathfindingOn=Switch
-self.pathCorridor=CorridorWidth or 400
-return self
-end
-function NAVYGROUP:SetPathfindingOn(CorridorWidth)
-self:SetPathfinding(true,CorridorWidth)
-return self
-end
-function NAVYGROUP:SetPathfindingOff()
-self:SetPathfinding(false,self.pathCorridor)
-return self
-end
-function NAVYGROUP:AddTaskFireAtPoint(Coordinate,Clock,Radius,Nshots,WeaponType,Prio)
-local DCStask=CONTROLLABLE.TaskFireAtPoint(nil,Coordinate:GetVec2(),Radius,Nshots,WeaponType)
-local task=self:AddTask(DCStask,Clock,nil,Prio)
-return task
-end
-function NAVYGROUP:AddTaskWaypointFireAtPoint(Coordinate,Waypoint,Radius,Nshots,WeaponType,Prio,Duration)
-Waypoint=Waypoint or self:GetWaypointNext()
-local DCStask=CONTROLLABLE.TaskFireAtPoint(nil,Coordinate:GetVec2(),Radius,Nshots,WeaponType)
-local task=self:AddTaskWaypoint(DCStask,Waypoint,nil,Prio,Duration)
-return task
-end
-function NAVYGROUP:AddTaskAttackGroup(TargetGroup,WeaponExpend,WeaponType,Clock,Prio)
-local DCStask=CONTROLLABLE.TaskAttackGroup(nil,TargetGroup,WeaponType,WeaponExpend,AttackQty,Direction,Altitude,AttackQtyLimit,GroupAttack)
-local task=self:AddTask(DCStask,Clock,nil,Prio)
-return task
-end
-function NAVYGROUP:_CreateTurnIntoWind(starttime,stoptime,speed,uturn,offset)
-local Tnow=timer.getAbsTime()
-if starttime and type(starttime)=="number"then
-starttime=UTILS.SecondsToClock(Tnow+starttime)
-end
-starttime=starttime or UTILS.SecondsToClock(Tnow)
-local Tstart=UTILS.ClockToSeconds(starttime)
-if uturn==nil then
-uturn=true
-end
-local Tstop=Tstart+90*60
-if stoptime==nil then
-Tstop=Tstart+90*60
-elseif type(stoptime)=="number"then
-Tstop=Tstart+stoptime
-else
-Tstop=UTILS.ClockToSeconds(stoptime)
-end
-if Tstart>Tstop then
-self:E(string.format("ERROR:Into wind stop time %s lies before start time %s. Input rejected!",UTILS.SecondsToClock(Tstart),UTILS.SecondsToClock(Tstop)))
-return self
-end
-if Tstop<=Tnow then
-self:E(string.format("WARNING: Into wind stop time %s already over. Tnow=%s! Input rejected.",UTILS.SecondsToClock(Tstop),UTILS.SecondsToClock(Tnow)))
-return self
-end
-self.intowindcounter=self.intowindcounter+1
-local recovery={}
-recovery.Tstart=Tstart
-recovery.Tstop=Tstop
-recovery.Open=false
-recovery.Over=false
-recovery.Speed=speed or 20
-recovery.Uturn=uturn and uturn or false
-recovery.Offset=offset or 0
-recovery.Id=self.intowindcounter
-return recovery
-end
-function NAVYGROUP:AddTurnIntoWind(starttime,stoptime,speed,uturn,offset)
-local recovery=self:_CreateTurnIntoWind(starttime,stoptime,speed,uturn,offset)
-table.insert(self.Qintowind,recovery)
-return recovery
-end
-function NAVYGROUP:RemoveTurnIntoWind(IntoWindData)
-if self.intowind and self.intowind.Id==IntoWindData.Id then
-self:TurnIntoWindStop()
-return
-end
-for i,_tiw in pairs(self.Qintowind)do
-local tiw=_tiw
-if tiw.Id==IntoWindData.Id then
-table.remove(self.Qintowind,i)
-break
-end
-end
-return self
-end
-function NAVYGROUP:IsHolding()
-return self:Is("Holding")
-end
-function NAVYGROUP:IsCruising()
-return self:Is("Cruising")
-end
-function NAVYGROUP:IsOnDetour()
-return self:Is("OnDetour")
-end
-function NAVYGROUP:IsDiving()
-return self:Is("Diving")
-end
-function NAVYGROUP:IsTurning()
-return self.turning
-end
-function NAVYGROUP:IsSteamingIntoWind()
-if self.intowind then
-return true
-else
-return false
-end
-end
-function NAVYGROUP:IsRecovering()
-if self.intowind then
-if self.intowind.Recovery==true then
-return true
-else
-return false
-end
-else
-return false
-end
-end
-function NAVYGROUP:IsLaunching()
-if self.intowind then
-if self.intowind.Recovery==false then
-return true
-else
-return false
-end
-else
-return false
-end
-end
-function NAVYGROUP:Status(From,Event,To)
-local fsmstate=self:GetState()
-local alive=self:IsAlive()
-local freepath=0
-if alive then
-self:_UpdatePosition()
-self:_CheckDetectedUnits()
-self:_CheckTurning()
-local disttoWP=math.min(self:GetDistanceToWaypoint(),UTILS.NMToMeters(10))
-freepath=disttoWP
-if not self:IsTurning()then
-freepath=self:_CheckFreePath(freepath,100)
-if disttoWP>1 and freepathself.Twaiting+self.dTwait then
-self.Twaiting=nil
-self.dTwait=nil
-if self:_CountPausedMissions()>0 then
-self:UnpauseMission()
-else
-self:Cruise()
-end
-end
-end
-end
-local mission=self:GetMissionCurrent()
-if mission and mission.updateDCSTask then
-if mission.type==AUFTRAG.Type.CAPTUREZONE then
-local Task=mission:GetGroupWaypointTask(self)
-if mission:GetGroupStatus(self)==AUFTRAG.GroupStatus.EXECUTING or mission:GetGroupStatus(self)==AUFTRAG.GroupStatus.STARTED then
-self:_UpdateTask(Task,mission)
-end
-end
-end
-else
-self:_CheckDamage()
-end
-if alive~=nil then
-if self.verbose>=1 then
-local nelem=self:CountElements()
-local Nelem=#self.elements
-local nTaskTot,nTaskSched,nTaskWP=self:CountRemainingTasks()
-local nMissions=self:CountRemainingMissison()
-local roe=self:GetROE()or-1
-local als=self:GetAlarmstate()or-1
-local wpidxCurr=self.currentwp
-local wpuidCurr=self:GetWaypointUIDFromIndex(wpidxCurr)or 0
-local wpidxNext=self:GetWaypointIndexNext()or 0
-local wpuidNext=self:GetWaypointUIDFromIndex(wpidxNext)or 0
-local wpN=#self.waypoints or 0
-local wpF=tostring(self.passedfinalwp)
-local speed=UTILS.MpsToKnots(self.velocity or 0)
-local speedEx=UTILS.MpsToKnots(self:GetExpectedSpeed())
-local alt=self.position and self.position.y or 0
-local hdg=self.heading or 0
-local life=self.life or 0
-local ammo=self:GetAmmoTot().Total
-local ndetected=self.detectionOn and tostring(self.detectedunits:Count())or"Off"
-local cargo=0
-for _,_element in pairs(self.elements)do
-local element=_element
-cargo=cargo+element.weightCargo
-end
-local intowind=self:IsSteamingIntoWind()and UTILS.SecondsToClock(self.intowind.Tstop-timer.getAbsTime(),true)or"N/A"
-local turning=tostring(self:IsTurning())
-local text=string.format("%s [%d/%d]: ROE/AS=%d/%d | T/M=%d/%d | Wp=%d[%d]-->%d[%d]/%d [%s] | Life=%.1f | v=%.1f (%d) | Hdg=%03d | Ammo=%d | Detect=%s | Cargo=%.1f | Turn=%s Collision=%d IntoWind=%s",
-fsmstate,nelem,Nelem,roe,als,nTaskTot,nMissions,wpidxCurr,wpuidCurr,wpidxNext,wpuidNext,wpN,wpF,life,speed,speedEx,hdg,ammo,ndetected,cargo,turning,freepath,intowind)
-self:I(self.lid..text)
-end
-else
-local text=string.format("State %s: Alive=%s",fsmstate,tostring(self:IsAlive()))
-self:T(self.lid..text)
-end
-if alive and self.verbose>=2 and#self.Qintowind>0 then
-local text=string.format(self.lid.."Turn into wind time windows:")
-if#self.Qintowind==0 then
-text=text.." none!"
-end
-for i,_recovery in pairs(self.Qintowind)do
-local recovery=_recovery
-local Cstart=UTILS.SecondsToClock(recovery.Tstart)
-local Cstop=UTILS.SecondsToClock(recovery.Tstop)
-text=text..string.format("\n[%d] ID=%d Start=%s Stop=%s Open=%s Over=%s",i,recovery.Id,Cstart,Cstop,tostring(recovery.Open),tostring(recovery.Over))
-end
-self:I(self.lid..text)
-end
-if self:IsCruising()and self.detectionOn and self.engagedetectedOn then
-local targetgroup,targetdist=self:_GetDetectedTarget()
-if targetgroup then
-self:I(self.lid..string.format("Engaging target group %s at distance %d meters",targetgroup:GetName(),targetdist))
-self:EngageTarget(targetgroup)
-end
-end
-self:_CheckCargoTransport()
-self:_PrintTaskAndMissionStatus()
-end
-function NAVYGROUP:onafterElementSpawned(From,Event,To,Element)
-self:T(self.lid..string.format("Element spawned %s",Element.name))
-self:_UpdateStatus(Element,OPSGROUP.ElementStatus.SPAWNED)
-end
-function NAVYGROUP:onafterSpawned(From,Event,To)
-self:T(self.lid..string.format("Group spawned!"))
-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("Has EPLRS = %s\n",tostring(self.isEPLRS))
-text=text..string.format("Is Submarine = %s\n",tostring(self.isSubmarine))
-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
-self:_UpdatePosition()
-self.isDead=false
-self.isDestroyed=false
-if self.isAI then
-self:SwitchROE(self.option.ROE)
-self:SwitchAlarmstate(self.option.Alarm)
-self:SwitchEmission(self.option.Emission)
-self:SwitchEPLRS(self.option.EPLRS)
-self:SwitchInvisible(self.option.Invisible)
-self:SwitchImmortal(self.option.Immortal)
-self:_SwitchTACAN()
-self:_SwitchICLS()
-if self.radioDefault then
-else
-self:SetDefaultRadio(self.radio.Freq,self.radio.Modu,false)
-end
-if#self.waypoints>1 then
-self:__Cruise(-0.1)
-else
-self:FullStop()
-end
-end
-end
-function NAVYGROUP:onbeforeUpdateRoute(From,Event,To,n,Speed,Depth)
-local allowed=true
-local trepeat=nil
-if self:IsWaiting()then
-self:T(self.lid.."Update route denied. Group is WAITING!")
-return false
-elseif self:IsInUtero()then
-self:T(self.lid.."Update route denied. Group is INUTERO!")
-return false
-elseif self:IsDead()then
-self:T(self.lid.."Update route denied. Group is DEAD!")
-return false
-elseif self:IsStopped()then
-self:T(self.lid.."Update route denied. Group is STOPPED!")
-return false
-elseif self:IsHolding()then
-self:T(self.lid.."Update route denied. Group is holding position!")
-return false
-elseif self:IsEngaging()then
-self:T(self.lid.."Update route allowed. Group is engaging!")
-return true
-end
-if self.taskcurrent>0 then
-local task=self:GetTaskByID(self.taskcurrent)
-if task then
-if task.dcstask.id==AUFTRAG.SpecialTask.PATROLZONE then
-self:T2(self.lid.."Allowing update route for Task: PatrolZone")
-elseif task.dcstask.id==AUFTRAG.SpecialTask.RECON then
-self:T2(self.lid.."Allowing update route for Task: ReconMission")
-elseif task.dcstask.id==AUFTRAG.SpecialTask.RELOCATECOHORT then
-self:T2(self.lid.."Allowing update route for Task: Relocate Cohort")
-elseif task.dcstask.id==AUFTRAG.SpecialTask.REARMING then
-self:T2(self.lid.."Allowing update route for Task: Rearming")
-else
-local taskname=task and task.description or"No description"
-self:T(self.lid..string.format("WARNING: Update route denied because taskcurrent=%d>0! Task description = %s",self.taskcurrent,tostring(taskname)))
-allowed=false
-end
-else
-self:T(self.lid..string.format("WARNING: before update route taskcurrent=%d (>0!) but no task?!",self.taskcurrent))
-allowed=false
-end
-end
-if not self.isAI then
-allowed=false
-end
-self:T2(self.lid..string.format("Onbefore Updateroute in state %s: allowed=%s (repeat in %s)",self:GetState(),tostring(allowed),tostring(trepeat)))
-if trepeat then
-self:__UpdateRoute(trepeat,n)
-end
-return allowed
-end
-function NAVYGROUP:onafterUpdateRoute(From,Event,To,n,N,Speed,Depth)
-n=n or self:GetWaypointIndexNext()
-N=N or#self.waypoints
-N=math.min(N,#self.waypoints)
-local waypoints={}
-for i=n,N do
-local wp=UTILS.DeepCopy(self.waypoints[i])
-if Speed then
-wp.speed=UTILS.KnotsToMps(Speed)
-else
-if wp.speed<0.1 then
-wp.speed=UTILS.KmphToMps(self.speedCruise)
-end
-end
-if Depth then
-wp.alt=-Depth
-elseif self.depth then
-wp.alt=-self.depth
-else
-wp.alt=wp.alt or 0
-end
-if i==n then
-self.speedWp=wp.speed
-self.altWp=wp.alt
-end
-table.insert(waypoints,wp)
-end
-local current=self:GetCoordinate():WaypointNaval(UTILS.MpsToKmph(self.speedWp),self.altWp)
-table.insert(waypoints,1,current)
-if self:IsEngaging()or not self.passedfinalwp then
-if self.verbose>=10 then
-for i=1,#waypoints do
-local wp=waypoints[i]
-local text=string.format("%s Waypoint [%d] UID=%d speed=%d",self.groupname,i-1,wp.uid or-1,wp.speed)
-self:I(self.lid..text)
-COORDINATE:NewFromWaypoint(wp):MarkToAll(text)
-end
-end
-self:T(self.lid..string.format("Updateing route: WP %d-->%d (%d/%d), Speed=%.1f knots, Depth=%d m",self.currentwp,n,#waypoints,#self.waypoints,UTILS.MpsToKnots(self.speedWp),self.altWp))
-self:Route(waypoints)
-else
-self:E(self.lid..string.format("WARNING: Passed final WP ==> Full Stop!"))
-self:FullStop()
-end
-end
-function NAVYGROUP:onafterDetour(From,Event,To,Coordinate,Speed,Depth,ResumeRoute)
-Depth=Depth or 0
-Speed=Speed or self:GetSpeedCruise()
-local uid=self:GetWaypointCurrent().uid
-local wp=self:AddWaypoint(Coordinate,Speed,uid,Depth,true)
-if ResumeRoute then
-wp.detour=1
-else
-wp.detour=0
-end
-end
-function NAVYGROUP:onafterDetourReached(From,Event,To)
-self:T(self.lid.."Group reached detour coordinate.")
-end
-function NAVYGROUP:onafterTurnIntoWind(From,Event,To,IntoWind)
-local heading,speed=self:GetHeadingIntoWind(IntoWind.Offset,IntoWind.Speed)
-IntoWind.Heading=heading
-IntoWind.Open=true
-IntoWind.Coordinate=self:GetCoordinate(true)
-self.intowind=IntoWind
-self:T(self.lid..string.format("Steaming into wind: Heading=%03d Speed=%.1f, Tstart=%d Tstop=%d",IntoWind.Heading,speed,IntoWind.Tstart,IntoWind.Tstop))
-local distance=UTILS.NMToMeters(1000)
-local coord=self:GetCoordinate()
-local Coord=coord:Translate(distance,IntoWind.Heading)
-local uid=self:GetWaypointCurrent().uid
-local wptiw=self:AddWaypoint(Coord,speed,uid)
-wptiw.intowind=true
-IntoWind.waypoint=wptiw
-if IntoWind.Uturn and false then
-IntoWind.Coordinate:MarkToAll("Return coord")
-end
-end
-function NAVYGROUP:onbeforeTurnIntoWindStop(From,Event,To)
-if self.intowind then
-return true
-else
-return false
-end
-end
-function NAVYGROUP:onafterTurnIntoWindStop(From,Event,To)
-self:TurnIntoWindOver(self.intowind)
-end
-function NAVYGROUP:onafterTurnIntoWindOver(From,Event,To,IntoWindData)
-if IntoWindData and self.intowind and IntoWindData.Id==self.intowind.Id then
-self:T2(self.lid.."Turn Into Wind Over!")
-self.intowind.Over=true
-self.intowind.Open=false
-self:RemoveWaypointByID(self.intowind.waypoint.uid)
-if self.intowind.Uturn then
-self:T(self.lid.."FF Turn Into Wind Over ==> Uturn!")
-local uid=self:GetWaypointCurrent().uid
-local wp=self:AddWaypoint(self.intowind.Coordinate,self:GetSpeedCruise(),uid);wp.temp=true
-else
-local indx=self:GetWaypointIndexNext()
-local speed=self:GetSpeedToWaypoint(indx)
-self:T(self.lid..string.format("FF Turn Into Wind Over ==> Next WP Index=%d at %.1f knots via update route!",indx,speed))
-self:__UpdateRoute(-1,indx,nil,speed)
-end
-self.intowind=nil
-self:RemoveTurnIntoWind(IntoWindData)
-end
-end
-function NAVYGROUP:onafterFullStop(From,Event,To)
-self:T(self.lid.."Full stop ==> holding")
-local pos=self:GetCoordinate()
-local wp=pos:WaypointNaval(0)
-self:Route({wp})
-end
-function NAVYGROUP:onafterCruise(From,Event,To,Speed)
-self.Twaiting=nil
-self.dTwait=nil
-self.depth=nil
-self:__UpdateRoute(-0.1,nil,nil,Speed)
-end
-function NAVYGROUP:onafterDive(From,Event,To,Depth,Speed)
-Depth=Depth or 50
-self:I(self.lid..string.format("Diving to %d meters",Depth))
-self.depth=Depth
-self:__UpdateRoute(-1,nil,nil,Speed)
-end
-function NAVYGROUP:onafterSurface(From,Event,To,Speed)
-self.depth=0
-self:__UpdateRoute(-1,nil,nil,Speed)
-end
-function NAVYGROUP:onafterTurningStarted(From,Event,To)
-self.turning=true
-end
-function NAVYGROUP:onafterTurningStopped(From,Event,To)
-self.turning=false
-self.collisionwarning=false
-if self:IsSteamingIntoWind()then
-self:TurnedIntoWind()
-end
-end
-function NAVYGROUP:onafterCollisionWarning(From,Event,To,Distance)
-self:T(self.lid..string.format("Iceberg ahead in %d meters!",Distance or-1))
-self.collisionwarning=true
-end
-function NAVYGROUP:onafterEngageTarget(From,Event,To,Target)
-self:T(self.lid.."Engaging Target")
-if Target:IsInstanceOf("TARGET")then
-self.engage.Target=Target
-else
-self.engage.Target=TARGET:New(Target)
-end
-self.engage.Coordinate=UTILS.DeepCopy(self.engage.Target:GetCoordinate())
-local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate,0.9)
-self.engage.roe=self:GetROE()
-self.engage.alarmstate=self:GetAlarmstate()
-self:SwitchAlarmstate(ENUMS.AlarmState.Auto)
-self:SwitchROE(ENUMS.ROE.OpenFire)
-local uid=self:GetWaypointCurrent().uid
-self.engage.Waypoint=self:AddWaypoint(intercoord,nil,uid,Formation,true)
-self.engage.Waypoint.detour=1
-end
-function NAVYGROUP:_UpdateEngageTarget()
-if self.engage.Target and self.engage.Target:IsAlive()then
-local vec3=self.engage.Target:GetVec3()
-if vec3 then
-local dist=UTILS.VecDist3D(vec3,self.engage.Coordinate:GetVec3())
-if dist>100 then
-self.engage.Coordinate:UpdateFromVec3(vec3)
-local uid=self:GetWaypointCurrent().uid
-self:RemoveWaypointByID(self.engage.Waypoint.uid)
-local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate,0.9)
-self.engage.Waypoint=self:AddWaypoint(intercoord,nil,uid,Formation,true)
-self.engage.Waypoint.detour=0
-end
-else
-self:Disengage()
-end
-else
-self:Disengage()
-end
-end
-function NAVYGROUP:onafterDisengage(From,Event,To)
-self:T(self.lid.."Disengage Target")
-self:SwitchROE(self.engage.roe)
-self:SwitchAlarmstate(self.engage.alarmstate)
-local task=self:GetTaskCurrent()
-if task and task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK then
-self:T(self.lid.."Disengage with current task GROUNDATTACK ==> Task Done!")
-self:TaskDone(task)
-end
-if self.engage.Waypoint then
-self:RemoveWaypointByID(self.engage.Waypoint.uid)
-end
-self:_CheckGroupDone(1)
-end
-function NAVYGROUP:onafterOutOfAmmo(From,Event,To)
-self:T(self.lid..string.format("Group is out of ammo at t=%.3f",timer.getTime()))
-if self.retreatOnOutOfAmmo then
-self:__Retreat(-1)
-return
-end
-if self.rtzOnOutOfAmmo then
-self:__RTZ(-1)
-end
-local task=self:GetTaskCurrent()
-if task then
-if task.dcstask.id=="FireAtPoint"or task.dcstask.id==AUFTRAG.SpecialTask.BARRAGE then
-self:T(self.lid..string.format("Cancelling current %s task because out of ammo!",task.dcstask.id))
-self:TaskCancel(task)
-end
-end
-end
-function NAVYGROUP:onafterRTZ(From,Event,To,Zone,Formation)
-local zone=Zone or self.homezone
-self:CancelAllMissions()
-if zone then
-if self:IsInZone(zone)then
-self:Returned()
-else
-self:T(self.lid..string.format("RTZ to Zone %s",zone:GetName()))
-local Coordinate=zone:GetRandomCoordinate()
-local uid=self:GetWaypointCurrentUID()
-local wp=self:AddWaypoint(Coordinate,nil,uid,Formation,true)
-wp.detour=0
-end
-else
-self:T(self.lid.."ERROR: No RTZ zone given!")
-end
-end
-function NAVYGROUP:onafterReturned(From,Event,To)
-self:T(self.lid..string.format("Group returned"))
-if self.legion then
-self:T(self.lid..string.format("Adding group back to warehouse stock"))
-self.legion:__AddAsset(10,self.group,1)
-end
-end
-function NAVYGROUP:AddWaypoint(Coordinate,Speed,AfterWaypointWithID,Depth,Updateroute)
-local coordinate=self:_CoordinateFromObject(Coordinate)
-local wpnumber=self:GetWaypointIndexAfterID(AfterWaypointWithID)
-Speed=Speed or self:GetSpeedCruise()
-local wp=coordinate:WaypointNaval(UTILS.KnotsToKmph(Speed),Depth)
-local waypoint=self:_CreateWaypoint(wp)
-if Depth then
-waypoint.alt=UTILS.FeetToMeters(Depth)
-end
-self:_AddWaypoint(waypoint,wpnumber)
-self:T(self.lid..string.format("Adding NAVAL waypoint index=%d uid=%d, speed=%.1f knots. Last waypoint passed was #%d. Total waypoints #%d",wpnumber,waypoint.uid,Speed,self.currentwp,#self.waypoints))
-if Updateroute==nil or Updateroute==true then
-self:__UpdateRoute(-0.01)
-end
-return waypoint
-end
-function NAVYGROUP:_InitGroup(Template)
-if self.groupinitialized then
-self:T(self.lid.."WARNING: Group was already initialized! Will NOT do it again!")
-return
-end
-local template=Template or self:_GetTemplate()
-self.isAI=true
-self.isLateActivated=template.lateActivation
-self.isUncontrolled=false
-self.speedMax=self.group:GetSpeedMax()
-if self.speedMax>3.6 then
-self.isMobile=true
-else
-self.isMobile=false
-end
-self.speedCruise=self.speedMax*0.7
-self.ammo=self:GetAmmoTot()
-self.radio.On=true
-self.radio.Freq=tonumber(template.units[1].frequency)/1000000
-self.radio.Modu=tonumber(template.units[1].modulation)
-self.optionDefault.Formation="Off Road"
-self.option.Formation=self.optionDefault.Formation
-self:SetDefaultTACAN(nil,nil,nil,nil,true)
-self.tacan=UTILS.DeepCopy(self.tacanDefault)
-self:SetDefaultICLS(nil,nil,nil,true)
-self.icls=UTILS.DeepCopy(self.iclsDefault)
-local units=self.group:GetUnits()
-local dcsgroup=Group.getByName(self.groupname)
-local size0=dcsgroup:getInitialSize()
-if#units~=size0 then
-self:E(self.lid..string.format("ERROR: Got #units=%d but group consists of %d units!",#units,size0))
-end
-for _,unit in pairs(units)do
-self:_AddElementByName(unit:GetName())
-end
-self.groupinitialized=true
-return self
-end
-function NAVYGROUP:_CheckFreePath(DistanceMax,dx)
-local distance=DistanceMax or 5000
-local dx=dx or 100
-if self:IsTurning()then
-return distance
-end
-local offsetY=0.1
-if UTILS.GetDCSMap()==DCSMAP.Caucasus then
-offsetY=5.01
-end
-local vec3=self:GetVec3()
-vec3.y=offsetY
-local heading=self:GetHeading()
-local function LoS(dist)
-local checkvec3=UTILS.VecTranslate(vec3,dist,heading)
-local los=land.isVisible(vec3,checkvec3)
-return los
-end
-if LoS(DistanceMax)then
-return DistanceMax
-end
-local function check()
-local xmin=0
-local xmax=DistanceMax
-local Nmax=100
-local eps=100
-local N=1
-while N<=Nmax do
-local d=xmax-xmin
-local x=xmin+d/2
-local los=LoS(x)
-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
-end
-if los then
-xmin=x
-else
-xmax=x
-end
-N=N+1
-end
-return 0
-end
-local _check=check()
-return _check
-end
-function NAVYGROUP:_CheckTurning()
-local unit=self.group:GetUnit(1)
-if unit and unit:IsAlive()then
-local vNew=self.orientX
-local vLast=self.orientXLast
-vNew.y=0;vLast.y=0
-local deltaLast=math.deg(math.acos(UTILS.VecDot(vNew,vLast)/UTILS.VecNorm(vNew)/UTILS.VecNorm(vLast)))
-local turning=math.abs(deltaLast)>=2
-if self.turning and not turning then
-self:TurningStopped()
-elseif turning and not self.turning then
-self:TurningStarted()
-end
-self.turning=turning
-end
-end
-function NAVYGROUP:_CheckTurnsIntoWind()
-local time=timer.getAbsTime()
-if self.intowind then
-if time>=self.intowind.Tstop then
-self:TurnIntoWindOver(self.intowind)
-end
-else
-local IntoWind=self:GetTurnIntoWindNext()
-if IntoWind then
-self:TurnIntoWind(IntoWind)
-end
-end
-end
-function NAVYGROUP:GetTurnIntoWindNext()
-if#self.Qintowind>0 then
-local time=timer.getAbsTime()
-table.sort(self.Qintowind,function(a,b)return a.Tstart=recovery.Tstart and timevdeckMax then
-v=Vmax
-theta=math.asin(v/(vwind*C))-math.asin(-1/C)
-elseif vdeckvwind then
-theta=math.pi/2
-v=math.sqrt(vdeck^2-vwind^2)
-else
-theta=math.asin(vdeck*math.sin(alpha)/vwind)
-v=vdeck*math.cos(alpha)-vwind*math.cos(theta)
-end
-local intowind=(540+(windto+math.deg(theta)))%360
-self:T(self.lid..string.format("Heading into Wind: vship=%.1f, vwind=%.1f, WindTo=%03d°, Theta=%03d°, Heading=%03d",v,vwind,windto,theta,intowind))
-return intowind,v
-end
-function NAVYGROUP:_FindPathToNextWaypoint()
-self:T3(self.lid.."Path finding")
-local astar=ASTAR:New()
-local position=self:GetCoordinate()
-local wpnext=self:GetWaypointNext()
-if wpnext==nil then
-return
-end
-local nextwp=wpnext.coordinate
-if wpnext.intowind then
-local hdg=self:GetHeading()
-nextwp=position:Translate(UTILS.NMToMeters(20),hdg,true)
-end
-local speed=UTILS.MpsToKnots(wpnext.speed)
-astar:SetStartCoordinate(position)
-astar:SetEndCoordinate(nextwp)
-local dist=position:Get2DDistance(nextwp)
-if dist<5 then
-return
-end
-local boxwidth=dist*2
-local spacex=dist*0.1
-local delta=dist/10
-astar:CreateGrid({land.SurfaceType.WATER},boxwidth,spacex,delta,delta,self.verbose>10)
-astar:SetValidNeighbourLoS(self.pathCorridor)
-local function findpath()
-local path=astar:GetPath(true,true)
-if path then
-local uid=self:GetWaypointCurrent().uid
-for i,_node in ipairs(path)do
-local node=_node
-local wp=self:AddWaypoint(node.coordinate,speed,uid)
-wp.astar=true
-uid=wp.uid
-if self.verbose>=10 then
-node.coordinate:MarkToAll(string.format("Path node #%d",i))
-end
-end
-return#path>0
-else
-return false
-end
-end
-return findpath()
-end
-OPERATION={
-ClassName="OPERATION",
-verbose=0,
-branches={},
-counterPhase=0,
-counterBranch=0,
-counterEdge=0,
-cohorts={},
-legions={},
-targets={},
-missions={},
-}
-_OPERATIONID=0
-OPERATION.PhaseStatus={
-PLANNED="Planned",
-ACTIVE="Active",
-OVER="Over",
-}
-OPERATION.version="0.2.0"
-function OPERATION:New(Name)
-local self=BASE:Inherit(self,FSM:New())
-_OPERATIONID=_OPERATIONID+1
-self.uid=_OPERATIONID
-self.name=Name or string.format("Operation-%02d",_OPERATIONID)
-self.lid=string.format("%s | ",self.name)
-self:SetStartState("Planned")
-self.branchMaster=self:AddBranch("Master")
-self.conditionStart=CONDITION:New("Operation %s start",self.name)
-self.conditionStart:SetNoneResult(false)
-self.conditionStart:SetDefaultPersistence(false)
-self.conditionOver=CONDITION:New("Operation %s over",self.name)
-self.conditionOver:SetNoneResult(false)
-self.conditionOver:SetDefaultPersistence(false)
-self.branchActive=self.branchMaster
-self:AddTransition("*","Start","Running")
-self:AddTransition("*","StatusUpdate","*")
-self:AddTransition("Running","Pause","Paused")
-self:AddTransition("Paused","Unpause","Running")
-self:AddTransition("*","PhaseOver","*")
-self:AddTransition("*","PhaseNext","*")
-self:AddTransition("*","PhaseChange","*")
-self:AddTransition("*","BranchSwitch","*")
-self:AddTransition("*","Over","Over")
-self:AddTransition("*","Stop","Stopped")
-self:__StatusUpdate(-1)
-return self
-end
-function OPERATION:SetVerbosity(VerbosityLevel)
-self.verbose=VerbosityLevel or 0
-return self
-end
-function OPERATION:SetTime(ClockStart,ClockStop)
-local Tnow=timer.getAbsTime()
-local Tstart=Tnow+5
-if ClockStart and type(ClockStart)=="number"then
-Tstart=Tnow+ClockStart
-elseif ClockStart and type(ClockStart)=="string"then
-Tstart=UTILS.ClockToSeconds(ClockStart)
-end
-local Tstop=nil
-if ClockStop and type(ClockStop)=="number"then
-Tstop=Tnow+ClockStop
-elseif ClockStop and type(ClockStop)=="string"then
-Tstop=UTILS.ClockToSeconds(ClockStop)
-end
-self.Tstart=Tstart
-self.Tstop=Tstop
-if Tstop then
-self.duration=self.Tstop-self.Tstart
-end
-return self
-end
-function OPERATION:AddConditonOverAll(Function,...)
-local cf=self.conditionOver:AddFunctionAll(Function,...)
-return cf
-end
-function OPERATION:AddConditonOverAny(Phase,Function,...)
-local cf=self.conditionOver:AddFunctionAny(Function,...)
-return cf
-end
-function OPERATION:AddPhase(Name,Branch,Duration)
-Branch=Branch or self.branchMaster
-local phase=self:_CreatePhase(Name)
-phase.branch=Branch
-phase.duration=Duration
-self:T(self.lid..string.format("Adding phase %s to branch %s",phase.name,Branch.name))
-table.insert(Branch.phases,phase)
-return phase
-end
-function OPERATION:InsertPhaseAfter(PhaseAfter,Name)
-for i=1,#self.phases do
-local phase=self.phases[i]
-if PhaseAfter.uid==phase.uid then
-local phase=self:_CreatePhase(Name)
-end
-end
-return nil
-end
-function OPERATION:GetName()
-return self.name or"Unknown"
-end
-function OPERATION:GetPhaseByName(Name)
-for _,_branch in pairs(self.branches)do
-local branch=_branch
-for _,_phase in pairs(branch.phases or{})do
-local phase=_phase
-if phase.name==Name then
-return phase
-end
-end
-end
-return nil
-end
-function OPERATION:SetPhaseStatus(Phase,Status)
-if Phase then
-self:T(self.lid..string.format("Phase %s status: %s-->%s",tostring(Phase.name),tostring(Phase.status),tostring(Status)))
-Phase.status=Status
-if Phase.status==OPERATION.PhaseStatus.ACTIVE then
-Phase.Tstart=timer.getAbsTime()
-Phase.nActive=Phase.nActive+1
-elseif Phase.status==OPERATION.PhaseStatus.OVER then
-self:PhaseOver(Phase)
-end
-end
-return self
-end
-function OPERATION:GetPhaseStatus(Phase)
-return Phase.status
-end
-function OPERATION:SetPhaseConditonOver(Phase,Condition)
-if Phase then
-self:T(self.lid..string.format("Setting phase %s conditon over %s",self:GetPhaseName(Phase),Condition and Condition.name or"None"))
-Phase.conditionOver=Condition
-end
-return self
-end
-function OPERATION:AddPhaseConditonOverAll(Phase,Function,...)
-if Phase then
-local cf=Phase.conditionOver:AddFunctionAll(Function,...)
-return cf
-end
-return nil
-end
-function OPERATION:AddPhaseConditonOverAny(Phase,Function,...)
-if Phase then
-local cf=Phase.conditionOver:AddFunctionAny(Function,...)
-return cf
-end
-return nil
-end
-function OPERATION:SetConditionFunctionPersistence(ConditionFunction,IsPersistent)
-ConditionFunction.persistence=IsPersistent
-return self
-end
-function OPERATION:AddPhaseConditonRepeatAll(Phase,Function,...)
-if Phase then
-Phase.conditionRepeat:AddFunctionAll(Function,...)
-end
-return self
-end
-function OPERATION:GetPhaseConditonOver(Phase,Condition)
-return Phase.conditionOver
-end
-function OPERATION:GetPhaseNactive(Phase)
-return Phase.nActive
-end
-function OPERATION:GetPhaseName(Phase)
-Phase=Phase or self.phase
-if Phase then
-return Phase.name
-end
-return"None"
-end
-function OPERATION:GetPhaseActive()
-return self.phase
-end
-function OPERATION:GetPhaseIndex(Phase)
-local branch=Phase.branch
-for i,_phase in pairs(branch.phases)do
-local phase=_phase
-if phase.uid==Phase.uid then
-return i,branch
-end
-end
-return nil
-end
-function OPERATION:GetPhaseNext(Branch,PhaseStatus)
-Branch=Branch or self:GetBranchActive()
-local phases=Branch.phases or{}
-local phase=nil
-if self.phase and self.phase.branch.uid==Branch.uid then
-phase=self.phase
-end
-local N=#phases
-self:T(self.lid..string.format("Getting next phase! Branch=%s, Phases=%d, Status=%s",Branch.name,N,tostring(PhaseStatus)))
-if N>0 then
-if phase==nil and PhaseStatus==nil then
-return phases[1]
-end
-local n=1
-if phase then
-n=self:GetPhaseIndex(phase)+1
-end
-for i=n,N do
-local phase=phases[i]
-if PhaseStatus==nil or PhaseStatus==phase.status then
-return phase
-end
-end
-end
-return nil
-end
-function OPERATION:CountPhases(Status,Branch)
-Branch=Branch or self.branchActive
-local N=0
-for _,_phase in pairs(Branch.phases)do
-local phase=_phase
-if Status==nil or Status==phase.status then
-N=N+1
-end
-end
-return N
-end
-function OPERATION:AddBranch(Name)
-local branch=self:_CreateBranch(Name)
-table.insert(self.branches,branch)
-return branch
-end
-function OPERATION:GetBranchMaster()
-return self.branchMaster
-end
-function OPERATION:GetBranchActive()
-return self.branchActive or self.branchMaster
-end
-function OPERATION:GetBranchName(Branch)
-Branch=Branch or self:GetBranchActive()
-if Branch then
-return Branch.name
-end
-return"None"
-end
-function OPERATION:AddEdge(PhaseFrom,PhaseTo,ConditionSwitch)
-local edge={}
-edge.phaseFrom=PhaseFrom
-edge.phaseTo=PhaseTo
-edge.branchFrom=PhaseFrom.branch
-edge.branchTo=PhaseTo.branch
-if ConditionSwitch then
-edge.conditionSwitch=ConditionSwitch
-else
-edge.conditionSwitch=CONDITION:New("Edge")
-edge.conditionSwitch:SetNoneResult(true)
-end
-table.insert(edge.branchFrom.edges,edge)
-return edge
-end
-function OPERATION:AddEdgeConditonSwitchAll(Edge,Function,...)
-if Edge then
-local cf=Edge.conditionSwitch:AddFunctionAll(Function,...)
-return cf
-end
-return nil
-end
-function OPERATION:AddMission(Mission,Phase)
-Mission.phase=Phase
-Mission.operation=self
-table.insert(self.missions,Mission)
-return self
-end
-function OPERATION:AddTarget(Target,Phase)
-Target.phase=Phase
-Target.operation=self
-table.insert(self.targets,Target)
-return self
-end
-function OPERATION:GetTargets(Phase)
-local N={}
-for _,_target in pairs(self.targets)do
-local target=_target
-if target:IsAlive()and(Phase==nil or target.phase==Phase)then
-table.insert(N,target)
-end
-end
-return N
-end
-function OPERATION:CountTargets(Phase)
-local N=0
-for _,_target in pairs(self.targets)do
-local target=_target
-if target:IsAlive()and(Phase==nil or target.phase==Phase)then
-N=N+1
-end
-end
-return N
-end
-function OPERATION:AssignCohort(Cohort)
-self:T(self.lid..string.format("Assiging Cohort %s to operation",Cohort.name))
-self.cohorts[Cohort.name]=Cohort
-end
-function OPERATION:AssignLegion(Legion)
-self.legions[Legion.alias]=Legion
-end
-function OPERATION:IsAssignedLegion(Legion)
-local legion=self.legions[Legion.alias]
-if legion then
-self:T(self.lid..string.format("Legion %s is assigned to this operation",Legion.alias))
-return true
-else
-self:T(self.lid..string.format("Legion %s is NOT assigned to this operation",Legion.alias))
-return false
-end
-end
-function OPERATION:IsAssignedCohort(Cohort)
-local cohort=self.cohorts[Cohort.name]
-if cohort then
-self:T(self.lid..string.format("Cohort %s is assigned to this operation",Cohort.name))
-return true
-else
-local Legion=Cohort.legion
-if Legion and self:IsAssignedLegion(Legion)then
-self:T(self.lid..string.format("Legion %s of Cohort %s is assigned to this operation",Legion.alias,Cohort.name))
-return true
-end
-self:T(self.lid..string.format("Cohort %s is NOT assigned to this operation",Cohort.name))
-return false
-end
-return nil
-end
-function OPERATION:IsAssignedCohortOrLegion(Object)
-local isAssigned=nil
-if Object:IsInstanceOf("COHORT")then
-isAssigned=self:IsAssignedCohort(Object)
-elseif Object:IsInstanceOf("LEGION")then
-isAssigned=self:IsAssignedLegion(Object)
-else
-self:E(self.lid.."ERROR: Unknown Object!")
-end
-return isAssigned
-end
-function OPERATION:IsPlanned()
-local is=self:is("Planned")
-return is
-end
-function OPERATION:IsRunning()
-local is=self:is("Running")
-return is
-end
-function OPERATION:IsPaused()
-local is=self:is("Paused")
-return is
-end
-function OPERATION:IsOver()
-local is=self:is("Over")
-return is
-end
-function OPERATION:IsStopped()
-local is=self:is("Stopped")
-return is
-end
-function OPERATION:IsNotOver()
-local is=not(self:IsOver()or self:IsStopped())
-return is
-end
-function OPERATION:IsPhaseActive(Phase)
-if Phase and Phase.status and Phase.status==OPERATION.PhaseStatus.ACTIVE then
-return true
-end
-return false
-end
-function OPERATION:IsPhaseActive(Phase)
-local phase=self:GetPhaseActive()
-if phase and phase.uid==Phase.uid then
-return true
-else
-return false
-end
-return nil
-end
-function OPERATION:IsPhasePlanned(Phase)
-if Phase and Phase.status and Phase.status==OPERATION.PhaseStatus.PLANNED then
-return true
-end
-return false
-end
-function OPERATION:IsPhaseOver(Phase)
-if Phase and Phase.status and Phase.status==OPERATION.PhaseStatus.OVER then
-return true
-end
-return false
-end
-function OPERATION:onafterStart(From,Event,To)
-self:T(self.lid..string.format("Starting Operation!"))
-return self
-end
-function OPERATION:onafterStatusUpdate(From,Event,To)
-local Tnow=timer.getAbsTime()
-local fsmstate=self:GetState()
-if self:IsPlanned()then
-if(self.Tstart and Tnow>self.Tstart or self.Tstart==nil)and(self.conditionStart==nil or self.conditionStart:Evaluate())then
-self:Start()
-end
-elseif self:IsNotOver()then
-if(self.Tstop and Tnow>self.Tstop or self.Tstop==nil)and(self.conditionOver==nil or self.conditionOver:Evaluate())then
-self:Over()
-end
-end
-if self:IsRunning()then
-self:_CheckPhases()
-end
-if self.verbose>=1 then
-local phaseName=self:GetPhaseName()
-local branchName=self:GetBranchName()
-local NphaseTot=self:CountPhases()
-local NphaseAct=self:CountPhases(OPERATION.PhaseStatus.ACTIVE)
-local NphasePla=self:CountPhases(OPERATION.PhaseStatus.PLANNED)
-local NphaseOvr=self:CountPhases(OPERATION.PhaseStatus.OVER)
-local text=string.format("State=%s: Phase=%s [%s], Phases=%d [Active=%d, Planned=%d, Over=%d]",fsmstate,phaseName,branchName,NphaseTot,NphaseAct,NphasePla,NphaseOvr)
-self:I(self.lid..text)
-end
-if self.verbose>=2 then
-local text="Phases:"
-for i,_phase in pairs(self.branchActive.phases)do
-local phase=_phase
-text=text..string.format("\n[%d] %s [uid=%d]: status=%s Nact=%d",i,phase.name,phase.uid,tostring(phase.status),phase.nActive)
-end
-if text=="Phases:"then text=text.." None"end
-self:I(self.lid..text)
-end
-self:__StatusUpdate(-30)
-return self
-end
-function OPERATION:onafterPhaseNext(From,Event,To)
-local Phase=self:GetPhaseNext()
-if Phase then
-self:PhaseChange(Phase)
-else
-self:Over()
-end
-return self
-end
-function OPERATION:onafterPhaseChange(From,Event,To,Phase)
-local oldphase="None"
-if self.phase then
-if self.phase.status~=OPERATION.PhaseStatus.OVER then
-self:SetPhaseStatus(self.phase,OPERATION.PhaseStatus.OVER)
-end
-oldphase=self.phase.name
-end
-self:I(self.lid..string.format("Phase change: %s --> %s",oldphase,Phase.name))
-self.phase=Phase
-self:SetPhaseStatus(Phase,OPERATION.PhaseStatus.ACTIVE)
-return self
-end
-function OPERATION:onafterPhaseOver(From,Event,To,Phase)
-Phase.conditionOver:RemoveNonPersistant()
-end
-function OPERATION:onafterBranchSwitch(From,Event,To,Branch,Phase)
-self:T(self.lid..string.format("Switching to branch %s",Branch.name))
-self.branchActive=Branch
-self:PhaseChange(Phase)
-return self
-end
-function OPERATION:onafterOver(From,Event,To)
-self:T(self.lid..string.format("Operation is over!"))
-self.phase=nil
-for _,_branch in pairs(self.branches)do
-local branch=_branch
-for _,_phase in pairs(branch.phases)do
-local phase=_phase
-if not self:IsPhaseOver(phase)then
-self:SetPhaseStatus(phase,OPERATION.PhaseStatus.OVER)
-end
-end
-end
-return self
-end
-function OPERATION:_CheckPhases()
-local phase=self:GetPhaseActive()
-if phase and phase.conditionOver then
-local isOver=phase.conditionOver:Evaluate()
-local Tnow=timer.getAbsTime()
-if phase.duration and phase.Tstart and Tnow-phase.Tstart>phase.duration then
-isOver=true
-end
-if isOver then
-self:SetPhaseStatus(phase,OPERATION.PhaseStatus.OVER)
-end
-end
-if phase==nil or phase.status==OPERATION.PhaseStatus.OVER then
-for _,_edge in pairs(self.branchActive.edges)do
-local edge=_edge
-if phase then
-end
-if(edge.phaseFrom==nil)or(phase and edge.phaseFrom.uid==phase.uid)then
-local switch=edge.conditionSwitch:Evaluate()
-if switch then
-local phaseTo=edge.phaseTo or self:GetPhaseNext(edge.branchTo,nil)
-if phaseTo then
-self:BranchSwitch(edge.branchTo,phaseTo)
-else
-self:Over()
-end
-return
-end
-end
-end
-self:PhaseNext()
-end
-end
-function OPERATION:_CreatePhase(Name)
-self.counterPhase=self.counterPhase+1
-local phase={}
-phase.uid=self.counterPhase
-phase.name=Name or string.format("Phase-%02d",self.counterPhase)
-phase.conditionOver=CONDITION:New(Name.." Over")
-phase.conditionOver:SetDefaultPersistence(false)
-phase.status=OPERATION.PhaseStatus.PLANNED
-phase.nActive=0
-return phase
-end
-function OPERATION:_CreateBranch(Name)
-self.counterBranch=self.counterBranch+1
-local branch={}
-branch.uid=self.counterBranch
-branch.name=Name or string.format("Branch-%02d",self.counterBranch)
-branch.phases={}
-branch.edges={}
-return branch
-end
-OPSGROUP={
-ClassName="OPSGROUP",
-verbose=0,
-lid=nil,
-groupname=nil,
-group=nil,
-template=nil,
-isLateActivated=nil,
-waypoints=nil,
-waypoints0=nil,
-currentwp=1,
-elements={},
-taskqueue={},
-taskcounter=nil,
-taskcurrent=nil,
-taskenroute=nil,
-taskpaused={},
-missionqueue={},
-currentmission=nil,
-detectedunits={},
-detectedgroups={},
-attribute=nil,
-checkzones=nil,
-inzones=nil,
-groupinitialized=nil,
-wpcounter=1,
-radio={},
-option={},
-optionDefault={},
-tacan={},
-icls={},
-callsign={},
-Ndestroyed=0,
-Nkills=0,
-Nhit=0,
-weaponData={},
-cargoqueue={},
-cargoBay={},
-mycarrier={},
-carrierLoader={},
-carrierUnloader={},
-useMEtasks=false,
-pausedmissions={},
-}
-OPSGROUP.ElementStatus={
-INUTERO="InUtero",
-SPAWNED="Spawned",
-PARKING="Parking",
-ENGINEON="Engine On",
-TAXIING="Taxiing",
-TAKEOFF="Takeoff",
-AIRBORNE="Airborne",
-LANDING="Landing",
-LANDED="Landed",
-ARRIVED="Arrived",
-DEAD="Dead",
-}
-OPSGROUP.GroupStatus={
-INUTERO="InUtero",
-PARKING="Parking",
-TAXIING="Taxiing",
-AIRBORNE="Airborne",
-INBOUND="Inbound",
-LANDING="Landing",
-LANDED="Landed",
-ARRIVED="Arrived",
-DEAD="Dead",
-}
-OPSGROUP.TaskStatus={
-SCHEDULED="scheduled",
-EXECUTING="executing",
-PAUSED="paused",
-DONE="done",
-}
-OPSGROUP.TaskType={
-SCHEDULED="scheduled",
-WAYPOINT="waypoint",
-}
-OPSGROUP.CarrierStatus={
-NOTCARRIER="not carrier",
-PICKUP="pickup",
-LOADING="loading",
-LOADED="loaded",
-TRANSPORTING="transporting",
-UNLOADING="unloading",
-}
-OPSGROUP.CargoStatus={
-AWAITING="Awaiting carrier",
-NOTCARGO="not cargo",
-ASSIGNED="assigned to carrier",
-BOARDING="boarding",
-LOADED="loaded",
-}
-OPSGROUP.version="1.0.0"
-function OPSGROUP:New(group)
-local self=BASE:Inherit(self,FSM:New())
-if type(group)=="string"then
-self.groupname=group
-self.group=GROUP:FindByName(self.groupname)
-else
-self.group=group
-self.groupname=group:GetName()
-end
-self.lid=string.format("OPSGROUP %s | ",tostring(self.groupname))
-if self.group then
-if not self:IsExist()then
-self:T(self.lid.."ERROR: GROUP does not exist! Returning nil")
-return nil
-end
-end
-self:_SetTemplate()
-self.dcsgroup=self:GetDCSGroup()
-self.controller=self.dcsgroup:getController()
-self.category=self.dcsgroup:getCategory()
-if self.category==Group.Category.GROUND then
-self.isArmygroup=true
-elseif self.category==Group.Category.TRAIN then
-self.isArmygroup=true
-self.isTrain=true
-elseif self.category==Group.Category.SHIP then
-self.isNavygroup=true
-elseif self.category==Group.Category.AIRPLANE then
-self.isFlightgroup=true
-elseif self.category==Group.Category.HELICOPTER then
-self.isFlightgroup=true
-self.isHelo=true
-else
-end
-self.attribute=self.group:GetAttribute()
-local units=self.group:GetUnits()
-if units then
-local masterunit=units[1]
-self.descriptors=masterunit:GetDesc()
-self.actype=masterunit:GetTypeName()
-self.isSubmarine=masterunit:HasAttribute("Submarines")
-self.isEPLRS=masterunit:HasAttribute("Datalink")
-if self:IsFlightgroup()then
-self.rangemax=self.descriptors.range and self.descriptors.range*1000 or 500*1000
-self.ceiling=self.descriptors.Hmax
-self.tankertype=select(2,masterunit:IsTanker())
-self.refueltype=select(2,masterunit:IsRefuelable())
-end
-end
-self.detectedunits=SET_UNIT:New()
-self.detectedgroups=SET_GROUP:New()
-self.inzones=SET_ZONE:New()
-self:SetDefaultAltitude()
-self:SetReturnToLegion()
-self.spot={}
-self.spot.On=false
-self.spot.timer=TIMER:New(self._UpdateLaser,self)
-self.spot.Coordinate=COORDINATE:New(0,0,0)
-self:SetLaser(1688,true,false,0.5)
-self.cargoStatus=OPSGROUP.CargoStatus.NOTCARGO
-self.carrierStatus=OPSGROUP.CarrierStatus.NOTCARRIER
-self:SetCarrierLoaderAllAspect()
-self:SetCarrierUnloaderAllAspect()
-self.taskcurrent=0
-self.taskcounter=0
-self:SetStartState("InUtero")
-self:AddTransition("InUtero","Spawned","Spawned")
-self:AddTransition("*","Respawn","InUtero")
-self:AddTransition("*","Dead","InUtero")
-self:AddTransition("*","InUtero","InUtero")
-self:AddTransition("*","Stop","Stopped")
-self:AddTransition("*","Hit","*")
-self:AddTransition("*","Damaged","*")
-self:AddTransition("*","Destroyed","*")
-self:AddTransition("*","UpdateRoute","*")
-self:AddTransition("*","PassingWaypoint","*")
-self:AddTransition("*","PassedFinalWaypoint","*")
-self:AddTransition("*","GotoWaypoint","*")
-self:AddTransition("*","Wait","*")
-self:AddTransition("*","DetectedUnit","*")
-self:AddTransition("*","DetectedUnitNew","*")
-self:AddTransition("*","DetectedUnitKnown","*")
-self:AddTransition("*","DetectedUnitLost","*")
-self:AddTransition("*","DetectedGroup","*")
-self:AddTransition("*","DetectedGroupNew","*")
-self:AddTransition("*","DetectedGroupKnown","*")
-self:AddTransition("*","DetectedGroupLost","*")
-self:AddTransition("*","OutOfAmmo","*")
-self:AddTransition("*","OutOfGuns","*")
-self:AddTransition("*","OutOfRockets","*")
-self:AddTransition("*","OutOfBombs","*")
-self:AddTransition("*","OutOfMissiles","*")
-self:AddTransition("*","OutOfTorpedos","*")
-self:AddTransition("*","OutOfMissilesAA","*")
-self:AddTransition("*","OutOfMissilesAG","*")
-self:AddTransition("*","OutOfMissilesAS","*")
-self:AddTransition("*","EnterZone","*")
-self:AddTransition("*","LeaveZone","*")
-self:AddTransition("*","LaserOn","*")
-self:AddTransition("*","LaserOff","*")
-self:AddTransition("*","LaserCode","*")
-self:AddTransition("*","LaserPause","*")
-self:AddTransition("*","LaserResume","*")
-self:AddTransition("*","LaserLostLOS","*")
-self:AddTransition("*","LaserGotLOS","*")
-self:AddTransition("*","TaskExecute","*")
-self:AddTransition("*","TaskPause","*")
-self:AddTransition("*","TaskCancel","*")
-self:AddTransition("*","TaskDone","*")
-self:AddTransition("*","MissionStart","*")
-self:AddTransition("*","MissionExecute","*")
-self:AddTransition("*","MissionCancel","*")
-self:AddTransition("*","PauseMission","*")
-self:AddTransition("*","UnpauseMission","*")
-self:AddTransition("*","MissionDone","*")
-self:AddTransition("*","ElementInUtero","*")
-self:AddTransition("*","ElementSpawned","*")
-self:AddTransition("*","ElementDestroyed","*")
-self:AddTransition("*","ElementDead","*")
-self:AddTransition("*","ElementDamaged","*")
-self:AddTransition("*","ElementHit","*")
-self:AddTransition("*","Board","*")
-self:AddTransition("*","Embarked","*")
-self:AddTransition("*","Disembarked","*")
-self:AddTransition("*","Pickup","*")
-self:AddTransition("*","Loading","*")
-self:AddTransition("*","Load","*")
-self:AddTransition("*","Loaded","*")
-self:AddTransition("*","LoadingDone","*")
-self:AddTransition("*","Transport","*")
-self:AddTransition("*","Unloading","*")
-self:AddTransition("*","Unload","*")
-self:AddTransition("*","Unloaded","*")
-self:AddTransition("*","UnloadingDone","*")
-self:AddTransition("*","Delivered","*")
-self:AddTransition("*","TransportCancel","*")
-self:AddTransition("*","HoverStart","*")
-self:AddTransition("*","HoverEnd","*")
-return self
-end
-function OPSGROUP:GetCoalition()
-return self.group:GetCoalition()
-end
-function OPSGROUP:GetLifePoints(Element)
-local life=0
-local life0=0
-if Element then
-local unit=Element.unit
-if unit then
-life=unit:GetLife()
-life0=unit:GetLife0()
-life=math.min(life,life0)
-end
-else
-for _,element in pairs(self.elements)do
-local l,l0=self:GetLifePoints(element)
-life=life+l
-life0=life0+l0
-end
-end
-return life,life0
-end
-function OPSGROUP:GetAttribute()
-return self.attribute
-end
-function OPSGROUP:SetVerbosity(VerbosityLevel)
-self.verbose=VerbosityLevel or 0
-return self
-end
-function OPSGROUP:_SetLegion(Legion)
-self:T2(self.lid..string.format("Adding opsgroup to legion %s",Legion.alias))
-self.legion=Legion
-return self
-end
-function OPSGROUP:SetReturnToLegion(Switch)
-if Switch==false then
-self.legionReturn=false
-else
-self.legionReturn=true
-end
-self:T(self.lid..string.format("Setting ReturnToLetion=%s",tostring(self.legionReturn)))
-return self
-end
-function OPSGROUP:SetDefaultSpeed(Speed)
-if Speed then
-self.speedCruise=UTILS.KnotsToKmph(Speed)
-end
-return self
-end
-function OPSGROUP:GetSpeedCruise()
-local speed=UTILS.KmphToKnots(self.speedCruise or self.speedMax*0.7)
-return speed
-end
-function OPSGROUP:SetDefaultAltitude(Altitude)
-if Altitude then
-self.altitudeCruise=UTILS.FeetToMeters(Altitude)
-else
-if self:IsFlightgroup()then
-if self.isHelo then
-self.altitudeCruise=UTILS.FeetToMeters(1500)
-else
-self.altitudeCruise=UTILS.FeetToMeters(10000)
-end
-else
-self.altitudeCruise=0
-end
-end
-return self
-end
-function OPSGROUP:GetCruiseAltitude()
-local alt=UTILS.MetersToFeet(self.altitudeCruise)
-return alt
-end
-function OPSGROUP:SetAltitude(Altitude,Keep,RadarAlt)
-if Altitude then
-Altitude=UTILS.FeetToMeters(Altitude)
-else
-if self:IsFlightgroup()then
-if self.isHelo then
-Altitude=UTILS.FeetToMeters(1500)
-else
-Altitude=UTILS.FeetToMeters(10000)
-end
-else
-Altitude=0
-end
-end
-local AltType="BARO"
-if RadarAlt then
-AltType="RADIO"
-end
-if self.controller then
-self.controller:setAltitude(Altitude,Keep,AltType)
-end
-return self
-end
-function OPSGROUP:GetAltitude()
-local alt=0
-if self.group then
-alt=self.group:GetAltitude()
-alt=UTILS.MetersToFeet(alt)
-end
-return alt
-end
-function OPSGROUP:SetSpeed(Speed,Keep,AltCorrected)
-if Speed then
-else
-Speed=UTILS.KmphToKnots(self.speedMax)
-end
-if AltCorrected then
-local altitude=self:GetAltitude()
-Speed=UTILS.KnotsToAltKIAS(Speed,altitude)
-end
-Speed=UTILS.KnotsToMps(Speed)
-if self.controller then
-self.controller:setSpeed(Speed,Keep)
-end
-return self
-end
-function OPSGROUP:SetDetection(Switch)
-self:T(self.lid..string.format("Detection is %s",tostring(Switch)))
-self.detectionOn=Switch
-return self
-end
-function OPSGROUP:GetDCSObject()
-return self.dcsgroup
-end
-function OPSGROUP:KnowTarget(TargetObject,KnowType,KnowDist,Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,OPSGROUP.KnowTarget,self,TargetObject,KnowType,KnowDist,0)
-else
-if TargetObject:IsInstanceOf("GROUP")then
-TargetObject=TargetObject:GetUnit(1)
-elseif TargetObject:IsInstanceOf("OPSGROUP")then
-TargetObject=TargetObject.group:GetUnit(1)
-end
-local object=TargetObject:GetDCSObject()
-for _,_element in pairs(self.elements)do
-local element=_element
-if element.controller then
-element.controller:knowTarget(object,true,true)
-end
-end
-self:T(self.lid..string.format("We should now know target %s",TargetObject:GetName()))
-end
-return self
-end
-function OPSGROUP:IsTargetDetected(TargetObject)
-local objects={}
-if TargetObject:IsInstanceOf("GROUP")then
-for _,unit in pairs(TargetObject:GetUnits())do
-table.insert(objects,unit:GetDCSObject())
-end
-elseif TargetObject:IsInstanceOf("OPSGROUP")then
-for _,unit in pairs(TargetObject.group:GetUnits())do
-table.insert(objects,unit:GetDCSObject())
-end
-elseif TargetObject:IsInstanceOf("UNIT")or TargetObject:IsInstanceOf("STATIC")then
-table.insert(objects,TargetObject:GetDCSObject())
-end
-for _,object in pairs(objects or{})do
-local detected,visible,lastTime,type,distance,lastPos,lastVel=self.controller:isTargetDetected(object,1,2,4,8,16,32)
-if detected then
-return true
-end
-for _,_element in pairs(self.elements)do
-local element=_element
-if element.controller then
-local detected,visible,lastTime,type,distance,lastPos,lastVel=
-element.controller:isTargetDetected(object,1,2,4,8,16,32)
-if detected then
-return true
-end
-end
-end
-end
-return false
-end
-function OPSGROUP:InWeaponRange(TargetCoord,WeaponBitType,RefCoord)
-RefCoord=RefCoord or self:GetCoordinate()
-local dist=TargetCoord:Get2DDistance(RefCoord)
-if WeaponBitType then
-local weapondata=self:GetWeaponData(WeaponBitType)
-if weapondata then
-if dist>=weapondata.RangeMin and dist<=weapondata.RangeMax then
-return true
-else
-return false
-end
-end
-else
-for _,_weapondata in pairs(self.weaponData or{})do
-local weapondata=_weapondata
-if dist>=weapondata.RangeMin and dist<=weapondata.RangeMax then
-return true
-end
-end
-return false
-end
-return nil
-end
-function OPSGROUP:GetCoordinateInRange(TargetCoord,WeaponBitType,RefCoord)
-local coordInRange=nil
-RefCoord=RefCoord or self:GetCoordinate()
-local weapondata=self:GetWeaponData(WeaponBitType)
-if weapondata then
-local heading=RefCoord:HeadingTo(TargetCoord)
-local dist=RefCoord:Get2DDistance(TargetCoord)
-if dist>weapondata.RangeMax then
-local d=(dist-weapondata.RangeMax)*1.05
-coordInRange=RefCoord:Translate(d,heading)
-self:T(self.lid..string.format("Out of max range = %.1f km for weapon %s",weapondata.RangeMax/1000,tostring(WeaponBitType)))
-elseif dist=ThreatLevelMin and threatlevel<=ThreatLevelMax then
-if threatlevellevelmax then
-threat=unit
-levelmax=threatlevel
-end
-end
-return threat,levelmax
-end
-function OPSGROUP:SetEngageDetectedOn(RangeMax,TargetTypes,EngageZoneSet,NoEngageZoneSet)
-if TargetTypes then
-if type(TargetTypes)~="table"then
-TargetTypes={TargetTypes}
-end
-else
-TargetTypes={"All"}
-end
-if EngageZoneSet and EngageZoneSet:IsInstanceOf("ZONE_BASE")then
-local zoneset=SET_ZONE:New():AddZone(EngageZoneSet)
-EngageZoneSet=zoneset
-end
-if NoEngageZoneSet and NoEngageZoneSet:IsInstanceOf("ZONE_BASE")then
-local zoneset=SET_ZONE:New():AddZone(NoEngageZoneSet)
-NoEngageZoneSet=zoneset
-end
-self.engagedetectedOn=true
-self.engagedetectedRmax=UTILS.NMToMeters(RangeMax or 25)
-self.engagedetectedTypes=TargetTypes
-self.engagedetectedEngageZones=EngageZoneSet
-self.engagedetectedNoEngageZones=NoEngageZoneSet
-self:T(self.lid..string.format("Engage detected ON: Rmax=%d NM",UTILS.MetersToNM(self.engagedetectedRmax)))
-self:SetDetection(true)
-return self
-end
-function OPSGROUP:SetEngageDetectedOff()
-self:T(self.lid..string.format("Engage detected OFF"))
-self.engagedetectedOn=false
-return self
-end
-function OPSGROUP:SetRearmOnOutOfAmmo()
-self.rearmOnOutOfAmmo=true
-return self
-end
-function OPSGROUP:SetRetreatOnOutOfAmmo()
-self.retreatOnOutOfAmmo=true
-return self
-end
-function OPSGROUP:SetReturnOnOutOfAmmo()
-self.rtzOnOutOfAmmo=true
-return self
-end
-function OPSGROUP:SetCargoBayLimit(Weight,UnitName)
-for _,_element in pairs(self.elements)do
-local element=_element
-if UnitName==nil or UnitName==element.name then
-element.weightMaxCargo=Weight
-if element.unit then
-element.unit:SetCargoBayWeightLimit(Weight)
-end
-end
-end
-return self
-end
-function OPSGROUP:HasLoS(Coordinate,Element,OffsetElement,OffsetCoordinate)
-if Coordinate then
-local Vec3={x=Coordinate.x,y=Coordinate.y,z=Coordinate.z}
-if OffsetCoordinate then
-Vec3=UTILS.VecAdd(Vec3,OffsetCoordinate)
-end
-local function checklos(vec3)
-if vec3 then
-if OffsetElement then
-vec3=UTILS.VecAdd(vec3,OffsetElement)
-end
-local _los=land.isVisible(vec3,Vec3)
-return _los
-end
-return nil
-end
-if Element then
-if Element.unit and Element.unit:IsAlive()then
-local vec3=Element.unit:GetVec3()
-local los=checklos(vec3)
-return los
-end
-else
-local gotit=false
-for _,_element in pairs(self.elements)do
-local element=_element
-if element and element.unit and element.unit:IsAlive()then
-gotit=true
-local vec3=element.unit:GetVec3()
-local los=checklos(vec3)
-if los then
-return true
-end
-end
-end
-if gotit then
-return false
-end
-end
-end
-return nil
-end
-function OPSGROUP:GetGroup()
-return self.group
-end
-function OPSGROUP:GetName()
-return self.groupname
-end
-function OPSGROUP:GetDCSGroup()
-local DCSGroup=Group.getByName(self.groupname)
-return DCSGroup
-end
-function OPSGROUP:GetUnit(UnitNumber)
-local DCSUnit=self:GetDCSUnit(UnitNumber)
-if DCSUnit then
-local unit=UNIT:Find(DCSUnit)
-return unit
-end
-return nil
-end
-function OPSGROUP:GetDCSUnit(UnitNumber)
-local DCSGroup=self:GetDCSGroup()
-if DCSGroup then
-local unit=DCSGroup:getUnit(UnitNumber or 1)
-return unit
-end
-return nil
-end
-function OPSGROUP:GetDCSUnits()
-local DCSGroup=self:GetDCSGroup()
-if DCSGroup then
-local units=DCSGroup:getUnits()
-return units
-end
-return nil
-end
-function OPSGROUP:GetVec2(UnitName)
-local vec3=self:GetVec3(UnitName)
-if vec3 then
-local vec2={x=vec3.x,y=vec3.z}
-return vec2
-end
-return nil
-end
-function OPSGROUP:GetVec3(UnitName)
-local vec3=nil
-local carrier=self:_GetMyCarrierElement()
-if carrier and carrier.status~=OPSGROUP.ElementStatus.DEAD and self:IsLoaded()then
-local unit=carrier.unit
-if unit and unit:IsExist()then
-vec3=unit:GetVec3()
-return vec3
-end
-end
-if self:IsExist()then
-local unit=nil
-if UnitName then
-unit=Unit.getByName(UnitName)
-else
-unit=self:GetDCSUnit()
-end
-if unit then
-local vec3=unit:getPoint()
-return vec3
-end
-end
-if self.position then
-return self.position
-end
-return nil
-end
-function OPSGROUP:GetCoordinate(NewObject,UnitName)
-local vec3=self:GetVec3(UnitName)or self.position
-if vec3 then
-self.coordinate=self.coordinate or COORDINATE:New(0,0,0)
-self.coordinate.x=vec3.x
-self.coordinate.y=vec3.y
-self.coordinate.z=vec3.z
-if NewObject then
-local coord=COORDINATE:NewFromCoordinate(self.coordinate)
-return coord
-else
-return self.coordinate
-end
-else
-self:T(self.lid.."WARNING: Cannot get coordinate!")
-end
-return nil
-end
-function OPSGROUP:GetVelocity(UnitName)
-if self:IsExist()then
-local unit=nil
-if UnitName then
-unit=Unit.getByName(UnitName)
-else
-unit=self:GetDCSUnit()
-end
-if unit then
-local velvec3=unit:getVelocity()
-local vel=UTILS.VecNorm(velvec3)
-return vel
-else
-self:T(self.lid.."WARNING: Unit does not exist. Cannot get velocity!")
-end
-else
-self:T(self.lid.."WARNING: Group does not exist. Cannot get velocity!")
-end
-return nil
-end
-function OPSGROUP:GetHeading(UnitName)
-if self:IsExist()then
-local unit=nil
-if UnitName then
-unit=Unit.getByName(UnitName)
-else
-unit=self:GetDCSUnit()
-end
-if unit then
-local pos=unit:getPosition()
-local heading=math.atan2(pos.x.z,pos.x.x)
-if heading<0 then
-heading=heading+2*math.pi
-end
-heading=math.deg(heading)
-return heading
-end
-else
-self:T(self.lid.."WARNING: Group does not exist. Cannot get heading!")
-end
-return nil
-end
-function OPSGROUP:GetOrientation(UnitName)
-if self:IsExist()then
-local unit=nil
-if UnitName then
-unit=Unit.getByName(UnitName)
-else
-unit=self:GetDCSUnit()
-end
-if unit then
-local pos=unit:getPosition()
-return pos.x,pos.y,pos.z
-end
-else
-self:T(self.lid.."WARNING: Group does not exist. Cannot get orientation!")
-end
-return nil
-end
-function OPSGROUP:GetOrientationX(UnitName)
-local X,Y,Z=self:GetOrientation(UnitName)
-return X
-end
-function OPSGROUP:CheckTaskDescriptionUnique(description)
-for _,_task in pairs(self.taskqueue)do
-local task=_task
-if task.description==description then
-return false
-end
-end
-return true
-end
-function OPSGROUP:DespawnUnit(UnitName,Delay,NoEventRemoveUnit)
-self:T(self.lid.."Despawn element "..tostring(UnitName))
-local element=self:GetElementByName(UnitName)
-if element then
-local DCSunit=Unit.getByName(UnitName)
-if DCSunit then
-DCSunit:destroy()
-self:ElementInUtero(element)
-if not NoEventRemoveUnit then
-self:CreateEventRemoveUnit(timer.getTime(),DCSunit)
-end
-end
-end
-end
-function OPSGROUP:DespawnElement(Element,Delay,NoEventRemoveUnit)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,OPSGROUP.DespawnElement,self,Element,0,NoEventRemoveUnit)
-else
-if Element then
-local DCSunit=Unit.getByName(Element.name)
-if DCSunit then
-DCSunit:destroy()
-if not NoEventRemoveUnit then
-self:CreateEventRemoveUnit(timer.getTime(),DCSunit)
-end
-end
-end
-end
-return self
-end
-function OPSGROUP:Despawn(Delay,NoEventRemoveUnit)
-if Delay and Delay>0 then
-self.scheduleIDDespawn=self:ScheduleOnce(Delay,OPSGROUP.Despawn,self,0,NoEventRemoveUnit)
-else
-self:T(self.lid..string.format("Despawning Group!"))
-local DCSGroup=self:GetDCSGroup()
-if DCSGroup then
-local units=self:GetDCSUnits()
-for i=1,#units do
-local unit=units[i]
-if unit then
-local name=unit:getName()
-if name then
-self:DespawnUnit(name,0,NoEventRemoveUnit)
-end
-end
-end
-end
-end
-return self
-end
-function OPSGROUP:ReturnToLegion(Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,OPSGROUP.ReturnToLegion,self)
-else
-if self.legion then
-self:T(self.lid..string.format("Adding asset back to LEGION"))
-self.legion:AddAsset(self.group,1)
-else
-self:E(self.lid..string.format("ERROR: Group does not belong to a LEGION!"))
-end
-end
-return self
-end
-function OPSGROUP:DestroyUnit(UnitName,Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,OPSGROUP.DestroyUnit,self,UnitName,0)
-else
-local unit=Unit.getByName(UnitName)
-if unit then
-local EventTime=timer.getTime()
-if self:IsFlightgroup()then
-self:CreateEventUnitLost(EventTime,unit)
-else
-self:CreateEventDead(EventTime,unit)
-end
-unit:destroy()
-end
-end
-end
-function OPSGROUP:Destroy(Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,OPSGROUP.Destroy,self,0)
-else
-local units=self:GetDCSUnits()
-if units then
-for _,unit in pairs(units)do
-if unit then
-self:DestroyUnit(unit:getName())
-end
-end
-end
-end
-return self
-end
-function OPSGROUP:Activate(delay)
-if delay and delay>0 then
-self:T2(self.lid..string.format("Activating late activated group in %d seconds",delay))
-self:ScheduleOnce(delay,OPSGROUP.Activate,self)
-else
-if self:IsAlive()==false then
-self:T(self.lid.."Activating late activated group")
-self.group:Activate()
-self.isLateActivated=false
-elseif self:IsAlive()==true then
-self:T(self.lid.."WARNING: Activating group that is already activated")
-else
-self:T(self.lid.."ERROR: Activating group that is does not exist!")
-end
-end
-return self
-end
-function OPSGROUP:Deactivate(delay)
-if delay and delay>0 then
-self:ScheduleOnce(delay,OPSGROUP.Deactivate,self)
-else
-if self:IsAlive()==true then
-self.template.lateActivation=true
-local template=UTILS.DeepCopy(self.template)
-self:_Respawn(0,template)
-end
-end
-return self
-end
-function OPSGROUP:SelfDestruction(Delay,ExplosionPower,ElementName)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,OPSGROUP.SelfDestruction,self,0,ExplosionPower,ElementName)
-else
-for i,_element in pairs(self.elements)do
-local element=_element
-if ElementName==nil or ElementName==element.name then
-local unit=element.unit
-if unit and unit:IsAlive()then
-unit:Explode(ExplosionPower or 100)
-end
-end
-end
-end
-return self
-end
-function OPSGROUP:SetSRS(PathToSRS,Gender,Culture,Voice,Port,PathToGoogleKey,Label,Volume)
-self.useSRS=true
-self.msrs=MSRS:New(PathToSRS,self.frequency,self.modulation)
-self.msrs:SetGender(Gender)
-self.msrs:SetCulture(Culture)
-self.msrs:SetVoice(Voice)
-self.msrs:SetPort(Port)
-self.msrs:SetLabel(Label)
-if PathToGoogleKey then
-self.msrs:SetGoogle(PathToGoogleKey)
-end
-self.msrs:SetCoalition(self:GetCoalition())
-self.msrs:SetVolume(Volume)
-return self
-end
-function OPSGROUP:RadioTransmission(Text,Delay,SayCallsign,Frequency)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,OPSGROUP.RadioTransmission,self,Text,0,SayCallsign)
-else
-if self.useSRS and self.msrs then
-local freq,modu,radioon=self:GetRadio()
-local coord=self:GetCoordinate()
-self.msrs:SetCoordinate(coord)
-if Frequency then
-self.msrs:SetFrequencies(Frequency)
-else
-self.msrs:SetFrequencies(freq)
-end
-self.msrs:SetModulations(modu)
-if SayCallsign then
-local callsign=self:GetCallsignName()
-Text=string.format("%s, %s",callsign,Text)
-end
-self:T(self.lid..string.format("Radio transmission on %.3f MHz %s: %s",freq,UTILS.GetModulationName(modu),Text))
-self.msrs:PlayText(Text)
-end
-end
-return self
-end
-function OPSGROUP:SetCarrierLoaderAllAspect(Length,Width)
-self.carrierLoader.type="front"
-self.carrierLoader.length=Length or 50
-self.carrierLoader.width=Width or 20
-return self
-end
-function OPSGROUP:SetCarrierLoaderFront(Length,Width)
-self.carrierLoader.type="front"
-self.carrierLoader.length=Length or 50
-self.carrierLoader.width=Width or 20
-return self
-end
-function OPSGROUP:SetCarrierLoaderBack(Length,Width)
-self.carrierLoader.type="back"
-self.carrierLoader.length=Length or 50
-self.carrierLoader.width=Width or 20
-return self
-end
-function OPSGROUP:SetCarrierLoaderStarboard(Length,Width)
-self.carrierLoader.type="right"
-self.carrierLoader.length=Length or 50
-self.carrierLoader.width=Width or 20
-return self
-end
-function OPSGROUP:SetCarrierLoaderPort(Length,Width)
-self.carrierLoader.type="left"
-self.carrierLoader.length=Length or 50
-self.carrierLoader.width=Width or 20
-return self
-end
-function OPSGROUP:SetCarrierUnloaderAllAspect(Length,Width)
-self.carrierUnloader.type="front"
-self.carrierUnloader.length=Length or 50
-self.carrierUnloader.width=Width or 20
-return self
-end
-function OPSGROUP:SetCarrierUnloaderFront(Length,Width)
-self.carrierUnloader.type="front"
-self.carrierUnloader.length=Length or 50
-self.carrierUnloader.width=Width or 20
-return self
-end
-function OPSGROUP:SetCarrierUnloaderBack(Length,Width)
-self.carrierUnloader.type="back"
-self.carrierUnloader.length=Length or 50
-self.carrierUnloader.width=Width or 20
-return self
-end
-function OPSGROUP:SetCarrierUnloaderStarboard(Length,Width)
-self.carrierUnloader.type="right"
-self.carrierUnloader.length=Length or 50
-self.carrierUnloader.width=Width or 20
-return self
-end
-function OPSGROUP:SetCarrierUnloaderPort(Length,Width)
-self.carrierUnloader.type="left"
-self.carrierUnloader.length=Length or 50
-self.carrierUnloader.width=Width or 20
-return self
-end
-function OPSGROUP:IsInZone(Zone)
-local vec2=self:GetVec2()
-local is=false
-if vec2 then
-is=Zone:IsVec2InZone(vec2)
-else
-self:T3(self.lid.."WARNING: Cannot get vec2 at IsInZone()!")
-end
-return is
-end
-function OPSGROUP:Get2DDistance(Coordinate)
-local a=self:GetVec2()
-local b={}
-if Coordinate.z then
-b.x=Coordinate.x
-b.y=Coordinate.z
-else
-b.x=Coordinate.x
-b.y=Coordinate.y
-end
-local dist=UTILS.VecDist2D(a,b)
-return dist
-end
-function OPSGROUP:IsFlightgroup()
-return self.isFlightgroup
-end
-function OPSGROUP:IsArmygroup()
-return self.isArmygroup
-end
-function OPSGROUP:IsNavygroup()
-return self.isNavygroup
-end
-function OPSGROUP:IsExist()
-local DCSGroup=self:GetDCSGroup()
-if DCSGroup then
-local exists=DCSGroup:isExist()
-return exists
-end
-return nil
-end
-function OPSGROUP:IsActive()
-if self.group then
-local active=self.group:IsActive()
-return active
-end
-return nil
-end
-function OPSGROUP:IsAlive()
-if self.group then
-local alive=self.group:IsAlive()
-return alive
-end
-return nil
-end
-function OPSGROUP:IsLateActivated()
-return self.isLateActivated
-end
-function OPSGROUP:IsInUtero()
-local is=self:Is("InUtero")and not self:IsDead()
-return is
-end
-function OPSGROUP:IsSpawned()
-local is=self:Is("Spawned")
-return is
-end
-function OPSGROUP:IsDead()
-return self.isDead
-end
-function OPSGROUP:IsDestroyed()
-return self.isDestroyed
-end
-function OPSGROUP:IsStopped()
-local is=self:Is("Stopped")
-return is
-end
-function OPSGROUP:IsUncontrolled()
-return self.isUncontrolled
-end
-function OPSGROUP:HasPassedFinalWaypoint()
-return self.passedfinalwp
-end
-function OPSGROUP:IsRearming()
-local rearming=self:Is("Rearming")or self:Is("Rearm")
-return rearming
-end
-function OPSGROUP:IsOutOfAmmo()
-return self.outofAmmo
-end
-function OPSGROUP:IsOutOfBombs()
-return self.outofBombs
-end
-function OPSGROUP:IsOutOfGuns()
-return self.outofGuns
-end
-function OPSGROUP:IsOutOfMissiles()
-return self.outofMissiles
-end
-function OPSGROUP:IsOutOfTorpedos()
-return self.outofTorpedos
-end
-function OPSGROUP:IsLasing()
-return self.spot.On
-end
-function OPSGROUP:IsRetreating()
-local is=self:is("Retreating")or self:is("Retreated")
-return is
-end
-function OPSGROUP:IsRetreated()
-local is=self:is("Retreated")
-return is
-end
-function OPSGROUP:IsReturning()
-local is=self:is("Returning")
-return is
-end
-function OPSGROUP:IsEngaging()
-local is=self:is("Engaging")
-return is
-end
-function OPSGROUP:IsWaiting()
-if self.Twaiting then
-return true
-end
-return false
-end
-function OPSGROUP:IsNotCarrier()
-return self.carrierStatus==OPSGROUP.CarrierStatus.NOTCARRIER
-end
-function OPSGROUP:IsCarrier()
-return not self:IsNotCarrier()
-end
-function OPSGROUP:IsPickingup()
-return self.carrierStatus==OPSGROUP.CarrierStatus.PICKUP
-end
-function OPSGROUP:IsLoading()
-return self.carrierStatus==OPSGROUP.CarrierStatus.LOADING
-end
-function OPSGROUP:IsTransporting()
-return self.carrierStatus==OPSGROUP.CarrierStatus.TRANSPORTING
-end
-function OPSGROUP:IsUnloading()
-return self.carrierStatus==OPSGROUP.CarrierStatus.UNLOADING
-end
-function OPSGROUP:IsCargo(CheckTransport)
-return not self:IsNotCargo(CheckTransport)
-end
-function OPSGROUP:IsNotCargo(CheckTransport)
-local notcargo=self.cargoStatus==OPSGROUP.CargoStatus.NOTCARGO
-if notcargo then
-return true
-else
-if CheckTransport then
-if self.cargoTransportUID==nil then
-return true
-else
-return false
-end
-else
-return false
-end
-end
-return notcargo
-end
-function OPSGROUP:_AddMyLift(Transport)
-self.mylifts=self.mylifts or{}
-self.mylifts[Transport.uid]=true
-return self
-end
-function OPSGROUP:_DelMyLift(Transport)
-if self.mylifts then
-self.mylifts[Transport.uid]=nil
-end
-return self
-end
-function OPSGROUP:IsAwaitingLift(Transport)
-if self.mylifts then
-for uid,iswaiting in pairs(self.mylifts)do
-if Transport==nil or Transport.uid==uid then
-if iswaiting==true then
-return true
-end
-end
-end
-end
-return false
-end
-function OPSGROUP:_GetPausedMission()
-if self.pausedmissions and#self.pausedmissions>0 then
-for _,mid in pairs(self.pausedmissions)do
-if mid then
-local mission=self:GetMissionByID(mid)
-if mission and mission:IsNotOver()then
-return mission
-end
-end
-end
-end
-return nil
-end
-function OPSGROUP:_CountPausedMissions()
-local N=0
-if self.pausedmissions and#self.pausedmissions>0 then
-for _,mid in pairs(self.pausedmissions)do
-local mission=self:GetMissionByID(mid)
-if mission and mission:IsNotOver()then
-N=N+1
-end
-end
-end
-return N
-end
-function OPSGROUP:_RemovePausedMission(AuftragsNummer)
-if self.pausedmissions and#self.pausedmissions>0 then
-for i=#self.pausedmissions,1,-1 do
-local mid=self.pausedmissions[i]
-if mid==AuftragsNummer then
-table.remove(self.pausedmissions,i)
-return self
-end
-end
-end
-return self
-end
-function OPSGROUP:IsBoarding(CarrierGroupName)
-if CarrierGroupName then
-local carrierGroup=self:_GetMyCarrierGroup()
-if carrierGroup and carrierGroup.groupname~=CarrierGroupName then
-return false
-end
-end
-return self.cargoStatus==OPSGROUP.CargoStatus.BOARDING
-end
-function OPSGROUP:IsLoaded(CarrierGroupName)
-local isloaded=self.cargoStatus==OPSGROUP.CargoStatus.LOADED
-if not isloaded then
-return false
-end
-if CarrierGroupName then
-if type(CarrierGroupName)~="table"then
-CarrierGroupName={CarrierGroupName}
-end
-for _,CarrierName in pairs(CarrierGroupName)do
-local carrierGroup=self:_GetMyCarrierGroup()
-if carrierGroup and carrierGroup.groupname==CarrierName then
-return isloaded
-end
-end
-return false
-end
-return isloaded
-end
-function OPSGROUP:IsBusy()
-if self:IsBoarding()then
-return true
-end
-if self:IsRearming()then
-return true
-end
-if self:IsReturning()then
-return true
-end
-if self:IsPickingup()or self:IsLoading()or self:IsTransporting()or self:IsUnloading()then
-return true
-end
-if self:IsEngaging()then
-return true
-end
-return false
-end
-function OPSGROUP:GetWaypoints()
-return self.waypoints
-end
-function OPSGROUP:MarkWaypoints(Duration)
-for i,_waypoint in pairs(self.waypoints or{})do
-local waypoint=_waypoint
-local text=string.format("Waypoint ID=%d of %s",waypoint.uid,self.groupname)
-text=text..string.format("\nSpeed=%.1f kts, Alt=%d ft (%s)",UTILS.MpsToKnots(waypoint.speed),UTILS.MetersToFeet(waypoint.alt or 0),"BARO")
-if waypoint.marker then
-if waypoint.marker.text~=text then
-waypoint.marker.text=text
-end
-else
-waypoint.marker=MARKER:New(waypoint.coordinate,text):ToCoalition(self:GetCoalition())
-end
-end
-if Duration then
-self:RemoveWaypointMarkers(Duration)
-end
-return self
-end
-function OPSGROUP:RemoveWaypointMarkers(Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,OPSGROUP.RemoveWaypointMarkers,self)
-else
-for i,_waypoint in pairs(self.waypoints or{})do
-local waypoint=_waypoint
-if waypoint.marker then
-waypoint.marker:Remove()
-end
-end
-end
-return self
-end
-function OPSGROUP:GetWaypointByID(uid)
-for _,_waypoint in pairs(self.waypoints or{})do
-local waypoint=_waypoint
-if waypoint.uid==uid then
-return waypoint
-end
-end
-return nil
-end
-function OPSGROUP:GetWaypointByIndex(index)
-for i,_waypoint in pairs(self.waypoints)do
-local waypoint=_waypoint
-if i==index then
-return waypoint
-end
-end
-return nil
-end
-function OPSGROUP:GetWaypointUIDFromIndex(index)
-for i,_waypoint in pairs(self.waypoints)do
-local waypoint=_waypoint
-if i==index then
-return waypoint.uid
-end
-end
-return nil
-end
-function OPSGROUP:GetWaypointIndex(uid)
-if uid then
-for i,_waypoint in pairs(self.waypoints or{})do
-local waypoint=_waypoint
-if waypoint.uid==uid then
-return i
-end
-end
-end
-return nil
-end
-function OPSGROUP:GetWaypointIndexNext(cyclic,i)
-if cyclic==nil then
-cyclic=self.adinfinitum
-end
-local N=#self.waypoints
-i=i or self.currentwp
-local n=math.min(i+1,N)
-if cyclic and i==N then
-n=1
-end
-return n
-end
-function OPSGROUP:GetWaypointIndexCurrent()
-return self.currentwp or 1
-end
-function OPSGROUP:GetWaypointIndexAfterID(uid)
-local index=self:GetWaypointIndex(uid)
-if index then
-return index+1
-else
-return#self.waypoints+1
-end
-end
-function OPSGROUP:GetWaypoint(indx)
-return self.waypoints[indx]
-end
-function OPSGROUP:GetWaypointFinal()
-return self.waypoints[#self.waypoints]
-end
-function OPSGROUP:GetWaypointNext(cyclic)
-local n=self:GetWaypointIndexNext(cyclic)
-return self.waypoints[n]
-end
-function OPSGROUP:GetWaypointCurrent()
-return self.waypoints[self.currentwp]
-end
-function OPSGROUP:GetWaypointCurrentUID()
-local wp=self:GetWaypointCurrent()
-if wp then
-return wp.uid
-end
-return nil
-end
-function OPSGROUP:GetNextWaypointCoordinate(cyclic)
-local waypoint=self:GetWaypointNext(cyclic)
-return waypoint.coordinate
-end
-function OPSGROUP:GetWaypointCoordinate(index)
-local waypoint=self:GetWaypoint(index)
-if waypoint then
-return waypoint.coordinate
-end
-return nil
-end
-function OPSGROUP:GetWaypointSpeed(indx)
-local waypoint=self:GetWaypoint(indx)
-if waypoint then
-return UTILS.MpsToKnots(waypoint.speed)
-end
-return nil
-end
-function OPSGROUP:GetWaypointUID(waypoint)
-return waypoint.uid
-end
-function OPSGROUP:GetWaypointID(indx)
-local waypoint=self:GetWaypoint(indx)
-if waypoint then
-return waypoint.uid
-end
-return nil
-end
-function OPSGROUP:GetSpeedToWaypoint(indx)
-local speed=self:GetWaypointSpeed(indx)
-if speed<=0.01 then
-speed=self:GetSpeedCruise()
-end
-return speed
-end
-function OPSGROUP:GetDistanceToWaypoint(indx)
-local dist=0
-if#self.waypoints>0 then
-indx=indx or self:GetWaypointIndexNext()
-local wp=self:GetWaypoint(indx)
-if wp then
-local coord=self:GetCoordinate()
-dist=coord:Get2DDistance(wp.coordinate)
-end
-end
-return dist
-end
-function OPSGROUP:GetTimeToWaypoint(indx)
-local s=self:GetDistanceToWaypoint(indx)
-local v=self:GetVelocity()
-local t=s/v
-if t==math.inf then
-return 365*24*60*60
-elseif t==math.nan then
-return 0
-else
-return t
-end
-end
-function OPSGROUP:GetExpectedSpeed()
-if self:IsHolding()or self:Is("Rearming")or self:IsWaiting()or self:IsRetreated()then
-return 0
-else
-return self.speedWp or 0
-end
-end
-function OPSGROUP:RemoveWaypointByID(uid)
-local index=self:GetWaypointIndex(uid)
-if index then
-self:RemoveWaypoint(index)
-end
-return self
-end
-function OPSGROUP:RemoveWaypoint(wpindex)
-if self.waypoints then
-local wp=self:GetWaypoint(wpindex)
-local istemp=wp.temp or wp.detour or wp.astar or wp.missionUID
-local N=#self.waypoints
-if N==1 then
-self:T(self.lid..string.format("ERROR: Cannot remove waypoint with index=%d! It is the only waypoint and a group needs at least ONE waypoint",wpindex))
-return self
-end
-if wpindex>N then
-self:T(self.lid..string.format("ERROR: Cannot remove waypoint with index=%d as there are only N=%d waypoints!",wpindex,N))
-return self
-end
-if wp and wp.marker then
-wp.marker:Remove()
-end
-table.remove(self.waypoints,wpindex)
-local n=#self.waypoints
-self:T(self.lid..string.format("Removing waypoint UID=%d [temp=%s]: index=%d [currentwp=%d]. N %d-->%d",wp.uid,tostring(istemp),wpindex,self.currentwp,N,n))
-if wpindex>self.currentwp then
-if self.currentwp>=n and not(self.adinfinitum or istemp)then
-self:_PassedFinalWaypoint(true,"Removed FUTURE waypoint we are currently moving to and that was the LAST waypoint")
-end
-self:_CheckGroupDone(1)
-else
-if self.currentwp==1 then
-if self.adinfinitum then
-self.currentwp=#self.waypoints
-else
-self.currentwp=1
-end
-else
-self.currentwp=self.currentwp-1
-end
-if(self.adinfinitum or istemp)then
-self:_PassedFinalWaypoint(false,"Removed PASSED temporary waypoint")
-end
-end
-end
-return self
-end
-function OPSGROUP:OnEventBirth(EventData)
-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.isFlightgroup then
-if EventData.Place then
-self.homebase=self.homebase or EventData.Place
-self.currbase=EventData.Place
-else
-self.currbase=nil
-end
-if self.homebase and not self.destbase then
-self.destbase=self.homebase
-end
-self:T(self.lid..string.format("EVENT: Element %s born at airbase %s ==> spawned",unitname,self.currbase and self.currbase:GetName()or"unknown"))
-else
-self:T3(self.lid..string.format("EVENT: Element %s born ==> spawned",unitname))
-end
-local element=self:GetElementByName(unitname)
-if element and element.status~=OPSGROUP.ElementStatus.SPAWNED then
-self:T(self.lid..string.format("EVENT: Element %s born ==> spawned",unitname))
-self:ElementSpawned(element)
-end
-end
-end
-function OPSGROUP:OnEventHit(EventData)
-if EventData and EventData.TgtGroup and EventData.TgtUnit and EventData.TgtGroupName and EventData.TgtGroupName==self.groupname then
-self:T2(self.lid..string.format("EVENT: Unit %s hit!",EventData.TgtUnitName))
-local unit=EventData.TgtUnit
-local group=EventData.TgtGroup
-local unitname=EventData.TgtUnitName
-local element=self:GetElementByName(unitname)
-self.Nhit=self.Nhit or 0
-self.Nhit=self.Nhit+1
-if element and element.status~=OPSGROUP.ElementStatus.DEAD then
-self:ElementHit(element,EventData.IniUnit)
-end
-end
-end
-function OPSGROUP:OnEventDead(EventData)
-if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then
-self:T2(self.lid..string.format("EVENT: Unit %s dead!",EventData.IniUnitName))
-local unit=EventData.IniUnit
-local group=EventData.IniGroup
-local unitname=EventData.IniUnitName
-local element=self:GetElementByName(unitname)
-if element and element.status~=OPSGROUP.ElementStatus.DEAD then
-self:T(self.lid..string.format("EVENT: Element %s dead ==> destroyed",element.name))
-self:ElementDestroyed(element)
-end
-end
-end
-function OPSGROUP:OnEventRemoveUnit(EventData)
-if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then
-self:T2(self.lid..string.format("EVENT: Unit %s removed!",EventData.IniUnitName))
-local unit=EventData.IniUnit
-local group=EventData.IniGroup
-local unitname=EventData.IniUnitName
-local element=self:GetElementByName(unitname)
-if element and element.status~=OPSGROUP.ElementStatus.DEAD then
-self:T(self.lid..string.format("EVENT: Element %s removed ==> dead",element.name))
-self:ElementDead(element)
-end
-end
-end
-function OPSGROUP:OnEventPlayerLeaveUnit(EventData)
-if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then
-self:T2(self.lid..string.format("EVENT: Player left Unit %s!",EventData.IniUnitName))
-local unit=EventData.IniUnit
-local group=EventData.IniGroup
-local unitname=EventData.IniUnitName
-local element=self:GetElementByName(unitname)
-if element and element.status~=OPSGROUP.ElementStatus.DEAD then
-self:T(self.lid..string.format("EVENT: Player left Element %s ==> dead",element.name))
-self:ElementDead(element)
-end
-end
-end
-function OPSGROUP:OnEventKill(EventData)
-if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then
-local targetname=tostring(EventData.TgtUnitName)
-self:T2(self.lid..string.format("EVENT: Unit %s killed object %s!",tostring(EventData.IniUnitName),targetname))
-local target=UNIT:FindByName(targetname)
-if not target then
-target=STATIC:FindByName(targetname,false)
-end
-if target then
-self:T(self.lid..string.format("EVENT: Unit %s killed unit/static %s!",tostring(EventData.IniUnitName),targetname))
-self.Nkills=self.Nkills+1
-local mission=self:GetMissionCurrent()
-if mission then
-mission.Nkills=mission.Nkills+1
-end
-end
-end
-end
-function OPSGROUP:SetTask(DCSTask)
-if self:IsAlive()then
-if self.taskenroute and#self.taskenroute>0 then
-if tostring(DCSTask.id)=="ComboTask"then
-for _,task in pairs(self.taskenroute)do
-table.insert(DCSTask.params.tasks,1,task)
-end
-else
-local tasks=UTILS.DeepCopy(self.taskenroute)
-table.insert(tasks,DCSTask)
-DCSTask=self.group.TaskCombo(self,tasks)
-end
-end
-self.controller:setTask(DCSTask)
-local text=string.format("SETTING Task %s",tostring(DCSTask.id))
-if tostring(DCSTask.id)=="ComboTask"then
-for i,task in pairs(DCSTask.params.tasks)do
-text=text..string.format("\n[%d] %s",i,tostring(task.id))
-end
-end
-self:T(self.lid..text)
-end
-return self
-end
-function OPSGROUP:PushTask(DCSTask)
-if self:IsAlive()then
-if self.taskenroute and#self.taskenroute>0 then
-if tostring(DCSTask.id)=="ComboTask"then
-for _,task in pairs(self.taskenroute)do
-table.insert(DCSTask.params.tasks,1,task)
-end
-else
-local tasks=UTILS.DeepCopy(self.taskenroute)
-table.insert(tasks,DCSTask)
-DCSTask=self.group.TaskCombo(self,tasks)
-end
-end
-self.controller:pushTask(DCSTask)
-local text=string.format("PUSHING Task %s",tostring(DCSTask.id))
-if tostring(DCSTask.id)=="ComboTask"then
-for i,task in pairs(DCSTask.params.tasks)do
-text=text..string.format("\n[%d] %s",i,tostring(task.id))
-end
-end
-self:T(self.lid..text)
-end
-return self
-end
-function OPSGROUP:HasTaskController()
-local hastask=nil
-if self.controller then
-hastask=self.controller:hasTask()
-end
-self:T3(self.lid..string.format("Controller hasTask=%s",tostring(hastask)))
-return hastask
-end
-function OPSGROUP:ClearTasks()
-local hastask=self:HasTaskController()
-if self:IsAlive()and self.controller and self:HasTaskController()then
-self:T(self.lid..string.format("CLEARING Tasks"))
-self.controller:resetTask()
-end
-return self
-end
-function OPSGROUP:AddTask(task,clock,description,prio,duration)
-local newtask=self:NewTaskScheduled(task,clock,description,prio,duration)
-table.insert(self.taskqueue,newtask)
-self:T(self.lid..string.format("Adding SCHEDULED task %s starting at %s",newtask.description,UTILS.SecondsToClock(newtask.time,true)))
-self:T3({newtask=newtask})
-return newtask
-end
-function OPSGROUP:NewTaskScheduled(task,clock,description,prio,duration)
-self.taskcounter=self.taskcounter+1
-local time=timer.getAbsTime()+5
-if clock then
-if type(clock)=="string"then
-time=UTILS.ClockToSeconds(clock)
-elseif type(clock)=="number"then
-time=timer.getAbsTime()+clock
-end
-end
-local newtask={}
-newtask.status=OPSGROUP.TaskStatus.SCHEDULED
-newtask.dcstask=task
-newtask.description=description or task.id
-newtask.prio=prio or 50
-newtask.time=time
-newtask.id=self.taskcounter
-newtask.duration=duration
-newtask.waypoint=-1
-newtask.type=OPSGROUP.TaskType.SCHEDULED
-newtask.stopflag=USERFLAG:New(string.format("%s StopTaskFlag %d",self.groupname,newtask.id))
-newtask.stopflag:Set(0)
-return newtask
-end
-function OPSGROUP:AddTaskWaypoint(task,Waypoint,description,prio,duration)
-Waypoint=Waypoint or self:GetWaypointNext()
-if Waypoint then
-self.taskcounter=self.taskcounter+1
-local newtask={}
-newtask.description=description or string.format("Task #%d",self.taskcounter)
-newtask.status=OPSGROUP.TaskStatus.SCHEDULED
-newtask.dcstask=task
-newtask.prio=prio or 50
-newtask.id=self.taskcounter
-newtask.duration=duration
-newtask.time=0
-newtask.waypoint=Waypoint.uid
-newtask.type=OPSGROUP.TaskType.WAYPOINT
-newtask.stopflag=USERFLAG:New(string.format("%s StopTaskFlag %d",self.groupname,newtask.id))
-newtask.stopflag:Set(0)
-table.insert(self.taskqueue,newtask)
-self:T(self.lid..string.format("Adding WAYPOINT task %s at WP ID=%d",newtask.description,newtask.waypoint))
-self:T3({newtask=newtask})
-return newtask
-end
-return nil
-end
-function OPSGROUP:AddTaskEnroute(task)
-if not self.taskenroute then
-self.taskenroute={}
-end
-local gotit=false
-for _,Task in pairs(self.taskenroute)do
-if Task.id==task.id then
-gotit=true
-break
-end
-end
-if not gotit then
-self:T(self.lid..string.format("Adding enroute task"))
-table.insert(self.taskenroute,task)
-end
-end
-function OPSGROUP:GetTasksWaypoint(id)
-local tasks={}
-self:_SortTaskQueue()
-for _,_task in pairs(self.taskqueue)do
-local task=_task
-if task.type==OPSGROUP.TaskType.WAYPOINT and task.status==OPSGROUP.TaskStatus.SCHEDULED and task.waypoint==id then
-table.insert(tasks,task)
-end
-end
-return tasks
-end
-function OPSGROUP:CountTasksWaypoint(id)
-local n=0
-for _,_task in pairs(self.taskqueue)do
-local task=_task
-if task.type==OPSGROUP.TaskType.WAYPOINT and task.status==OPSGROUP.TaskStatus.SCHEDULED and task.waypoint==id then
-n=n+1
-end
-end
-return n
-end
-function OPSGROUP:_SortTaskQueue()
-local function _sort(a,b)
-local taskA=a
-local taskB=b
-return(taskA.prio=task.time then
-return task
-end
-end
-return nil
-end
-function OPSGROUP:GetTaskCurrent()
-local task=self:GetTaskByID(self.taskcurrent,OPSGROUP.TaskStatus.EXECUTING)
-return task
-end
-function OPSGROUP:GetTaskByID(id,status)
-for _,_task in pairs(self.taskqueue)do
-local task=_task
-if task.id==id then
-if status==nil or status==task.status then
-return task
-end
-end
-end
-return nil
-end
-function OPSGROUP:onbeforeTaskExecute(From,Event,To,Task)
-local Mission=self:GetMissionByTaskID(Task.id)
-if Mission and(Mission.Tpush or#Mission.conditionPush>0)then
-if Mission:IsReadyToPush()then
-if self:IsWaiting()then
-self.Twaiting=nil
-self.dTwait=nil
-if self:IsFlightgroup()then
-self.flaghold:Set(1)
-end
-end
-else
-if self:IsWaiting()then
-else
-local alt=Mission.missionAltitude and UTILS.MetersToFeet(Mission.missionAltitude)or nil
-self:Wait(nil,alt)
-end
-local dt=Mission.Tpush and Mission.Tpush-timer.getAbsTime()or 20
-self:T(self.lid..string.format("Mission %s task execute suspended for %d seconds",Mission.name,dt))
-self:__TaskExecute(-dt,Task)
-return false
-end
-end
-if Mission and Mission.opstransport then
-local delivered=Mission.opstransport:IsCargoDelivered(self.groupname)
-if not delivered then
-local dt=30
-self:T(self.lid..string.format("Mission %s task execute suspended for %d seconds because we were not delivered",Mission.name,dt))
-self:__TaskExecute(-dt,Task)
-if(self:IsArmygroup()or self:IsNavygroup())and self:IsCruising()then
-self:FullStop()
-end
-return false
-end
-end
-return true
-end
-function OPSGROUP:onafterTaskExecute(From,Event,To,Task)
-local text=string.format("Task %s ID=%d execute",tostring(Task.description),Task.id)
-self:T(self.lid..text)
-self:T2({Task})
-if self.taskcurrent>0 then
-self:TaskCancel()
-end
-self.taskcurrent=Task.id
-Task.timestamp=timer.getAbsTime()
-Task.status=OPSGROUP.TaskStatus.EXECUTING
-if self:GetTaskCurrent()==nil then
-table.insert(self.taskqueue,Task)
-end
-local Mission=self:GetMissionByTaskID(self.taskcurrent)
-self:_UpdateTask(Task,Mission)
-if Mission then
-self:MissionExecute(Mission)
-end
-end
-function OPSGROUP:_UpdateTask(Task,Mission)
-Mission=Mission or self:GetMissionByTaskID(self.taskcurrent)
-if Task.dcstask.id==AUFTRAG.SpecialTask.FORMATION then
-local followSet=SET_GROUP:New():AddGroup(self.group)
-local param=Task.dcstask.params
-local followUnit=UNIT:FindByName(param.unitname)
-Task.formation=AI_FORMATION:New(followUnit,followSet,AUFTRAG.SpecialTask.FORMATION,"Follow X at given parameters.")
-Task.formation:FormationCenterWing(-param.offsetX,50,math.abs(param.altitude),50,param.offsetZ,50)
-Task.formation:SetFollowTimeInterval(param.dtFollow)
-Task.formation:SetFlightModeFormation(self.group)
-Task.formation:Start()
-elseif Task.dcstask.id==AUFTRAG.SpecialTask.PATROLZONE then
-local zone=Task.dcstask.params.zone
-local surfacetypes=nil
-if self:IsArmygroup()then
-surfacetypes={land.SurfaceType.LAND,land.SurfaceType.ROAD}
-elseif self:IsNavygroup()then
-surfacetypes={land.SurfaceType.WATER,land.SurfaceType.SHALLOW_WATER}
-end
-local Coordinate=zone:GetRandomCoordinate(nil,nil,surfacetypes)
-local Speed=Task.dcstask.params.speed and UTILS.MpsToKnots(Task.dcstask.params.speed)or UTILS.KmphToKnots(self.speedCruise)
-local Altitude=Task.dcstask.params.altitude and UTILS.MetersToFeet(Task.dcstask.params.altitude)or nil
-local currUID=self:GetWaypointCurrent().uid
-local wp=nil
-if self.isFlightgroup then
-wp=FLIGHTGROUP.AddWaypoint(self,Coordinate,Speed,currUID,Altitude)
-elseif self.isArmygroup then
-wp=ARMYGROUP.AddWaypoint(self,Coordinate,Speed,currUID,Task.dcstask.params.formation)
-elseif self.isNavygroup then
-wp=NAVYGROUP.AddWaypoint(self,Coordinate,Speed,currUID,Altitude)
-end
-wp.missionUID=Mission and Mission.auftragsnummer or nil
-elseif Task.dcstask.id==AUFTRAG.SpecialTask.RECON then
-local target=Task.dcstask.params.target
-self.reconindecies={}
-for i=1,#target.targets do
-table.insert(self.reconindecies,i)
-end
-local n=1
-if Task.dcstask.params.randomly then
-n=UTILS.GetRandomTableElement(self.reconindecies)
-else
-table.remove(self.reconindecies,n)
-end
-local object=target.targets[n]
-local zone=object.Object
-local Coordinate=zone:GetRandomCoordinate()
-local Speed=Task.dcstask.params.speed and UTILS.MpsToKnots(Task.dcstask.params.speed)or UTILS.KmphToKnots(self.speedCruise)
-local Altitude=Task.dcstask.params.altitude and UTILS.MetersToFeet(Task.dcstask.params.altitude)or nil
-local currUID=self:GetWaypointCurrent().uid
-local wp=nil
-if self.isFlightgroup then
-wp=FLIGHTGROUP.AddWaypoint(self,Coordinate,Speed,currUID,Altitude)
-elseif self.isArmygroup then
-wp=ARMYGROUP.AddWaypoint(self,Coordinate,Speed,currUID,Task.dcstask.params.formation)
-elseif self.isNavygroup then
-wp=NAVYGROUP.AddWaypoint(self,Coordinate,Speed,currUID,Altitude)
-end
-wp.missionUID=Mission and Mission.auftragsnummer or nil
-elseif Task.dcstask.id==AUFTRAG.SpecialTask.AMMOSUPPLY or Task.dcstask.id==AUFTRAG.SpecialTask.FUELSUPPLY then
-elseif Task.dcstask.id==AUFTRAG.SpecialTask.REARMING then
-local rearmed=self:_CheckAmmoFull()
-if rearmed then
-self:T2(self.lid.."Ammo already full ==> reaming task done!")
-self:TaskDone(Task)
-else
-self:T2(self.lid.."Ammo not full ==> Rearm()")
-self:Rearm()
-end
-elseif Task.dcstask.id==AUFTRAG.SpecialTask.ALERT5 then
-elseif Task.dcstask.id==AUFTRAG.SpecialTask.ONGUARD or Task.dcstask.id==AUFTRAG.SpecialTask.ARMOREDGUARD then
-if self:IsArmygroup()or self:IsNavygroup()then
-self:FullStop()
-else
-end
-elseif Task.dcstask.id==AUFTRAG.SpecialTask.NOTHING then
-if self:IsArmygroup()or self:IsNavygroup()then
-self:__FullStop(0.1)
-else
-end
-elseif Task.dcstask.id==AUFTRAG.SpecialTask.AIRDEFENSE or Task.dcstask.id==AUFTRAG.SpecialTask.EWR then
-if self:IsArmygroup()or self:IsNavygroup()then
-self:FullStop()
-else
-end
-elseif Task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK or Task.dcstask.id==AUFTRAG.SpecialTask.ARMORATTACK then
-local target=Task.dcstask.params.target
-local speed=self.speedMax and UTILS.KmphToKnots(self.speedMax)or nil
-if Task.dcstask.params.speed then
-speed=Task.dcstask.params.speed
-end
-if target then
-self:EngageTarget(target,speed,Task.dcstask.params.formation)
-end
-elseif Task.dcstask.id==AUFTRAG.SpecialTask.PATROLRACETRACK then
-if self.isFlightgroup then
-self:T("We are Special Auftrag Patrol Race Track, starting now ...")
-local aircraft=self:GetGroup()
-aircraft:PatrolRaceTrack(Task.dcstask.params.TrackPoint1,Task.dcstask.params.TrackPoint2,Task.dcstask.params.TrackAltitude,Task.dcstask.params.TrackSpeed,Task.dcstask.params.TrackFormation,false,1)
-end
-elseif Task.dcstask.id==AUFTRAG.SpecialTask.HOVER then
-if self.isFlightgroup then
-self:T("We are Special Auftrag HOVER, hovering now ...")
-local alt=Task.dcstask.params.hoverAltitude
-local time=Task.dcstask.params.hoverTime
-local mSpeed=Task.dcstask.params.missionSpeed or self.speedCruise or 150
-local Speed=UTILS.KmphToKnots(mSpeed)
-local CruiseAlt=UTILS.FeetToMeters(Task.dcstask.params.missionAltitude or 1000)
-local helo=self:GetGroup()
-helo:SetSpeed(0.01,true)
-helo:SetAltitude(alt,true,"BARO")
-self:HoverStart()
-local function FlyOn(Helo,Speed,CruiseAlt,Task)
-if Helo then
-Helo:SetSpeed(Speed,true)
-Helo:SetAltitude(CruiseAlt,true,"BARO")
-self:T("We are Special Auftrag HOVER, end of hovering now ...")
-self:TaskDone(Task)
-self:HoverEnd()
-end
-end
-local timer=TIMER:New(FlyOn,helo,Speed,CruiseAlt,Task)
-timer:Start(time)
-end
-elseif Task.dcstask.id==AUFTRAG.SpecialTask.RELOCATECOHORT then
-self:T(self.lid.."Executing task for relocation mission")
-local legion=Task.dcstask.params.legion
-local Coordinate=legion.spawnzone:GetRandomCoordinate()
-local currUID=self:GetWaypointCurrent().uid
-local wp=nil
-if self.isArmygroup then
-self:T2(self.lid.."Routing group to spawn zone of new legion")
-wp=ARMYGROUP.AddWaypoint(self,Coordinate,UTILS.KmphToKnots(self.speedCruise),currUID,Mission.optionFormation)
-elseif self.isFlightgroup then
-self:T2(self.lid.."Routing group to intermediate point near new legion")
-Coordinate=self:GetCoordinate():GetIntermediateCoordinate(Coordinate,0.8)
-wp=FLIGHTGROUP.AddWaypoint(self,Coordinate,UTILS.KmphToKnots(self.speedCruise),currUID,UTILS.MetersToFeet(self.altitudeCruise))
-elseif self.isNavygroup then
-self:T2(self.lid.."Routing group to spawn zone of new legion")
-wp=NAVYGROUP.AddWaypoint(self,Coordinate,UTILS.KmphToKnots(self.speedCruise),currUID)
-else
-end
-wp.missionUID=Mission and Mission.auftragsnummer or nil
-elseif Task.dcstask.id==AUFTRAG.SpecialTask.CAPTUREZONE then
-if self:IsEngaging()then
-self:T2(self.lid..string.format("CaptureZone: Engaging currently!"))
-else
-local Coalitions=UTILS.GetCoalitionEnemy(self:GetCoalition(),false)
-local zoneCurr=Task.target
-if zoneCurr then
-self:T(self.lid..string.format("Current target zone=%s owner=%s",zoneCurr:GetName(),zoneCurr:GetOwnerName()))
-if zoneCurr:GetOwner()==self:GetCoalition()then
-self:T(self.lid..string.format("Zone %s captured ==> Task DONE!",zoneCurr:GetName()))
-self:TaskDone(Task)
-else
-self:T(self.lid..string.format("Zone %s NOT captured!",zoneCurr:GetName()))
-if Mission:GetGroupStatus(self)==AUFTRAG.GroupStatus.EXECUTING then
-self:T(self.lid..string.format("Zone %s NOT captured and EXECUTING ==> Find target",zoneCurr:GetName()))
-local targetgroup=zoneCurr:GetScannedGroupSet():GetClosestGroup(self.coordinate,Coalitions)
-if targetgroup then
-self:T(self.lid..string.format("Zone %s NOT captured: engaging target %s",zoneCurr:GetName(),targetgroup:GetName()))
-self:EngageTarget(targetgroup)
-else
-if self:IsFlightgroup()then
-self:T(self.lid..string.format("Zone %s not captured but no target group could be found ==> TaskDone as FLIGHTGROUPS cannot capture zones",zoneCurr:GetName()))
-self:TaskDone(Task)
-else
-self:T(self.lid..string.format("Zone %s not captured but no target group could be found. Should be captured in the next zone evaluation.",zoneCurr:GetName()))
-end
-end
-else
-self:T(self.lid..string.format("Zone %s NOT captured and NOT EXECUTING",zoneCurr:GetName()))
-end
-end
-else
-self:T(self.lid..string.format("NO Current target zone=%s"))
-end
-end
-else
-if Task.type==OPSGROUP.TaskType.SCHEDULED or Task.ismission then
-local DCSTask=nil
-if Task.dcstask.id==AUFTRAG.SpecialTask.BARRAGE then
-local vec2=self:GetVec2()
-local param=Task.dcstask.params
-local heading=param.heading or math.random(1,360)
-local Altitude=param.altitude or 500
-local Alpha=param.angle or math.random(45,85)
-local distance=Altitude/math.tan(math.rad(Alpha))
-local tvec2=UTILS.Vec2Translate(vec2,distance,heading)
-self:T(self.lid..string.format("Barrage: Shots=%s, Altitude=%d m, Angle=%d°, heading=%03d°, distance=%d m",tostring(param.shots),Altitude,Alpha,heading,distance))
-DCSTask=CONTROLLABLE.TaskFireAtPoint(nil,tvec2,param.radius,param.shots,param.weaponType,Altitude)
-elseif Task.ismission and Task.dcstask.id=='FireAtPoint'then
-DCSTask=UTILS.DeepCopy(Task.dcstask)
-local ammo=self:GetAmmoTot()
-local nAmmo=ammo.Total
-local weaponType=DCSTask.params.weaponType or-1
-if weaponType==ENUMS.WeaponFlag.CruiseMissile then
-nAmmo=ammo.MissilesCR
-elseif weaponType==ENUMS.WeaponFlag.AnyRocket then
-nAmmo=ammo.Rockets
-elseif weaponType==ENUMS.WeaponFlag.Cannons then
-nAmmo=ammo.Guns
-end
-local nShots=DCSTask.params.expendQty or 1
-self:T(self.lid..string.format("Fire at point with nshots=%d of %d",nShots,nAmmo))
-if nShots==-1 then
-nShots=nAmmo
-self:T(self.lid..string.format("Fire at point taking max amount of ammo = %d",nShots))
-elseif nShots<1 then
-local p=nShots
-nShots=UTILS.Round(p*nAmmo,0)
-self:T(self.lid..string.format("Fire at point taking %.1f percent amount of ammo = %d",p,nShots))
-else
-nShots=math.min(nShots,nAmmo)
-end
-DCSTask.params.expendQty=nShots
-else
-DCSTask=Task.dcstask
-end
-self:_SandwitchDCSTask(DCSTask,Task)
-elseif Task.type==OPSGROUP.TaskType.WAYPOINT then
-else
-self:T(self.lid.."ERROR: Unknown task type: ")
-end
-end
-end
-function OPSGROUP:_SandwitchDCSTask(DCSTask,Task,SetTask,Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,OPSGROUP._SandwitchDCSTask,self,DCSTask,Task,SetTask)
-else
-local DCStasks={}
-if DCSTask.id=='ComboTask'then
-for TaskID,Task in ipairs(DCSTask.params.tasks)do
-table.insert(DCStasks,Task)
-end
-else
-table.insert(DCStasks,DCSTask)
-end
-local TaskCombo=self.group:TaskCombo(DCStasks)
-local TaskCondition=self.group:TaskCondition(nil,Task.stopflag:GetName(),1,nil,Task.duration)
-local TaskControlled=self.group:TaskControlled(TaskCombo,TaskCondition)
-local TaskDone=self.group:TaskFunction("OPSGROUP._TaskDone",self,Task)
-local TaskFinal=self.group:TaskCombo({TaskControlled,TaskDone})
-if SetTask then
-self:SetTask(TaskFinal)
-else
-self:PushTask(TaskFinal)
-end
-end
-end
-function OPSGROUP:onafterTaskCancel(From,Event,To,Task)
-local currenttask=self:GetTaskCurrent()
-Task=Task or currenttask
-if Task then
-if currenttask and Task.id==currenttask.id then
-local stopflag=Task.stopflag:Get()
-local text=string.format("Current task %s ID=%d cancelled (flag %s=%d)",Task.description,Task.id,Task.stopflag:GetName(),stopflag)
-self:T(self.lid..text)
-Task.stopflag:Set(1)
-local done=false
-if Task.dcstask.id==AUFTRAG.SpecialTask.FORMATION then
-Task.formation:Stop()
-done=true
-elseif Task.dcstask.id==AUFTRAG.SpecialTask.PATROLZONE then
-done=true
-elseif Task.dcstask.id==AUFTRAG.SpecialTask.RECON then
-done=true
-elseif Task.dcstask.id==AUFTRAG.SpecialTask.AMMOSUPPLY then
-done=true
-elseif Task.dcstask.id==AUFTRAG.SpecialTask.FUELSUPPLY then
-done=true
-elseif Task.dcstask.id==AUFTRAG.SpecialTask.REARMING then
-done=true
-elseif Task.dcstask.id==AUFTRAG.SpecialTask.ALERT5 then
-done=true
-elseif Task.dcstask.id==AUFTRAG.SpecialTask.ONGUARD or Task.dcstask.id==AUFTRAG.SpecialTask.ARMOREDGUARD then
-done=true
-elseif Task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK or Task.dcstask.id==AUFTRAG.SpecialTask.ARMORATTACK then
-done=true
-elseif Task.dcstask.id==AUFTRAG.SpecialTask.NOTHING then
-done=true
-elseif stopflag==1 or(not self:IsAlive())or self:IsDead()or self:IsStopped()then
-done=true
-end
-if done then
-self:TaskDone(Task)
-end
-else
-self:T(self.lid..string.format("TaskCancel: Setting task %s ID=%d to DONE",Task.description,Task.id))
-self:TaskDone(Task)
-end
-else
-local text=string.format("WARNING: No (current) task to cancel!")
-self:T(self.lid..text)
-end
-end
-function OPSGROUP:onbeforeTaskDone(From,Event,To,Task)
-local allowed=true
-if Task.status==OPSGROUP.TaskStatus.PAUSED then
-allowed=false
-end
-return allowed
-end
-function OPSGROUP:onafterTaskDone(From,Event,To,Task)
-local text=string.format("Task done: %s ID=%d",Task.description,Task.id)
-self:T(self.lid..text)
-if Task.id==self.taskcurrent then
-self.taskcurrent=0
-end
-Task.status=OPSGROUP.TaskStatus.DONE
-if Task.backupROE then
-self:SwitchROE(Task.backupROE)
-end
-local Mission=self:GetMissionByTaskID(Task.id)
-if Mission and Mission:IsNotOver()then
-local status=Mission:GetGroupStatus(self)
-if status~=AUFTRAG.GroupStatus.PAUSED then
-if Mission.type==AUFTRAG.Type.CAPTUREZONE and Mission:CountMissionTargets()>0 then
-self:T(self.lid.."Remove mission waypoints")
-self:_RemoveMissionWaypoints(Mission,false)
-if self:IsFlightgroup()then
-else
-self:T(self.lid.."Task done ==> Route to mission for next opszone")
-self:MissionStart(Mission)
-return
-end
-end
-local EgressUID=Mission:GetGroupEgressWaypointUID(self)
-if EgressUID then
-self:T(self.lid..string.format("Task Done but Egress waypoint defined ==> Will call Mission Done once group passed waypoint UID=%d!",EgressUID))
-else
-self:T(self.lid.."Task Done ==> Mission Done!")
-self:MissionDone(Mission)
-end
-else
-if self:IsOnMission(Mission.auftragsnummer)then
-self.currentmission=nil
-end
-self:T(self.lid.."Remove mission waypoints")
-self:_RemoveMissionWaypoints(Mission,false)
-end
-else
-if Task.description=="Engage_Target"then
-self:T(self.lid.."Task DONE Engage_Target ==> Cruise")
-self:Disengage()
-end
-if Task.description==AUFTRAG.SpecialTask.ONGUARD or Task.description==AUFTRAG.SpecialTask.ARMOREDGUARD or Task.description==AUFTRAG.SpecialTask.NOTHING then
-self:T(self.lid.."Task DONE OnGuard ==> Cruise")
-self:Cruise()
-end
-if Task.description=="Task_Land_At"then
-self:T(self.lid.."Taske DONE Task_Land_At ==> Wait")
-self:Cruise()
-self:Wait(20,100)
-else
-self:T(self.lid.."Task Done but NO mission found ==> _CheckGroupDone in 1 sec")
-self:_CheckGroupDone(1)
-end
-end
-end
-function OPSGROUP:AddMission(Mission)
-Mission:AddOpsGroup(self)
-Mission:SetGroupStatus(self,AUFTRAG.GroupStatus.SCHEDULED)
-Mission:Scheduled()
-Mission.Nelements=Mission.Nelements+#self.elements
-Mission.Ngroups=Mission.Ngroups+1
-table.insert(self.missionqueue,Mission)
-self.adinfinitum=Mission.DCStask.params.adinfinitum and Mission.DCStask.params.adinfinitum or false
-local text=string.format("Added %s mission %s starting at %s, stopping at %s",
-tostring(Mission.type),tostring(Mission.name),UTILS.SecondsToClock(Mission.Tstart,true),Mission.Tstop and UTILS.SecondsToClock(Mission.Tstop,true)or"INF")
-self:T(self.lid..text)
-return self
-end
-function OPSGROUP:RemoveMission(Mission)
-for i=#self.missionqueue,1,-1 do
-local mission=self.missionqueue[i]
-if mission.auftragsnummer==Mission.auftragsnummer then
-local Task=Mission:GetGroupWaypointTask(self)
-if Task then
-self:RemoveTask(Task)
-end
-for j=#self.pausedmissions,1,-1 do
-local mid=self.pausedmissions[j]
-if Mission.auftragsnummer==mid then
-table.remove(self.pausedmissions,j)
-end
-end
-table.remove(self.missionqueue,i)
-return self
-end
-end
-return self
-end
-function OPSGROUP:CancelAllMissions()
-self:T(self.lid.."Cancelling ALL missions!")
-for _,_mission in pairs(self.missionqueue)do
-local mission=_mission
-local mystatus=mission:GetGroupStatus(self)
-if not(mystatus==AUFTRAG.GroupStatus.DONE or mystatus==AUFTRAG.GroupStatus.CANCELLED)then
-self:T(self.lid.."Cancelling mission "..tostring(mission:GetName()))
-self:MissionCancel(mission)
-end
-end
-end
-function OPSGROUP:CountRemainingMissison()
-local N=0
-for _,_mission in pairs(self.missionqueue)do
-local mission=_mission
-if mission and mission:IsNotOver()then
-local status=mission:GetGroupStatus(self)
-if status~=AUFTRAG.GroupStatus.DONE and status~=AUFTRAG.GroupStatus.CANCELLED then
-N=N+1
-end
-end
-end
-return N
-end
-function OPSGROUP:CountRemainingTransports()
-local N=0
-for _,_transport in pairs(self.cargoqueue)do
-local transport=_transport
-local mystatus=transport:GetCarrierTransportStatus(self)
-local status=transport:GetState()
-self:T(self.lid..string.format("Transport my status=%s [%s]",mystatus,status))
-if transport and mystatus==OPSTRANSPORT.Status.SCHEDULED and status~=OPSTRANSPORT.Status.DELIVERED and status~=OPSTRANSPORT.Status.CANCELLED then
-N=N+1
-end
-end
-if N==0 and self.cargoTransport and
-self.cargoTransport:GetState()~=OPSTRANSPORT.Status.DELIVERED and self.cargoTransport:GetCarrierTransportStatus(self)~=OPSTRANSPORT.Status.DELIVERED and
-self.cargoTransport:GetState()~=OPSTRANSPORT.Status.CANCELLED and self.cargoTransport:GetCarrierTransportStatus(self)~=OPSTRANSPORT.Status.CANCELLED then
-N=1
-end
-return N
-end
-function OPSGROUP:_GetNextMission()
-if self:IsPickingup()or self:IsLoading()or self:IsTransporting()or self:IsUnloading()or self:IsLoaded()then
-return nil
-end
-local Nmissions=#self.missionqueue
-if Nmissions==0 then
-return nil
-end
-local function _sort(a,b)
-local taskA=a
-local taskB=b
-return(taskA.prio3.6 or true then
-self:RouteToMission(Mission,3)
-else
-self:T(self.lid.."Immobile GROUP!")
-local Clock=Mission.Tpush and UTILS.SecondsToClock(Mission.Tpush)or 5
-local Task=self:AddTask(Mission.DCStask,Clock,Mission.name,Mission.prio,Mission.duration)
-Task.ismission=true
-Mission:SetGroupWaypointTask(self,Task)
-self:__TaskExecute(3,Task)
-end
-end
-function OPSGROUP:onafterMissionExecute(From,Event,To,Mission)
-local text=string.format("Executing %s Mission %s, target %s",Mission.type,tostring(Mission.name),Mission:GetTargetName())
-self:T(self.lid..text)
-Mission:SetGroupStatus(self,AUFTRAG.GroupStatus.EXECUTING)
-Mission:Executing()
-if self:IsHolding()and not self:HasPassedFinalWaypoint()then
-self:Cruise()
-end
-if Mission.engagedetectedOn then
-self:SetEngageDetectedOn(UTILS.MetersToNM(Mission.engagedetectedRmax),Mission.engagedetectedTypes,Mission.engagedetectedEngageZones,Mission.engagedetectedNoEngageZones)
-end
-if self.isFlightgroup then
-if Mission.prohibitABExecute==true then
-self:SetProhibitAfterburner()
-self:T(self.lid.."Set prohibit AB")
-elseif Mission.prohibitABExecute==false then
-self:SetAllowAfterburner()
-self:T2(self.lid.."Set allow AB")
-end
-end
-end
-function OPSGROUP:onafterPauseMission(From,Event,To)
-local Mission=self:GetMissionCurrent()
-if Mission then
-Mission:SetGroupStatus(self,AUFTRAG.GroupStatus.PAUSED)
-local Task=Mission:GetGroupWaypointTask(self)
-self:T(self.lid..string.format("Pausing current mission %s. Task=%s",tostring(Mission.name),tostring(Task and Task.description or"WTF")))
-self:TaskCancel(Task)
-self:_RemoveMissionWaypoints(Mission)
-table.insert(self.pausedmissions,1,Mission.auftragsnummer)
-end
-end
-function OPSGROUP:onafterUnpauseMission(From,Event,To)
-local mission=self:_GetPausedMission()
-if mission then
-self:T(self.lid..string.format("Unpausing mission %s [%s]",mission:GetName(),mission:GetType()))
-self:MissionStart(mission)
-for i,mid in pairs(self.pausedmissions)do
-if mid==mission.auftragsnummer then
-self:T(self.lid..string.format("Removing paused mission id=%d",mid))
-table.remove(self.pausedmissions,i)
-break
-end
-end
-else
-self:T(self.lid.."ERROR: No mission to unpause!")
-end
-end
-function OPSGROUP:onafterMissionCancel(From,Event,To,Mission)
-if self:IsOnMission(Mission.auftragsnummer)then
-local Task=Mission:GetGroupWaypointTask(self)
-if Task then
-self:T(self.lid..string.format("Cancel current mission %s. Task=%s",tostring(Mission.name),tostring(Task and Task.description or"WTF")))
-self:TaskCancel(Task)
-else
-self:MissionDone(Mission)
-end
-else
-Mission:SetGroupStatus(self,AUFTRAG.GroupStatus.CANCELLED)
-self:RemoveMission(Mission)
-self:_CheckGroupDone(1)
-end
-end
-function OPSGROUP:_RemoveMissionWaypoints(Mission,Silently)
-for i=#self.waypoints,1,-1 do
-local wp=self.waypoints[i]
-if wp.missionUID==Mission.auftragsnummer then
-if Silently then
-table.remove(self.waypoints,i)
-else
-self:RemoveWaypoint(i)
-end
-end
-end
-end
-function OPSGROUP:onafterMissionDone(From,Event,To,Mission)
-local text=string.format("Mission %s DONE!",Mission.name)
-self:T(self.lid..text)
-Mission:SetGroupStatus(self,AUFTRAG.GroupStatus.DONE)
-if self:IsOnMission(Mission.auftragsnummer)then
-self.currentmission=nil
-end
-self:_RemoveMissionWaypoints(Mission)
-if Mission.patroldata then
-Mission.patroldata.noccupied=Mission.patroldata.noccupied-1
-AIRWING.UpdatePatrolPointMarker(Mission.patroldata)
-end
-if Mission.engagedetectedOn then
-self:SetEngageDetectedOff()
-end
-if Mission.optionROE then
-self:SwitchROE()
-end
-if self:IsFlightgroup()and Mission.optionROT then
-self:SwitchROT()
-end
-if Mission.optionAlarm then
-self:SwitchAlarmstate()
-end
-if Mission.optionEPLRS then
-self:SwitchEPLRS()
-end
-if Mission.optionEmission then
-self:SwitchEmission()
-end
-if Mission.optionInvisible then
-self:SwitchInvisible()
-end
-if Mission.optionImmortal then
-self:SwitchImmortal()
-end
-if Mission.optionFormation and self:IsFlightgroup()then
-self:SwitchFormation()
-end
-if Mission.radio then
-self:SwitchRadio()
-end
-if Mission.tacan then
-self:_SwitchTACAN()
-local cohort=self.cohort
-if cohort then
-cohort:ReturnTacan(Mission.tacan.Channel)
-end
-local asset=Mission:GetAssetByName(self.groupname)
-if asset then
-asset.tacan=nil
-end
-end
-if Mission.icls then
-self:_SwitchICLS()
-end
-if self.legion and Mission.legionReturn~=nil then
-self:SetReturnToLegion(Mission.legionReturn)
-end
-local delay=1
-if Mission.type==AUFTRAG.Type.ARTY then
-delay=60
-elseif Mission.type==AUFTRAG.Type.RELOCATECOHORT then
-local legion=Mission.DCStask.params.legion
-self:T(self.lid..string.format("Asset relocated to new legion=%s",tostring(legion.alias)))
-local asset=Mission:GetAssetByName(self.groupname)
-if asset then
-asset.wid=legion.uid
-end
-self.legion=legion
-if self.isArmygroup then
-self:T2(self.lid.."Adding asset via ReturnToLegion()")
-self:ReturnToLegion()
-elseif self.isFlightgroup then
-self:T2(self.lid.."Adding asset via RTB to new legion airbase")
-self:RTB(self.legion.airbase)
-end
-return
-end
-if self.isFlightgroup then
-if Mission.prohibitAB==true then
-self:T2("Setting prohibit AB")
-self:SetProhibitAfterburner()
-elseif Mission.prohibitAB==false then
-self:T2("Setting allow AB")
-self:SetAllowAfterburner()
-end
-end
-if self.legion and self.legionReturn==false and self.waypoints and#self.waypoints==1 then
-local Coordinate=self:GetCoordinate()
-if self.isArmygroup then
-ARMYGROUP.AddWaypoint(self,Coordinate,0,nil,nil,false)
-elseif self.isNavygroup then
-NAVYGROUP.AddWaypoint(self,Coordinate,0,nil,nil,false)
-end
-self:RemoveWaypoint(1)
-self:_PassedFinalWaypoint(true,"Passed final waypoint as group is done with mission but should NOT return to its legion")
-end
-self:_CheckGroupDone(delay)
-end
-function OPSGROUP:RouteToMission(mission,delay)
-if delay and delay>0 then
-self:ScheduleOnce(delay,OPSGROUP.RouteToMission,self,mission)
-else
-self:T(self.lid..string.format("Route To Mission"))
-if self:IsDead()or self:IsStopped()then
-self:T(self.lid..string.format("Route To Mission: I am DEAD or STOPPED! Ooops..."))
-return
-end
-if self:IsCargo()then
-self:T(self.lid..string.format("Route To Mission: I am CARGO! You cannot route me..."))
-return
-end
-if mission.type==AUFTRAG.Type.OPSTRANSPORT then
-self:T(self.lid..string.format("Route To Mission: I am OPSTRANSPORT! Add transport and return..."))
-self:AddOpsTransport(mission.opstransport)
-return
-end
-if mission.type==AUFTRAG.Type.ALERT5 then
-self:T(self.lid..string.format("Route To Mission: I am ALERT5! Go right to MissionExecute()..."))
-self:MissionExecute(mission)
-return
-end
-local uid=self:GetWaypointCurrentUID()
-local waypointcoord=nil
-local currentcoord=self:GetCoordinate()
-local roadcoord=currentcoord:GetClosestPointToRoad()
-local roaddist=nil
-if roadcoord then
-roaddist=currentcoord:Get2DDistance(roadcoord)
-end
-local targetzone=nil
-local randomradius=mission.missionWaypointRadius or 1000
-local surfacetypes=nil
-if self:IsArmygroup()then
-surfacetypes={land.SurfaceType.LAND,land.SurfaceType.ROAD}
-elseif self:IsNavygroup()then
-surfacetypes={land.SurfaceType.WATER,land.SurfaceType.SHALLOW_WATER}
-end
-local targetobject=mission:GetObjective(currentcoord,UTILS.GetCoalitionEnemy(self:GetCoalition(),true))
-if targetobject then
-self:T(self.lid..string.format("Route to mission target object %s",targetobject:GetName()))
-end
-if mission.opstransport and not mission.opstransport:IsCargoDelivered(self.groupname)then
-local tzc=mission.opstransport:GetTZCofCargo(self.groupname)
-local pickupzone=tzc.PickupZone
-if self:IsInZone(pickupzone)then
-self:PauseMission()
-self:FullStop()
-return
-else
-waypointcoord=pickupzone:GetRandomCoordinate()
-end
-elseif mission.type==AUFTRAG.Type.PATROLZONE or
-mission.type==AUFTRAG.Type.BARRAGE or
-mission.type==AUFTRAG.Type.AMMOSUPPLY or
-mission.type==AUFTRAG.Type.FUELSUPPLY or
-mission.type==AUFTRAG.Type.REARMING or
-mission.type==AUFTRAG.Type.AIRDEFENSE or
-mission.type==AUFTRAG.Type.EWR then
-targetzone=targetobject
-waypointcoord=targetzone:GetRandomCoordinate(nil,nil,surfacetypes)
-elseif mission.type==AUFTRAG.Type.ONGUARD or mission.type==AUFTRAG.Type.ARMOREDGUARD then
-waypointcoord=mission:GetMissionWaypointCoord(self.group,nil,surfacetypes)
-elseif mission.type==AUFTRAG.Type.NOTHING then
-targetzone=targetobject
-waypointcoord=targetzone:GetRandomCoordinate(nil,nil,surfacetypes)
-elseif mission.type==AUFTRAG.Type.HOVER then
-local zone=targetobject
-waypointcoord=zone:GetCoordinate()
-elseif mission.type==AUFTRAG.Type.RELOCATECOHORT then
-local ToCoordinate=mission.DCStask.params.legion:GetCoordinate()
-if self.isFlightgroup then
-waypointcoord=currentcoord:GetIntermediateCoordinate(ToCoordinate,0.2):SetAltitude(self.altitudeCruise)
-elseif self.isArmygroup then
-if roadcoord then
-waypointcoord=roadcoord
-else
-waypointcoord=currentcoord:GetIntermediateCoordinate(ToCoordinate,100)
-end
-else
-waypointcoord=currentcoord:GetIntermediateCoordinate(ToCoordinate,0.05)
-end
-elseif mission.type==AUFTRAG.Type.CAPTUREZONE then
-targetzone=targetobject:GetZone()
-waypointcoord=targetzone:GetRandomCoordinate(nil,nil,surfacetypes)
-else
-waypointcoord=mission:GetMissionWaypointCoord(self.group,randomradius,surfacetypes)
-end
-for _,task in pairs(mission.enrouteTasks)do
-self:AddTaskEnroute(task)
-end
-local SpeedToMission=mission.missionSpeed and UTILS.KmphToKnots(mission.missionSpeed)or self:GetSpeedCruise()
-if mission.type==AUFTRAG.Type.TROOPTRANSPORT then
-mission.DCStask=mission:GetDCSMissionTask(self.group)
-local pradius=mission.transportPickupRadius
-local pickupZone=ZONE_RADIUS:New("Pickup Zone",mission.transportPickup:GetVec2(),pradius)
-for _,_group in pairs(mission.transportGroupSet.Set)do
-local group=_group
-if group and group:IsAlive()then
-local pcoord=pickupZone:GetRandomCoordinate(20,pradius,{land.SurfaceType.LAND,land.SurfaceType.ROAD})
-local DCSTask=group:TaskEmbarkToTransport(pcoord,pradius)
-group:SetTask(DCSTask,5)
-end
-end
-elseif mission.type==AUFTRAG.Type.ARTY then
-local targetcoord=mission:GetTargetCoordinate()
-local inRange=self:InWeaponRange(targetcoord,mission.engageWeaponType)
-if inRange then
-waypointcoord=self:GetCoordinate(true)
-else
-local coordInRange=self:GetCoordinateInRange(targetcoord,mission.engageWeaponType,waypointcoord)
-if coordInRange then
-local waypoint=nil
-if self:IsFlightgroup()then
-waypoint=FLIGHTGROUP.AddWaypoint(self,waypointcoord,SpeedToMission,uid,UTILS.MetersToFeet(mission.missionAltitude or self.altitudeCruise),false)
-elseif self:IsArmygroup()then
-waypoint=ARMYGROUP.AddWaypoint(self,waypointcoord,SpeedToMission,uid,mission.optionFormation,false)
-elseif self:IsNavygroup()then
-waypoint=NAVYGROUP.AddWaypoint(self,waypointcoord,SpeedToMission,uid,UTILS.MetersToFeet(mission.missionAltitude or self.altitudeCruise),false)
-end
-waypoint.missionUID=mission.auftragsnummer
-waypointcoord=coordInRange
-uid=waypoint.uid
-end
-end
-end
-local d=currentcoord:Get2DDistance(waypointcoord)
-self:T(self.lid..string.format("Distance to ingress waypoint=%.1f m",d))
-local waypoint=nil
-if self:IsFlightgroup()then
-waypoint=FLIGHTGROUP.AddWaypoint(self,waypointcoord,SpeedToMission,uid,UTILS.MetersToFeet(mission.missionAltitude or self.altitudeCruise),false)
-elseif self:IsArmygroup()then
-local formation=mission.optionFormation
-if d<1000 or mission.type==AUFTRAG.Type.RELOCATECOHORT then
-formation=ENUMS.Formation.Vehicle.OffRoad
-end
-waypoint=ARMYGROUP.AddWaypoint(self,waypointcoord,SpeedToMission,uid,formation,false)
-elseif self:IsNavygroup()then
-waypoint=NAVYGROUP.AddWaypoint(self,waypointcoord,SpeedToMission,uid,UTILS.MetersToFeet(mission.missionAltitude or self.altitudeCruise),false)
-end
-waypoint.missionUID=mission.auftragsnummer
-local waypointtask=self:AddTaskWaypoint(mission.DCStask,waypoint,mission.name,mission.prio,mission.duration)
-waypointtask.ismission=true
-waypointtask.target=targetobject
-mission:SetGroupWaypointTask(self,waypointtask)
-mission:SetGroupWaypointIndex(self,waypoint.uid)
-local egresscoord=mission:GetMissionEgressCoord()
-if egresscoord then
-local Ewaypoint=nil
-if self:IsFlightgroup()then
-Ewaypoint=FLIGHTGROUP.AddWaypoint(self,egresscoord,SpeedToMission,waypoint.uid,UTILS.MetersToFeet(mission.missionAltitude or self.altitudeCruise),false)
-elseif self:IsArmygroup()then
-Ewaypoint=ARMYGROUP.AddWaypoint(self,egresscoord,SpeedToMission,waypoint.uid,mission.optionFormation,false)
-elseif self:IsNavygroup()then
-Ewaypoint=NAVYGROUP.AddWaypoint(self,egresscoord,SpeedToMission,waypoint.uid,UTILS.MetersToFeet(mission.missionAltitude or self.altitudeCruise),false)
-end
-Ewaypoint.missionUID=mission.auftragsnummer
-mission:SetGroupEgressWaypointUID(self,Ewaypoint.uid)
-end
-if targetzone and self:IsInZone(targetzone)then
-self:T(self.lid.."Already in mission zone ==> TaskExecute()")
-self:TaskExecute(waypointtask)
-return
-elseif d<25 then
-self:T(self.lid.."Already within 25 meters of mission waypoint ==> TaskExecute()")
-self:TaskExecute(waypointtask)
-return
-end
-if self.speedMax<=3.6 or mission.teleport then
-self:Teleport(waypointcoord,nil,true)
-self:__TaskExecute(-1,waypointtask)
-else
-if self:IsArmygroup()then
-self:Cruise(SpeedToMission)
-elseif self:IsNavygroup()then
-self:Cruise(SpeedToMission)
-elseif self:IsFlightgroup()then
-self:UpdateRoute()
-end
-end
-self:_SetMissionOptions(mission)
-end
-end
-function OPSGROUP:_SetMissionOptions(mission)
-if mission.optionROE then
-self:SwitchROE(mission.optionROE)
-end
-if mission.optionROT then
-self:SwitchROT(mission.optionROT)
-end
-if mission.optionAlarm then
-self:SwitchAlarmstate(mission.optionAlarm)
-end
-if mission.optionEPLRS then
-self:SwitchEPLRS(mission.optionEPLRS)
-end
-if mission.optionEmission then
-self:SwitchEmission(mission.optionEmission)
-end
-if mission.optionInvisible then
-self:SwitchInvisible(mission.optionInvisible)
-end
-if mission.optionImmortal then
-self:SwitchImmortal(mission.optionImmortal)
-end
-if mission.optionFormation and self:IsFlightgroup()then
-self:SwitchFormation(mission.optionFormation)
-end
-if mission.radio then
-self:SwitchRadio(mission.radio.Freq,mission.radio.Modu)
-end
-if mission.tacan then
-self:SwitchTACAN(mission.tacan.Channel,mission.tacan.Morse,mission.tacan.BeaconName,mission.tacan.Band)
-end
-if mission.icls then
-self:SwitchICLS(mission.icls.Channel,mission.icls.Morse,mission.icls.UnitName)
-end
-if self.isFlightgroup then
-if mission.prohibitAB==true then
-self:SetProhibitAfterburner()
-self:T2("Set prohibit AB")
-elseif mission.prohibitAB==false then
-self:SetAllowAfterburner()
-self:T2("Set allow AB")
-end
-end
-return self
-end
-function OPSGROUP:_QueueUpdate()
-if self:IsExist()then
-local mission=self:_GetNextMission()
-if mission then
-local currentmission=self:GetMissionCurrent()
-if currentmission then
-if mission.urgent and mission.prio0 then
-self:T(self.lid..string.format("WARNING: Got current task ==> WAIT event is suspended for 30 sec!"))
-Tsuspend=-30
-allowed=false
-end
-if self.cargoTransport then
-self:T(self.lid..string.format("WARNING: Got current TRANSPORT assignment ==> WAIT event is suspended for 30 sec!"))
-Tsuspend=-30
-allowed=false
-end
-if Tsuspend and not allowed then
-self:__Wait(Tsuspend,Duration)
-end
-return allowed
-end
-function OPSGROUP:onafterWait(From,Event,To,Duration)
-self:FullStop()
-self.Twaiting=timer.getAbsTime()
-self.dTwait=Duration
-end
-function OPSGROUP:onafterPassingWaypoint(From,Event,To,Waypoint)
-local task=self:GetTaskCurrent()
-local mission=nil
-if task then
-mission=self:GetMissionByTaskID(task.id)
-end
-if task and task.dcstask.id==AUFTRAG.SpecialTask.PATROLZONE then
-self:RemoveWaypointByID(Waypoint.uid)
-local zone=task.dcstask.params.zone
-local surfacetypes=nil
-if self:IsArmygroup()then
-surfacetypes={land.SurfaceType.LAND,land.SurfaceType.ROAD}
-elseif self:IsNavygroup()then
-surfacetypes={land.SurfaceType.WATER,land.SurfaceType.SHALLOW_WATER}
-end
-local Coordinate=zone:GetRandomCoordinate(nil,nil,surfacetypes)
-local Speed=task.dcstask.params.speed and UTILS.MpsToKnots(task.dcstask.params.speed)or UTILS.KmphToKnots(self.speedCruise)
-local Altitude=UTILS.MetersToFeet(task.dcstask.params.altitude or self.altitudeCruise)
-local currUID=self:GetWaypointCurrent().uid
-local wp=nil
-if self.isFlightgroup then
-wp=FLIGHTGROUP.AddWaypoint(self,Coordinate,Speed,currUID,Altitude)
-elseif self.isArmygroup then
-wp=ARMYGROUP.AddWaypoint(self,Coordinate,Speed,currUID,task.dcstask.params.formation)
-elseif self.isNavygroup then
-wp=NAVYGROUP.AddWaypoint(self,Coordinate,Speed,currUID,Altitude)
-end
-wp.missionUID=mission and mission.auftragsnummer or nil
-elseif task and task.dcstask.id==AUFTRAG.SpecialTask.RECON then
-local target=task.dcstask.params.target
-if self.adinfinitum and#self.reconindecies==0 then
-self.reconindecies={}
-for i=1,#target.targets do
-table.insert(self.reconindecies,i)
-end
-end
-if#self.reconindecies>0 then
-local n=1
-if task.dcstask.params.randomly then
-n=UTILS.GetRandomTableElement(self.reconindecies)
-else
-n=self.reconindecies[1]
-table.remove(self.reconindecies,1)
-end
-local object=target.targets[n]
-local zone=object.Object
-local Coordinate=zone:GetRandomCoordinate()
-local Speed=task.dcstask.params.speed and UTILS.MpsToKnots(task.dcstask.params.speed)or UTILS.KmphToKnots(self.speedCruise)
-local Altitude=task.dcstask.params.altitude and UTILS.MetersToFeet(task.dcstask.params.altitude)or nil
-local currUID=self:GetWaypointCurrent().uid
-local wp=nil
-if self.isFlightgroup then
-wp=FLIGHTGROUP.AddWaypoint(self,Coordinate,Speed,currUID,Altitude)
-elseif self.isArmygroup then
-wp=ARMYGROUP.AddWaypoint(self,Coordinate,Speed,currUID,task.dcstask.params.formation)
-elseif self.isNavygroup then
-wp=NAVYGROUP.AddWaypoint(self,Coordinate,Speed,currUID,Altitude)
-end
-wp.missionUID=mission and mission.auftragsnummer or nil
-else
-local wpindex=self:GetWaypointIndex(Waypoint.uid)
-if wpindex==nil or wpindex==#self.waypoints then
-if not self.adinfinitum or#self.waypoints<=1 then
-self:_PassedFinalWaypoint(true,"Passing waypoint and NOT adinfinitum and #self.waypoints<=1")
-end
-end
-self:TaskDone(task)
-end
-elseif task and task.dcstask.id==AUFTRAG.SpecialTask.RELOCATECOHORT then
-local legion=task.dcstask.params.legion
-self:T(self.lid..string.format("Asset arrived at relocation task waypoint ==> Task Done!"))
-self:TaskDone(task)
-elseif task and task.dcstask.id==AUFTRAG.SpecialTask.REARMING then
-self:T(self.lid..string.format("FF Rearming Mission ==> Rearm()"))
-self:Rearm()
-else
-local ntasks=self:_SetWaypointTasks(Waypoint)
-local wpindex=self:GetWaypointIndex(Waypoint.uid)
-if wpindex==nil or wpindex==#self.waypoints then
-if self.adinfinitum then
-if Waypoint.missionUID then
-else
-if#self.waypoints<=1 then
-self:_PassedFinalWaypoint(true,"PassingWaypoint: adinfinitum but only ONE WAYPOINT left")
-else
-self:__UpdateRoute(-0.01,1,1)
-end
-end
-else
-self:_PassedFinalWaypoint(true,"PassingWaypoint: wpindex=#self.waypoints (or wpindex=nil)")
-end
-elseif wpindex==1 then
-if self.adinfinitum then
-if#self.waypoints<=1 then
-self:_PassedFinalWaypoint(true,"PassingWaypoint: adinfinitum but only ONE WAYPOINT left")
-else
-if not Waypoint.missionUID then
-self:__UpdateRoute(-0.01,2)
-end
-end
-end
-end
-local isEgress=false
-if Waypoint.missionUID then
-self:T2(self.lid..string.format("Passing mission waypoint UID=%s",tostring(Waypoint.uid)))
-local mission=self:GetMissionByID(Waypoint.missionUID)
-local EgressUID=mission and mission:GetGroupEgressWaypointUID(self)or nil
-isEgress=EgressUID and Waypoint.uid==EgressUID
-if isEgress and mission:GetGroupStatus(self)~=AUFTRAG.GroupStatus.DONE then
-self:MissionDone(mission)
-end
-end
-if ntasks==0 and self:HasPassedFinalWaypoint()and not isEgress then
-self:_CheckGroupDone(0.01)
-end
-local text=string.format("Group passed waypoint %s/%d ID=%d: final=%s detour=%s astar=%s",
-tostring(wpindex),#self.waypoints,Waypoint.uid,tostring(self.passedfinalwp),tostring(Waypoint.detour),tostring(Waypoint.astar))
-self:T(self.lid..text)
-end
-local wpnext=self:GetWaypointNext()
-if wpnext then
-self.speedWp=wpnext.speed
-end
-end
-function OPSGROUP:_SetWaypointTasks(Waypoint)
-local tasks=self:GetTasksWaypoint(Waypoint.uid)
-local text=string.format("WP uid=%d tasks:",Waypoint.uid)
-local missiontask=nil
-if#tasks>0 then
-for i,_task in pairs(tasks)do
-local task=_task
-text=text..string.format("\n[%d] %s",i,task.description)
-if task.ismission then
-missiontask=task
-end
-end
-else
-text=text.." None"
-end
-self:T(self.lid..text)
-if missiontask then
-self:T(self.lid.."Executing mission task")
-local mission=self:GetMissionByTaskID(missiontask.id)
-if mission then
-if mission.opstransport and not mission.opstransport:IsCargoDelivered(self.groupname)then
-self:PauseMission()
-return
-end
-end
-self:TaskExecute(missiontask)
-return 1
-end
-local taskswp={}
-for _,task in pairs(tasks)do
-local Task=task
-table.insert(taskswp,self.group:TaskFunction("OPSGROUP._TaskExecute",self,Task))
-local TaskCondition=self.group:TaskCondition(nil,Task.stopflag:GetName(),1,nil,Task.duration)
-table.insert(taskswp,self.group:TaskControlled(Task.dcstask,TaskCondition))
-table.insert(taskswp,self.group:TaskFunction("OPSGROUP._TaskDone",self,Task))
-end
-if#taskswp>0 then
-self:SetTask(self.group:TaskCombo(taskswp))
-end
-return#taskswp
-end
-function OPSGROUP:onafterPassedFinalWaypoint(From,Event,To)
-self:T(self.lid..string.format("Group passed final waypoint"))
-end
-function OPSGROUP:onafterGotoWaypoint(From,Event,To,UID,Speed)
-local n=self:GetWaypointIndex(UID)
-if n then
-Speed=Speed or self:GetSpeedToWaypoint(n)
-self:T(self.lid..string.format("Goto Waypoint UID=%d index=%d from %d at speed %.1f knots",UID,n,self.currentwp,Speed))
-self:__UpdateRoute(0.1,n,nil,Speed)
-end
-end
-function OPSGROUP:onafterDetectedUnit(From,Event,To,Unit)
-local unitname=Unit and Unit:GetName()or"unknown"
-self:T2(self.lid..string.format("Detected unit %s",unitname))
-if self.detectedunits:FindUnit(unitname)then
-self:DetectedUnitKnown(Unit)
-else
-self:DetectedUnitNew(Unit)
-end
-end
-function OPSGROUP:onafterDetectedUnitNew(From,Event,To,Unit)
-self:T(self.lid..string.format("Detected New unit %s",Unit:GetName()))
-self.detectedunits:AddUnit(Unit)
-end
-function OPSGROUP:onafterDetectedGroup(From,Event,To,Group)
-local groupname=Group and Group:GetName()or"unknown"
-self:T(self.lid..string.format("Detected group %s",groupname))
-if self.detectedgroups:FindGroup(groupname)then
-self:DetectedGroupKnown(Group)
-else
-self:DetectedGroupNew(Group)
-end
-end
-function OPSGROUP:onafterDetectedGroupNew(From,Event,To,Group)
-self:T(self.lid..string.format("Detected New group %s",Group:GetName()))
-self.detectedgroups:AddGroup(Group)
-end
-function OPSGROUP:onafterEnterZone(From,Event,To,Zone)
-local zonename=Zone and Zone:GetName()or"unknown"
-self:T2(self.lid..string.format("Entered Zone %s",zonename))
-self.inzones:Add(Zone:GetName(),Zone)
-end
-function OPSGROUP:onafterLeaveZone(From,Event,To,Zone)
-local zonename=Zone and Zone:GetName()or"unknown"
-self:T2(self.lid..string.format("Left Zone %s",zonename))
-self.inzones:Remove(zonename,true)
-end
-function OPSGROUP:onbeforeLaserOn(From,Event,To,Target)
-if self.spot.On then
-return false
-end
-if Target then
-self:SetLaserTarget(Target)
-else
-self:T(self.lid.."ERROR: No target provided for LASER!")
-return false
-end
-local element=self:GetElementAlive()
-if element then
-self.spot.element=element
-local offsetY=2
-if self.isFlightgroup or self.isNavygroup then
-offsetY=element.height
-end
-self.spot.offset={x=0,y=offsetY,z=0}
-if self.spot.CheckLOS then
-local los=self:HasLoS(self.spot.Coordinate,self.spot.element,self.spot.offset)
-if los then
-self:LaserGotLOS()
-else
-self:T(self.lid.."LASER got no LOS currently. Trying to switch the laser on again in 10 sec")
-self:__LaserOn(-10,Target)
-return false
-end
-end
-else
-self:T(self.lid.."ERROR: No element alive for lasing")
-return false
-end
-return true
-end
-function OPSGROUP:onafterLaserOn(From,Event,To,Target)
-if not self.spot.timer:IsRunning()then
-self.spot.timer:Start(nil,self.spot.dt)
-end
-local DCSunit=self.spot.element.unit:GetDCSObject()
-self.spot.Laser=Spot.createLaser(DCSunit,self.spot.offset,self.spot.vec3,self.spot.Code or 1688)
-if self.spot.IRon then
-self.spot.IR=Spot.createInfraRed(DCSunit,self.spot.offset,self.spot.vec3)
-end
-self.spot.On=true
-self.spot.Paused=false
-self:T(self.lid.."Switching LASER on")
-end
-function OPSGROUP:onbeforeLaserOff(From,Event,To)
-return self.spot.On or self.spot.Paused
-end
-function OPSGROUP:onafterLaserOff(From,Event,To)
-self:T(self.lid.."Switching LASER off")
-if self.spot.On then
-self.spot.Laser:destroy()
-self.spot.IR:destroy()
-self.spot.Laser=nil
-self.spot.IR=nil
-end
-self.spot.timer:Stop()
-self.spot.TargetUnit=nil
-self.spot.On=false
-self.spot.Paused=false
-end
-function OPSGROUP:onafterLaserPause(From,Event,To)
-self:T(self.lid.."Switching LASER off temporarily")
-self.spot.Laser:destroy()
-self.spot.IR:destroy()
-self.spot.Laser=nil
-self.spot.IR=nil
-self.spot.On=false
-self.spot.Paused=true
-end
-function OPSGROUP:onbeforeLaserResume(From,Event,To)
-return self.spot.Paused
-end
-function OPSGROUP:onafterLaserResume(From,Event,To)
-self:T(self.lid.."Resuming LASER")
-self.spot.Paused=false
-local target=nil
-if self.spot.TargetType==0 then
-target=self.spot.Coordinate
-elseif self.spot.TargetType==1 or self.spot.TargetType==2 then
-target=self.spot.TargetUnit
-elseif self.spot.TargetType==3 then
-target=self.spot.TargetGroup
-end
-if target then
-self:T(self.lid.."Switching LASER on again")
-self:LaserOn(target)
-end
-end
-function OPSGROUP:onafterLaserCode(From,Event,To,Code)
-self.spot.Code=Code or 1688
-self:T2(self.lid..string.format("Setting LASER Code to %d",self.spot.Code))
-if self.spot.On then
-self:T(self.lid..string.format("New LASER Code is %d",self.spot.Code))
-self.spot.Laser:setCode(self.spot.Code)
-end
-end
-function OPSGROUP:onafterLaserLostLOS(From,Event,To)
-self.spot.LOS=false
-self.spot.lostLOS=true
-if self.spot.On then
-self:LaserPause()
-end
-end
-function OPSGROUP:onafterLaserGotLOS(From,Event,To)
-self.spot.LOS=true
-if self.spot.lostLOS then
-self.spot.lostLOS=false
-if self.spot.Paused then
-self:LaserResume()
-end
-end
-end
-function OPSGROUP:SetLaserTarget(Target)
-if Target then
-if Target:IsInstanceOf("SCENERY")then
-self.spot.TargetType=0
-self.spot.offsetTarget={x=0,y=1,z=0}
-elseif Target:IsInstanceOf("POSITIONABLE")then
-local target=Target
-if target:IsAlive()then
-if target:IsInstanceOf("GROUP")then
-self.spot.TargetGroup=target
-self.spot.TargetUnit=target:GetHighestThreat()
-self.spot.TargetType=3
-else
-self.spot.TargetUnit=target
-if target:IsInstanceOf("STATIC")then
-self.spot.TargetType=1
-elseif target:IsInstanceOf("UNIT")then
-self.spot.TargetType=2
-end
-end
-local size,x,y,z=self.spot.TargetUnit:GetObjectSize()
-if y then
-self.spot.offsetTarget={x=0,y=y*0.75,z=0}
-else
-self.spot.offsetTarget={x=0,2,z=0}
-end
-else
-self:T("WARNING: LASER target is not alive!")
-return
-end
-elseif Target:IsInstanceOf("COORDINATE")then
-self.spot.TargetType=0
-self.spot.offsetTarget={x=0,y=0,z=0}
-else
-self:T(self.lid.."ERROR: LASER target should be a POSITIONABLE (GROUP, UNIT or STATIC) or a COORDINATE object!")
-return
-end
-self.spot.vec3=UTILS.VecAdd(Target:GetVec3(),self.spot.offsetTarget)
-self.spot.Coordinate:UpdateFromVec3(self.spot.vec3)
-end
-end
-function OPSGROUP:_UpdateLaser()
-if self.spot.TargetUnit then
-if self.spot.TargetUnit:IsAlive()then
-local vec3=self.spot.TargetUnit:GetVec3()
-vec3=UTILS.VecAdd(vec3,self.spot.offsetTarget)
-local dist=UTILS.VecDist3D(vec3,self.spot.vec3)
-self.spot.vec3=vec3
-self.spot.Coordinate:UpdateFromVec3(vec3)
-if dist>1 then
-if self.spot.On then
-self.spot.Laser:setPoint(vec3)
-if self.spot.IRon then
-self.spot.IR:setPoint(vec3)
-end
-end
-end
-else
-if self.spot.TargetGroup and self.spot.TargetGroup:IsAlive()then
-local unit=self.spot.TargetGroup:GetHighestThreat()
-if unit then
-self:T(self.lid..string.format("Switching to target unit %s in the group",unit:GetName()))
-self.spot.TargetUnit=unit
-return
-else
-self:T(self.lid.."Target is not alive any more ==> switching LASER off")
-self:LaserOff()
-return
-end
-else
-self:T(self.lid.."Target is not alive any more ==> switching LASER off")
-self:LaserOff()
-return
-end
-end
-end
-if self.spot.CheckLOS then
-local los=self:HasLoS(self.spot.Coordinate,self.spot.element,self.spot.offset)
-if los then
-if self.spot.lostLOS then
-self:LaserGotLOS()
-end
-else
-if not self.spot.lostLOS then
-self:LaserLostLOS()
-end
-end
-end
-end
-function OPSGROUP:onbeforeElementSpawned(From,Event,To,Element)
-if Element and Element.status==OPSGROUP.ElementStatus.SPAWNED then
-self:T2(self.lid..string.format("Element %s is already spawned ==> Transition denied!",Element.name))
-return false
-end
-return true
-end
-function OPSGROUP:onafterElementInUtero(From,Event,To,Element)
-self:T(self.lid..string.format("Element in utero %s",Element.name))
-self:_UpdateStatus(Element,OPSGROUP.ElementStatus.INUTERO)
-end
-function OPSGROUP:onafterElementDamaged(From,Event,To,Element)
-self:T(self.lid..string.format("Element damaged %s",Element.name))
-if Element and(Element.status~=OPSGROUP.ElementStatus.DEAD and Element.status~=OPSGROUP.ElementStatus.INUTERO)then
-local lifepoints=0
-if Element.DCSunit and Element.DCSunit:isExist()then
-lifepoints=Element.DCSunit:getLife()
-self:T(self.lid..string.format("Element life %s: %.2f/%.2f",Element.name,lifepoints,Element.life0))
-else
-self:T(self.lid..string.format("Element.DCSunit %s does not exist!",Element.name))
-end
-if lifepoints<=1.0 then
-self:T(self.lid..string.format("Element %s life %.2f <= 1.0 ==> Destroyed!",Element.name,lifepoints))
-self:ElementDestroyed(Element)
-end
-end
-end
-function OPSGROUP:onafterElementHit(From,Event,To,Element,Enemy)
-Element.Nhit=Element.Nhit+1
-self:T(self.lid..string.format("Element hit %s by %s [n=%d, N=%d]",Element.name,Enemy and Enemy:GetName()or"unknown",Element.Nhit,self.Nhit))
-self:__Hit(-3,Enemy)
-end
-function OPSGROUP:onafterHit(From,Event,To,Enemy)
-self:T(self.lid..string.format("Group hit by %s",Enemy and Enemy:GetName()or"unknown"))
-end
-function OPSGROUP:onafterElementDestroyed(From,Event,To,Element)
-self:T(self.lid..string.format("Element destroyed %s",Element.name))
-for _,_mission in pairs(self.missionqueue)do
-local mission=_mission
-mission:ElementDestroyed(self,Element)
-end
-self.Ndestroyed=self.Ndestroyed+1
-self:ElementDead(Element)
-end
-function OPSGROUP:onafterElementDead(From,Event,To,Element)
-self:I(self.lid..string.format("Element dead %s at t=%.3f",Element.name,timer.getTime()))
-self:_UpdateStatus(Element,OPSGROUP.ElementStatus.DEAD)
-if self.spot.On and self.spot.element.name==Element.name then
-self:LaserOff()
-if self:GetNelements()>0 then
-local target=nil
-if self.spot.TargetType==0 then
-target=self.spot.Coordinate
-elseif self.spot.TargetType==1 or self.spot.TargetType==2 then
-if self.spot.TargetUnit and self.spot.TargetUnit:IsAlive()then
-target=self.spot.TargetUnit
-end
-elseif self.spot.TargetType==3 then
-if self.spot.TargetGroup and self.spot.TargetGroup:IsAlive()then
-target=self.spot.TargetGroup
-end
-end
-if target then
-self:__LaserOn(-1,target)
-end
-end
-end
-for i=#Element.cargoBay,1,-1 do
-local mycargo=Element.cargoBay[i]
-if mycargo.group then
-self:_DelCargobay(mycargo.group)
-if mycargo.group and not(mycargo.group:IsDead()or mycargo.group:IsStopped())then
-mycargo.group:_RemoveMyCarrier()
-if mycargo.reserved then
-mycargo.group:_NewCargoStatus(OPSGROUP.CargoStatus.NOTCARGO)
-else
-for _,cargoelement in pairs(mycargo.group.elements)do
-self:T2(self.lid.."Cargo element dead "..cargoelement.name)
-mycargo.group:ElementDead(cargoelement)
-end
-end
-end
-else
-if self.cargoTZC then
-for _,_cargo in pairs(self.cargoTZC.Cargos)do
-local cargo=_cargo
-if cargo.uid==mycargo.cargoUID then
-cargo.storage.cargoLost=cargo.storage.cargoLost+mycargo.storageAmount
-end
-end
-end
-self:_DelCargobayElement(Element,mycargo)
-end
-end
-end
-function OPSGROUP:onafterRespawn(From,Event,To,Template)
-self:T(self.lid.."Respawning group!")
-local template=UTILS.DeepCopy(Template or self.template)
-template.lateActivation=false
-self:_Respawn(0,template)
-end
-function OPSGROUP:Teleport(Coordinate,Delay,NoPauseMission)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,OPSGROUP.Teleport,self,Coordinate,0,NoPauseMission)
-else
-self:T(self.lid.."FF Teleporting...")
-if self:IsOnMission()and not NoPauseMission then
-self:T(self.lid.."Pausing current mission for telport")
-self:PauseMission()
-end
-local Template=UTILS.DeepCopy(self.template)
-Template.lateActivation=self:IsLateActivated()
-Template.uncontrolled=false
-if self:IsFlightgroup()then
-Template.route.points[1]=Coordinate:WaypointAir("BARO",COORDINATE.WaypointType.TurningPoint,COORDINATE.WaypointAction.TurningPoint,300,true,nil,nil,"Spawnpoint")
-elseif self:IsArmygroup()then
-Template.route.points[1]=Coordinate:WaypointGround(0)
-elseif self:IsNavygroup()then
-Template.route.points[1]=Coordinate:WaypointNaval(0)
-end
-local units=Template.units
-local d={}
-for i=1,#units do
-local unit=units[i]
-d[i]={x=Coordinate.x+(units[i].x-units[1].x),y=Coordinate.z+units[i].y-units[1].y}
-end
-for i=#units,1,-1 do
-local unit=units[i]
-local element=self:GetElementByName(unit.name)
-if element and element.status~=OPSGROUP.ElementStatus.DEAD then
-unit.parking=nil
-unit.parking_id=nil
-local vec3=element.unit:GetVec3()
-local heading=element.unit:GetHeading()
-unit.x=d[i].x
-unit.y=d[i].y
-unit.alt=Coordinate.y
-unit.heading=math.rad(heading)
-unit.psi=-unit.heading
-else
-table.remove(units,i)
-end
-end
-self:_Respawn(0,Template,true)
-end
-end
-function OPSGROUP:_Respawn(Delay,Template,Reset)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,OPSGROUP._Respawn,self,0,Template,Reset)
-else
-self:T2(self.lid.."FF _Respawn")
-Template=Template or self:_GetTemplate(true)
-self.Ndestroyed=0
-self.Nhit=0
-if self:IsAlive()then
-local units=Template.units
-for i=#units,1,-1 do
-local unit=units[i]
-local element=self:GetElementByName(unit.name)
-if element and element.status~=OPSGROUP.ElementStatus.DEAD then
-if not Reset then
-unit.parking=element.parking and element.parking.TerminalID or unit.parking
-unit.parking_id=nil
-local vec3=element.unit:GetVec3()
-local heading=element.unit:GetHeading()
-unit.x=vec3.x
-unit.y=vec3.z
-unit.alt=vec3.y
-unit.heading=math.rad(heading)
-unit.psi=-unit.heading
-end
-else
-table.remove(units,i)
-self.Ndestroyed=self.Ndestroyed+1
-end
-end
-self:Despawn(0,true)
-else
-for _,_element in pairs(self.elements)do
-local element=_element
-self:ElementInUtero(element)
-end
-end
-self:T({Template=Template})
-self.group=_DATABASE:Spawn(Template)
-self.dcsgroup=self:GetDCSGroup()
-self.controller=self.dcsgroup:getController()
-self.isLateActivated=Template.lateActivation
-self.isUncontrolled=Template.uncontrolled
-self.isDead=false
-self.isDestroyed=false
-self.groupinitialized=false
-self.wpcounter=1
-self.currentwp=1
-self:_InitWaypoints()
-self:_InitGroup(Template)
-end
-return self
-end
-function OPSGROUP:onafterInUtero(From,Event,To)
-self:T(self.lid..string.format("Group inutero at t=%.3f",timer.getTime()))
-end
-function OPSGROUP:onafterDamaged(From,Event,To)
-self:T(self.lid..string.format("Group damaged at t=%.3f",timer.getTime()))
-end
-function OPSGROUP:onafterDestroyed(From,Event,To)
-self:T(self.lid..string.format("Group destroyed at t=%.3f",timer.getTime()))
-self.isDestroyed=true
-end
-function OPSGROUP:onbeforeDead(From,Event,To)
-if self.Ndestroyed==#self.elements then
-self:Destroyed()
-end
-end
-function OPSGROUP:onafterDead(From,Event,To)
-self:T(self.lid..string.format("Group dead at t=%.3f",timer.getTime()))
-self.isDead=true
-for _,_mission in pairs(self.missionqueue)do
-local mission=_mission
-self:T(self.lid.."Cancelling mission because group is dead! Mission name "..tostring(mission:GetName()))
-self:MissionCancel(mission)
-mission:GroupDead(self)
-end
-self:ClearWaypoints()
-self.groupinitialized=false
-self.cargoStatus=OPSGROUP.CargoStatus.NOTCARGO
-self.carrierStatus=OPSGROUP.CarrierStatus.NOTCARRIER
-local mycarrier=self:_GetMyCarrierGroup()
-if mycarrier and not mycarrier:IsDead()then
-mycarrier:_DelCargobay(self)
-self:_RemoveMyCarrier()
-end
-for i,_transport in pairs(self.cargoqueue)do
-local transport=_transport
-transport:__DeadCarrierGroup(1,self)
-end
-self.cargoqueue={}
-self.cargoTransport=nil
-self.cargoTZC=nil
-if self.Ndestroyed==#self.elements then
-if self.cohort then
-self.cohort:DelGroup(self.groupname)
-end
-else
-end
-if self.legion then
-if not self:IsInUtero()then
-local asset=self.legion:GetAssetByName(self.groupname)
-local request=self.legion:GetRequestByID(asset.rid)
-self.legion:AssetDead(asset,request)
-end
-self:__Stop(-5)
-elseif not self.isAI then
-self:__Stop(-1)
-end
-end
-function OPSGROUP:onbeforeStop(From,Event,To)
-if self:IsAlive()then
-self:T(self.lid..string.format("WARNING: Group is still alive! Will not stop the FSM. Use :Despawn() instead"))
-return false
-end
-return true
-end
-function OPSGROUP:onafterStop(From,Event,To)
-self:UnHandleEvent(EVENTS.Birth)
-self:UnHandleEvent(EVENTS.Dead)
-self:UnHandleEvent(EVENTS.RemoveUnit)
-if self.isFlightgroup then
-self:UnHandleEvent(EVENTS.EngineStartup)
-self:UnHandleEvent(EVENTS.Takeoff)
-self:UnHandleEvent(EVENTS.Land)
-self:UnHandleEvent(EVENTS.EngineShutdown)
-self:UnHandleEvent(EVENTS.PilotDead)
-self:UnHandleEvent(EVENTS.Ejection)
-self:UnHandleEvent(EVENTS.Crash)
-self.currbase=nil
-elseif self.isArmygroup then
-self:UnHandleEvent(EVENTS.Hit)
-end
-for _,_mission in pairs(self.missionqueue)do
-local mission=_mission
-self:MissionCancel(mission)
-end
-self.timerCheckZone:Stop()
-self.timerQueueUpdate:Stop()
-self.timerStatus:Stop()
-self.CallScheduler:Clear()
-if self.Scheduler then
-self.Scheduler:Clear()
-end
-if self.flightcontrol then
-for _,_element in pairs(self.elements)do
-local element=_element
-if element.parking then
-self.flightcontrol:SetParkingFree(element.parking)
-end
-end
-self.flightcontrol:_RemoveFlight(self)
-end
-if self:IsAlive()and not(self:IsDead()or self:IsStopped())then
-local life,life0=self:GetLifePoints()
-local state=self:GetState()
-local text=string.format("WARNING: Group is still alive! Current state=%s. Life points=%d/%d. Use OPSGROUP:Destroy() or OPSGROUP:Despawn() for a clean stop",state,life,life0)
-self:T(self.lid..text)
-end
-_DATABASE.FLIGHTGROUPS[self.groupname]=nil
-self:I(self.lid.."STOPPED! Unhandled events, cleared scheduler and removed from _DATABASE")
-end
-function OPSGROUP:onafterOutOfAmmo(From,Event,To)
-self:T(self.lid..string.format("Group is out of ammo at t=%.3f",timer.getTime()))
-end
-function OPSGROUP:_CheckCargoTransport()
-local Time=timer.getAbsTime()
-if self.verbose>=1 then
-local text=""
-for _,_element in pairs(self.elements)do
-local element=_element
-for _,_cargo in pairs(element.cargoBay)do
-local cargo=_cargo
-if cargo.group then
-text=text..string.format("\n- %s in carrier %s, reserved=%s",tostring(cargo.group:GetName()),tostring(element.name),tostring(cargo.reserved))
-else
-text=text..string.format("\n- storage %s=%d kg in carrier %s [UID=%s]",
-tostring(cargo.storageType),tostring(cargo.storageAmount*cargo.storageWeight),tostring(element.name),tostring(cargo.cargoUID))
-end
-end
-end
-if text==""then
-text=" empty"
-end
-self:T(self.lid.."Cargo bay:"..text)
-end
-if self.verbose>=3 then
-local text=""
-for i,_transport in pairs(self.cargoqueue)do
-local transport=_transport
-local pickupzone=transport:GetPickupZone()
-local deployzone=transport:GetDeployZone()
-local pickupname=pickupzone and pickupzone:GetName()or"unknown"
-local deployname=deployzone and deployzone:GetName()or"unknown"
-text=text..string.format("\n[%d] UID=%d Status=%s: %s --> %s",i,transport.uid,transport:GetState(),pickupname,deployname)
-for j,_cargo in pairs(transport:GetCargos())do
-local cargo=_cargo
-if cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then
-local state=cargo.opsgroup:GetState()
-local status=cargo.opsgroup.cargoStatus
-local name=cargo.opsgroup.groupname
-local carriergroup,carrierelement,reserved=cargo.opsgroup:_GetMyCarrier()
-local carrierGroupname=carriergroup and carriergroup.groupname or"none"
-local carrierElementname=carrierelement and carrierelement.name or"none"
-text=text..string.format("\n (%d) %s [%s]: %s, carrier=%s(%s), delivered=%s",j,name,state,status,carrierGroupname,carrierElementname,tostring(cargo.delivered))
-else
-end
-end
-end
-if text~=""then
-self:T(self.lid.."Cargo queue:"..text)
-end
-end
-if self.cargoTransport and self.cargoTransport:GetCarrierTransportStatus(self)==OPSTRANSPORT.Status.DELIVERED then
-self:DelOpsTransport(self.cargoTransport)
-self.cargoTransport=nil
-self.cargoTZC=nil
-end
-local mission=self:GetMissionCurrent()
-if(not self.cargoTransport)and(mission==nil or mission.type==AUFTRAG.Type.NOTHING)then
-self.cargoTransport=self:_GetNextCargoTransport()
-if self.cargoTransport and mission then
-self:MissionCancel(mission)
-end
-if self.cargoTransport and not self:IsActive()then
-self:Activate()
-end
-end
-if self.cargoTransport then
-if self:IsNotCarrier()then
-self.Tpickingup=nil
-self.Tloading=nil
-self.Ttransporting=nil
-self.Tunloading=nil
-self.cargoTZC=self.cargoTransport:_GetTransportZoneCombo(self)
-if self.cargoTZC then
-self:T(self.lid..string.format("Not carrier ==> pickup at %s [TZC UID=%d]",self.cargoTZC.PickupZone and self.cargoTZC.PickupZone:GetName()or"unknown",self.cargoTZC.uid))
-self:__Pickup(-1)
-else
-self:T2(self.lid.."Not carrier ==> No TZC found")
-end
-elseif self:IsPickingup()then
-self.Tpickingup=self.Tpickingup or Time
-local tpickingup=Time-self.Tpickingup
-self:T(self.lid..string.format("Picking up at %s [TZC UID=%d] for %s sec...",self.cargoTZC.PickupZone and self.cargoTZC.PickupZone:GetName()or"unknown",self.cargoTZC.uid,tpickingup))
-elseif self:IsLoading()then
-self.Tloading=self.Tloading or Time
-local tloading=Time-self.Tloading
-self:T(self.lid..string.format("Loading at %s [TZC UID=%d] for %.1f sec...",self.cargoTZC.PickupZone and self.cargoTZC.PickupZone:GetName()or"unknown",self.cargoTZC.uid,tloading))
-local boarding=false
-local gotcargo=false
-for _,_cargo in pairs(self.cargoTZC.Cargos)do
-local cargo=_cargo
-if cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then
-if cargo.opsgroup and cargo.opsgroup:IsBoarding(self.groupname)then
-boarding=true
-end
-if cargo.opsgroup and cargo.opsgroup:IsLoaded(self.groupname)then
-gotcargo=true
-end
-else
-local mycargo=self:_GetMyCargoBayFromUID(cargo.uid)
-if mycargo and mycargo.storageAmount>0 then
-gotcargo=true
-end
-end
-end
-if gotcargo and self.cargoTransport:_CheckRequiredCargos(self.cargoTZC,self)and not boarding then
-self:T(self.lid.."Boarding/loading finished ==> Loaded")
-self.Tloading=nil
-self:LoadingDone()
-else
-self:Loading()
-end
-elseif self:IsTransporting()then
-self.Ttransporting=self.Ttransporting or Time
-local ttransporting=Time-self.Ttransporting
-self:T(self.lid.."Transporting (nothing to do)")
-elseif self:IsUnloading()then
-self.Tunloading=self.Tunloading or Time
-local tunloading=Time-self.Tunloading
-self:T(self.lid.."Unloading ==> Checking if all cargo was delivered")
-local delivered=true
-for _,_cargo in pairs(self.cargoTZC.Cargos)do
-local cargo=_cargo
-if cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then
-local carrierGroup=cargo.opsgroup:_GetMyCarrierGroup()
-if(carrierGroup and carrierGroup:GetName()==self:GetName())and not cargo.delivered then
-delivered=false
-break
-end
-else
-local mycargo=self:_GetMyCargoBayFromUID(cargo.uid)
-if mycargo and not cargo.delivered then
-delivered=false
-break
-end
-end
-end
-if delivered then
-self:T(self.lid.."Unloading finished ==> UnloadingDone")
-self:UnloadingDone()
-else
-self:Unloading()
-end
-end
-if self.verbose>=2 and self.cargoTransport then
-local pickupzone=self.cargoTransport:GetPickupZone(self.cargoTZC)
-local deployzone=self.cargoTransport:GetDeployZone(self.cargoTZC)
-local pickupname=pickupzone and pickupzone:GetName()or"unknown"
-local deployname=deployzone and deployzone:GetName()or"unknown"
-local text=string.format("Carrier [%s]: %s --> %s",self.carrierStatus,pickupname,deployname)
-for _,_cargo in pairs(self.cargoTransport:GetCargos(self.cargoTZC))do
-local cargo=_cargo
-if cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then
-local name=cargo.opsgroup:GetName()
-local gstatus=cargo.opsgroup:GetState()
-local cstatus=cargo.opsgroup.cargoStatus
-local weight=cargo.opsgroup:GetWeightTotal()
-local carriergroup,carrierelement,reserved=cargo.opsgroup:_GetMyCarrier()
-local carrierGroupname=carriergroup and carriergroup.groupname or"none"
-local carrierElementname=carrierelement and carrierelement.name or"none"
-text=text..string.format("\n- %s (%.1f kg) [%s]: %s, carrier=%s (%s), delivered=%s",name,weight,gstatus,cstatus,carrierElementname,carrierGroupname,tostring(cargo.delivered))
-else
-end
-end
-self:I(self.lid..text)
-end
-end
-return self
-end
-function OPSGROUP:_IsInCargobay(OpsGroup)
-for _,_element in pairs(self.elements)do
-local element=_element
-for _,_cargo in pairs(element.cargoBay)do
-local cargo=_cargo
-if cargo.group.groupname==OpsGroup.groupname then
-return true
-end
-end
-end
-return false
-end
-function OPSGROUP:_AddCargobay(CargoGroup,CarrierElement,Reserved)
-local cargo=self:_GetCargobay(CargoGroup)
-if cargo then
-cargo.reserved=Reserved
-else
-cargo={}
-cargo.group=CargoGroup
-cargo.reserved=Reserved
-table.insert(CarrierElement.cargoBay,cargo)
-end
-CargoGroup:_SetMyCarrier(self,CarrierElement,Reserved)
-self.cargoBay[CargoGroup.groupname]=CarrierElement.name
-if not Reserved then
-local weight=CargoGroup:GetWeightTotal()
-self:AddWeightCargo(CarrierElement.name,weight)
-end
-return self
-end
-function OPSGROUP:_AddCargobayStorage(CarrierElement,CargoUID,StorageType,StorageAmount,StorageWeight)
-local MyCargo=self:_CreateMyCargo(CargoUID,nil,StorageType,StorageAmount,StorageWeight)
-self:_AddMyCargoBay(MyCargo,CarrierElement)
-end
-function OPSGROUP:_CreateMyCargo(CargoUID,OpsGroup,StorageType,StorageAmount,StorageWeight)
-local cargo={}
-cargo.cargoUID=CargoUID
-cargo.group=OpsGroup
-cargo.storageType=StorageType
-cargo.storageAmount=StorageAmount
-cargo.storageWeight=StorageWeight
-cargo.reserved=false
-return cargo
-end
-function OPSGROUP:_AddMyCargoBay(MyCargo,CarrierElement)
-table.insert(CarrierElement.cargoBay,MyCargo)
-if not MyCargo.reserved then
-local weight=0
-if MyCargo.group then
-weight=MyCargo.group:GetWeightTotal()
-else
-weight=MyCargo.storageAmount*MyCargo.storageWeight
-end
-self:AddWeightCargo(CarrierElement.name,weight)
-end
-end
-function OPSGROUP:_GetMyCargoBayFromUID(uid)
-for _,_element in pairs(self.elements)do
-local element=_element
-for i,_mycargo in pairs(element.cargoBay)do
-local mycargo=_mycargo
-if mycargo.cargoUID and mycargo.cargoUID==uid then
-return mycargo,element,i
-end
-end
-end
-return nil,nil,nil
-end
-function OPSGROUP:GetCargoGroups(CarrierName)
-local cargos={}
-for _,_element in pairs(self.elements)do
-local element=_element
-if CarrierName==nil or element.name==CarrierName then
-for _,_cargo in pairs(element.cargoBay)do
-local cargo=_cargo
-if not cargo.reserved then
-table.insert(cargos,cargo.group)
-end
-end
-end
-end
-return cargos
-end
-function OPSGROUP:_GetCargobay(CargoGroup)
-local CarrierElement=nil
-local cargobayIndex=nil
-local reserved=nil
-for i,_element in pairs(self.elements)do
-local element=_element
-for j,_cargo in pairs(element.cargoBay)do
-local cargo=_cargo
-if cargo.group and cargo.group.groupname==CargoGroup.groupname then
-return cargo,j,element
-end
-end
-end
-return nil,nil,nil
-end
-function OPSGROUP:_GetCargobayElement(Element,CargoUID)
-self:T3({Element=Element,CargoUID=CargoUID})
-for i,_mycargo in pairs(Element.cargoBay)do
-local mycargo=_mycargo
-if mycargo.cargoUID and mycargo.cargoUID==CargoUID then
-return mycargo
-end
-end
-return nil
-end
-function OPSGROUP:_DelCargobayElement(Element,MyCargo)
-for i,_mycargo in pairs(Element.cargoBay)do
-local mycargo=_mycargo
-if mycargo.cargoUID and MyCargo.cargoUID and mycargo.cargoUID==MyCargo.cargoUID then
-if MyCargo.group then
-self:RedWeightCargo(Element.name,MyCargo.group:GetWeightTotal())
-else
-self:RedWeightCargo(Element.name,MyCargo.storageAmount*MyCargo.storageWeight)
-end
-table.remove(Element.cargoBay,i)
-return true
-end
-end
-return false
-end
-function OPSGROUP:_DelCargobay(CargoGroup)
-if self.cargoBay[CargoGroup.groupname]then
-self.cargoBay[CargoGroup.groupname]=nil
-end
-local cargoBayItem,cargoBayIndex,CarrierElement=self:_GetCargobay(CargoGroup)
-if cargoBayItem and cargoBayIndex then
-self:T(self.lid..string.format("Removing cargo group %s from cargo bay (index=%d) of carrier %s",CargoGroup:GetName(),cargoBayIndex,CarrierElement.name))
-table.remove(CarrierElement.cargoBay,cargoBayIndex)
-if not cargoBayItem.reserved then
-local weight=CargoGroup:GetWeightTotal()
-self:RedWeightCargo(CarrierElement.name,weight)
-end
-return true
-end
-self:T(self.lid.."ERROR: Group is not in cargo bay. Cannot remove it!")
-return false
-end
-function OPSGROUP:_GetNextCargoTransport()
-local coord=self:GetCoordinate()
-local function _sort(a,b)
-local transportA=a
-local transportB=b
-return(transportA.priomaxweight then
-maxweight=weight
-end
-end
-end
-return maxweight
-end
-function OPSGROUP:GetWeightCargo(UnitName,IncludeReserved)
-local weight=0
-for _,_element in pairs(self.elements)do
-local element=_element
-if(UnitName==nil or UnitName==element.name)and element.status~=OPSGROUP.ElementStatus.DEAD then
-weight=weight+element.weightCargo or 0
-end
-end
-local gewicht=0
-for _,_element in pairs(self.elements)do
-local element=_element
-if(UnitName==nil or UnitName==element.name)and(element and element.status~=OPSGROUP.ElementStatus.DEAD)then
-for _,_cargo in pairs(element.cargoBay)do
-local cargo=_cargo
-if(not cargo.reserved)or(cargo.reserved==true and(IncludeReserved==true or IncludeReserved==nil))then
-if cargo.group then
-gewicht=gewicht+cargo.group:GetWeightTotal()
-else
-gewicht=gewicht+cargo.storageAmount*cargo.storageWeight
-end
-end
-end
-end
-end
-self:T3(self.lid..string.format("Unit=%s (reserved=%s): weight=%d, gewicht=%d",tostring(UnitName),tostring(IncludeReserved),weight,gewicht))
-if IncludeReserved==false and gewicht~=weight then
-self:T(self.lid..string.format("ERROR: FF weight!=gewicht: weight=%.1f, gewicht=%.1f",weight,gewicht))
-end
-return gewicht
-end
-function OPSGROUP:GetWeightCargoMax(UnitName)
-local weight=0
-for _,_element in pairs(self.elements)do
-local element=_element
-if(UnitName==nil or UnitName==element.name)and element.status~=OPSGROUP.ElementStatus.DEAD then
-weight=weight+element.weightMaxCargo
-end
-end
-return weight
-end
-function OPSGROUP:GetCargoOpsGroups()
-local opsgroups={}
-for _,_element in pairs(self.elements)do
-local element=_element
-for _,_cargo in pairs(element.cargoBay)do
-local cargo=_cargo
-table.insert(opsgroups,cargo.group)
-end
-end
-return opsgroups
-end
-function OPSGROUP:AddWeightCargo(UnitName,Weight)
-local element=self:GetElementByName(UnitName)
-if element then
-element.weightCargo=element.weightCargo+Weight
-self:T(self.lid..string.format("%s: Adding %.1f kg cargo weight. New cargo weight=%.1f kg",UnitName,Weight,element.weightCargo))
-if self.isFlightgroup then
-trigger.action.setUnitInternalCargo(element.name,element.weightCargo)
-end
-end
-return self
-end
-function OPSGROUP:RedWeightCargo(UnitName,Weight)
-self:AddWeightCargo(UnitName,-Weight)
-return self
-end
-function OPSGROUP:_GetWeightStorage(Storage,Total,Reserved,Amount)
-local weight=Storage.cargoAmount
-if not Total then
-weight=weight-Storage.cargoLost-Storage.cargoLoaded-Storage.cargoDelivered
-end
-if Reserved then
-weight=weight-Storage.cargoReserved
-end
-if not Amount then
-weight=weight*Storage.cargoWeight
-end
-return weight
-end
-function OPSGROUP:CanCargo(Cargo)
-if Cargo then
-local weight=math.huge
-if Cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then
-local weight=Cargo.opsgroup:GetWeightTotal()
-for _,_element in pairs(self.elements)do
-local element=_element
-if element and element.status~=OPSGROUP.ElementStatus.DEAD and element.weightMaxCargo>=weight then
-return true
-end
-end
-else
-weight=Cargo.storage.cargoWeight
-end
-local bay=0
-for _,_element in pairs(self.elements)do
-local element=_element
-if element and element.status~=OPSGROUP.ElementStatus.DEAD then
-bay=bay+element.weightMaxCargo
-end
-end
-if bay>=weight then
-return true
-end
-end
-return false
-end
-function OPSGROUP:FindCarrierForCargo(Weight)
-for _,_element in pairs(self.elements)do
-local element=_element
-local free=self:GetFreeCargobay(element.name)
-if free>=Weight then
-return element
-else
-self:T3(self.lid..string.format("%s: Weight %d>%d free cargo bay",element.name,Weight,free))
-end
-end
-return nil
-end
-function OPSGROUP:_SetMyCarrier(CarrierGroup,CarrierElement,Reserved)
-self:T(self.lid..string.format("Setting My Carrier: %s (%s), reserved=%s",CarrierGroup:GetName(),tostring(CarrierElement.name),tostring(Reserved)))
-self.mycarrier.group=CarrierGroup
-self.mycarrier.element=CarrierElement
-self.mycarrier.reserved=Reserved
-self.cargoTransportUID=CarrierGroup.cargoTransport and CarrierGroup.cargoTransport.uid or nil
-end
-function OPSGROUP:_GetMyCarrierGroup()
-if self.mycarrier and self.mycarrier.group then
-return self.mycarrier.group
-end
-return nil
-end
-function OPSGROUP:_GetMyCarrierElement()
-if self.mycarrier and self.mycarrier.element then
-return self.mycarrier.element
-end
-return nil
-end
-function OPSGROUP:_IsMyCarrierReserved()
-if self.mycarrier then
-return self.mycarrier.reserved
-end
-return nil
-end
-function OPSGROUP:_GetMyCarrier()
-return self.mycarrier.group,self.mycarrier.element,self.mycarrier.reserved
-end
-function OPSGROUP:_RemoveMyCarrier()
-self:T(self.lid..string.format("Removing my carrier!"))
-self.mycarrier.group=nil
-self.mycarrier.element=nil
-self.mycarrier.reserved=nil
-self.mycarrier={}
-self.cargoTransportUID=nil
-return self
-end
-function OPSGROUP:onafterPickup(From,Event,To)
-local oldstatus=self.carrierStatus
-self:_NewCarrierStatus(OPSGROUP.CarrierStatus.PICKUP)
-local TZC=self.cargoTZC
-local Zone=TZC.PickupZone
-local inzone=self:IsInZone(Zone)
-local airbasePickup=TZC.PickupAirbase
-local ready4loading=false
-if self:IsArmygroup()or self:IsNavygroup()then
-ready4loading=inzone
-else
-ready4loading=self.currbase and airbasePickup and self.currbase:GetName()==airbasePickup:GetName()and self:IsParking()
-if ready4loading==false and self.isHelo and self:IsLandedAt()and inzone then
-ready4loading=true
-end
-end
-if ready4loading then
-if(self:IsArmygroup()or self:IsNavygroup())and not self:IsHolding()then
-self:FullStop()
-end
-self:__Loading(-5)
-else
-local surfacetypes=nil
-if self:IsArmygroup()or self:IsFlightgroup()then
-surfacetypes={land.SurfaceType.LAND}
-elseif self:IsNavygroup()then
-surfacetypes={land.SurfaceType.WATER}
-end
-local Coordinate=Zone:GetRandomCoordinate(nil,nil,surfacetypes)
-local uid=self:GetWaypointCurrentUID()
-if self:IsFlightgroup()then
-if self:IsParking()and self:IsUncontrolled()then
-self:StartUncontrolled()
-end
-if airbasePickup then
-local path=self.cargoTransport:_GetPathTransport(self.category,self.cargoTZC)
-if path and oldstatus~=OPSGROUP.CarrierStatus.NOTCARRIER then
-for i=#path.waypoints,1,-1 do
-local wp=path.waypoints[i]
-local coordinate=COORDINATE:NewFromWaypoint(wp)
-local waypoint=FLIGHTGROUP.AddWaypoint(self,coordinate,nil,uid,nil,false);waypoint.temp=true
-uid=waypoint.uid
-if i==1 then
-waypoint.temp=false
-waypoint.detour=1
-end
-end
-else
-local coordinate=self:GetCoordinate():GetIntermediateCoordinate(Coordinate,0.5)
-local waypoint=FLIGHTGROUP.AddWaypoint(self,coordinate,nil,uid,UTILS.MetersToFeet(self.altitudeCruise),true);waypoint.detour=1
-end
-elseif self.isHelo then
-local waypoint=FLIGHTGROUP.AddWaypoint(self,Coordinate,nil,uid,UTILS.MetersToFeet(self.altitudeCruise),false);waypoint.detour=1
-else
-self:T(self.lid.."ERROR: Transportcarrier aircraft cannot land in Pickup zone! Specify a ZONE_AIRBASE as pickup zone")
-end
-if self.isHelo and self:IsLandedAt()then
-local Task=self:GetTaskCurrent()
-if Task then
-self:TaskCancel(Task)
-else
-self:T(self.lid.."ERROR: No current task but landed at?!")
-end
-end
-if self:IsWaiting()then
-self:__Cruise(-2)
-end
-elseif self:IsNavygroup()then
-local path=self.cargoTransport:_GetPathTransport(self.category,self.cargoTZC)
-if path then
-for i=#path.waypoints,1,-1 do
-local wp=path.waypoints[i]
-local coordinate=COORDINATE:NewFromWaypoint(wp)
-local waypoint=NAVYGROUP.AddWaypoint(self,coordinate,nil,uid,nil,false);waypoint.temp=true
-uid=waypoint.uid
-end
-end
-local waypoint=NAVYGROUP.AddWaypoint(self,Coordinate,nil,uid,self.altitudeCruise,false);waypoint.detour=1
-self:__Cruise(-2)
-elseif self:IsArmygroup()then
-local path=self.cargoTransport:_GetPathTransport(self.category,self.cargoTZC)
-local Formation=self.cargoTransport:_GetFormationPickup(self.cargoTZC,self)
-if path and oldstatus~=OPSGROUP.CarrierStatus.NOTCARRIER then
-for i=#path.waypoints,1,-1 do
-local wp=path.waypoints[i]
-local coordinate=COORDINATE:NewFromWaypoint(wp)
-local waypoint=ARMYGROUP.AddWaypoint(self,coordinate,nil,uid,wp.action,false);waypoint.temp=true
-uid=waypoint.uid
-end
-end
-local waypoint=ARMYGROUP.AddWaypoint(self,Coordinate,nil,uid,Formation,false);waypoint.detour=1
-self:__Cruise(-2,nil,Formation)
-end
-end
-end
-function OPSGROUP:_SortCargo(Cargos)
-local function _sort(a,b)
-local cargoA=a
-local cargoB=b
-local weightA=0
-local weightB=0
-if cargoA.opsgroup then
-weightA=cargoA.opsgroup:GetWeightTotal()
-else
-weightA=self:_GetWeightStorage(cargoA.storage)
-end
-if cargoB.opsgroup then
-weightB=cargoB.opsgroup:GetWeightTotal()
-else
-weightB=self:_GetWeightStorage(cargoB.storage)
-end
-return weightA>weightB
-end
-table.sort(Cargos,_sort)
-return Cargos
-end
-function OPSGROUP:onafterLoading(From,Event,To)
-self:_NewCarrierStatus(OPSGROUP.CarrierStatus.LOADING)
-local cargos={}
-for _,_cargo in pairs(self.cargoTZC.Cargos)do
-local cargo=_cargo
-local canCargo=self:CanCargo(cargo)
-local isCarrier=false
-local isNotCargo=true
-local isHolding=cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and(cargo.opsgroup:IsHolding()or cargo.opsgroup:IsLoaded())or true
-local inZone=cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and(cargo.opsgroup:IsInZone(self.cargoTZC.EmbarkZone)or cargo.opsgroup:IsInUtero())or true
-local isOnMission=cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and cargo.opsgroup:IsOnMission()or false
-if isOnMission then
-local mission=cargo.opsgroup:GetMissionCurrent()
-if mission and((mission.opstransport and mission.opstransport.uid==self.cargoTransport.uid)or mission.type==AUFTRAG.Type.NOTHING)then
-isOnMission=not isHolding
-end
-end
-local isAvail=true
-if cargo.type==OPSTRANSPORT.CargoType.STORAGE then
-local nAvail=cargo.storage.storageFrom:GetAmount(cargo.storage.cargoType)
-if nAvail>0 then
-isAvail=true
-else
-isAvail=false
-end
-else
-isCarrier=cargo.opsgroup:IsPickingup()or cargo.opsgroup:IsLoading()or cargo.opsgroup:IsTransporting()or cargo.opsgroup:IsUnloading()
-isNotCargo=cargo.opsgroup:IsNotCargo(true)
-end
-local isDead=cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and cargo.opsgroup:IsDead()or false
-self:T(self.lid..string.format("Loading: canCargo=%s, isCarrier=%s, isNotCargo=%s, isHolding=%s, isOnMission=%s",
-tostring(canCargo),tostring(isCarrier),tostring(isNotCargo),tostring(isHolding),tostring(isOnMission)))
-if canCargo and inZone and isNotCargo and isHolding and isAvail and(not(cargo.delivered or isDead or isCarrier or isOnMission))then
-table.insert(cargos,cargo)
-end
-end
-self:_SortCargo(cargos)
-for _,_cargo in pairs(cargos)do
-local cargo=_cargo
-local weight=nil
-if cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then
-weight=cargo.opsgroup:GetWeightTotal()
-local carrier=self:FindCarrierForCargo(weight)
-if carrier then
-cargo.opsgroup:Board(self,carrier)
-end
-else
-weight=self:_GetWeightStorage(cargo.storage,false)
-local Amount=cargo.storage.storageFrom:GetAmount(cargo.storage.cargoType)
-local Weight=Amount*cargo.storage.cargoWeight
-weight=math.min(weight,Weight)
-self:T(self.lid..string.format("Loading storage weight=%d kg (warehouse has %d kg)!",weight,Weight))
-for _,_element in pairs(self.elements)do
-local element=_element
-local free=self:GetFreeCargobay(element.name)
-local w=math.min(weight,free)
-if w>=cargo.storage.cargoWeight then
-local amount=math.floor(w/cargo.storage.cargoWeight)
-cargo.storage.storageFrom:RemoveAmount(cargo.storage.cargoType,amount)
-cargo.storage.cargoLoaded=cargo.storage.cargoLoaded+amount
-self:_AddCargobayStorage(element,cargo.uid,cargo.storage.cargoType,amount,cargo.storage.cargoWeight)
-weight=weight-amount*cargo.storage.cargoWeight
-local text=string.format("Element %s: loaded amount=%d (weight=%d) ==> left=%d kg",element.name,amount,amount*cargo.storage.cargoWeight,weight)
-self:T(self.lid..text)
-if weight<=0 then
-break
-end
-end
-end
-end
-end
-end
-function OPSGROUP:_NewCargoStatus(Status)
-if self.verbose>=2 then
-self:I(self.lid..string.format("New cargo status: %s --> %s",tostring(self.cargoStatus),tostring(Status)))
-end
-self.cargoStatus=Status
-end
-function OPSGROUP:_NewCarrierStatus(Status)
-if self.verbose>=2 then
-self:I(self.lid..string.format("New carrier status: %s --> %s",tostring(self.carrierStatus),tostring(Status)))
-end
-self.carrierStatus=Status
-end
-function OPSGROUP:_TransferCargo(CargoGroup,CarrierGroup,CarrierElement)
-self:T(self.lid..string.format("Transferring cargo %s to new carrier group %s",CargoGroup:GetName(),CarrierGroup:GetName()))
-self:Unload(CargoGroup)
-CarrierGroup:Load(CargoGroup,CarrierElement)
-end
-function OPSGROUP:onafterLoad(From,Event,To,CargoGroup,Carrier)
-self:T(self.lid..string.format("Loading group %s",tostring(CargoGroup.groupname)))
-local carrier=Carrier or CargoGroup:_GetMyCarrierElement()
-if not carrier then
-local weight=CargoGroup:GetWeightTotal()
-carrier=self:FindCarrierForCargo(weight)
-end
-if carrier then
-CargoGroup:_NewCargoStatus(OPSGROUP.CargoStatus.LOADED)
-CargoGroup:ClearWaypoints()
-self:_AddCargobay(CargoGroup,carrier,false)
-if CargoGroup:IsAlive()then
-CargoGroup:Despawn(0,true)
-end
-CargoGroup:Embarked(self,carrier)
-self:Loaded(CargoGroup)
-if self.cargoTransport then
-CargoGroup:_DelMyLift(self.cargoTransport)
-self.cargoTransport:Loaded(CargoGroup,self,carrier)
-else
-self:T(self.lid..string.format("WARNING: Loaded cargo but no current OPSTRANSPORT assignment!"))
-end
-else
-self:T(self.lid.."ERROR: Cargo has no carrier on Load event!")
-end
-end
-function OPSGROUP:onafterLoadingDone(From,Event,To)
-self:T(self.lid.."Carrier Loading Done ==> Transport")
-self:__Transport(1)
-end
-function OPSGROUP:onbeforeTransport(From,Event,To)
-if self.cargoTransport==nil then
-return false
-elseif self.cargoTransport:IsDelivered()then
-return false
-end
-return true
-end
-function OPSGROUP:onafterTransport(From,Event,To)
-self:_NewCarrierStatus(OPSGROUP.CarrierStatus.TRANSPORTING)
-local Zone=self.cargoTZC.DeployZone
-local inzone=self:IsInZone(Zone)
-local airbaseDeploy=self.cargoTZC.DeployAirbase
-local ready2deploy=false
-if self:IsArmygroup()or self:IsNavygroup()then
-ready2deploy=inzone
-else
-ready2deploy=self.currbase and airbaseDeploy and self.currbase:GetName()==airbaseDeploy:GetName()and self:IsParking()
-if ready2deploy==false and(self.isHelo or self.isVTOL)and self:IsLandedAt()and inzone then
-ready2deploy=true
-end
-end
-if inzone then
-if(self:IsArmygroup()or self:IsNavygroup())and not self:IsHolding()then
-self:FullStop()
-end
-self:__Unloading(-5)
-else
-local surfacetypes=nil
-if self:IsArmygroup()or self:IsFlightgroup()then
-surfacetypes={land.SurfaceType.LAND}
-elseif self:IsNavygroup()then
-surfacetypes={land.SurfaceType.WATER,land.SurfaceType.SHALLOW_WATER}
-end
-local Coordinate=Zone:GetRandomCoordinate(nil,nil,surfacetypes)
-local uid=self:GetWaypointCurrentUID()
-if self:IsFlightgroup()then
-if self:IsParking()and self:IsUncontrolled()then
-self:StartUncontrolled()
-end
-if airbaseDeploy then
-local path=self.cargoTransport:_GetPathTransport(self.category,self.cargoTZC)
-if path then
-for i=1,#path.waypoints do
-local wp=path.waypoints[i]
-local coordinate=COORDINATE:NewFromWaypoint(wp)
-local waypoint=FLIGHTGROUP.AddWaypoint(self,coordinate,nil,uid,nil,false);waypoint.temp=true
-uid=waypoint.uid
-if i==#path.waypoints then
-waypoint.temp=false
-waypoint.detour=1
-end
-end
-else
-local coordinate=self:GetCoordinate():GetIntermediateCoordinate(Coordinate,0.5)
-local waypoint=FLIGHTGROUP.AddWaypoint(self,coordinate,nil,uid,UTILS.MetersToFeet(self.altitudeCruise),true);waypoint.detour=1
-end
-elseif self.isHelo then
-local waypoint=FLIGHTGROUP.AddWaypoint(self,Coordinate,nil,uid,UTILS.MetersToFeet(self.altitudeCruise),false);waypoint.detour=1
-else
-self:T(self.lid.."ERROR: Aircraft (cargo carrier) cannot land in Deploy zone! Specify a ZONE_AIRBASE as deploy zone")
-end
-if self.isHelo and self:IsLandedAt()then
-local Task=self:GetTaskCurrent()
-if Task then
-self:TaskCancel(Task)
-else
-self:T(self.lid.."ERROR: No current task but landed at?!")
-end
-end
-elseif self:IsArmygroup()then
-local path=self.cargoTransport:_GetPathTransport(self.category,self.cargoTZC)
-local Formation=self.cargoTransport:_GetFormationTransport(self.cargoTZC,self)
-if path then
-for i=1,#path.waypoints do
-local wp=path.waypoints[i]
-local coordinate=COORDINATE:NewFromWaypoint(wp)
-local waypoint=ARMYGROUP.AddWaypoint(self,coordinate,nil,uid,wp.action,false);waypoint.temp=true
-uid=waypoint.uid
-end
-end
-local waypoint=ARMYGROUP.AddWaypoint(self,Coordinate,nil,uid,Formation,false);waypoint.detour=1
-self:Cruise(nil,Formation)
-elseif self:IsNavygroup()then
-local path=self.cargoTransport:_GetPathTransport(self.category,self.cargoTZC)
-if path then
-for i=1,#path.waypoints do
-local wp=path.waypoints[i]
-local coordinate=COORDINATE:NewFromWaypoint(wp)
-local waypoint=NAVYGROUP.AddWaypoint(self,coordinate,nil,uid,nil,false);waypoint.temp=true
-uid=waypoint.uid
-end
-end
-local waypoint=NAVYGROUP.AddWaypoint(self,Coordinate,nil,uid,self.altitudeCruise,false);waypoint.detour=1
-self:Cruise()
-end
-end
-end
-function OPSGROUP:onafterUnloading(From,Event,To)
-self:_NewCarrierStatus(OPSGROUP.CarrierStatus.UNLOADING)
-self:T(self.lid.."Unloading..")
-local zone=self.cargoTZC.DisembarkZone or self.cargoTZC.DeployZone
-for _,_cargo in pairs(self.cargoTZC.Cargos)do
-local cargo=_cargo
-if cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then
-if cargo.opsgroup:IsLoaded(self.groupname)and not cargo.opsgroup:IsDead()then
-local carrier=nil
-local carrierGroup=nil
-local disembarkToCarriers=cargo.disembarkCarriers~=nil or self.cargoTZC.disembarkToCarriers
-if cargo.disembarkZone then
-zone=cargo.disembarkZone
-end
-self:T(self.lid..string.format("Unloading cargo %s to zone %s",cargo.opsgroup:GetName(),zone and zone:GetName()or"No Zone Found!"))
-if zone and zone:IsInstanceOf("ZONE_AIRBASE")and zone:GetAirbase():IsShip()then
-local shipname=zone:GetAirbase():GetName()
-local ship=UNIT:FindByName(shipname)
-local group=ship:GetGroup()
-carrierGroup=_DATABASE:GetOpsGroup(group:GetName())
-carrier=carrierGroup:GetElementByName(shipname)
-end
-if disembarkToCarriers then
-self:T(self.lid..string.format("Trying to find disembark carriers in zone %s",zone:GetName()))
-local disembarkCarriers=cargo.disembarkCarriers or self.cargoTZC.DisembarkCarriers
-carrier,carrierGroup=self.cargoTransport:FindTransferCarrierForCargo(cargo.opsgroup,zone,disembarkCarriers,self.cargoTZC.DeployAirbase)
-end
-if(disembarkToCarriers and carrier and carrierGroup)or(not disembarkToCarriers)then
-cargo.delivered=true
-self.cargoTransport.Ndelivered=self.cargoTransport.Ndelivered+1
-if carrier and carrierGroup then
-self:_TransferCargo(cargo.opsgroup,carrierGroup,carrier)
-elseif zone and zone:IsInstanceOf("ZONE_AIRBASE")and zone:GetAirbase():IsShip()then
-self:T(self.lid.."ERROR: Deploy/disembark zone is a ZONE_AIRBASE of a ship! Where to put the cargo? Dumping into the sea, sorry!")
-self:Unload(cargo.opsgroup)
-else
-if self.cargoTransport:GetDisembarkInUtero(self.cargoTZC)then
-self:Unload(cargo.opsgroup)
-else
-local DisembarkZone=cargo.disembarkZone or self.cargoTransport:GetDisembarkZone(self.cargoTZC)
-local Coordinate=nil
-if DisembarkZone then
-Coordinate=DisembarkZone:GetRandomCoordinate()
-else
-local element=cargo.opsgroup:_GetMyCarrierElement()
-if element then
-local zoneCarrier=self:GetElementZoneUnload(element.name)
-Coordinate=zoneCarrier:GetRandomCoordinate()
-else
-self:E(self.lid..string.format("ERROR carrier element nil!"))
-end
-end
-local Heading=math.random(0,359)
-local activation=self.cargoTransport:GetDisembarkActivation(self.cargoTZC)
-if cargo.disembarkActivation~=nil then
-activation=cargo.disembarkActivation
-end
-self:Unload(cargo.opsgroup,Coordinate,activation,Heading)
-end
-self.cargoTransport:Unloaded(cargo.opsgroup,self)
-end
-else
-self:T(self.lid.."Cargo needs carrier but no carrier is avaiable (yet)!")
-end
-else
-end
-else
-if not cargo.delivered then
-for _,_element in pairs(self.elements)do
-local element=_element
-local mycargo=self:_GetCargobayElement(element,cargo.uid)
-if mycargo then
-cargo.storage.storageTo:AddAmount(mycargo.storageType,mycargo.storageAmount)
-cargo.storage.cargoDelivered=cargo.storage.cargoDelivered+mycargo.storageAmount
-cargo.storage.cargoLoaded=cargo.storage.cargoLoaded-mycargo.storageAmount
-self:_DelCargobayElement(element,mycargo)
-self:T2(self.lid..string.format("Cargo loaded=%d, delivered=%d, lost=%d",cargo.storage.cargoLoaded,cargo.storage.cargoDelivered,cargo.storage.cargoLost))
-end
-end
-local amountToDeliver=self:_GetWeightStorage(cargo.storage,false,false,true)
-local amountTotal=self:_GetWeightStorage(cargo.storage,true,false,true)
-local text=string.format("Amount delivered=%d, total=%d",amountToDeliver,amountTotal)
-self:T(self.lid..text)
-if amountToDeliver<=0 then
-cargo.delivered=true
-self.cargoTransport.Ndelivered=self.cargoTransport.Ndelivered+1
-local text=string.format("Ndelivered=%d delivered=%s",self.cargoTransport.Ndelivered,tostring(cargo.delivered))
-self:T(self.lid..text)
-end
-end
-end
-end
-end
-function OPSGROUP:onbeforeUnload(From,Event,To,OpsGroup,Coordinate,Heading)
-local removed=self:_DelCargobay(OpsGroup)
-return removed
-end
-function OPSGROUP:onafterUnload(From,Event,To,OpsGroup,Coordinate,Activated,Heading)
-OpsGroup:_NewCargoStatus(OPSGROUP.CargoStatus.NOTCARGO)
-if Coordinate then
-local Template=UTILS.DeepCopy(OpsGroup.template)
-if Activated==false then
-Template.lateActivation=true
-else
-Template.lateActivation=false
-end
-for _,Unit in pairs(Template.units)do
-local element=OpsGroup:GetElementByName(Unit.name)
-if element then
-local vec3=element.vec3
-local rvec2={x=Unit.x-Template.x,y=Unit.y-Template.y}
-local cvec2={x=Coordinate.x,y=Coordinate.z}
-Unit.x=cvec2.x+rvec2.x
-Unit.y=cvec2.y+rvec2.y
-Unit.alt=land.getHeight({x=Unit.x,y=Unit.y})
-Unit.heading=Heading and math.rad(Heading)or Unit.heading
-Unit.psi=-Unit.heading
-end
-end
-OpsGroup:_Respawn(0,Template)
-if OpsGroup:IsNavygroup()then
-OpsGroup:ClearWaypoints()
-OpsGroup.currentwp=1
-OpsGroup.passedfinalwp=true
-NAVYGROUP.AddWaypoint(OpsGroup,Coordinate,nil,nil,nil,false)
-elseif OpsGroup:IsArmygroup()then
-OpsGroup:ClearWaypoints()
-OpsGroup.currentwp=1
-OpsGroup.passedfinalwp=true
-ARMYGROUP.AddWaypoint(OpsGroup,Coordinate,nil,nil,nil,false)
-end
-else
-OpsGroup.position=self:GetVec3()
-end
-OpsGroup:Disembarked(OpsGroup:_GetMyCarrierGroup(),OpsGroup:_GetMyCarrierElement())
-self:Unloaded(OpsGroup)
-OpsGroup:_RemoveMyCarrier()
-end
-function OPSGROUP:onafterUnloaded(From,Event,To,OpsGroupCargo)
-self:T(self.lid..string.format("Unloaded OPSGROUP %s",OpsGroupCargo:GetName()))
-if OpsGroupCargo.legion and OpsGroupCargo:IsInZone(OpsGroupCargo.legion.spawnzone)then
-self:T(self.lid..string.format("Unloaded group %s returned to legion",OpsGroupCargo:GetName()))
-OpsGroupCargo:Returned()
-end
-local paused=OpsGroupCargo:_CountPausedMissions()>0
-if paused then
-OpsGroupCargo:UnpauseMission()
-end
-end
-function OPSGROUP:onafterUnloadingDone(From,Event,To)
-self:T(self.lid.."Cargo unloading done..")
-if self:IsFlightgroup()and self:IsLandedAt()then
-local Task=self:GetTaskCurrent()
-self:__TaskCancel(5,Task)
-end
-local delivered=self:_CheckGoPickup(self.cargoTransport)
-if not delivered then
-self.cargoTZC=self.cargoTransport:_GetTransportZoneCombo(self)
-if self.cargoTZC then
-self:T(self.lid.."Unloaded: Still cargo left ==> Pickup")
-self:Pickup()
-else
-self:T(self.lid..string.format("WARNING: Not all cargo was delivered but could not get a transport zone combo ==> setting carrier state to NOT CARRIER"))
-self:_NewCarrierStatus(OPSGROUP.CarrierStatus.NOTCARRIER)
-end
-else
-self:T(self.lid.."Unloaded: ALL cargo unloaded ==> Delivered (current)")
-self:Delivered(self.cargoTransport)
-end
-end
-function OPSGROUP:onafterDelivered(From,Event,To,CargoTransport)
-if self.cargoTransport and self.cargoTransport.uid==CargoTransport.uid then
-if self:IsPickingup()then
-local wpindex=self:GetWaypointIndexNext(false)
-if wpindex then
-self:RemoveWaypoint(wpindex)
-end
-self.isLandingAtAirbase=nil
-elseif self:IsLoading()then
-elseif self:IsTransporting()then
-elseif self:IsUnloading()then
-end
-self:_NewCarrierStatus(OPSGROUP.CarrierStatus.NOTCARRIER)
-if self:IsFlightgroup()then
-local function atbase(_airbase)
-local airbase=_airbase
-if airbase and self.currbase then
-if airbase.AirbaseName==self.currbase.AirbaseName then
-return true
-end
-end
-return false
-end
-if self:IsUncontrolled()and not atbase(self.destbase)then
-self:StartUncontrolled()
-end
-if self:IsLandedAt()then
-local Task=self:GetTaskCurrent()
-self:TaskCancel(Task)
-end
-else
-self:__Cruise(-0.1)
-end
-self.cargoTransport:SetCarrierTransportStatus(self,OPSTRANSPORT.Status.DELIVERED)
-self:T(self.lid..string.format("All cargo of transport UID=%d delivered ==> check group done in 0.2 sec",self.cargoTransport.uid))
-self:_CheckGroupDone(0.2)
-end
-end
-function OPSGROUP:onafterTransportCancel(From,Event,To,Transport)
-if self.cargoTransport and self.cargoTransport.uid==Transport.uid then
-self:T(self.lid..string.format("Cancel current transport %d",Transport.uid))
-local calldelivered=false
-if self:IsPickingup()then
-calldelivered=true
-elseif self:IsLoading()then
-local cargos=Transport:GetCargoOpsGroups(false)
-for _,_opsgroup in pairs(cargos)do
-local opsgroup=_opsgroup
-if opsgroup:IsBoarding(self.groupname)then
-opsgroup:RemoveWaypoint(self.currentwp+1)
-self:_DelCargobay(opsgroup)
-opsgroup:_RemoveMyCarrier()
-opsgroup:_NewCargoStatus(OPSGROUP.CargoStatus.NOTCARGO)
-elseif opsgroup:IsLoaded(self.groupname)then
-local zoneCarrier=self:GetElementZoneUnload(opsgroup:_GetMyCarrierElement().name)
-local Coordinate=zoneCarrier and zoneCarrier:GetRandomCoordinate()or self.cargoTransport:GetEmbarkZone(self.cargoTZC):GetRandomCoordinate()
-local Heading=math.random(0,359)
-self:Unload(opsgroup,Coordinate,self.cargoTransport:GetDisembarkActivation(self.cargoTZC),Heading)
-self.cargoTransport:Unloaded(opsgroup,self)
-end
-end
-calldelivered=true
-elseif self:IsTransporting()then
-elseif self:IsUnloading()then
-else
-end
-if calldelivered then
-self:__Delivered(-2,Transport)
-end
-else
-Transport:SetCarrierTransportStatus(self,AUFTRAG.GroupStatus.CANCELLED)
-self:DelOpsTransport(Transport)
-self:_CheckGroupDone(1)
-end
-end
-function OPSGROUP:onbeforeBoard(From,Event,To,CarrierGroup,Carrier)
-if self:IsDead()then
-self:T(self.lid.."Group DEAD ==> Deny Board transition!")
-return false
-elseif CarrierGroup:IsDead()then
-self:T(self.lid.."Carrier Group DEAD ==> Deny Board transition!")
-self:_NewCargoStatus(OPSGROUP.CargoStatus.NOTCARGO)
-return false
-elseif Carrier.status==OPSGROUP.ElementStatus.DEAD then
-self:T(self.lid.."Carrier Element DEAD ==> Deny Board transition!")
-self:_NewCargoStatus(OPSGROUP.CargoStatus.NOTCARGO)
-return false
-end
-return true
-end
-function OPSGROUP:onafterBoard(From,Event,To,CarrierGroup,Carrier)
-local CarrierIsArmyOrNavy=CarrierGroup:IsArmygroup()or CarrierGroup:IsNavygroup()
-local CargoIsArmyOrNavy=self:IsArmygroup()or self:IsNavygroup()
-if(CarrierIsArmyOrNavy and(CarrierGroup:GetVelocity(Carrier.name)<=1))or(CarrierGroup:IsFlightgroup()and(CarrierGroup:IsParking()or CarrierGroup:IsLandedAt()))then
-local board=self.speedMax>0 and CargoIsArmyOrNavy and self:IsAlive()and CarrierGroup:IsAlive()
-if self:IsArmygroup()and CarrierGroup:IsNavygroup()then
-board=false
-end
-if self:IsLoaded()then
-self:T(self.lid..string.format("Group is loaded currently ==> Moving directly to new carrier - No Unload(), Disembart() events triggered!"))
-self:_RemoveMyCarrier()
-CarrierGroup:Load(self)
-elseif board then
-self:_NewCargoStatus(OPSGROUP.CargoStatus.BOARDING)
-self:T(self.lid..string.format("Boarding group=%s [%s], carrier=%s",CarrierGroup:GetName(),CarrierGroup:GetState(),tostring(Carrier.name)))
-local Coordinate=Carrier.unit:GetCoordinate()
-self:ClearWaypoints(self.currentwp+1)
-if self.isArmygroup then
-local waypoint=ARMYGROUP.AddWaypoint(self,Coordinate,nil,nil,ENUMS.Formation.Vehicle.Diamond);waypoint.detour=1
-self:Cruise()
-else
-local waypoint=NAVYGROUP.AddWaypoint(self,Coordinate);waypoint.detour=1
-self:Cruise()
-end
-CarrierGroup:_AddCargobay(self,Carrier,true)
-else
-self:T(self.lid..string.format("Board [loaded=%s] with direct load to carrier group=%s, element=%s",tostring(self:IsLoaded()),CarrierGroup:GetName(),tostring(Carrier.name)))
-local mycarriergroup=self:_GetMyCarrierGroup()
-if mycarriergroup then
-self:T(self.lid..string.format("Current carrier group %s",mycarriergroup:GetName()))
-end
-if mycarriergroup and mycarriergroup:GetName()~=CarrierGroup:GetName()then
-self:T(self.lid.."Unloading from mycarrier")
-mycarriergroup:Unload(self)
-end
-CarrierGroup:Load(self)
-end
-else
-self:T(self.lid.."Carrier not ready for boarding yet ==> repeating boarding call in 10 sec")
-self:__Board(-10,CarrierGroup,Carrier)
-CarrierGroup:_AddCargobay(self,Carrier,true)
-end
-end
-function OPSGROUP:_CheckInZones()
-if self.checkzones and self:IsAlive()then
-local Ncheck=self.checkzones:Count()
-local Ninside=self.inzones:Count()
-self:T(self.lid..string.format("Check if group is in %d zones. Currently it is in %d zones.",self.checkzones:Count(),self.inzones:Count()))
-local leftzones={}
-for inzonename,inzone in pairs(self.inzones:GetSet())do
-local isstillinzone=self.group:IsInZone(inzone)
-if not isstillinzone then
-table.insert(leftzones,inzone)
-end
-end
-for _,leftzone in pairs(leftzones)do
-self:LeaveZone(leftzone)
-end
-local enterzones={}
-for checkzonename,_checkzone in pairs(self.checkzones:GetSet())do
-local checkzone=_checkzone
-local isincheckzone=self.group:IsInZone(checkzone)
-if isincheckzone and not self.inzones:_Find(checkzonename)then
-table.insert(enterzones,checkzone)
-end
-end
-for _,enterzone in pairs(enterzones)do
-self:EnterZone(enterzone)
-end
-end
-end
-function OPSGROUP:_CheckDetectedUnits()
-if self.detectionOn and self.group and not self:IsDead()then
-local detectedtargets=self.group:GetDetectedTargets()
-local detected={}
-local groups={}
-for DetectionObjectID,Detection in pairs(detectedtargets or{})do
-local DetectedObject=Detection.object
-if DetectedObject and DetectedObject:isExist()and DetectedObject.id_<50000000 then
-local unit=UNIT:Find(DetectedObject)
-if unit and unit:IsAlive()then
-local unitname=unit:GetName()
-table.insert(detected,unit)
-self:DetectedUnit(unit)
-local group=unit:GetGroup()
-if group then
-groups[group:GetName()]=group
-end
-end
-end
-end
-for groupname,group in pairs(groups)do
-self:DetectedGroup(group)
-end
-local lost={}
-for _,_unit in pairs(self.detectedunits:GetSet())do
-local unit=_unit
-local gotit=false
-for _,_du in pairs(detected)do
-local du=_du
-if unit:GetName()==du:GetName()then
-gotit=true
-end
-end
-if not gotit then
-table.insert(lost,unit:GetName())
-self:DetectedUnitLost(unit)
-end
-end
-self.detectedunits:RemoveUnitsByName(lost)
-local lost={}
-for _,_group in pairs(self.detectedgroups:GetSet())do
-local group=_group
-local gotit=false
-for _,_du in pairs(groups)do
-local du=_du
-if group:GetName()==du:GetName()then
-gotit=true
-end
-end
-if not gotit then
-table.insert(lost,group:GetName())
-self:DetectedGroupLost(group)
-end
-end
-self.detectedgroups:RemoveGroupsByName(lost)
-end
-end
-function OPSGROUP:_CheckGroupDone(delay)
-local fsmstate=self:GetState()
-if self:IsAlive()and self.isAI then
-if delay and delay>0 then
-self:T(self.lid..string.format("Check OPSGROUP done? [state=%s] in %.3f seconds...",fsmstate,delay))
-self:ScheduleOnce(delay,self._CheckGroupDone,self)
-else
-self:T(self.lid..string.format("Check OSGROUP done? [state=%s]",fsmstate))
-if self:IsEngaging()then
-self:T(self.lid.."Engaging! Group NOT done ==> UpdateRoute()")
-self:UpdateRoute()
-return
-end
-if self:IsReturning()then
-self:T(self.lid.."Returning! Group NOT done...")
-return
-end
-if self:IsRearming()then
-self:T(self.lid.."Rearming! Group NOT done...")
-return
-end
-if self:IsRetreating()then
-self:T(self.lid.."Retreating! Group NOT done...")
-return
-end
-if self:IsBoarding()then
-self:T(self.lid.."Boarding! Group NOT done...")
-return
-end
-if self:IsWaiting()then
-self:T(self.lid.."Waiting! Group NOT done...")
-return
-end
-local nTasks=self:CountRemainingTasks()
-local nMissions=self:CountRemainingMissison()
-local nTransports=self:CountRemainingTransports()
-local nPaused=self:_CountPausedMissions()
-if nPaused>0 and nPaused==nMissions then
-local missionpaused=self:_GetPausedMission()
-self:T(self.lid..string.format("Found paused mission %s [%s]. Unpausing mission...",missionpaused.name,missionpaused.type))
-self:UnpauseMission()
-return
-end
-if nTasks>0 or nMissions>0 or nTransports>0 then
-self:T(self.lid..string.format("Group still has tasks, missions or transports ==> NOT DONE"))
-return
-end
-local waypoint=self:GetWaypoint(self.currentwp)
-if waypoint then
-local ntasks=self:CountTasksWaypoint(waypoint.uid)
-if ntasks>0 then
-self:T(self.lid..string.format("Still got %d tasks for the current waypoint UID=%d ==> RETURN (no action)",ntasks,waypoint.uid))
-return
-end
-end
-if self.adinfinitum then
-if#self.waypoints>0 then
-local i=self:GetWaypointIndexNext(true)
-local speed=self:GetSpeedToWaypoint(i)
-self:Cruise(speed)
-self:T(self.lid..string.format("Adinfinitum=TRUE ==> Goto WP index=%d at speed=%d knots",i,speed))
-else
-self:T(self.lid..string.format("WARNING: No waypoints left! Commanding a Full Stop"))
-self:__FullStop(-1)
-end
-else
-if self:HasPassedFinalWaypoint()then
-if self.legion and self.legionReturn then
-self:T(self.lid..string.format("Passed final WP, adinfinitum=FALSE, LEGION set ==> RTZ"))
-if self.isArmygroup then
-self:T2(self.lid.."RTZ to legion spawn zone")
-self:RTZ(self.legion.spawnzone)
-elseif self.isNavygroup then
-self:T2(self.lid.."RTZ to legion port zone")
-self:RTZ(self.legion.portzone)
-end
-else
-self:__FullStop(-1)
-self:T(self.lid..string.format("Passed final WP, adinfinitum=FALSE ==> Full Stop"))
-end
-else
-if#self.waypoints>0 then
-self:T(self.lid..string.format("NOT Passed final WP, #WP>0 ==> Update Route"))
-self:Cruise()
-else
-self:T(self.lid..string.format("WARNING: No waypoints left! Commanding a Full Stop"))
-self:__FullStop(-1)
-end
-end
-end
-end
-end
-end
-function OPSGROUP:_CheckStuck()
-if self:IsHolding()or self:Is("Rearming")or self:IsWaiting()or self:HasPassedFinalWaypoint()then
-return
-end
-local Tnow=timer.getTime()
-local ExpectedSpeed=self:GetExpectedSpeed()
-local speed=self:GetVelocity()
-if speed<0.1 then
-if ExpectedSpeed>0 and not self.stuckTimestamp then
-self:T2(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected",speed,ExpectedSpeed))
-self.stuckTimestamp=Tnow
-self.stuckVec3=self:GetVec3()
-end
-else
-self.stuckTimestamp=nil
-end
-if self.stuckTimestamp then
-local holdtime=Tnow-self.stuckTimestamp
-if holdtime>=5*60 and holdtime<10*60 then
-self:T(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected for %d sec",speed,ExpectedSpeed,holdtime))
-if self:IsEngaging()then
-self:__Disengage(1)
-elseif self:IsReturning()then
-self:T2(self.lid.."RTZ because of stuck")
-self:__RTZ(1)
-else
-self:__Cruise(1)
-end
-elseif holdtime>=10*60 and holdtime<30*60 then
-self:T(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected for %d sec",speed,ExpectedSpeed,holdtime))
-local mission=self:GetMissionCurrent()
-if mission then
-self:T(self.lid..string.format("WARNING: Cancelling mission %s [%s] due to being stuck",mission:GetName(),mission:GetType()))
-self:MissionCancel(mission)
-else
-if self:IsReturning()then
-self:T2(self.lid.."RTZ because of stuck")
-self:__RTZ(1)
-else
-self:__Cruise(1)
-end
-end
-elseif holdtime>=30*60 then
-self:T(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected for %d sec",speed,ExpectedSpeed,holdtime))
-if self.legion then
-self:T(self.lid..string.format("Asset is returned to its legion after being stuck!"))
-self:ReturnToLegion()
-end
-end
-end
-end
-function OPSGROUP:_CheckDamage()
-self:T(self.lid..string.format("Checking damage..."))
-self.life=0
-local damaged=false
-for _,_element in pairs(self.elements)do
-local element=_element
-if element.status~=OPSGROUP.ElementStatus.DEAD and element.status~=OPSGROUP.ElementStatus.INUTERO then
-local life=element.unit:GetLife()
-self.life=self.life+life
-if life0 then
-local ammo=self:GetAmmoTot()
-if self:IsRearming()then
-if ammo.Total>=self.ammo.Total then
-self:Rearmed()
-end
-end
-if self.outofAmmo and ammo.Total>0 then
-self.outofAmmo=false
-end
-if ammo.Total==0 and not self.outofAmmo then
-self.outofAmmo=true
-self:OutOfAmmo()
-end
-if self.outofGuns and ammo.Guns>0 then
-self.outofGuns=false
-end
-if ammo.Guns==0 and self.ammo.Guns>0 and not self.outofGuns then
-self.outofGuns=true
-self:OutOfGuns()
-end
-if self.outofRockets and ammo.Rockets>0 then
-self.outofRockets=false
-end
-if ammo.Rockets==0 and self.ammo.Rockets>0 and not self.outofRockets then
-self.outofRockets=true
-self:OutOfRockets()
-end
-if self.outofBombs and ammo.Bombs>0 then
-self.outofBombs=false
-end
-if ammo.Bombs==0 and self.ammo.Bombs>0 and not self.outofBombs then
-self.outofBombs=true
-self:OutOfBombs()
-end
-if self.outofMissiles and ammo.Missiles>0 then
-self.outofMissiles=false
-end
-if ammo.Missiles==0 and self.ammo.Missiles>0 and not self.outofMissiles then
-self.outofMissiles=true
-self:OutOfMissiles()
-end
-if self.outofMissilesAA and ammo.MissilesAA>0 then
-self.outofMissilesAA=false
-end
-if ammo.MissilesAA==0 and self.ammo.MissilesAA>0 and not self.outofMissilesAA then
-self.outofMissilesAA=true
-self:OutOfMissilesAA()
-end
-if self.outofMissilesAG and ammo.MissilesAG>0 then
-self.outofMissilesAG=false
-end
-if ammo.MissilesAG==0 and self.ammo.MissilesAG>0 and not self.outofMissilesAG then
-self.outofMissilesAG=true
-self:OutOfMissilesAG()
-end
-if self.outofMissilesAS and ammo.MissilesAS>0 then
-self.outofMissilesAS=false
-end
-if ammo.MissilesAS==0 and self.ammo.MissilesAS>0 and not self.outofMissilesAS then
-self.outofMissilesAS=true
-self:OutOfMissilesAS()
-end
-if self.outofTorpedos and ammo.Torpedos>0 then
-self.outofTorpedos=false
-end
-if ammo.Torpedos==0 and self.ammo.Torpedos>0 and not self.outofTorpedos then
-self.outofTorpedos=true
-self:OutOfTorpedos()
-end
-if self:IsEngaging()and ammo.Total==0 then
-self:Disengage()
-end
-end
-end
-function OPSGROUP:_PrintTaskAndMissionStatus()
-if self.verbose>=3 and#self.taskqueue>0 then
-local text=string.format("Tasks #%d",#self.taskqueue)
-for i,_task in pairs(self.taskqueue)do
-local task=_task
-local name=task.description
-local taskid=task.dcstask.id or"unknown"
-local status=task.status
-local clock=UTILS.SecondsToClock(task.time,true)
-local eta=task.time-timer.getAbsTime()
-local started=task.timestamp and UTILS.SecondsToClock(task.timestamp,true)or"N/A"
-local duration=-1
-if task.duration then
-duration=task.duration
-if task.timestamp then
-duration=task.duration-(timer.getAbsTime()-task.timestamp)
-else
-duration=task.duration
-end
-end
-if task.type==OPSGROUP.TaskType.SCHEDULED then
-text=text..string.format("\n[%d] %s (%s): status=%s, scheduled=%s (%d sec), started=%s, duration=%d",i,taskid,name,status,clock,eta,started,duration)
-elseif task.type==OPSGROUP.TaskType.WAYPOINT then
-text=text..string.format("\n[%d] %s (%s): status=%s, waypoint=%d, started=%s, duration=%d, stopflag=%d",i,taskid,name,status,task.waypoint,started,duration,task.stopflag:Get())
-end
-end
-self:I(self.lid..text)
-end
-if self.verbose>=2 then
-local Mission=self:GetMissionByID(self.currentmission)
-local text=string.format("Missions %d, Current: %s",self:CountRemainingMissison(),Mission and Mission.name or"none")
-for i,_mission in pairs(self.missionqueue)do
-local mission=_mission
-local Cstart=UTILS.SecondsToClock(mission.Tstart,true)
-local Cstop=mission.Tstop and UTILS.SecondsToClock(mission.Tstop,true)or"INF"
-text=text..string.format("\n[%d] %s (%s) status=%s (%s), Time=%s-%s, prio=%d wp=%s targets=%d",
-i,tostring(mission.name),mission.type,mission:GetGroupStatus(self),tostring(mission.status),Cstart,Cstop,mission.prio,tostring(mission:GetGroupWaypointIndex(self)),mission:CountMissionTargets())
-end
-self:I(self.lid..text)
-end
-end
-function OPSGROUP:_SimpleTaskFunction(Function,uid)
-local DCSScript={}
-DCSScript[#DCSScript+1]=string.format('local mygroup = _DATABASE:FindOpsGroup(\"%s\") ',self.groupname)
-DCSScript[#DCSScript+1]=string.format('%s(mygroup, %d)',Function,uid)
-local DCSTask=CONTROLLABLE.TaskWrappedAction(self,CONTROLLABLE.CommandDoScript(self,table.concat(DCSScript)))
-return DCSTask
-end
-function OPSGROUP:_CreateWaypoint(waypoint)
-waypoint.uid=self.wpcounter
-waypoint.npassed=0
-waypoint.coordinate=COORDINATE:New(waypoint.x,waypoint.alt,waypoint.y)
-waypoint.name=string.format("Waypoint UID=%d",waypoint.uid)
-waypoint.patrol=false
-waypoint.detour=false
-waypoint.astar=false
-waypoint.temp=false
-local taskswp={}
-local TaskPassingWaypoint=self:_SimpleTaskFunction("OPSGROUP._PassingWaypoint",waypoint.uid)
-table.insert(taskswp,TaskPassingWaypoint)
-waypoint.task=self.group:TaskCombo(taskswp)
-self.wpcounter=self.wpcounter+1
-return waypoint
-end
-function OPSGROUP:_AddWaypoint(waypoint,wpnumber)
-wpnumber=wpnumber or#self.waypoints+1
-table.insert(self.waypoints,wpnumber,waypoint)
-self:T(self.lid..string.format("Adding waypoint at index=%d with UID=%d",wpnumber,waypoint.uid))
-if self.currentwp and wpnumber>self.currentwp then
-self:_PassedFinalWaypoint(false,string.format("_AddWaypoint: wpnumber/index %d>%d self.currentwp",wpnumber,self.currentwp))
-end
-end
-function OPSGROUP:_InitWaypoints(WpIndexMin,WpIndexMax)
-self.waypoints0=UTILS.DeepCopy(_DATABASE:GetGroupTemplate(self.groupname).route.points)
-self.waypoints={}
-WpIndexMin=WpIndexMin or 1
-WpIndexMax=WpIndexMax or#self.waypoints0
-WpIndexMax=math.min(WpIndexMax,#self.waypoints0)
-for i=WpIndexMin,WpIndexMax do
-local wp=self.waypoints0[i]
-local Coordinate=COORDINATE:NewFromWaypoint(wp)
-wp.speed=wp.speed or 0
-local speedknots=UTILS.MpsToKnots(wp.speed)
-if i<=2 then
-self.speedWp=wp.speed
-end
-local Speed=UTILS.MpsToKnots(wp.speed)
-local Waypoint=nil
-if self:IsFlightgroup()then
-Waypoint=FLIGHTGROUP.AddWaypoint(self,Coordinate,Speed,nil,Altitude,false)
-elseif self:IsArmygroup()then
-Waypoint=ARMYGROUP.AddWaypoint(self,Coordinate,Speed,nil,wp.action,false)
-elseif self:IsNavygroup()then
-Waypoint=NAVYGROUP.AddWaypoint(self,Coordinate,Speed,nil,Depth,false)
-end
-local DCStasks=wp.task and wp.task.params.tasks or nil
-if DCStasks and self.useMEtasks then
-for _,DCStask in pairs(DCStasks)do
-if DCStask.id and DCStask.id~="WrappedAction"then
-self:AddTaskWaypoint(DCStask,Waypoint,"ME Task")
-end
-end
-end
-end
-self:T(self.lid..string.format("Initializing %d waypoints",#self.waypoints))
-if self:IsFlightgroup()then
-self.homebase=self.homebase or self:GetHomebaseFromWaypoints()
-local destbase=self:GetDestinationFromWaypoints()
-self.destbase=self.destbase or destbase
-self.currbase=self:GetHomebaseFromWaypoints()
-if destbase and#self.waypoints>1 then
-table.remove(self.waypoints,#self.waypoints)
-end
-if self.destbase==nil then
-self.destbase=self.homebase
-end
-end
-if#self.waypoints>0 then
-if#self.waypoints==1 then
-self:_PassedFinalWaypoint(true,"_InitWaypoints: #self.waypoints==1")
-end
-else
-self:T(self.lid.."WARNING: No waypoints initialized. Number of waypoints is 0!")
-end
-return self
-end
-function OPSGROUP:Route(waypoints,delay)
-if delay and delay>0 then
-self:ScheduleOnce(delay,OPSGROUP.Route,self,waypoints)
-else
-if self:IsAlive()then
-local DCSTask={
-id='Mission',
-params={
-airborne=self:IsFlightgroup(),
-route={points=waypoints},
-},
-}
-self:SetTask(DCSTask)
-else
-self:T(self.lid.."ERROR: Group is not alive! Cannot route group.")
-end
-end
-return self
-end
-function OPSGROUP:_UpdateWaypointTasks(n)
-local waypoints=self.waypoints or{}
-local nwaypoints=#waypoints
-for i,_wp in pairs(waypoints)do
-local wp=_wp
-if i>=n or nwaypoints==1 then
-self:T2(self.lid..string.format("Updating waypoint task for waypoint %d/%d ID=%d. Last waypoint passed %d",i,nwaypoints,wp.uid,self.currentwp))
-local taskswp={}
-local TaskPassingWaypoint=self.group:TaskFunction("OPSGROUP._PassingWaypoint",self,wp.uid)
-table.insert(taskswp,TaskPassingWaypoint)
-wp.task=self.group:TaskCombo(taskswp)
-end
-end
-end
-function OPSGROUP._PassingWaypoint(opsgroup,uid)
-local text=string.format("Group passing waypoint uid=%d",uid)
-opsgroup:T(opsgroup.lid..text)
-local waypoint=opsgroup:GetWaypointByID(uid)
-if waypoint then
-waypoint.npassed=waypoint.npassed+1
-local currentwp=opsgroup.currentwp
-opsgroup.currentwp=opsgroup:GetWaypointIndex(uid)
-local wpistemp=waypoint.temp or waypoint.detour or waypoint.astar
-if wpistemp then
-opsgroup:RemoveWaypointByID(uid)
-end
-local wpnext=opsgroup:GetWaypointNext()
-if wpnext then
-opsgroup:T(opsgroup.lid..string.format("Next waypoint UID=%d index=%d",wpnext.uid,opsgroup:GetWaypointIndex(wpnext.uid)))
-if opsgroup.isArmygroup then
-opsgroup.option.Formation=wpnext.action
-end
-opsgroup.speed=wpnext.speed
-if opsgroup.speed<0.01 then
-opsgroup.speed=UTILS.KmphToMps(opsgroup.speedCruise)
-end
-else
-opsgroup:_PassedFinalWaypoint(true,"_PassingWaypoint No next Waypoint found")
-end
-if opsgroup.currentwp==#opsgroup.waypoints and not(opsgroup.adinfinitum or wpistemp)then
-opsgroup:_PassedFinalWaypoint(true,"_PassingWaypoint currentwp==#waypoints and NOT adinfinitum and NOT a temporary waypoint")
-end
-if waypoint.temp then
-if(opsgroup:IsNavygroup()or opsgroup:IsArmygroup())and opsgroup.currentwp==#opsgroup.waypoints then
-opsgroup:Cruise()
-end
-elseif waypoint.astar then
-opsgroup:Cruise()
-elseif waypoint.detour then
-if opsgroup:IsRearming()then
-opsgroup:Rearming()
-elseif opsgroup:IsRetreating()then
-opsgroup:Retreated()
-elseif opsgroup:IsReturning()then
-opsgroup:Returned()
-elseif opsgroup:IsPickingup()then
-if opsgroup:IsFlightgroup()then
-if opsgroup.cargoTZC then
-if opsgroup.cargoTZC.PickupAirbase then
-opsgroup:LandAtAirbase(opsgroup.cargoTZC.PickupAirbase)
-else
-local coordinate=opsgroup.cargoTZC.PickupZone:GetRandomCoordinate(nil,nil,{land.SurfaceType.LAND})
-opsgroup:LandAt(coordinate,60*60)
-end
-else
-local coordinate=opsgroup:GetCoordinate()
-opsgroup:LandAt(coordinate,60*60)
-end
-else
-opsgroup:FullStop()
-opsgroup:__Loading(-5)
-end
-elseif opsgroup:IsTransporting()then
-if opsgroup:IsFlightgroup()then
-if opsgroup.cargoTZC then
-if opsgroup.cargoTZC.DeployAirbase then
-opsgroup:LandAtAirbase(opsgroup.cargoTZC.DeployAirbase)
-else
-local coordinate=opsgroup.cargoTZC.DeployZone:GetRandomCoordinate(nil,nil,{land.SurfaceType.LAND})
-opsgroup:LandAt(coordinate,60*60)
-end
-else
-local coordinate=opsgroup:GetCoordinate()
-opsgroup:LandAt(coordinate,60*60)
-end
-else
-opsgroup:FullStop()
-opsgroup:Unloading()
-end
-elseif opsgroup:IsBoarding()then
-local carrierGroup=opsgroup:_GetMyCarrierGroup()
-local carrier=opsgroup:_GetMyCarrierElement()
-if carrierGroup and carrierGroup:IsAlive()then
-if carrier and carrier.unit and carrier.unit:IsAlive()then
-carrierGroup:Load(opsgroup)
-else
-opsgroup:E(opsgroup.lid.."ERROR: Group cannot board assigned carrier UNIT as it is NOT alive!")
-end
-else
-opsgroup:E(opsgroup.lid.."ERROR: Group cannot board assigned carrier GROUP as it is NOT alive!")
-end
-elseif opsgroup:IsEngaging()then
-opsgroup:T(opsgroup.lid.."Passing engaging waypoint")
-else
-opsgroup:DetourReached()
-if waypoint.detour==0 then
-opsgroup:FullStop()
-elseif waypoint.detour==1 then
-opsgroup:Cruise()
-else
-opsgroup:E("ERROR: waypoint.detour should be 0 or 1")
-opsgroup:FullStop()
-end
-end
-else
-if opsgroup.ispathfinding then
-opsgroup.ispathfinding=false
-end
-opsgroup:PassingWaypoint(waypoint)
-end
-end
-end
-function OPSGROUP._TaskExecute(group,opsgroup,task)
-local text=string.format("_TaskExecute %s",task.description)
-opsgroup:T3(opsgroup.lid..text)
-if opsgroup then
-opsgroup:TaskExecute(task)
-end
-end
-function OPSGROUP._TaskDone(group,opsgroup,task)
-local text=string.format("_TaskDone %s",task.description)
-opsgroup:T(opsgroup.lid..text)
-if opsgroup then
-opsgroup:TaskDone(task)
-end
-end
-function OPSGROUP:SetDefaultROE(roe)
-self.optionDefault.ROE=roe or ENUMS.ROE.ReturnFire
-return self
-end
-function OPSGROUP:SwitchROE(roe)
-if self:IsAlive()or self:IsInUtero()then
-self.option.ROE=roe or self.optionDefault.ROE
-if self:IsInUtero()then
-self:T2(self.lid..string.format("Setting current ROE=%d when GROUP is SPAWNED",self.option.ROE))
-else
-self.group:OptionROE(self.option.ROE)
-self:T(self.lid..string.format("Setting current ROE=%d (%s)",self.option.ROE,self:_GetROEName(self.option.ROE)))
-end
-else
-self:T(self.lid.."WARNING: Cannot switch ROE! Group is not alive")
-end
-return self
-end
-function OPSGROUP:_GetROEName(roe)
-local name="unknown"
-if roe==0 then
-name="Weapon Free"
-elseif roe==1 then
-name="Open Fire/Weapon Free"
-elseif roe==2 then
-name="Open Fire"
-elseif roe==3 then
-name="Return Fire"
-elseif roe==4 then
-name="Weapon Hold"
-end
-return name
-end
-function OPSGROUP:GetROE()
-return self.option.ROE or self.optionDefault.ROE
-end
-function OPSGROUP:SetDefaultROT(rot)
-self.optionDefault.ROT=rot or ENUMS.ROT.PassiveDefense
-return self
-end
-function OPSGROUP:SwitchROT(rot)
-if self:IsFlightgroup()then
-if self:IsAlive()or self:IsInUtero()then
-self.option.ROT=rot or self.optionDefault.ROT
-if self:IsInUtero()then
-self:T2(self.lid..string.format("Setting current ROT=%d when GROUP is SPAWNED",self.option.ROT))
-else
-self.group:OptionROT(self.option.ROT)
-self:T(self.lid..string.format("Setting current ROT=%d (0=NoReaction, 1=Passive, 2=Evade, 3=ByPass, 4=AllowAbort)",self.option.ROT))
-end
-else
-self:T(self.lid.."WARNING: Cannot switch ROT! Group is not alive")
-end
-end
-return self
-end
-function OPSGROUP:GetROT()
-return self.option.ROT or self.optionDefault.ROT
-end
-function OPSGROUP:SetDefaultAlarmstate(alarmstate)
-self.optionDefault.Alarm=alarmstate or 0
-return self
-end
-function OPSGROUP:SwitchAlarmstate(alarmstate)
-if self:IsAlive()or self:IsInUtero()then
-if self.isArmygroup or self.isNavygroup then
-self.option.Alarm=alarmstate or self.optionDefault.Alarm
-if self:IsInUtero()then
-self:T2(self.lid..string.format("Setting current Alarm State=%d when GROUP is SPAWNED",self.option.Alarm))
-else
-if self.option.Alarm==0 then
-self.group:OptionAlarmStateAuto()
-elseif self.option.Alarm==1 then
-self.group:OptionAlarmStateGreen()
-elseif self.option.Alarm==2 then
-self.group:OptionAlarmStateRed()
-else
-self:T("ERROR: Unknown Alarm State! Setting to AUTO")
-self.group:OptionAlarmStateAuto()
-self.option.Alarm=0
-end
-self:T(self.lid..string.format("Setting current Alarm State=%d (0=Auto, 1=Green, 2=Red)",self.option.Alarm))
-end
-end
-else
-self:T(self.lid.."WARNING: Cannot switch Alarm State! Group is not alive.")
-end
-return self
-end
-function OPSGROUP:GetAlarmstate()
-return self.option.Alarm or self.optionDefault.Alarm
-end
-function OPSGROUP:SetDefaultEPLRS(OnOffSwitch)
-if OnOffSwitch==nil then
-self.optionDefault.EPLRS=self.isEPLRS
-else
-self.optionDefault.EPLRS=OnOffSwitch
-end
-return self
-end
-function OPSGROUP:SwitchEPLRS(OnOffSwitch)
-if self:IsAlive()or self:IsInUtero()then
-if OnOffSwitch==nil then
-self.option.EPLRS=self.optionDefault.EPLRS
-else
-self.option.EPLRS=OnOffSwitch
-end
-if self:IsInUtero()then
-self:T2(self.lid..string.format("Setting current EPLRS=%s when GROUP is SPAWNED",tostring(self.option.EPLRS)))
-else
-self.group:CommandEPLRS(self.option.EPLRS)
-self:T(self.lid..string.format("Setting current EPLRS=%s",tostring(self.option.EPLRS)))
-end
-else
-self:E(self.lid.."WARNING: Cannot switch EPLRS! Group is not alive")
-end
-return self
-end
-function OPSGROUP:GetEPLRS()
-return self.option.EPLRS or self.optionDefault.EPLRS
-end
-function OPSGROUP:SetDefaultEmission(OnOffSwitch)
-if OnOffSwitch==nil then
-self.optionDefault.Emission=true
-else
-self.optionDefault.EPLRS=OnOffSwitch
-end
-return self
-end
-function OPSGROUP:SwitchEmission(OnOffSwitch)
-if self:IsAlive()or self:IsInUtero()then
-if OnOffSwitch==nil then
-self.option.Emission=self.optionDefault.Emission
-else
-self.option.Emission=OnOffSwitch
-end
-if self:IsInUtero()then
-self:T2(self.lid..string.format("Setting current EMISSION=%s when GROUP is SPAWNED",tostring(self.option.Emission)))
-else
-self.group:EnableEmission(self.option.Emission)
-self:T(self.lid..string.format("Setting current EMISSION=%s",tostring(self.option.Emission)))
-end
-else
-self:E(self.lid.."WARNING: Cannot switch Emission! Group is not alive")
-end
-return self
-end
-function OPSGROUP:GetEmission()
-return self.option.Emission or self.optionDefault.Emission
-end
-function OPSGROUP:SetDefaultInvisible(OnOffSwitch)
-if OnOffSwitch==nil then
-self.optionDefault.Invisible=true
-else
-self.optionDefault.Invisible=OnOffSwitch
-end
-return self
-end
-function OPSGROUP:SwitchInvisible(OnOffSwitch)
-if self:IsAlive()or self:IsInUtero()then
-if OnOffSwitch==nil then
-self.option.Invisible=self.optionDefault.Invisible
-else
-self.option.Invisible=OnOffSwitch
-end
-if self:IsInUtero()then
-self:T2(self.lid..string.format("Setting current INVISIBLE=%s when GROUP is SPAWNED",tostring(self.option.Invisible)))
-else
-self.group:SetCommandInvisible(self.option.Invisible)
-self:T(self.lid..string.format("Setting current INVISIBLE=%s",tostring(self.option.Invisible)))
-end
-else
-self:E(self.lid.."WARNING: Cannot switch Invisible! Group is not alive")
-end
-return self
-end
-function OPSGROUP:SetDefaultImmortal(OnOffSwitch)
-if OnOffSwitch==nil then
-self.optionDefault.Immortal=true
-else
-self.optionDefault.Immortal=OnOffSwitch
-end
-return self
-end
-function OPSGROUP:SwitchImmortal(OnOffSwitch)
-if self:IsAlive()or self:IsInUtero()then
-if OnOffSwitch==nil then
-self.option.Immortal=self.optionDefault.Immortal
-else
-self.option.Immortal=OnOffSwitch
-end
-if self:IsInUtero()then
-self:T2(self.lid..string.format("Setting current IMMORTAL=%s when GROUP is SPAWNED",tostring(self.option.Immortal)))
-else
-self.group:SetCommandImmortal(self.option.Immortal)
-self:T(self.lid..string.format("Setting current IMMORTAL=%s",tostring(self.option.Immortal)))
-end
-else
-self:E(self.lid.."WARNING: Cannot switch Immortal! Group is not alive")
-end
-return self
-end
-function OPSGROUP:SetDefaultTACAN(Channel,Morse,UnitName,Band,OffSwitch)
-self.tacanDefault={}
-self.tacanDefault.Channel=Channel or 74
-self.tacanDefault.Morse=Morse or"XXX"
-self.tacanDefault.BeaconName=UnitName
-if self:IsFlightgroup()then
-Band=Band or"Y"
-else
-Band=Band or"X"
-end
-self.tacanDefault.Band=Band
-if OffSwitch then
-self.tacanDefault.On=false
-else
-self.tacanDefault.On=true
-end
-return self
-end
-function OPSGROUP:_SwitchTACAN(Tacan)
-if Tacan then
-self:SwitchTACAN(Tacan.Channel,Tacan.Morse,Tacan.BeaconName,Tacan.Band)
-else
-if self.tacanDefault.On then
-self:SwitchTACAN()
-else
-self:TurnOffTACAN()
-end
-end
-end
-function OPSGROUP:SwitchTACAN(Channel,Morse,UnitName,Band)
-if self:IsInUtero()then
-self:T(self.lid..string.format("Switching TACAN to DEFAULT when group is spawned"))
-self:SetDefaultTACAN(Channel,Morse,UnitName,Band)
-elseif self:IsAlive()then
-Channel=Channel or self.tacanDefault.Channel
-Morse=Morse or self.tacanDefault.Morse
-Band=Band or self.tacanDefault.Band
-UnitName=UnitName or self.tacanDefault.BeaconName
-local unit=self:GetUnit(1)
-if UnitName then
-if type(UnitName)=="number"then
-unit=self.group:GetUnit(UnitName)
-else
-unit=UNIT:FindByName(UnitName)
-end
-end
-if not unit then
-self:T(self.lid.."WARNING: Could not get TACAN unit. Trying first unit in the group")
-unit=self:GetUnit(1)
-end
-if unit and unit:IsAlive()then
-local UnitID=unit:GetID()
-local Type=BEACON.Type.TACAN
-local System=BEACON.System.TACAN
-if self:IsFlightgroup()then
-System=BEACON.System.TACAN_TANKER_Y
-end
-local Frequency=UTILS.TACANToFrequency(Channel,Band)
-unit:CommandActivateBeacon(Type,System,Frequency,UnitID,Channel,Band,true,Morse,true)
-self.tacan.Channel=Channel
-self.tacan.Morse=Morse
-self.tacan.Band=Band
-self.tacan.BeaconName=unit:GetName()
-self.tacan.BeaconUnit=unit
-self.tacan.On=true
-self:T(self.lid..string.format("Switching TACAN to Channel %d%s Morse %s on unit %s",self.tacan.Channel,self.tacan.Band,tostring(self.tacan.Morse),self.tacan.BeaconName))
-else
-self:T(self.lid.."ERROR: Cound not set TACAN! Unit is not alive")
-end
-else
-self:T(self.lid.."ERROR: Cound not set TACAN! Group is not alive and not in utero any more")
-end
-return self
-end
-function OPSGROUP:TurnOffTACAN()
-if self.tacan.BeaconUnit and self.tacan.BeaconUnit:IsAlive()then
-self.tacan.BeaconUnit:CommandDeactivateBeacon()
-end
-self:T(self.lid..string.format("Switching TACAN OFF"))
-self.tacan.On=false
-end
-function OPSGROUP:GetTACAN()
-return self.tacan.Channel,self.tacan.Morse,self.tacan.Band,self.tacan.On,self.tacan.BeaconName
-end
-function OPSGROUP:GetBeaconTACAN()
-return self.tacan
-end
-function OPSGROUP:SetDefaultICLS(Channel,Morse,UnitName,OffSwitch)
-self.iclsDefault={}
-self.iclsDefault.Channel=Channel or 1
-self.iclsDefault.Morse=Morse or"XXX"
-self.iclsDefault.BeaconName=UnitName
-if OffSwitch then
-self.iclsDefault.On=false
-else
-self.iclsDefault.On=true
-end
-return self
-end
-function OPSGROUP:_SwitchICLS(Icls)
-if Icls then
-self:SwitchICLS(Icls.Channel,Icls.Morse,Icls.BeaconName)
-else
-if self.iclsDefault.On then
-self:SwitchICLS()
-else
-self:TurnOffICLS()
-end
-end
-end
-function OPSGROUP:SwitchICLS(Channel,Morse,UnitName)
-if self:IsInUtero()then
-self:SetDefaultICLS(Channel,Morse,UnitName)
-self:T2(self.lid..string.format("Switching ICLS to Channel %d Morse %s on unit %s when GROUP is SPAWNED",self.iclsDefault.Channel,tostring(self.iclsDefault.Morse),tostring(self.iclsDefault.BeaconName)))
-elseif self:IsAlive()then
-Channel=Channel or self.iclsDefault.Channel
-Morse=Morse or self.iclsDefault.Morse
-local unit=self:GetUnit(1)
-if UnitName then
-if type(UnitName)=="number"then
-unit=self:GetUnit(UnitName)
-else
-unit=UNIT:FindByName(UnitName)
-end
-end
-if not unit then
-self:T(self.lid.."WARNING: Could not get ICLS unit. Trying first unit in the group")
-unit=self:GetUnit(1)
-end
-if unit and unit:IsAlive()then
-local UnitID=unit:GetID()
-unit:CommandActivateICLS(Channel,UnitID,Morse)
-self.icls.Channel=Channel
-self.icls.Morse=Morse
-self.icls.Band=nil
-self.icls.BeaconName=unit:GetName()
-self.icls.BeaconUnit=unit
-self.icls.On=true
-self:T(self.lid..string.format("Switching ICLS to Channel %d Morse %s on unit %s",self.icls.Channel,tostring(self.icls.Morse),self.icls.BeaconName))
-else
-self:T(self.lid.."ERROR: Cound not set ICLS! Unit is not alive.")
-end
-end
-return self
-end
-function OPSGROUP:TurnOffICLS()
-if self.icls.BeaconUnit and self.icls.BeaconUnit:IsAlive()then
-self.icls.BeaconUnit:CommandDeactivateICLS()
-end
-self:T(self.lid..string.format("Switching ICLS OFF"))
-self.icls.On=false
-end
-function OPSGROUP:SetDefaultRadio(Frequency,Modulation,OffSwitch)
-self.radioDefault={}
-self.radioDefault.Freq=Frequency or 251
-self.radioDefault.Modu=Modulation or radio.modulation.AM
-if OffSwitch then
-self.radioDefault.On=false
-else
-self.radioDefault.On=true
-end
-return self
-end
-function OPSGROUP:GetRadio()
-return self.radio.Freq,self.radio.Modu,self.radio.On
-end
-function OPSGROUP:SwitchRadio(Frequency,Modulation)
-if self:IsInUtero()then
-self:SetDefaultRadio(Frequency,Modulation)
-self:T2(self.lid..string.format("Switching radio to frequency %.3f MHz %s when GROUP is SPAWNED",self.radioDefault.Freq,UTILS.GetModulationName(self.radioDefault.Modu)))
-elseif self:IsAlive()then
-Frequency=Frequency or self.radioDefault.Freq
-Modulation=Modulation or self.radioDefault.Modu
-if self:IsFlightgroup()and not self.radio.On then
-self.group:SetOption(AI.Option.Air.id.SILENCE,false)
-end
-self.group:CommandSetFrequency(Frequency,Modulation)
-self.radio.Freq=Frequency
-self.radio.Modu=Modulation
-self.radio.On=true
-self:T(self.lid..string.format("Switching radio to frequency %.3f MHz %s",self.radio.Freq,UTILS.GetModulationName(self.radio.Modu)))
-else
-self:T(self.lid.."ERROR: Cound not set Radio! Group is not alive or not in utero any more")
-end
-return self
-end
-function OPSGROUP:TurnOffRadio()
-if self:IsAlive()then
-if self:IsFlightgroup()then
-self.group:SetOption(AI.Option.Air.id.SILENCE,true)
-self.radio.On=false
-self:T(self.lid..string.format("Switching radio OFF"))
-else
-self:T(self.lid.."ERROR: Radio can only be turned off for aircraft!")
-end
-end
-return self
-end
-function OPSGROUP:SetDefaultFormation(Formation)
-self.optionDefault.Formation=Formation
-return self
-end
-function OPSGROUP:SwitchFormation(Formation)
-if self:IsAlive()then
-Formation=Formation or self.optionDefault.Formation
-if self:IsFlightgroup()then
-self.group:SetOption(AI.Option.Air.id.FORMATION,Formation)
-elseif self.isArmygroup then
-else
-self:T(self.lid.."ERROR: Formation can only be set for aircraft or ground units!")
-return self
-end
-self.option.Formation=Formation
-self:T(self.lid..string.format("Switching formation to %s",tostring(self.option.Formation)))
-end
-return self
-end
-function OPSGROUP:SetDefaultCallsign(CallsignName,CallsignNumber)
-self:T(self.lid..string.format("Setting Default callsing %s-%s",tostring(CallsignName),tostring(CallsignNumber)))
-self.callsignDefault={}
-self.callsignDefault.NumberSquad=CallsignName
-self.callsignDefault.NumberGroup=CallsignNumber or 1
-self.callsignDefault.NameSquad=UTILS.GetCallsignName(self.callsign.NumberSquad)
-return self
-end
-function OPSGROUP:SwitchCallsign(CallsignName,CallsignNumber)
-if self:IsInUtero()then
-self:SetDefaultCallsign(CallsignName,CallsignNumber)
-elseif self:IsAlive()then
-CallsignName=CallsignName or self.callsignDefault.NumberSquad
-CallsignNumber=CallsignNumber or self.callsignDefault.NumberGroup
-self.callsign.NumberSquad=CallsignName
-self.callsign.NumberGroup=CallsignNumber
-self:T(self.lid..string.format("Switching callsign to %d-%d",self.callsign.NumberSquad,self.callsign.NumberGroup))
-self.group:CommandSetCallsign(self.callsign.NumberSquad,self.callsign.NumberGroup)
-self.callsignName=UTILS.GetCallsignName(self.callsign.NumberSquad).."-"..self.callsign.NumberGroup
-self.callsign.NameSquad=UTILS.GetCallsignName(self.callsign.NumberSquad)
-for _,_element in pairs(self.elements)do
-local element=_element
-if element.status~=OPSGROUP.ElementStatus.DEAD then
-element.callsign=element.unit:GetCallsign()
-end
-end
-else
-self:T(self.lid.."ERROR: Group is not alive and not in utero! Cannot switch callsign")
-end
-return self
-end
-function OPSGROUP:GetCallsignName(ShortCallsign,Keepnumber,CallsignTranslations)
-local element=self:GetElementAlive()
-if element then
-self:T2(self.lid..string.format("Callsign %s",tostring(element.callsign)))
-local name=element.callsign or"Ghostrider11"
-name=name:gsub("-","")
-if self.group:IsPlayer()or CallsignTranslations then
-name=self.group:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations)
-end
-return name
-end
-return"Ghostrider11"
-end
-function OPSGROUP:_UpdatePosition()
-if self:IsExist()then
-self.positionLast=self.position or self:GetVec3()
-self.headingLast=self.heading or self:GetHeading()
-self.orientXLast=self.orientX or self:GetOrientationX()
-self.velocityLast=self.velocity or self.group:GetVelocityMPS()
-self.position=self:GetVec3()
-self.heading=self:GetHeading()
-self.orientX=self:GetOrientationX()
-self.velocity=self:GetVelocity()
-for _,_element in pairs(self.elements)do
-local element=_element
-element.vec3=self:GetVec3(element.name)
-end
-local Tnow=timer.getTime()
-self.dTpositionUpdate=self.TpositionUpdate and Tnow-self.TpositionUpdate or 0
-self.TpositionUpdate=Tnow
-if not self.traveldist then
-self.traveldist=0
-end
-self.travelds=UTILS.VecNorm(UTILS.VecSubstract(self.position,self.positionLast))
-self.traveldist=self.traveldist+self.travelds
-end
-return self
-end
-function OPSGROUP:_AllSameStatus(status)
-for _,_element in pairs(self.elements)do
-local element=_element
-if element.status==OPSGROUP.ElementStatus.DEAD then
-elseif element.status~=status then
-return false
-end
-end
-return true
-end
-function OPSGROUP:_AllSimilarStatus(status)
-if status==OPSGROUP.ElementStatus.DEAD then
-for _,_element in pairs(self.elements)do
-local element=_element
-if element.status~=OPSGROUP.ElementStatus.DEAD then
-return false
-end
-end
-return true
-end
-for _,_element in pairs(self.elements)do
-local element=_element
-self:T2(self.lid..string.format("Status=%s, element %s status=%s",status,element.name,element.status))
-if element.status~=OPSGROUP.ElementStatus.DEAD then
-if status==OPSGROUP.ElementStatus.INUTERO then
-if element.status~=status then
-return false
-end
-elseif status==OPSGROUP.ElementStatus.SPAWNED then
-if element.status~=status and
-element.status==OPSGROUP.ElementStatus.INUTERO then
-return false
-end
-elseif status==OPSGROUP.ElementStatus.PARKING then
-if element.status~=status or
-(element.status==OPSGROUP.ElementStatus.INUTERO or
-element.status==OPSGROUP.ElementStatus.SPAWNED)then
-return false
-end
-elseif status==OPSGROUP.ElementStatus.ENGINEON then
-if element.status~=status and
-(element.status==OPSGROUP.ElementStatus.INUTERO or
-element.status==OPSGROUP.ElementStatus.SPAWNED or
-element.status==OPSGROUP.ElementStatus.PARKING)then
-return false
-end
-elseif status==OPSGROUP.ElementStatus.TAXIING then
-if element.status~=status and
-(element.status==OPSGROUP.ElementStatus.INUTERO or
-element.status==OPSGROUP.ElementStatus.SPAWNED or
-element.status==OPSGROUP.ElementStatus.PARKING or
-element.status==OPSGROUP.ElementStatus.ENGINEON)then
-return false
-end
-elseif status==OPSGROUP.ElementStatus.TAKEOFF then
-if element.status~=status and
-(element.status==OPSGROUP.ElementStatus.INUTERO or
-element.status==OPSGROUP.ElementStatus.SPAWNED or
-element.status==OPSGROUP.ElementStatus.PARKING or
-element.status==OPSGROUP.ElementStatus.ENGINEON or
-element.status==OPSGROUP.ElementStatus.TAXIING)then
-return false
-end
-elseif status==OPSGROUP.ElementStatus.AIRBORNE then
-if element.status~=status and
-(element.status==OPSGROUP.ElementStatus.INUTERO or
-element.status==OPSGROUP.ElementStatus.SPAWNED or
-element.status==OPSGROUP.ElementStatus.PARKING or
-element.status==OPSGROUP.ElementStatus.ENGINEON or
-element.status==OPSGROUP.ElementStatus.TAXIING or
-element.status==OPSGROUP.ElementStatus.TAKEOFF)then
-return false
-end
-elseif status==OPSGROUP.ElementStatus.LANDED then
-if element.status~=status and
-(element.status==OPSGROUP.ElementStatus.AIRBORNE or
-element.status==OPSGROUP.ElementStatus.LANDING)then
-return false
-end
-elseif status==OPSGROUP.ElementStatus.ARRIVED then
-if element.status~=status and
-(element.status==OPSGROUP.ElementStatus.AIRBORNE or
-element.status==OPSGROUP.ElementStatus.LANDING or
-element.status==OPSGROUP.ElementStatus.LANDED)then
-return false
-end
-end
-else
-end
-end
-self:T2(self.lid..string.format("All %d elements have similar status %s ==> returning TRUE",#self.elements,status))
-return true
-end
-function OPSGROUP:_UpdateStatus(element,newstatus,airbase)
-local oldstatus=element.status
-element.status=newstatus
-self:T3(self.lid..string.format("UpdateStatus element=%s: %s --> %s",element.name,oldstatus,newstatus))
-for _,_element in pairs(self.elements)do
-local Element=_element
-self:T3(self.lid..string.format("Element %s: %s",Element.name,Element.status))
-end
-if newstatus==OPSGROUP.ElementStatus.INUTERO then
-if self:_AllSimilarStatus(newstatus)then
-self:InUtero()
-end
-elseif newstatus==OPSGROUP.ElementStatus.SPAWNED then
-if self:_AllSimilarStatus(newstatus)then
-self:Spawned()
-end
-elseif newstatus==OPSGROUP.ElementStatus.PARKING then
-if self:_AllSimilarStatus(newstatus)then
-self:Parking()
-end
-elseif newstatus==OPSGROUP.ElementStatus.ENGINEON then
-elseif newstatus==OPSGROUP.ElementStatus.TAXIING then
-if self:_AllSimilarStatus(newstatus)then
-self:Taxiing()
-end
-elseif newstatus==OPSGROUP.ElementStatus.TAKEOFF then
-if self:_AllSimilarStatus(newstatus)then
-self:Takeoff(airbase)
-end
-elseif newstatus==OPSGROUP.ElementStatus.AIRBORNE then
-if self:_AllSimilarStatus(newstatus)then
-self:Airborne()
-end
-elseif newstatus==OPSGROUP.ElementStatus.LANDED then
-if self:_AllSimilarStatus(newstatus)then
-if self:IsLandingAt()then
-self:LandedAt()
-else
-self:Landed(airbase)
-end
-end
-elseif newstatus==OPSGROUP.ElementStatus.ARRIVED then
-if self:_AllSimilarStatus(newstatus)then
-if self:IsLanded()then
-self:Arrived()
-elseif self:IsAirborne()then
-self:Landed()
-self:Arrived()
-end
-end
-elseif newstatus==OPSGROUP.ElementStatus.DEAD then
-if self:_AllSimilarStatus(newstatus)then
-self:Dead()
-end
-end
-end
-function OPSGROUP:_SetElementStatusAll(status)
-for _,_element in pairs(self.elements)do
-local element=_element
-if element.status~=OPSGROUP.ElementStatus.DEAD then
-element.status=status
-end
-end
-end
-function OPSGROUP:GetElementByName(unitname)
-if unitname and type(unitname)=="string"then
-for _,_element in pairs(self.elements)do
-local element=_element
-if element.name==unitname then
-return element
-end
-end
-end
-return nil
-end
-function OPSGROUP:GetElementZoneBoundingBox(UnitName)
-local element=self:GetElementByName(UnitName)
-if element and element.status~=OPSGROUP.ElementStatus.DEAD then
-element.zoneBoundingbox=element.zoneBoundingbox or ZONE_POLYGON_BASE:New(element.name.." Zone Bounding Box",{})
-local l=element.length
-local w=element.width
-local X=self:GetOrientationX(element.name)
-local heading=math.deg(math.atan2(X.z,X.x))
-self:T(self.lid..string.format("Element %s bouding box: l=%d w=%d heading=%d",element.name,l,w,heading))
-local b={}
-b[1]={x=l/2,y=-w/2}
-b[2]={x=l/2,y=w/2}
-b[3]={x=-l/2,y=w/2}
-b[4]={x=-l/2,y=-w/2}
-for i,p in pairs(b)do
-b[i]=UTILS.Vec2Rotate2D(p,heading)
-end
-local vec2=self:GetVec2(element.name)
-local d=UTILS.Vec2Norm(vec2)
-local h=UTILS.Vec2Hdg(vec2)
-for i,p in pairs(b)do
-b[i]=UTILS.Vec2Translate(p,d,h)
-end
-element.zoneBoundingbox:UpdateFromVec2(b)
-return element.zoneBoundingbox
-end
-return nil
-end
-function OPSGROUP:GetElementZoneLoad(UnitName)
-local element=self:GetElementByName(UnitName)
-if element and element.status~=OPSGROUP.ElementStatus.DEAD then
-element.zoneLoad=element.zoneLoad or ZONE_POLYGON_BASE:New(element.name.." Zone Load",{})
-self:_GetElementZoneLoader(element,element.zoneLoad,self.carrierLoader)
-return element.zoneLoad
-end
-return nil
-end
-function OPSGROUP:GetElementZoneUnload(UnitName)
-local element=self:GetElementByName(UnitName)
-if element and element.status~=OPSGROUP.ElementStatus.DEAD then
-element.zoneUnload=element.zoneUnload or ZONE_POLYGON_BASE:New(element.name.." Zone Unload",{})
-self:_GetElementZoneLoader(element,element.zoneUnload,self.carrierUnloader)
-return element.zoneUnload
-end
-return nil
-end
-function OPSGROUP:_GetElementZoneLoader(Element,Zone,Loader)
-if Element.status~=OPSGROUP.ElementStatus.DEAD then
-local l=Element.length
-local w=Element.width
-local X=self:GetOrientationX(Element.name)
-local heading=math.deg(math.atan2(X.z,X.x))
-local b={}
-if Loader.type:lower()=="front"then
-table.insert(b,{x=l/2,y=-Loader.width/2})
-table.insert(b,{x=l/2+Loader.length,y=-Loader.width/2})
-table.insert(b,{x=l/2+Loader.length,y=Loader.width/2})
-table.insert(b,{x=l/2,y=Loader.width/2})
-elseif Loader.type:lower()=="back"then
-table.insert(b,{x=-l/2,y=-Loader.width/2})
-table.insert(b,{x=-l/2-Loader.length,y=-Loader.width/2})
-table.insert(b,{x=-l/2-Loader.length,y=Loader.width/2})
-table.insert(b,{x=-l/2,y=Loader.width/2})
-elseif Loader.type:lower()=="left"then
-table.insert(b,{x=Loader.length/2,y=-w/2})
-table.insert(b,{x=Loader.length/2,y=-w/2-Loader.width})
-table.insert(b,{x=-Loader.length/2,y=-w/2-Loader.width})
-table.insert(b,{x=-Loader.length/2,y=-w/2})
-elseif Loader.type:lower()=="right"then
-table.insert(b,{x=Loader.length/2,y=w/2})
-table.insert(b,{x=Loader.length/2,y=w/2+Loader.width})
-table.insert(b,{x=-Loader.length/2,y=w/2+Loader.width})
-table.insert(b,{x=-Loader.length/2,y=w/2})
-else
-b[1]={x=l/2,y=-w/2}
-b[2]={x=l/2,y=w/2}
-b[3]={x=-l/2,y=w/2}
-b[4]={x=-l/2,y=-w/2}
-table.insert(b,{x=b[1].x+Loader.length,y=b[1].y-Loader.width})
-table.insert(b,{x=b[2].x+Loader.length,y=b[2].y+Loader.width})
-table.insert(b,{x=b[3].x-Loader.length,y=b[3].y+Loader.width})
-table.insert(b,{x=b[4].x-Loader.length,y=b[4].y-Loader.width})
-end
-for i,p in pairs(b)do
-b[i]=UTILS.Vec2Rotate2D(p,heading)
-end
-local vec2=self:GetVec2(Element.name)
-local d=UTILS.Vec2Norm(vec2)
-local h=UTILS.Vec2Hdg(vec2)
-for i,p in pairs(b)do
-b[i]=UTILS.Vec2Translate(p,d,h)
-end
-Zone:UpdateFromVec2(b)
-return Zone
-end
-return nil
-end
-function OPSGROUP:GetElementAlive()
-for _,_element in pairs(self.elements)do
-local element=_element
-if element.status~=OPSGROUP.ElementStatus.DEAD then
-if element.unit and element.unit:IsAlive()then
-return element
-end
-end
-end
-return nil
-end
-function OPSGROUP:GetNelements(status)
-local n=0
-for _,_element in pairs(self.elements)do
-local element=_element
-if element.status~=OPSGROUP.ElementStatus.DEAD then
-if element.unit and element.unit:IsAlive()then
-if status==nil or element.status==status then
-n=n+1
-end
-end
-end
-end
-return n
-end
-function OPSGROUP:GetAmmoElement(element)
-return self:GetAmmoUnit(element.unit)
-end
-function OPSGROUP:GetAmmoTot()
-local units=self.group:GetUnits()
-local Ammo={}
-Ammo.Total=0
-Ammo.Guns=0
-Ammo.Rockets=0
-Ammo.Bombs=0
-Ammo.Torpedos=0
-Ammo.Missiles=0
-Ammo.MissilesAA=0
-Ammo.MissilesAG=0
-Ammo.MissilesAS=0
-Ammo.MissilesCR=0
-Ammo.MissilesSA=0
-for _,_unit in pairs(units or{})do
-local unit=_unit
-if unit and unit:IsExist()then
-local ammo=self:GetAmmoUnit(unit)
-Ammo.Total=Ammo.Total+ammo.Total
-Ammo.Guns=Ammo.Guns+ammo.Guns
-Ammo.Rockets=Ammo.Rockets+ammo.Rockets
-Ammo.Bombs=Ammo.Bombs+ammo.Bombs
-Ammo.Torpedos=Ammo.Torpedos+ammo.Torpedos
-Ammo.Missiles=Ammo.Missiles+ammo.Missiles
-Ammo.MissilesAA=Ammo.MissilesAA+ammo.MissilesAA
-Ammo.MissilesAG=Ammo.MissilesAG+ammo.MissilesAG
-Ammo.MissilesAS=Ammo.MissilesAS+ammo.MissilesAS
-Ammo.MissilesCR=Ammo.MissilesCR+ammo.MissilesCR
-Ammo.MissilesSA=Ammo.MissilesSA+ammo.MissilesSA
-end
-end
-return Ammo
-end
-function OPSGROUP:GetAmmoUnit(unit,display)
-if display==nil then
-display=false
-end
-local nammo=0
-local nshells=0
-local nrockets=0
-local nmissiles=0
-local nmissilesAA=0
-local nmissilesAG=0
-local nmissilesAS=0
-local nmissilesSA=0
-local nmissilesBM=0
-local nmissilesCR=0
-local ntorps=0
-local nbombs=0
-unit=unit or self.group:GetUnit(1)
-if unit and unit:IsExist()then
-local text=string.format("OPSGROUP group %s - unit %s:\n",self.groupname,unit:GetName())
-local ammotable=unit:GetAmmo()
-if ammotable then
-local weapons=#ammotable
-for w=1,weapons do
-local Nammo=ammotable[w]["count"]
-local rmin=ammotable[w]["desc"]["rangeMin"]or 0
-local rmax=ammotable[w]["desc"]["rangeMaxAltMin"]or 0
-local Tammo=ammotable[w]["desc"]["typeName"]
-local _weaponString=UTILS.Split(Tammo,"%.")
-local _weaponName=_weaponString[#_weaponString]
-local Category=ammotable[w].desc.category
-local MissileCategory=nil
-if Category==Weapon.Category.MISSILE then
-MissileCategory=ammotable[w].desc.missileCategory
-end
-if Category==Weapon.Category.SHELL then
-nshells=nshells+Nammo
-text=text..string.format("- %d shells of type %s, range=%d - %d meters\n",Nammo,_weaponName,rmin,rmax)
-elseif Category==Weapon.Category.ROCKET then
-nrockets=nrockets+Nammo
-text=text..string.format("- %d rockets of type %s, \n",Nammo,_weaponName,rmin,rmax)
-elseif Category==Weapon.Category.BOMB then
-nbombs=nbombs+Nammo
-text=text..string.format("- %d bombs of type %s\n",Nammo,_weaponName)
-elseif Category==Weapon.Category.MISSILE then
-if MissileCategory==Weapon.MissileCategory.AAM then
-nmissiles=nmissiles+Nammo
-nmissilesAA=nmissilesAA+Nammo
-elseif MissileCategory==Weapon.MissileCategory.SAM then
-nmissiles=nmissiles+Nammo
-nmissilesSA=nmissilesSA+Nammo
-elseif MissileCategory==Weapon.MissileCategory.ANTI_SHIP then
-nmissiles=nmissiles+Nammo
-nmissilesAS=nmissilesAS+Nammo
-elseif MissileCategory==Weapon.MissileCategory.BM then
-nmissiles=nmissiles+Nammo
-nmissilesBM=nmissilesBM+Nammo
-elseif MissileCategory==Weapon.MissileCategory.CRUISE then
-nmissiles=nmissiles+Nammo
-nmissilesCR=nmissilesCR+Nammo
-elseif MissileCategory==Weapon.MissileCategory.OTHER then
-nmissiles=nmissiles+Nammo
-nmissilesAG=nmissilesAG+Nammo
-end
-text=text..string.format("- %d %s missiles of type %s, range=%d - %d meters\n",Nammo,self:_MissileCategoryName(MissileCategory),_weaponName,rmin,rmax)
-elseif Category==Weapon.Category.TORPEDO then
-ntorps=ntorps+Nammo
-text=text..string.format("- %d torpedos of type %s\n",Nammo,_weaponName)
-else
-text=text..string.format("- %d unknown ammo of type %s (category=%d, missile category=%s)\n",Nammo,Tammo,Category,tostring(MissileCategory))
-end
-end
-end
-if display then
-self:I(self.lid..text)
-else
-self:T3(self.lid..text)
-end
-end
-nammo=nshells+nrockets+nmissiles+nbombs+ntorps
-local ammo={}
-ammo.Total=nammo
-ammo.Guns=nshells
-ammo.Rockets=nrockets
-ammo.Bombs=nbombs
-ammo.Torpedos=ntorps
-ammo.Missiles=nmissiles
-ammo.MissilesAA=nmissilesAA
-ammo.MissilesAG=nmissilesAG
-ammo.MissilesAS=nmissilesAS
-ammo.MissilesCR=nmissilesCR
-ammo.MissilesBM=nmissilesBM
-ammo.MissilesSA=nmissilesSA
-return ammo
-end
-function OPSGROUP:_MissileCategoryName(categorynumber)
-local cat="unknown"
-if categorynumber==Weapon.MissileCategory.AAM then
-cat="air-to-air"
-elseif categorynumber==Weapon.MissileCategory.SAM then
-cat="surface-to-air"
-elseif categorynumber==Weapon.MissileCategory.BM then
-cat="ballistic"
-elseif categorynumber==Weapon.MissileCategory.ANTI_SHIP then
-cat="anti-ship"
-elseif categorynumber==Weapon.MissileCategory.CRUISE then
-cat="cruise"
-elseif categorynumber==Weapon.MissileCategory.OTHER then
-cat="other"
-end
-return cat
-end
-function OPSGROUP:_PassedFinalWaypoint(final,comment)
-self:T(self.lid..string.format("Passed final waypoint=%s [from %s]: comment \"%s\"",tostring(final),tostring(self.passedfinalwp),tostring(comment)))
-if final==true and not self.passedfinalwp then
-self:PassedFinalWaypoint()
-end
-self.passedfinalwp=final
-end
-function OPSGROUP:_CoordinateFromObject(Object)
-if Object then
-if Object:IsInstanceOf("COORDINATE")then
-return Object
-else
-if Object:IsInstanceOf("POSITIONABLE")or Object:IsInstanceOf("ZONE_BASE")then
-self:T(self.lid.."WARNING: Coordinate is not a COORDINATE but a POSITIONABLE or ZONE. Trying to get coordinate")
-local coord=Object:GetCoordinate()
-return coord
-else
-self:T(self.lid.."ERROR: Coordinate is neither a COORDINATE nor any POSITIONABLE or ZONE!")
-end
-end
-else
-self:T(self.lid.."ERROR: Object passed is nil!")
-end
-return nil
-end
-function OPSGROUP:_IsElement(unitname)
-for _,_element in pairs(self.elements)do
-local element=_element
-if element.name==unitname then
-return true
-end
-end
-return false
-end
-function OPSGROUP:CountElements(States)
-if States then
-if type(States)=="string"then
-States={States}
-end
-else
-States=OPSGROUP.ElementStatus
-end
-local IncludeDeads=true
-local N=0
-for _,_element in pairs(self.elements)do
-local element=_element
-if element and(IncludeDeads or element.status~=OPSGROUP.ElementStatus.DEAD)then
-for _,state in pairs(States)do
-if element.status==state then
-N=N+1
-break
-end
-end
-end
-end
-return N
-end
-function OPSGROUP:_AddElementByName(unitname)
-local unit=UNIT:FindByName(unitname)
-if unit then
-local unittemplate=unit:GetTemplate()
-local element=self:GetElementByName(unitname)
-if element then
-else
-element={}
-element.status=OPSGROUP.ElementStatus.INUTERO
-table.insert(self.elements,element)
-end
-element.name=unitname
-element.unit=unit
-element.DCSunit=Unit.getByName(unitname)
-element.gid=element.DCSunit:getNumber()
-element.uid=element.DCSunit:getID()
-element.controller=element.DCSunit:getController()
-element.Nhit=0
-element.opsgroup=self
-element.skill=unittemplate.skill or"Unknown"
-if element.skill=="Client"or element.skill=="Player"then
-element.ai=false
-element.client=CLIENT:FindByName(unitname)
-element.playerName=element.DCSunit:getPlayerName()
-else
-element.ai=true
-end
-element.descriptors=unit:GetDesc()
-element.category=unit:GetUnitCategory()
-element.categoryname=unit:GetCategoryName()
-element.typename=unit:GetTypeName()
-element.ammo0=self:GetAmmoUnit(unit,false)
-element.life=unit:GetLife()
-element.life0=math.max(unit:GetLife0(),element.life)
-element.size,element.length,element.height,element.width=unit:GetObjectSize()
-element.weightEmpty=element.descriptors.massEmpty or 666
-if self.isArmygroup then
-element.weightMaxTotal=element.weightEmpty+10*95
-elseif self.isNavygroup then
-element.weightMaxTotal=element.weightEmpty+10*1000
-else
-element.weightMaxTotal=element.descriptors.massMax or element.weightEmpty+8*95
-end
-unit:SetCargoBayWeightLimit()
-element.weightMaxCargo=unit.__.CargoBayWeightLimit
-if element.cargoBay then
-element.weightCargo=self:GetWeightCargo(element.name,false)
-else
-element.cargoBay={}
-element.weightCargo=0
-end
-element.weight=element.weightEmpty+element.weightCargo
-if self.isFlightgroup then
-element.callsign=element.unit:GetCallsign()
-element.modex=unittemplate.onboard_num
-element.payload=unittemplate.payload
-element.pylons=unittemplate.payload and unittemplate.payload.pylons or nil
-element.fuelmass0=unittemplate.payload and unittemplate.payload.fuel or 0
-element.fuelmass=element.fuelmass0
-element.fuelrel=element.unit:GetFuel()
-else
-element.callsign="Peter-1-1"
-element.modex="000"
-element.payload={}
-element.pylons={}
-element.fuelmass0=99999
-element.fuelmass=99999
-element.fuelrel=1
-end
-local text=string.format("Adding element %s: status=%s, skill=%s, life=%.1f/%.1f category=%s (%d), type=%s, size=%.1f (L=%.1f H=%.1f W=%.1f), weight=%.1f/%.1f (cargo=%.1f/%.1f)",
-element.name,element.status,element.skill,element.life,element.life0,element.categoryname,element.category,element.typename,
-element.size,element.length,element.height,element.width,element.weight,element.weightMaxTotal,element.weightCargo,element.weightMaxCargo)
-self:T(self.lid..text)
-if unit:IsAlive()and element.status~=OPSGROUP.ElementStatus.SPAWNED then
-self:__ElementSpawned(0.05,element)
-end
-return element
-end
-return nil
-end
-function OPSGROUP:_SetTemplate(Template)
-self.template=Template or UTILS.DeepCopy(_DATABASE:GetGroupTemplate(self.groupname))
-self:T3(self.lid.."Setting group template")
-return self
-end
-function OPSGROUP:_GetTemplate(Copy)
-if self.template then
-if Copy then
-local template=UTILS.DeepCopy(self.template)
-return template
-else
-return self.template
-end
-else
-self:T(self.lid..string.format("ERROR: No template was set yet!"))
-end
-return nil
-end
-function OPSGROUP:ClearWaypoints(IndexMin,IndexMax)
-IndexMin=IndexMin or 1
-IndexMax=IndexMax or#self.waypoints
-for i=IndexMax,IndexMin,-1 do
-table.remove(self.waypoints,i)
-end
-end
-function OPSGROUP:_GetDetectedTarget()
-local targetgroup=nil
-local targetdist=math.huge
-for _,_group in pairs(self.detectedgroups:GetSet())do
-local group=_group
-if group and group:IsAlive()then
-local targetVec3=group:GetVec3()
-local distance=UTILS.VecDist3D(self.position,targetVec3)
-if distance<=self.engagedetectedRmax and distance=1 then
-local text=string.format("Added cargo groups:")
-local Weight=0
-for _,_cargo in pairs(self:GetCargos())do
-local cargo=_cargo
-local weight=cargo.opsgroup:GetWeightTotal()
-Weight=Weight+weight
-text=text..string.format("\n- %s [%s] weight=%.1f kg",cargo.opsgroup:GetName(),cargo.opsgroup:GetState(),weight)
-end
-text=text..string.format("\nTOTAL: Ncargo=%d, Weight=%.1f kg",self.Ncargo,Weight)
-self:I(self.lid..text)
-end
-return self
-end
-function OPSTRANSPORT:AddCargoStorage(StorageFrom,StorageTo,CargoType,CargoAmount,CargoWeight,TransportZoneCombo)
-TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-local cargo=self:_CreateCargoStorage(StorageFrom,StorageTo,CargoType,CargoAmount,CargoWeight,TransportZoneCombo)
-if cargo then
-self.Ncargo=self.Ncargo+1
-table.insert(TransportZoneCombo.Cargos,cargo)
-end
-end
-function OPSTRANSPORT:SetPickupZone(PickupZone,TransportZoneCombo)
-TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-TransportZoneCombo.PickupZone=PickupZone
-if PickupZone and PickupZone:IsInstanceOf("ZONE_AIRBASE")then
-TransportZoneCombo.PickupAirbase=PickupZone._.ZoneAirbase
-end
-return self
-end
-function OPSTRANSPORT:GetPickupZone(TransportZoneCombo)
-TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-return TransportZoneCombo.PickupZone
-end
-function OPSTRANSPORT:SetDeployZone(DeployZone,TransportZoneCombo)
-TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-TransportZoneCombo.DeployZone=DeployZone
-if DeployZone and DeployZone:IsInstanceOf("ZONE_AIRBASE")then
-TransportZoneCombo.DeployAirbase=DeployZone._.ZoneAirbase
-end
-return self
-end
-function OPSTRANSPORT:GetDeployZone(TransportZoneCombo)
-TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-return TransportZoneCombo.DeployZone
-end
-function OPSTRANSPORT:SetEmbarkZone(EmbarkZone,TransportZoneCombo)
-TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-TransportZoneCombo.EmbarkZone=EmbarkZone or TransportZoneCombo.PickupZone
-return self
-end
-function OPSTRANSPORT:GetEmbarkZone(TransportZoneCombo)
-TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-return TransportZoneCombo.EmbarkZone
-end
-function OPSTRANSPORT:SetDisembarkZone(DisembarkZone,TransportZoneCombo)
-TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-TransportZoneCombo.DisembarkZone=DisembarkZone
-return self
-end
-function OPSTRANSPORT:GetDisembarkZone(TransportZoneCombo)
-TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-return TransportZoneCombo.DisembarkZone
-end
-function OPSTRANSPORT:SetDisembarkActivation(Active,TransportZoneCombo)
-TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-if Active==true or Active==nil then
-TransportZoneCombo.disembarkActivation=true
-else
-TransportZoneCombo.disembarkActivation=false
-end
-return self
-end
-function OPSTRANSPORT:GetDisembarkActivation(TransportZoneCombo)
-TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-return TransportZoneCombo.disembarkActivation
-end
-function OPSTRANSPORT:SetDisembarkCarriers(Carriers,TransportZoneCombo)
-self:T(self.lid.."Setting transfer carriers!")
-TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-TransportZoneCombo.disembarkToCarriers=true
-self:_AddDisembarkCarriers(Carriers,TransportZoneCombo.DisembarkCarriers)
-return self
-end
-function OPSTRANSPORT:_AddDisembarkCarriers(Carriers,Table)
-if Carriers:IsInstanceOf("GROUP")or Carriers:IsInstanceOf("OPSGROUP")then
-local carrier=self:_GetOpsGroupFromObject(Carriers)
-if carrier then
-table.insert(Table,carrier)
-end
-elseif Carriers:IsInstanceOf("SET_GROUP")or Carriers:IsInstanceOf("SET_OPSGROUP")then
-for _,object in pairs(Carriers:GetSet())do
-local carrier=self:_GetOpsGroupFromObject(object)
-if carrier then
-table.insert(Table,carrier)
-end
-end
-else
-self:E(self.lid.."ERROR: Carriers must be a GROUP, OPSGROUP, SET_GROUP or SET_OPSGROUP object!")
-end
-end
-function OPSTRANSPORT:GetDisembarkCarriers(TransportZoneCombo)
-TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-return TransportZoneCombo.DisembarkCarriers
-end
-function OPSTRANSPORT:SetDisembarkInUtero(InUtero,TransportZoneCombo)
-TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-if InUtero==true or InUtero==nil then
-TransportZoneCombo.disembarkInUtero=true
-else
-TransportZoneCombo.disembarkInUtero=false
-end
-return self
-end
-function OPSTRANSPORT:GetDisembarkInUtero(TransportZoneCombo)
-TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-return TransportZoneCombo.disembarkInUtero
-end
-function OPSTRANSPORT:SetFormationPickup(Formation,TransportZoneCombo)
-TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-TransportZoneCombo.PickupFormation=Formation
-return self
-end
-function OPSTRANSPORT:_GetFormationDefault(OpsGroup)
-if OpsGroup.isArmygroup then
-return self.formationArmy
-elseif OpsGroup.isFlightgroup then
-if OpsGroup.isHelo then
-return self.formationHelo
-else
-return self.formationPlane
-end
-else
-return ENUMS.Formation.Vehicle.OffRoad
-end
-return nil
-end
-function OPSTRANSPORT:_GetFormationPickup(TransportZoneCombo,OpsGroup)
-TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-local formation=TransportZoneCombo.PickupFormation or self:_GetFormationDefault(OpsGroup)
-return formation
-end
-function OPSTRANSPORT:SetFormationTransport(Formation,TransportZoneCombo)
-TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-TransportZoneCombo.TransportFormation=Formation
-return self
-end
-function OPSTRANSPORT:_GetFormationTransport(TransportZoneCombo,OpsGroup)
-TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-local formation=TransportZoneCombo.TransportFormation or self:_GetFormationDefault(OpsGroup)
-return formation
-end
-function OPSTRANSPORT:SetRequiredCargos(Cargos,TransportZoneCombo)
-self:T(self.lid.."Setting required cargos!")
-TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-TransportZoneCombo.RequiredCargos=TransportZoneCombo.RequiredCargos or{}
-if Cargos:IsInstanceOf("GROUP")or Cargos:IsInstanceOf("OPSGROUP")then
-local cargo=self:_GetOpsGroupFromObject(Cargos)
-if cargo then
-table.insert(TransportZoneCombo.RequiredCargos,cargo)
-end
-elseif Cargos:IsInstanceOf("SET_GROUP")or Cargos:IsInstanceOf("SET_OPSGROUP")then
-for _,object in pairs(Cargos:GetSet())do
-local cargo=self:_GetOpsGroupFromObject(object)
-if cargo then
-table.insert(TransportZoneCombo.RequiredCargos,cargo)
-end
-end
-else
-self:E(self.lid.."ERROR: Required Cargos must be a GROUP, OPSGROUP, SET_GROUP or SET_OPSGROUP object!")
-end
-return self
-end
-function OPSTRANSPORT:GetRequiredCargos(TransportZoneCombo)
-TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-return TransportZoneCombo.RequiredCargos
-end
-function OPSTRANSPORT:SetRequiredCarriers(NcarriersMin,NcarriersMax)
-self.nCarriersMin=NcarriersMin or 1
-self.nCarriersMax=NcarriersMax or self.nCarriersMin
-if self.nCarriersMax0 then
-self:ScheduleOnce(Delay,OPSTRANSPORT._DelCarrier,self,CarrierGroup)
-else
-if self:IsCarrier(CarrierGroup)then
-for i=#self.carriers,1,-1 do
-local carrier=self.carriers[i]
-if carrier.groupname==CarrierGroup.groupname then
-self:T(self.lid..string.format("Removing carrier %s",CarrierGroup.groupname))
-table.remove(self.carriers,i)
-end
-end
-end
-end
-return self
-end
-function OPSTRANSPORT:_GetCarrierNames()
-local names={}
-for _,_carrier in pairs(self.carriers)do
-local carrier=_carrier
-if carrier:IsAlive()~=nil then
-table.insert(names,carrier.groupname)
-end
-end
-return names
-end
-function OPSTRANSPORT:GetCargoOpsGroups(Delivered,Carrier,TransportZoneCombo)
-local cargos=self:GetCargos(TransportZoneCombo,Carrier,Delivered)
-local opsgroups={}
-for _,_cargo in pairs(cargos)do
-local cargo=_cargo
-if cargo.type=="OPSGROUP"then
-if cargo.opsgroup and not(cargo.opsgroup:IsDead()or cargo.opsgroup:IsStopped())then
-table.insert(opsgroups,cargo.opsgroup)
-end
-end
-end
-return opsgroups
-end
-function OPSTRANSPORT:GetCargoStorages(Delivered,Carrier,TransportZoneCombo)
-local cargos=self:GetCargos(TransportZoneCombo,Carrier,Delivered)
-local opsgroups={}
-for _,_cargo in pairs(cargos)do
-local cargo=_cargo
-if cargo.type=="STORAGE"then
-table.insert(opsgroups,cargo.storage)
-end
-end
-return opsgroups
-end
-function OPSTRANSPORT:GetCarriers()
-return self.carriers
-end
-function OPSTRANSPORT:GetCargos(TransportZoneCombo,Carrier,Delivered)
-local tczs=self.tzCombos
-if TransportZoneCombo then
-tczs={TransportZoneCombo}
-end
-local cargos={}
-for _,_tcz in pairs(tczs)do
-local tcz=_tcz
-for _,_cargo in pairs(tcz.Cargos)do
-local cargo=_cargo
-if Delivered==nil or cargo.delivered==Delivered then
-if Carrier==nil or Carrier:CanCargo(cargo)then
-table.insert(cargos,cargo)
-end
-end
-end
-end
-return cargos
-end
-function OPSTRANSPORT:GetCargoTotalWeight(Cargo,IncludeReserved)
-local weight=0
-if Cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then
-weight=Cargo.opsgroup:GetWeightTotal(nil,IncludeReserved)
-else
-if type(Cargo.storage.cargoType)=="number"then
-if IncludeReserved then
-return Cargo.storage.cargoAmount+Cargo.storage.cargoReserved
-else
-return Cargo.storage.cargoAmount
-end
-else
-if IncludeReserved then
-return Cargo.storage.cargoAmount*100
-else
-return(Cargo.storage.cargoAmount+Cargo.storage.cargoReserved)*100
-end
-end
-end
-return weight
-end
-function OPSTRANSPORT:SetTime(ClockStart,ClockStop)
-local Tnow=timer.getAbsTime()
-local Tstart=Tnow+5
-if ClockStart and type(ClockStart)=="number"then
-Tstart=Tnow+ClockStart
-elseif ClockStart and type(ClockStart)=="string"then
-Tstart=UTILS.ClockToSeconds(ClockStart)
-end
-local Tstop=nil
-if ClockStop and type(ClockStop)=="number"then
-Tstop=Tnow+ClockStop
-elseif ClockStop and type(ClockStop)=="string"then
-Tstop=UTILS.ClockToSeconds(ClockStop)
-end
-self.Tstart=Tstart
-self.Tstop=Tstop
-if Tstop then
-self.duration=self.Tstop-self.Tstart
-end
-return self
-end
-function OPSTRANSPORT:SetPriority(Prio,Importance,Urgent)
-self.prio=Prio or 50
-self.urgent=Urgent
-self.importance=Importance
-return self
-end
-function OPSTRANSPORT:SetVerbosity(Verbosity)
-self.verbose=Verbosity or 0
-return self
-end
-function OPSTRANSPORT:AddConditionStart(ConditionFunction,...)
-if ConditionFunction then
-local condition={}
-condition.func=ConditionFunction
-condition.arg={}
-if arg then
-condition.arg=arg
-end
-table.insert(self.conditionStart,condition)
-end
-return self
-end
-function OPSTRANSPORT:AddPathTransport(PathGroup,Reversed,Radius,TransportZoneCombo)
-TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-if type(PathGroup)=="string"then
-PathGroup=GROUP:FindByName(PathGroup)
-end
-local path={}
-path.category=PathGroup:GetCategory()
-path.radius=Radius or 0
-path.waypoints=PathGroup:GetTaskRoute()
-table.insert(TransportZoneCombo.TransportPaths,path)
-return self
-end
-function OPSTRANSPORT:_GetPathTransport(Category,TransportZoneCombo)
-TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-local pathsTransport=TransportZoneCombo.TransportPaths
-if pathsTransport and#pathsTransport>0 then
-local paths={}
-for _,_path in pairs(pathsTransport)do
-local path=_path
-if path.category==Category then
-table.insert(paths,path)
-end
-end
-if#paths>0 then
-local path=paths[math.random(#paths)]
-return path
-end
-end
-return nil
-end
-function OPSTRANSPORT:SetCarrierTransportStatus(CarrierGroup,Status)
-local oldstatus=self:GetCarrierTransportStatus(CarrierGroup)
-self:T(self.lid..string.format("New carrier transport status for %s: %s --> %s",CarrierGroup:GetName(),oldstatus,Status))
-self.carrierTransportStatus[CarrierGroup.groupname]=Status
-return self
-end
-function OPSTRANSPORT:GetCarrierTransportStatus(CarrierGroup)
-local status=self.carrierTransportStatus[CarrierGroup.groupname]or"unknown"
-return status
-end
-function OPSTRANSPORT:GetUID()
-return self.uid
-end
-function OPSTRANSPORT:GetNcargoDelivered()
-return self.Ndelivered
-end
-function OPSTRANSPORT:GetNcargoTotal()
-return self.Ncargo
-end
-function OPSTRANSPORT:GetNcarrier()
-return self.Ncarrier
-end
-function OPSTRANSPORT:AddAsset(Asset,TransportZoneCombo)
-self:T(self.lid..string.format("Adding asset carrier \"%s\" to transport",tostring(Asset.spawngroupname)))
-self.assets=self.assets or{}
-table.insert(self.assets,Asset)
-return self
-end
-function OPSTRANSPORT:DelAsset(Asset)
-for i,_asset in pairs(self.assets or{})do
-local asset=_asset
-if asset.uid==Asset.uid then
-self:T(self.lid..string.format("Removing asset \"%s\" from transport",tostring(Asset.spawngroupname)))
-table.remove(self.assets,i)
-return self
-end
-end
-return self
-end
-function OPSTRANSPORT:AddAssetCargo(Asset,TransportZoneCombo)
-self:T(self.lid..string.format("Adding asset cargo \"%s\" to transport and TZC=%s",tostring(Asset.spawngroupname),TransportZoneCombo and TransportZoneCombo.uid or"N/A"))
-self.assetsCargo=self.assetsCargo or{}
-table.insert(self.assetsCargo,Asset)
-TransportZoneCombo.assetsCargo=TransportZoneCombo.assetsCargo or{}
-TransportZoneCombo.assetsCargo[Asset.spawngroupname]=Asset
-return self
-end
-function OPSTRANSPORT:GetTZCofCargo(GroupName)
-for _,_tzc in pairs(self.tzCombos)do
-local tzc=_tzc
-for _,_cargo in pairs(tzc.Cargos)do
-local cargo=_cargo
-if cargo.opsgroup:GetName()==GroupName then
-return tzc
-end
-end
-end
-return nil
-end
-function OPSTRANSPORT:AddLegion(Legion)
-self:T(self.lid..string.format("Adding legion %s",Legion.alias))
-table.insert(self.legions,Legion)
-return self
-end
-function OPSTRANSPORT:RemoveLegion(Legion)
-for i=#self.legions,1,-1 do
-local legion=self.legions[i]
-if legion.alias==Legion.alias then
-self:T(self.lid..string.format("Removing legion %s",Legion.alias))
-table.remove(self.legions,i)
-return self
-end
-end
-self:E(self.lid..string.format("ERROR: Legion %s not found and could not be removed!",Legion.alias))
-return self
-end
-function OPSTRANSPORT:IsCarrier(CarrierGroup)
-if CarrierGroup then
-for _,_carrier in pairs(self.carriers)do
-local carrier=_carrier
-if carrier.groupname==CarrierGroup.groupname then
-return true
-end
-end
-end
-return false
-end
-function OPSTRANSPORT:IsReadyToGo()
-local text=self.lid.."Is ReadyToGo? "
-local Tnow=timer.getAbsTime()
-local gotzones=false
-for _,_tz in pairs(self.tzCombos)do
-local tz=_tz
-if tz.PickupZone and tz.DeployZone then
-gotzones=true
-break
-end
-end
-if not gotzones then
-text=text.."No, pickup/deploy zone combo not yet defined!"
-return false
-end
-if self.Tstart and Tnowself.Tstop then
-text=text.."Nope, stop time already passed!"
-self:T(text)
-return false
-end
-local startme=self:EvalConditionsAll(self.conditionStart)
-if not startme then
-text=text..("No way, at least one start condition is not true!")
-self:T(text)
-return false
-end
-text=text.."Yes!"
-self:T(text)
-return true
-end
-function OPSTRANSPORT:SetLegionStatus(Legion,Status)
-local status=self:GetLegionStatus(Legion)
-self:T(self.lid..string.format("Setting LEGION %s to status %s-->%s",Legion.alias,tostring(status),tostring(Status)))
-self.statusLegion[Legion.alias]=Status
-return self
-end
-function OPSTRANSPORT:GetLegionStatus(Legion)
-local status=self.statusLegion[Legion.alias]or"unknown"
-return status
-end
-function OPSTRANSPORT:IsPlanned()
-local is=self:is(OPSTRANSPORT.Status.PLANNED)
-return is
-end
-function OPSTRANSPORT:IsQueued(Legion)
-local is=self:is(OPSTRANSPORT.Status.QUEUED)
-if Legion then
-is=self:GetLegionStatus(Legion)==OPSTRANSPORT.Status.QUEUED
-end
-return is
-end
-function OPSTRANSPORT:IsRequested(Legion)
-local is=self:is(OPSTRANSPORT.Status.REQUESTED)
-if Legion then
-is=self:GetLegionStatus(Legion)==OPSTRANSPORT.Status.REQUESTED
-end
-return is
-end
-function OPSTRANSPORT:IsScheduled()
-local is=self:is(OPSTRANSPORT.Status.SCHEDULED)
-return is
-end
-function OPSTRANSPORT:IsExecuting()
-local is=self:is(OPSTRANSPORT.Status.EXECUTING)
-return is
-end
-function OPSTRANSPORT:IsDelivered(Nmin)
-local is=self:is(OPSTRANSPORT.Status.DELIVERED)
-if is==false and Nmin and self.Ndelivered>=math.min(self.Ncargo,Nmin)then
-is=true
-end
-return is
-end
-function OPSTRANSPORT:onafterStatusUpdate(From,Event,To)
-local fsmstate=self:GetState()
-if self.verbose>=1 then
-local text=string.format("%s: Ncargo=%d/%d, Ncarrier=%d/%d, Nlegions=%d",fsmstate:upper(),self.Ncargo,self.Ndelivered,#self.carriers,self.Ncarrier,#self.legions)
-if self.verbose>=2 then
-for i,_tz in pairs(self.tzCombos)do
-local tz=_tz
-local pickupzone=tz.PickupZone and tz.PickupZone:GetName()or"Unknown"
-local deployzone=tz.DeployZone and tz.DeployZone:GetName()or"Unknown"
-text=text..string.format("\n[%d] %s --> %s: Ncarriers=%d, Ncargo=%d (%d)",i,pickupzone,deployzone,tz.Ncarriers,#tz.Cargos,tz.Ncargo)
-end
-end
-if self.verbose>=3 then
-text=text..string.format("\nCargos:")
-for _,_cargo in pairs(self:GetCargos())do
-local cargo=_cargo
-if cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then
-local carrier=cargo.opsgroup:_GetMyCarrierElement()
-local name=carrier and carrier.name or"none"
-local cstate=carrier and carrier.status or"N/A"
-text=text..string.format("\n- %s: %s [%s], weight=%d kg, carrier=%s [%s], delivered=%s [UID=%s]",
-cargo.opsgroup:GetName(),cargo.opsgroup.cargoStatus:upper(),cargo.opsgroup:GetState(),cargo.opsgroup:GetWeightTotal(),name,cstate,tostring(cargo.delivered),tostring(cargo.opsgroup.cargoTransportUID))
-else
-local storage=cargo.storage
-text=text..string.format("\n- storage type=%s: amount: total=%d loaded=%d, lost=%d, delivered=%d, delivered=%s [UID=%s]",
-storage.cargoType,storage.cargoAmount,storage.cargoLoaded,storage.cargoLost,storage.cargoDelivered,tostring(cargo.delivered),tostring(cargo.uid))
-end
-end
-text=text..string.format("\nCarriers:")
-for _,_carrier in pairs(self.carriers)do
-local carrier=_carrier
-text=text..string.format("\n- %s: %s [%s], Cargo Bay [current/reserved/total]=%d/%d/%d kg [free %d/%d/%d kg]",
-carrier:GetName(),carrier.carrierStatus:upper(),carrier:GetState(),
-carrier:GetWeightCargo(nil,false),carrier:GetWeightCargo(),carrier:GetWeightCargoMax(),
-carrier:GetFreeCargobay(nil,false),carrier:GetFreeCargobay(),carrier:GetFreeCargobayMax())
-end
-end
-self:I(self.lid..text)
-end
-self:_CheckDelivered()
-if not self:IsDelivered()then
-self:__StatusUpdate(-30)
-end
-end
-function OPSTRANSPORT:IsCargoDelivered(GroupName)
-for _,_cargo in pairs(self:GetCargos())do
-local cargo=_cargo
-if cargo.opsgroup:GetName()==GroupName then
-return cargo.delivered
-end
-end
-return nil
-end
-function OPSTRANSPORT:onafterPlanned(From,Event,To)
-self:T(self.lid..string.format("New status: %s-->%s",From,To))
-end
-function OPSTRANSPORT:onafterScheduled(From,Event,To)
-self:T(self.lid..string.format("New status: %s-->%s",From,To))
-end
-function OPSTRANSPORT:onafterExecuting(From,Event,To)
-self:T(self.lid..string.format("New status: %s-->%s",From,To))
-end
-function OPSTRANSPORT:onbeforeDelivered(From,Event,To)
-if From==OPSTRANSPORT.Status.DELIVERED then
-return false
-end
-return true
-end
-function OPSTRANSPORT:onafterDelivered(From,Event,To)
-self:T(self.lid..string.format("New status: %s-->%s",From,To))
-for i=#self.carriers,1,-1 do
-local carrier=self.carriers[i]
-if self:GetCarrierTransportStatus(carrier)~=OPSTRANSPORT.Status.DELIVERED then
-carrier:Delivered(self)
-end
-end
-end
-function OPSTRANSPORT:onafterLoaded(From,Event,To,OpsGroupCargo,OpsGroupCarrier,CarrierElement)
-self:I(self.lid..string.format("Loaded OPSGROUP %s into carrier %s",OpsGroupCargo:GetName(),tostring(CarrierElement.name)))
-end
-function OPSTRANSPORT:onafterUnloaded(From,Event,To,OpsGroupCargo,OpsGroupCarrier)
-self:I(self.lid..string.format("Unloaded OPSGROUP %s",OpsGroupCargo:GetName()))
-end
-function OPSTRANSPORT:onafterDeadCarrierGroup(From,Event,To,OpsGroup)
-self:I(self.lid..string.format("Carrier OPSGROUP %s dead!",OpsGroup:GetName()))
-self.NcarrierDead=self.NcarrierDead+1
-self:_DelCarrier(OpsGroup)
-if#self.carriers==0 then
-self:DeadCarrierAll()
-end
-end
-function OPSTRANSPORT:onafterDeadCarrierAll(From,Event,To)
-self:I(self.lid..string.format("ALL Carrier OPSGROUPs are dead!"))
-if self.opszone then
-self:I(self.lid..string.format("Cancelling transport on CHIEF level"))
-self.chief:TransportCancel(self)
-else
-self:_CheckDelivered()
-if not self:IsDelivered()then
-self:Planned()
-end
-end
-end
-function OPSTRANSPORT:onafterCancel(From,Event,To)
-local Ngroups=#self.carriers
-self:I(self.lid..string.format("CANCELLING transport in status %s. Will wait for %d carrier groups to report DONE before evaluation",self:GetState(),Ngroups))
-self.Tover=timer.getAbsTime()
-if self.chief then
-self:T(self.lid..string.format("CHIEF will cancel the transport. Will wait for mission DONE before evaluation!"))
-self.chief:TransportCancel(self)
-elseif self.commander then
-self:T(self.lid..string.format("COMMANDER will cancel the transport. Will wait for transport DELIVERED before evaluation!"))
-self.commander:TransportCancel(self)
-elseif self.legions and#self.legions>0 then
-for _,_legion in pairs(self.legions or{})do
-local legion=_legion
-self:T(self.lid..string.format("LEGION %s will cancel the transport. Will wait for transport DELIVERED before evaluation!",legion.alias))
-legion:TransportCancel(self)
-end
-else
-self:T(self.lid..string.format("No legion, commander or chief. Attached OPS groups will cancel the transport on their own. Will wait for transport DELIVERED before evaluation!"))
-for _,_carrier in pairs(self:GetCarriers())do
-local carrier=_carrier
-carrier:TransportCancel(self)
-end
-local cargos=self:GetCargoOpsGroups(false)
-for _,_cargo in pairs(cargos)do
-local cargo=_cargo
-cargo:_DelMyLift(self)
-end
-end
-if self:IsPlanned()or self:IsQueued()or self:IsRequested()or Ngroups==0 then
-self:T(self.lid..string.format("Cancelled transport was in %s stage with %d carrier groups assigned and alive. Call it DELIVERED!",self:GetState(),Ngroups))
-self:Delivered()
-end
-end
-function OPSTRANSPORT:_CheckDelivered()
-if self.Ncargo>0 then
-local done=true
-local dead=true
-for _,_cargo in pairs(self:GetCargos())do
-local cargo=_cargo
-if cargo.delivered then
-dead=false
-elseif cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and cargo.opsgroup==nil then
-dead=false
-elseif cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and cargo.opsgroup:IsDestroyed()then
-elseif cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and cargo.opsgroup:IsDead()then
-elseif cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and cargo.opsgroup:IsStopped()then
-dead=false
-else
-done=false
-dead=false
-end
-end
-if dead then
-self:I(self.lid.."All cargo DEAD ==> Delivered!")
-self:Delivered()
-elseif done then
-self:I(self.lid.."All cargo DONE ==> Delivered!")
-self:Delivered()
-end
-end
-end
-function OPSTRANSPORT:_CheckRequiredCargos(TransportZoneCombo,CarrierGroup)
-TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-local requiredCargos=TransportZoneCombo.Cargos
-if TransportZoneCombo.RequiredCargos and#TransportZoneCombo.RequiredCargos>0 then
-requiredCargos=TransportZoneCombo.RequiredCargos
-else
-requiredCargos={}
-for _,_cargo in pairs(TransportZoneCombo.Cargos)do
-local cargo=_cargo
-table.insert(requiredCargos,cargo.opsgroup)
-end
-end
-if requiredCargos==nil or#requiredCargos==0 then
-return true
-end
-local carrierNames=self:_GetCarrierNames()
-local weightmin=nil
-for _,_cargo in pairs(requiredCargos)do
-local cargo=_cargo
-local isLoaded=cargo:IsLoaded(carrierNames)
-if not isLoaded then
-local weight=cargo:GetWeightTotal()
-if weightmin==nil or weight=1 then
-local dist=tz.PickupZone:Get2DDistance(vec2)
-local ncarriers=0
-for _,_carrier in pairs(self.carriers)do
-local carrier=_carrier
-if carrier and carrier:IsAlive()and carrier.cargoTZC and carrier.cargoTZC.uid==tz.uid then
-ncarriers=ncarriers+1
-end
-end
-local candidate={tzc=tz,distance=dist/1000,ncargo=ncargo,ncarriers=ncarriers}
-candidate.penalty=penalty(candidate)
-table.insert(candidates,candidate)
-end
-end
-end
-if#candidates>0 then
-local function optTZC(candA,candB)
-return candA.penalty=3 then
-local text="TZC optimized"
-for i,candidate in pairs(candidates)do
-text=text..string.format("\n[%d] TPZ=%d, Ncarriers=%d, Ncargo=%d, Distance=%.1f km, PENALTY=%d",i,candidate.tzc.uid,candidate.ncarriers,candidate.ncargo,candidate.distance,candidate.penalty)
-end
-self:I(self.lid..text)
-end
-return candidates[1].tzc
-else
-self:T(self.lid..string.format("Could NOT find a pickup zone (with cargo) for carrier group %s",Carrier:GetName()))
-end
-return nil
-end
-function OPSTRANSPORT:_GetOpsGroupFromObject(Object)
-local opsgroup=nil
-if Object:IsInstanceOf("OPSGROUP")then
-opsgroup=Object
-elseif Object:IsInstanceOf("GROUP")then
-opsgroup=_DATABASE:GetOpsGroup(Object)
-if not opsgroup then
-if Object:IsAir()then
-opsgroup=FLIGHTGROUP:New(Object)
-elseif Object:IsShip()then
-opsgroup=NAVYGROUP:New(Object)
-else
-opsgroup=ARMYGROUP:New(Object)
-end
-end
-else
-self:E(self.lid.."ERROR: Object must be a GROUP or OPSGROUP object!")
-return nil
-end
-return opsgroup
-end
-OPSZONE={
-ClassName="OPSZONE",
-verbose=0,
-Nred=0,
-Nblu=0,
-Nnut=0,
-Ncoal={},
-Tred=0,
-Tblu=0,
-Tnut=0,
-chiefs={},
-Missions={},
-}
-OPSZONE.ZoneType={
-Circular="Circular",
-Polygon="Polygon",
-}
-OPSZONE.version="0.6.1"
-function OPSZONE:New(Zone,CoalitionOwner)
-local self=BASE:Inherit(self,FSM:New())
-if Zone then
-if type(Zone)=="string"then
-local Name=Zone
-Zone=ZONE:FindByName(Name)
-if not Zone then
-local airbase=AIRBASE:FindByName(Name)
-if airbase then
-Zone=ZONE_AIRBASE:New(Name,2000)
-end
-end
-if not Zone then
-self:E(string.format("ERROR: No ZONE or ZONE_AIRBASE found for name: %s",Name))
-return nil
-end
-end
-else
-self:E("ERROR: First parameter Zone is nil in OPSZONE:New(Zone) call!")
-return nil
-end
-if Zone:IsInstanceOf("ZONE_AIRBASE")then
-self.airbase=Zone._.ZoneAirbase
-self.airbaseName=self.airbase:GetName()
-self.zoneType=OPSZONE.ZoneType.Circular
-self.zoneCircular=Zone
-elseif Zone:IsInstanceOf("ZONE_RADIUS")then
-self.zoneType=OPSZONE.ZoneType.Circular
-self.zoneCircular=Zone
-elseif Zone:IsInstanceOf("ZONE_POLYGON_BASE")then
-self.zoneType=OPSZONE.ZoneType.Polygon
-local zone=Zone
-self.zoneCircular=zone:GetZoneRadius(nil,true)
-else
-self:E("ERROR: OPSZONE must be a SPHERICAL zone due to DCS restrictions!")
-return nil
-end
-self.lid=string.format("OPSZONE %s | ",Zone:GetName())
-self.zone=Zone
-self.zoneName=Zone:GetName()
-self.zoneRadius=self.zoneCircular:GetRadius()
-self.Missions={}
-self.ScanUnitSet=SET_UNIT:New():FilterZones({Zone})
-self.ScanGroupSet=SET_GROUP:New():FilterZones({Zone})
-_DATABASE:AddOpsZone(self)
-self.ownerCurrent=CoalitionOwner or coalition.side.NEUTRAL
-self.ownerPrevious=CoalitionOwner or coalition.side.NEUTRAL
-self.isContested=false
-self.Ncoal[coalition.side.BLUE]=0
-self.Ncoal[coalition.side.RED]=0
-self.Ncoal[coalition.side.NEUTRAL]=0
-if self.airbase then
-self.ownerCurrent=self.airbase:GetCoalition()
-self.ownerPrevious=self.airbase:GetCoalition()
-end
-self:SetObjectCategories()
-self:SetUnitCategories()
-self:SetDrawZone()
-self:SetMarkZone(true)
-self:SetCaptureTime()
-self:SetCaptureNunits()
-self:SetCaptureThreatlevel()
-self.timerStatus=TIMER:New(OPSZONE.Status,self)
-self:SetStartState("Stopped")
-self:AddTransition("Stopped","Start","Empty")
-self:AddTransition("*","Stop","Stopped")
-self:AddTransition("*","Evaluated","*")
-self:AddTransition("*","Captured","Guarded")
-self:AddTransition("Empty","Guarded","Guarded")
-self:AddTransition("*","Empty","Empty")
-self:AddTransition("*","Attacked","Attacked")
-self:AddTransition("*","Defeated","Guarded")
-return self
-end
-function OPSZONE:SetVerbosity(VerbosityLevel)
-self.verbose=VerbosityLevel or 0
-return self
-end
-function OPSZONE:SetObjectCategories(Categories)
-if Categories and type(Categories)~="table"then
-Categories={Categories}
-end
-self.ObjectCategories=Categories or{Object.Category.UNIT,Object.Category.STATIC}
-return self
-end
-function OPSZONE:SetUnitCategories(Categories)
-if Categories and type(Categories)~="table"then
-Categories={Categories}
-end
-self.UnitCategories=Categories or{Unit.Category.GROUND_UNIT}
-return self
-end
-function OPSZONE:SetCaptureThreatlevel(Threatlevel)
-self.threatlevelCapture=Threatlevel or 0
-return self
-end
-function OPSZONE:SetCaptureNunits(Nunits)
-Nunits=Nunits or 1
-self.nunitsCapture=Nunits
-return self
-end
-function OPSZONE:SetCaptureTime(Tcapture)
-self.TminCaptured=Tcapture or 0
-return self
-end
-function OPSZONE:SetNeutralCanCapture(CanCapture)
-self.neutralCanCapture=CanCapture
-return self
-end
-function OPSZONE:SetDrawZone(Switch)
-if Switch==false then
-self.drawZone=false
-else
-self.drawZone=true
-end
-return self
-end
-function OPSZONE:SetMarkZone(Switch,ReadOnly)
-if Switch then
-self.markZone=true
-local Coordinate=self:GetCoordinate()
-self.markerText=self:_GetMarkerText()
-self.marker=self.marker or MARKER:New(Coordinate,self.markerText)
-if ReadOnly==false then
-self.marker.readonly=false
-else
-self.marker.readonly=true
-end
-self.marker:ToAll()
-else
-if self.marker then
-self.marker:Remove()
-end
-self.marker=nil
-self.markZone=false
-end
-return self
-end
-function OPSZONE:GetOwner()
-return self.ownerCurrent
-end
-function OPSZONE:GetOwnerName()
-return UTILS.GetCoalitionName(self.ownerCurrent)
-end
-function OPSZONE:GetCoordinate()
-local coordinate=self.zone:GetCoordinate()
-return coordinate
-end
-function OPSZONE:GetScannedUnitSet()
-return self.ScanUnitSet
-end
-function OPSZONE:GetScannedGroupSet()
-return self.ScanGroupSet
-end
-function OPSZONE:GetRandomCoordinate(inner,outer,surfacetypes)
-local zone=self:GetZone()
-local coord=zone:GetRandomCoordinate(inner,outer,surfacetypes)
-return coord
-end
-function OPSZONE:GetName()
-return self.zoneName
-end
-function OPSZONE:GetZone()
-return self.zone
-end
-function OPSZONE:GetPreviousOwner()
-return self.ownerPrevious
-end
-function OPSZONE:GetAttackDuration()
-if self:IsAttacked()and self.Tattacked then
-local dT=timer.getAbsTime()-self.Tattacked
-return dT
-end
-return nil
-end
-function OPSZONE:IsRed()
-local is=self.ownerCurrent==coalition.side.RED
-return is
-end
-function OPSZONE:IsBlue()
-local is=self.ownerCurrent==coalition.side.BLUE
-return is
-end
-function OPSZONE:IsNeutral()
-local is=self.ownerCurrent==coalition.side.NEUTRAL
-return is
-end
-function OPSZONE:IsCoalition(Coalition)
-local is=self.ownerCurrent==Coalition
-return is
-end
-function OPSZONE:IsStarted()
-local is=not self:IsStopped()
-return is
-end
-function OPSZONE:IsStopped()
-local is=self:is("Stopped")
-return is
-end
-function OPSZONE:IsGuarded()
-local is=self:is("Guarded")
-return is
-end
-function OPSZONE:IsEmpty()
-local is=self:is("Empty")
-return is
-end
-function OPSZONE:IsAttacked()
-local is=self:is("Attacked")
-return is
-end
-function OPSZONE:IsContested()
-return self.isContested
-end
-function OPSZONE:IsStopped()
-local is=self:is("Stopped")
-return is
-end
-function OPSZONE:onafterStart(From,Event,To)
-self:I(self.lid..string.format("Starting OPSZONE v%s",OPSZONE.version))
-self.timerStatus=self.timerStatus or TIMER:New(OPSZONE.Status,self)
-self.timerStatus:Start(1,120)
-if self.airbase then
-self:HandleEvent(EVENTS.BaseCaptured)
-end
-end
-function OPSZONE:onafterStop(From,Event,To)
-self:I(self.lid..string.format("Stopping OPSZONE"))
-self.timerStatus:Stop()
-self.zone:UndrawZone()
-if self.markZone then
-self.marker:Remove()
-end
-self:UnHandleEvent(EVENTS.BaseCaptured)
-self.CallScheduler:Clear()
-if self.Scheduler then
-self.Scheduler:Clear()
-end
-end
-function OPSZONE:Status()
-local fsmstate=self:GetState()
-local contested=tostring(self:IsContested())
-if self.verbose>=1 then
-local text=string.format("State %s: Owner %d (previous %d), contested=%s, Nunits: red=%d, blue=%d, neutral=%d",fsmstate,self.ownerCurrent,self.ownerPrevious,contested,self.Nred,self.Nblu,self.Nnut)
-self:I(self.lid..text)
-end
-self:Scan()
-self:EvaluateZone()
-self:_UpdateMarker()
-if self.zone.DrawID and not self.drawZone then
-self.zone:UndrawZone()
-end
-end
-function OPSZONE:onbeforeCaptured(From,Event,To,NewOwnerCoalition)
-if self.ownerCurrent==NewOwnerCoalition then
-self:T(self.lid.."")
-end
-return true
-end
-function OPSZONE:onafterCaptured(From,Event,To,NewOwnerCoalition)
-self:T(self.lid..string.format("Zone captured by coalition=%d",NewOwnerCoalition))
-self.ownerPrevious=self.ownerCurrent
-self.ownerCurrent=NewOwnerCoalition
-if self.drawZone then
-self.zone:UndrawZone()
-local color=self:_GetZoneColor()
-self.zone:DrawZone(nil,color,1.0,color,0.5)
-end
-for _,_chief in pairs(self.chiefs)do
-local chief=_chief
-if chief.coalition==self.ownerCurrent then
-chief:ZoneCaptured(self)
-else
-chief:ZoneLost(self)
-end
-end
-end
-function OPSZONE:onafterEmpty(From,Event,To)
-self:T(self.lid..string.format("Zone is empty EVENT"))
-end
-function OPSZONE:onafterAttacked(From,Event,To,AttackerCoalition)
-self:T(self.lid..string.format("Zone is being attacked by coalition=%s!",tostring(AttackerCoalition)))
-end
-function OPSZONE:onafterDefeated(From,Event,To,DefeatedCoalition)
-self:T(self.lid..string.format("Defeated attack on zone by coalition=%d",DefeatedCoalition))
-self.Tattacked=nil
-end
-function OPSZONE:onenterGuarded(From,Event,To)
-if From~=To then
-self:T(self.lid..string.format("Zone is guarded"))
-self.Tattacked=nil
-if self.drawZone then
-self.zone:UndrawZone()
-local color=self:_GetZoneColor()
-self.zone:DrawZone(nil,color,1.0,color,0.5)
-end
-end
-end
-function OPSZONE:onenterAttacked(From,Event,To,AttackerCoalition)
-if From~="Attacked"then
-self:T(self.lid..string.format("Zone is Attacked"))
-self.Tattacked=timer.getAbsTime()
-if AttackerCoalition then
-for _,_chief in pairs(self.chiefs)do
-local chief=_chief
-if chief.coalition~=AttackerCoalition then
-chief:ZoneAttacked(self)
-end
-end
-end
-if self.drawZone then
-self.zone:UndrawZone()
-local color={1,204/255,204/255}
-self.zone:DrawZone(nil,color,1.0,color,0.5)
-end
-self:_CleanMissionTable()
-end
-end
-function OPSZONE:onenterEmpty(From,Event,To)
-if From~=To then
-self:T(self.lid..string.format("Zone is empty now"))
-for _,_chief in pairs(self.chiefs)do
-local chief=_chief
-chief:ZoneEmpty(self)
-end
-if self.drawZone then
-self.zone:UndrawZone()
-local color=self:_GetZoneColor()
-self.zone:DrawZone(nil,color,1.0,color,0.2)
-end
-end
-end
-function OPSZONE:Scan()
-if self.verbose>=3 then
-local text=string.format("Scanning zone %s R=%.1f m",self.zoneName,self.zoneRadius)
-self:I(self.lid..text)
-end
-local SphereSearch={id=world.VolumeType.SPHERE,params={point=self.zone:GetVec3(),radius=self.zoneRadius}}
-local Nred=0
-local Nblu=0
-local Nnut=0
-local Tred=0
-local Tblu=0
-local Tnut=0
-self.ScanGroupSet:Clear(false)
-self.ScanUnitSet:Clear(false)
-local function EvaluateZone(_ZoneObject)
-local ZoneObject=_ZoneObject
-if ZoneObject then
-local ObjectCategory=Object.getCategory(ZoneObject)
-if ObjectCategory==Object.Category.UNIT and ZoneObject:isExist()and ZoneObject:isActive()then
-local DCSUnit=ZoneObject
-local function Included()
-if not self.UnitCategories then
-return true
-else
-local CategoryDCSUnit=ZoneObject:getDesc().category
-for _,UnitCategory in pairs(self.UnitCategories)do
-if UnitCategory==CategoryDCSUnit then
-return true
-end
-end
-end
-return false
-end
-if Included()then
-local Coalition=DCSUnit:getCoalition()
-local tl=0
-local unit=UNIT:Find(DCSUnit)
-if unit then
-local inzone=true
-if self.zoneType==OPSZONE.ZoneType.Polygon then
-inzone=unit:IsInZone(self.zone)
-end
-if inzone then
-tl=unit:GetThreatLevel()
-self.ScanUnitSet:AddUnit(unit)
-local group=unit:GetGroup()
-if group then
-self.ScanGroupSet:AddGroup(group,true)
-end
-if Coalition==coalition.side.RED then
-Nred=Nred+1
-Tred=Tred+tl
-elseif Coalition==coalition.side.BLUE then
-Nblu=Nblu+1
-Tblu=Tblu+tl
-elseif Coalition==coalition.side.NEUTRAL then
-Nnut=Nnut+1
-Tnut=Tnut+tl
-end
-if self.verbose>=4 then
-self:I(self.lid..string.format("Found unit %s (coalition=%d)",DCSUnit:getName(),Coalition))
-end
-end
-end
-end
-elseif ObjectCategory==Object.Category.STATIC and ZoneObject:isExist()then
-local DCSStatic=ZoneObject
-local Coalition=DCSStatic:getCoalition()
-local inzone=true
-if self.zoneType==OPSZONE.ZoneType.Polygon then
-local Vec3=DCSStatic:getPoint()
-inzone=self.zone:IsVec3InZone(Vec3)
-end
-if inzone then
-if Coalition==coalition.side.RED then
-Nred=Nred+1
-elseif Coalition==coalition.side.BLUE then
-Nblu=Nblu+1
-elseif Coalition==coalition.side.NEUTRAL then
-Nnut=Nnut+1
-end
-if self.verbose>=4 then
-self:I(self.lid..string.format("Found static %s (coalition=%d)",DCSStatic:getName(),Coalition))
-end
-end
-elseif ObjectCategory==Object.Category.SCENERY then
-local SceneryType=ZoneObject:getTypeName()
-local SceneryName=ZoneObject:getName()
-self:T2(self.lid..string.format("Found scenery type=%s, name=%s",SceneryType,SceneryName))
-end
-end
-return true
-end
-world.searchObjects(self.ObjectCategories,SphereSearch,EvaluateZone)
-if self.verbose>=3 then
-local text=string.format("Scan result Nred=%d, Nblue=%d, Nneutral=%d",Nred,Nblu,Nnut)
-if self.verbose>=4 then
-for _,_unit in pairs(self.ScanUnitSet:GetSet())do
-local unit=_unit
-text=text..string.format("\nUnit %s coalition=%s",unit:GetName(),unit:GetCoalitionName())
-end
-for _,_group in pairs(self.ScanGroupSet:GetSet())do
-local group=_group
-text=text..string.format("\nGroup %s coalition=%s",group:GetName(),group:GetCoalitionName())
-end
-end
-self:I(self.lid..text)
-end
-self.Nred=Nred
-self.Nblu=Nblu
-self.Nnut=Nnut
-self.Ncoal[coalition.side.BLUE]=Nblu
-self.Ncoal[coalition.side.RED]=Nred
-self.Ncoal[coalition.side.NEUTRAL]=Nnut
-self.Tblu=Tblu
-self.Tred=Tred
-self.Tnut=Tnut
-return self
-end
-function OPSZONE:EvaluateZone()
-local Nred=self.Nred
-local Nblu=self.Nblu
-local Nnut=self.Nnut
-local Tnow=timer.getAbsTime()
-local function captured(coal)
-if not self.airbase then
-if not self.Tcaptured then
-self.Tcaptured=Tnow
-end
-if Tnow-self.Tcaptured>=self.TminCaptured then
-self:Captured(coal)
-self.Tcaptured=nil
-end
-end
-end
-if self:IsRed()then
-if Nred==0 then
-if Nblu>=self.nunitsCapture and self.Tblu>=self.threatlevelCapture then
-captured(coalition.side.BLUE)
-elseif Nnut>=self.nunitsCapture and self.Tnut>=self.threatlevelCapture and self.neutralCanCapture then
-captured(coalition.side.NEUTRAL)
-end
-else
-if Nblu>0 then
-if not self:IsAttacked()and self.Tnut>=self.threatlevelCapture then
-self:Attacked(coalition.side.BLUE)
-end
-elseif Nblu==0 then
-if self:IsAttacked()and self:IsContested()then
-self:Defeated(coalition.side.BLUE)
-elseif self:IsEmpty()then
-self:Guarded()
-end
-end
-end
-if Nblu==0 then
-self.isContested=false
-else
-self.isContested=true
-end
-elseif self:IsBlue()then
-if Nblu==0 then
-if Nred>=self.nunitsCapture and self.Tred>=self.threatlevelCapture then
-captured(coalition.side.RED)
-elseif Nnut>=self.nunitsCapture and self.Tnut>=self.threatlevelCapture and self.neutralCanCapture then
-captured(coalition.side.NEUTRAL)
-end
-else
-if Nred>0 then
-if not self:IsAttacked()and self.Tnut>=self.threatlevelCapture then
-self:Attacked(coalition.side.RED)
-end
-elseif Nred==0 then
-if self:IsAttacked()and self:IsContested()then
-self:Defeated(coalition.side.RED)
-elseif self:IsEmpty()then
-self:Guarded()
-end
-end
-end
-if Nred==0 then
-self.isContested=false
-else
-self.isContested=true
-end
-elseif self:IsNeutral()then
-if Nred>0 and Nblu>0 then
-self:T(self.lid.."FF neutrals left neutral zone and red and blue are present! What to do?")
-if not self:IsAttacked()then
-self:Attacked()
-end
-self.isContested=true
-elseif Nred>=self.nunitsCapture and self.Tred>=self.threatlevelCapture then
-captured(coalition.side.RED)
-elseif Nblu>=self.nunitsCapture and self.Tblu>=self.threatlevelCapture then
-captured(coalition.side.BLUE)
-end
-else
-self:E(self.lid.."ERROR: Unknown coaliton!")
-end
-if Nblu==0 and Nred==0 and Nnut==0 and(not self:IsEmpty())then
-self:Empty()
-end
-if self.airbase then
-local airbasecoalition=self.airbase:GetCoalition()
-if airbasecoalition~=self.ownerCurrent then
-self:T(self.lid..string.format("Captured airbase %s: Coaltion %d-->%d",self.airbaseName,self.ownerCurrent,airbasecoalition))
-self:Captured(airbasecoalition)
-end
-end
-self:Evaluated()
-end
-function OPSZONE:OnEventHit(EventData)
-if self.HitsOn then
-local UnitHit=EventData.TgtUnit
-if UnitHit and UnitHit:IsInZone(self)and UnitHit:GetCoalition()==self.ownerCurrent then
-self.HitTimeLast=timer.getTime()
-if not self:IsAttacked()then
-self:T3(self.lid.."Hit ==> Attack")
-self:Attacked()
-end
-end
-end
-end
-function OPSZONE:OnEventBaseCaptured(EventData)
-if EventData and EventData.Place and self.airbase and self.airbaseName then
-local airbase=EventData.Place
-if EventData.PlaceName==self.airbaseName then
-local CoalitionNew=airbase:GetCoalition()
-self:I(self.lid..string.format("EVENT BASE CAPTURED: New coalition of airbase %s: %d [previous=%d]",self.airbaseName,CoalitionNew,self.ownerCurrent))
-if CoalitionNew~=self.ownerCurrent then
-self:Captured(CoalitionNew)
-end
-end
-end
-end
-function OPSZONE:_GetZoneColor()
-local color={0,0,0}
-if self.ownerCurrent==coalition.side.NEUTRAL then
-color=self.ZoneOwnerNeutral or{1,1,1}
-elseif self.ownerCurrent==coalition.side.BLUE then
-color=self.ZoneOwnerBlue or{0,0,1}
-elseif self.ownerCurrent==coalition.side.RED then
-color=self.ZoneOwnerRed or{1,0,0}
-else
-end
-return color
-end
-function OPSZONE:SetZoneColor(Neutral,Blue,Red)
-self.ZoneOwnerNeutral=Neutral or{1,1,1}
-self.ZoneOwnerBlue=Blue or{0,0,1}
-self.ZoneOwnerRed=Red or{1,0,0}
-return self
-end
-function OPSZONE:_UpdateMarker()
-if self.markZone then
-local text=self:_GetMarkerText()
-if text~=self.markerText then
-self.markerText=text
-self.marker:UpdateText(self.markerText)
-end
-end
-end
-function OPSZONE:_GetMarkerText()
-local owner=UTILS.GetCoalitionName(self.ownerCurrent)
-local prevowner=UTILS.GetCoalitionName(self.ownerPrevious)
-local text=string.format("%s [N=%d, TL=%d T=%d]:\nOwner=%s [%s]\nState=%s [Contested=%s]\nBlue=%d [TL=%d]\nRed=%d [TL=%d]\nNeutral=%d [TL=%d]",
-self.zoneName,self.nunitsCapture or 0,self.threatlevelCapture or 0,self.TminCaptured or 0,
-owner,prevowner,self:GetState(),tostring(self:IsContested()),
-self.Nblu,self.Tblu,self.Nred,self.Tred,self.Nnut,self.Tnut)
-return text
-end
-function OPSZONE:_AddChief(Chief)
-table.insert(self.chiefs,Chief)
-end
-function OPSZONE:_AddMission(Coalition,Type,Auftrag)
-local entry={}
-entry.Coalition=Coalition or coalition.side.NEUTRAL
-entry.Type=Type or""
-entry.Mission=Auftrag or nil
-table.insert(self.Missions,entry)
-return self
-end
-function OPSZONE:_GetMissions()
-return self.Missions
-end
-function OPSZONE:_FindMissions(Coalition,Type)
-local foundmissions={}
-local found=false
-for _,_entry in pairs(self.Missions)do
-local entry=_entry
-if entry.Coalition==Coalition and entry.Type==Type and entry.Mission and entry.Mission:IsNotOver()then
-table.insert(foundmissions,entry.Mission)
-found=true
-end
-end
-return found,foundmissions
-end
-function OPSZONE:_CleanMissionTable()
-local missions={}
-for _,_entry in pairs(self.Missions)do
-local entry=_entry
-if entry.Mission and entry.Mission:IsNotOver()then
-table.insert(missions,entry)
-end
-end
-self.Missions=missions
-return self
-end
-PLATOON={
-ClassName="PLATOON",
-verbose=0,
-weaponData={},
-}
-PLATOON.version="0.1.0"
-function PLATOON:New(TemplateGroupName,Ngroups,PlatoonName)
-local self=BASE:Inherit(self,COHORT:New(TemplateGroupName,Ngroups,PlatoonName))
-self:AddMissionCapability(AUFTRAG.Type.NOTHING,50)
-self.isGround=true
-self.ammo=self:_CheckAmmo()
-return self
-end
-function PLATOON:SetBrigade(Brigade)
-self.legion=Brigade
-return self
-end
-function PLATOON:GetBrigade()
-return self.legion
-end
-function PLATOON:onafterStatus(From,Event,To)
-if self.verbose>=1 then
-local fsmstate=self:GetState()
-local callsign=self.callsignName and UTILS.GetCallsignName(self.callsignName)or"N/A"
-local skill=self.skill and tostring(self.skill)or"N/A"
-local NassetsTot=#self.assets
-local NassetsInS=self:CountAssets(true)
-local NassetsQP=0;local NassetsP=0;local NassetsQ=0
-if self.legion then
-NassetsQP,NassetsP,NassetsQ=self.legion:CountAssetsOnMission(nil,self)
-end
-local text=string.format("%s [Type=%s, Call=%s, Skill=%s]: Assets Total=%d, Stock=%d, Mission=%d [Active=%d, Queue=%d]",
-fsmstate,self.aircrafttype,callsign,skill,NassetsTot,NassetsInS,NassetsQP,NassetsP,NassetsQ)
-self:T(self.lid..text)
-if self.verbose>=3 and self.weaponData then
-local text="Weapon Data:"
-for bit,_weapondata in pairs(self.weaponData)do
-local weapondata=_weapondata
-text=text..string.format("\n- Bit=%s: Rmin=%.1f km, Rmax=%.1f km",bit,weapondata.RangeMin/1000,weapondata.RangeMax/1000)
-end
-self:I(self.lid..text)
-end
-self:_CheckAssetStatus()
-end
-if not self:IsStopped()then
-self:__Status(-60)
-end
-end
-do
-_PlayerTaskNr=0
-PLAYERTASK={
-ClassName="PLAYERTASK",
-verbose=false,
-lid=nil,
-PlayerTaskNr=nil,
-Type=nil,
-TTSType=nil,
-Target=nil,
-Clients=nil,
-Repeat=false,
-repeats=0,
-RepeatNo=1,
-TargetMarker=nil,
-SmokeColor=nil,
-FlareColor=nil,
-conditionSuccess={},
-conditionFailure={},
-TaskController=nil,
-timestamp=0,
-lastsmoketime=0,
-Freetext=nil,
-FreetextTTS=nil,
-TaskSubType=nil,
-NextTaskSuccess={},
-NextTaskFailure={},
-FinalState="none",
-PreviousCount=0,
-}
-PLAYERTASK.version="0.1.22"
-function PLAYERTASK:New(Type,Target,Repeat,Times,TTSType)
-local self=BASE:Inherit(self,FSM:New())
-self.Type=Type
-self.Repeat=false
-self.repeats=0
-self.RepeatNo=1
-self.Clients=FIFO:New()
-self.TargetMarker=nil
-self.SmokeColor=SMOKECOLOR.Red
-self.conditionSuccess={}
-self.conditionFailure={}
-self.TaskController=nil
-self.timestamp=timer.getAbsTime()
-self.TTSType=TTSType or"close air support"
-self.lastsmoketime=0
-if Repeat then
-self.Repeat=true
-self.RepeatNo=Times or 1
-end
-_PlayerTaskNr=_PlayerTaskNr+1
-self.PlayerTaskNr=_PlayerTaskNr
-self.lid=string.format("PlayerTask #%d %s | ",self.PlayerTaskNr,tostring(self.Type))
-if Target and Target.ClassName and Target.ClassName=="TARGET"then
-self.Target=Target
-elseif Target and Target.ClassName then
-self.Target=TARGET:New(Target)
-else
-self:E(self.lid.."*** NO VALID TARGET!")
-return self
-end
-self.PreviousCount=self.Target:CountTargets()
-self:T(self.lid.."Created.")
-self:SetStartState("Planned")
-self:AddTransition("*","Planned","Planned")
-self:AddTransition("*","Requested","Requested")
-self:AddTransition("*","ClientAdded","*")
-self:AddTransition("*","ClientRemoved","*")
-self:AddTransition("*","Executing","Executing")
-self:AddTransition("*","Progress","*")
-self:AddTransition("*","Done","Done")
-self:AddTransition("*","Cancel","Done")
-self:AddTransition("*","Success","Done")
-self:AddTransition("*","ClientAborted","*")
-self:AddTransition("*","Failed","Failed")
-self:AddTransition("*","Status","*")
-self:AddTransition("*","Stop","Stopped")
-self:__Status(-5)
-return self
-end
-function PLAYERTASK:_SetController(Controller)
-self:T(self.lid.."_SetController")
-self.TaskController=Controller
-return self
-end
-function PLAYERTASK:SetCoalition(Coalition)
-self:T(self.lid.."SetCoalition")
-self.coalition=Coalition or coalition.side.BLUE
-return self
-end
-function PLAYERTASK:GetCoalition()
-self:T(self.lid.."GetCoalition")
-return self.coalition
-end
-function PLAYERTASK:GetTarget()
-self:T(self.lid.."GetTarget")
-return self.Target
-end
-function PLAYERTASK:AddFreetext(Text)
-self:T(self.lid.."AddFreetext")
-self.Freetext=Text
-return self
-end
-function PLAYERTASK:HasFreetext()
-self:T(self.lid.."HasFreetext")
-return self.Freetext~=nil and true or false
-end
-function PLAYERTASK:HasFreetextTTS()
-self:T(self.lid.."HasFreetextTTS")
-return self.FreetextTTS~=nil and true or false
-end
-function PLAYERTASK:SetSubType(Type)
-self:T(self.lid.."AddSubType")
-self.TaskSubType=Type
-return self
-end
-function PLAYERTASK:GetSubType()
-self:T(self.lid.."GetSubType")
-return self.TaskSubType
-end
-function PLAYERTASK:GetFreetext()
-self:T(self.lid.."GetFreetext")
-return self.Freetext or self.FreetextTTS or"No Details"
-end
-function PLAYERTASK:AddFreetextTTS(TextTTS)
-self:T(self.lid.."AddFreetextTTS")
-self.FreetextTTS=TextTTS
-return self
-end
-function PLAYERTASK:GetFreetextTTS()
-self:T(self.lid.."GetFreetextTTS")
-return self.FreetextTTS or self.Freetext or"No Details"
-end
-function PLAYERTASK:SetMenuName(Text)
-self:T(self.lid.."SetMenuName")
-self.Target.name=Text
-return self
-end
-function PLAYERTASK:AddNextTaskAfterSuccess(Task)
-self:T(self.lid.."AddNextTaskAfterSuccess")
-table.insert(self.NextTaskSuccess,Task)
-return self
-end
-function PLAYERTASK:AddNextTaskAfterFailure(Task)
-self:T(self.lid.."AddNextTaskAfterFailure")
-table.insert(self.NextTaskFailure,Task)
-return self
-end
-function PLAYERTASK:IsDone()
-self:T(self.lid.."IsDone?")
-local IsDone=false
-local state=self:GetState()
-if state=="Done"or state=="Stopped"then
-IsDone=true
-end
-return IsDone
-end
-function PLAYERTASK:GetClients()
-self:T(self.lid.."GetClients")
-local clientlist=self.Clients:GetIDStackSorted()or{}
-local count=self.Clients:Count()
-return clientlist,count
-end
-function PLAYERTASK:GetClientObjects()
-self:T(self.lid.."GetClientObjects")
-local clientlist=self.Clients:GetDataTable()or{}
-local count=self.Clients:Count()
-return clientlist,count
-end
-function PLAYERTASK:CountClients()
-self:T(self.lid.."CountClients")
-return self.Clients:Count()
-end
-function PLAYERTASK:HasPlayerName(Name)
-self:T(self.lid.."HasPlayerName?")
-return self.Clients:HasUniqueID(Name)
-end
-function PLAYERTASK:AddClient(Client)
-self:T(self.lid.."AddClient")
-local name=Client:GetPlayerName()
-if not self.Clients:HasUniqueID(name)then
-self.Clients:Push(Client,name)
-self:__ClientAdded(-2,Client)
-end
-if self.TaskController and self.TaskController.Scoring then
-self.TaskController.Scoring:_AddPlayerFromUnit(Client)
-end
-return self
-end
-function PLAYERTASK:RemoveClient(Client,Name)
-self:T(self.lid.."RemoveClient")
-local name=Name or Client:GetPlayerName()
-if self.Clients:HasUniqueID(name)then
-self.Clients:PullByID(name)
-if self.verbose then
-self.Clients:Flush()
-end
-self:__ClientRemoved(-2,Client)
-if self.Clients:Count()==0 then
-self:__Failed(-1)
-end
-end
-return self
-end
-function PLAYERTASK:ClientAbort(Client)
-self:T(self.lid.."ClientAbort")
-if Client and Client:IsAlive()then
-self:RemoveClient(Client)
-self:__ClientAborted(-1,Client)
-return self
-else
-if self.Clients:Count()==0 then
-self:__Failed(-1)
-end
-end
-return self
-end
-function PLAYERTASK:MarkTargetOnF10Map(Text,Coalition,ReadOnly)
-self:T(self.lid.."MarkTargetOnF10Map")
-if self.Target then
-local coordinate=self.Target:GetCoordinate()
-if coordinate then
-if self.TargetMarker then
-self.TargetMarker:Remove()
-end
-local text=Text or("Target of "..self.lid)
-self.TargetMarker=MARKER:New(coordinate,text)
-if ReadOnly then
-self.TargetMarker:ReadOnly()
-end
-if Coalition then
-self.TargetMarker:ToCoalition(Coalition)
-else
-self.TargetMarker:ToAll()
-end
-end
-end
-return self
-end
-function PLAYERTASK:SmokeTarget(Color)
-self:T(self.lid.."SmokeTarget")
-local color=Color or SMOKECOLOR.Red
-if not self.lastsmoketime then self.lastsmoketime=0 end
-local TDiff=timer.getAbsTime()-self.lastsmoketime
-if self.Target and TDiff>299 then
-local coordinate=self.Target:GetAverageCoordinate()
-if coordinate then
-coordinate:Smoke(color)
-self.lastsmoketime=timer.getAbsTime()
-end
-end
-return self
-end
-function PLAYERTASK:FlareTarget(Color)
-self:T(self.lid.."SmokeTarget")
-local color=Color or FLARECOLOR.Red
-if self.Target then
-local coordinate=self.Target:GetAverageCoordinate()
-if coordinate then
-coordinate:Flare(color,0)
-end
-end
-return self
-end
-function PLAYERTASK:IlluminateTarget(Power,Height)
-self:T(self.lid.."IlluminateTarget")
-local Power=Power or 1000
-local Height=Height or 150
-if self.Target then
-local coordinate=self.Target:GetAverageCoordinate()
-if coordinate then
-local bcoord=COORDINATE:NewFromVec2(coordinate:GetVec2(),Height)
-bcoord:IlluminationBomb(Power)
-end
-end
-return self
-end
-function PLAYERTASK:AddConditionSuccess(ConditionFunction,...)
-local condition={}
-condition.func=ConditionFunction
-condition.arg={}
-if arg then
-condition.arg=arg
-end
-table.insert(self.conditionSuccess,condition)
-return self
-end
-function PLAYERTASK:AddConditionFailure(ConditionFunction,...)
-local condition={}
-condition.func=ConditionFunction
-condition.arg={}
-if arg then
-condition.arg=arg
-end
-table.insert(self.conditionFailure,condition)
-return self
-end
-function PLAYERTASK:_EvalConditionsAny(Conditions)
-for _,_condition in pairs(Conditions or{})do
-local condition=_condition
-local istrue=condition.func(unpack(condition.arg))
-if istrue then
-return true
-end
-end
-return false
-end
-function PLAYERTASK:onafterStatus(From,Event,To)
-self:T({From,Event,To})
-self:T(self.lid.."onafterStatus")
-local status=self:GetState()
-if status=="Stopped"then return self end
-local targetdead=false
-if self.Type~=AUFTRAG.Type.CTLD and self.Type~=AUFTRAG.Type.CSAR then
-if self.Target:IsDead()or self.Target:IsDestroyed()or self.Target:CountTargets()==0 then
-targetdead=true
-self:__Success(-2)
-status="Success"
-return self
-end
-end
-local clientsalive=false
-if status=="Executing"then
-local ClientTable=self.Clients:GetDataTable()
-for _,_client in pairs(ClientTable)do
-local client=_client
-if client:IsAlive()then
-clientsalive=true
-end
-end
-if status=="Executing"and(not clientsalive)and(not targetdead)then
-self:__Failed(-2)
-status="Failed"
-end
-end
-if status~="Done"and status~="Stopped"then
-local successCondition=self:_EvalConditionsAny(self.conditionSuccess)
-local failureCondition=self:_EvalConditionsAny(self.conditionFailure)
-if failureCondition and status~="Failed"then
-self:__Failed(-2)
-status="Failed"
-elseif successCondition then
-self:__Success(-2)
-status="Success"
-end
-if status~="Failed"and status~="Success"then
-local targetcount=self.Target:CountTargets()
-if targetcount0 then
-for _,_client in pairs(clients)do
-self.TaskController.Scoring:AddGoalScore(_client,self.Type,nil,10)
-end
-end
-end
-self.TaskController:__TaskProgress(-1,self,TargetCount)
-end
-return self
-end
-function PLAYERTASK:onafterPlanned(From,Event,To)
-self:T({From,Event,To})
-self.timestamp=timer.getAbsTime()
-return self
-end
-function PLAYERTASK:onafterRequested(From,Event,To)
-self:T({From,Event,To})
-self.timestamp=timer.getAbsTime()
-return self
-end
-function PLAYERTASK:onafterExecuting(From,Event,To)
-self:T({From,Event,To})
-self.timestamp=timer.getAbsTime()
-return self
-end
-function PLAYERTASK:onafterStop(From,Event,To)
-self:T({From,Event,To})
-self.timestamp=timer.getAbsTime()
-return self
-end
-function PLAYERTASK:onafterClientAdded(From,Event,To,Client)
-self:T({From,Event,To})
-if Client and self.verbose then
-local text=string.format("Player %s joined task %03d!",Client:GetPlayerName()or"Generic",self.PlayerTaskNr)
-self:T(self.lid..text)
-end
-self.timestamp=timer.getAbsTime()
-return self
-end
-function PLAYERTASK:onafterDone(From,Event,To)
-self:T({From,Event,To})
-if self.TaskController then
-self.TaskController:__TaskDone(-1,self)
-end
-self.timestamp=timer.getAbsTime()
-self:__Stop(-1)
-return self
-end
-function PLAYERTASK:onafterCancel(From,Event,To)
-self:T({From,Event,To})
-if self.TaskController then
-self.TaskController:__TaskCancelled(-1,self)
-end
-self.timestamp=timer.getAbsTime()
-self.FinalState="Cancel"
-self:__Done(-1)
-return self
-end
-function PLAYERTASK:onafterSuccess(From,Event,To)
-self:T({From,Event,To})
-if self.TaskController then
-self.TaskController:__TaskSuccess(-1,self)
-end
-if self.TargetMarker then
-self.TargetMarker:Remove()
-end
-if self.TaskController.Scoring then
-local clients,count=self:GetClientObjects()
-if count>0 then
-for _,_client in pairs(clients)do
-local auftrag=self:GetSubType()
-self.TaskController.Scoring:AddGoalScore(_client,self.Type,nil,self.TaskController.Scores[self.Type])
-end
-end
-end
-self.timestamp=timer.getAbsTime()
-self.FinalState="Success"
-self:__Done(-1)
-return self
-end
-function PLAYERTASK:onafterFailed(From,Event,To)
-self:T({From,Event,To})
-self.repeats=self.repeats+1
-if self.Repeat and(self.repeats<=self.RepeatNo)then
-if self.TaskController then
-self.TaskController:__TaskRepeatOnFailed(-1,self)
-end
-self:__Planned(-1)
-return self
-else
-if self.TargetMarker then
-self.TargetMarker:Remove()
-end
-self.FinalState="Failed"
-self:__Done(-1)
-end
-if self.TaskController.Scoring then
-local clients,count=self:GetClientObjects()
-if count>0 then
-for _,_client in pairs(clients)do
-local auftrag=self:GetSubType()
-self.TaskController.Scoring:AddGoalScore(_client,self.Type,nil,-self.TaskController.Scores[self.Type])
-end
-end
-end
-self.timestamp=timer.getAbsTime()
-return self
-end
-end
-do
-PLAYERTASKCONTROLLER={
-ClassName="PLAYERTASKCONTROLLER",
-verbose=false,
-lid=nil,
-TargetQueue=nil,
-ClientSet=nil,
-UseGroupNames=true,
-PlayerMenu={},
-usecluster=false,
-MenuName=nil,
-ClusterRadius=0.5,
-NoScreenOutput=false,
-TargetRadius=500,
-UseWhiteList=false,
-WhiteList={},
-gettext=nil,
-locale="en",
-precisionbombing=false,
-taskinfomenu=false,
-activehasinfomenu=false,
-MarkerReadOnly=false,
-customcallsigns={},
-ShortCallsign=true,
-Keepnumber=false,
-CallsignTranslations=nil,
-PlayerFlashMenu={},
-PlayerJoinMenu={},
-PlayerInfoMenu={},
-PlayerMenuTag={},
-noflaresmokemenu=false,
-illumenu=false,
-TransmitOnlyWithPlayers=true,
-buddylasing=false,
-PlayerRecce=nil,
-Coalition=nil,
-MenuParent=nil,
-ShowMagnetic=true,
-InfoHasLLDDM=false,
-InfoHasCoordinate=false,
-UseTypeNames=false,
-Scoring=nil,
-MenuNoTask=nil,
-}
-PLAYERTASKCONTROLLER.Type={
-A2A="Air-To-Air",
-A2G="Air-To-Ground",
-A2S="Air-To-Sea",
-A2GS="Air-To-Ground-Sea",
-}
-AUFTRAG.Type.PRECISIONBOMBING="Precision Bombing"
-AUFTRAG.Type.CTLD="Combat Transport"
-AUFTRAG.Type.CSAR="Combat Rescue"
-PLAYERTASKCONTROLLER.Scores={
-[AUFTRAG.Type.PRECISIONBOMBING]=100,
-[AUFTRAG.Type.CTLD]=100,
-[AUFTRAG.Type.CSAR]=100,
-[AUFTRAG.Type.INTERCEPT]=100,
-[AUFTRAG.Type.ANTISHIP]=100,
-[AUFTRAG.Type.CAS]=100,
-[AUFTRAG.Type.BAI]=100,
-[AUFTRAG.Type.SEAD]=100,
-[AUFTRAG.Type.BOMBING]=100,
-[AUFTRAG.Type.BOMBRUNWAY]=100,
-}
-PLAYERTASKCONTROLLER.SeadAttributes={
-SAM=GROUP.Attribute.GROUND_SAM,
-AAA=GROUP.Attribute.GROUND_AAA,
-EWR=GROUP.Attribute.GROUND_EWR,
-}
-PLAYERTASKCONTROLLER.Messages={
-EN={
-TASKABORT="Task aborted!",
-NOACTIVETASK="No active task!",
-FREQUENCIES="frequencies ",
-FREQUENCY="frequency %.3f",
-BROADCAST="%s, %s, switch to %s for task assignment!",
-CASTTS="close air support",
-SEADTTS="suppress air defense",
-BOMBTTS="bombing",
-PRECBOMBTTS="precision bombing",
-BAITTS="battle field air interdiction",
-ANTISHIPTTS="anti-ship",
-INTERCEPTTS="intercept",
-BOMBRUNWAYTTS="bomb runway",
-HAVEACTIVETASK="You already have one active task! Complete it first!",
-PILOTJOINEDTASK="%s, %s. You have been assigned %s task %03d",
-TASKNAME="%s Task ID %03d",
-TASKNAMETTS="%s Task ID %03d",
-THREATHIGH="high",
-THREATMEDIUM="medium",
-THREATLOW="low",
-THREATTEXT="%s\nThreat: %s\nTargets left: %d\nCoord: %s",
-THREATTEXTTTS="%s, %s. Target information for %s. Threat level %s. Targets left %d. Target location %s.",
-MARKTASK="%s, %s, copy, task %03d location marked on map!",
-SMOKETASK="%s, %s, copy, task %03d location smoked!",
-FLARETASK="%s, %s, copy, task %03d location illuminated!",
-ABORTTASK="All stations, %s, %s has aborted %s task %03d!",
-UNKNOWN="Unknown",
-MENUTASKING=" Tasking ",
-MENUACTIVE="Active Task",
-MENUINFO="Info",
-MENUMARK="Mark on map",
-MENUSMOKE="Smoke",
-MENUFLARE="Flare",
-MENUILLU="Illuminate",
-MENUABORT="Abort",
-MENUJOIN="Join Task",
-MENUTASKINFO="Task Info",
-MENUTASKNO="TaskNo",
-MENUNOTASKS="Currently no tasks available.",
-TASKCANCELLED="Task #%03d %s is cancelled!",
-TASKCANCELLEDTTS="%s, task %03d %s is cancelled!",
-TASKSUCCESS="Task #%03d %s completed successfully!",
-TASKSUCCESSTTS="%s, task %03d %s completed successfully!",
-TASKFAILED="Task #%03d %s was a failure!",
-TASKFAILEDTTS="%s, task %03d %s was a failure!",
-TASKFAILEDREPLAN="Task #%03d %s available for reassignment!",
-TASKFAILEDREPLANTTS="%s, task %03d %s vailable for reassignment!",
-TASKADDED="%s has a new %s task available!",
-PILOTS="\nPilot(s): ",
-PILOTSTTS=". Pilot(s): ",
-YES="Yes",
-NO="No",
-NONE="None",
-POINTEROVERTARGET="%s, %s, pointer in reach for task %03d, lasing!",
-POINTERTARGETREPORT="\nPointer in reach: %s\nLasing: %s",
-RECCETARGETREPORT="\nRecce %s in reach: %s\nLasing: %s",
-POINTERTARGETLASINGTTS=". Pointer in reach and lasing.",
-TARGET="Target",
-FLASHON="%s - Flashing directions is now ON!",
-FLASHOFF="%s - Flashing directions is now OFF!",
-FLASHMENU="Flash Directions Switch",
-BRIEFING="Briefing",
-TARGETLOCATION="Target location",
-COORDINATE="Coordinate",
-INFANTRY="Infantry",
-TECHNICAL="Technical",
-ARTILLERY="Artillery",
-TANKS="Tanks",
-AIRDEFENSE="Airdefense",
-SAM="SAM",
-GROUP="Group",
-UNARMEDSHIP="Merchant",
-LIGHTARMEDSHIP="Light Boat",
-CORVETTE="Corvette",
-FRIGATE="Frigate",
-CRUISER="Cruiser",
-DESTROYER="Destroyer",
-CARRIER="Aircraft Carrier",
-},
-DE={
-TASKABORT="Auftrag abgebrochen!",
-NOACTIVETASK="Kein aktiver Auftrag!",
-FREQUENCIES="Frequenzen ",
-FREQUENCY="Frequenz %.3f",
-BROADCAST="%s, %s, Radio %s für Aufgabenzuteilung!",
-CASTTS="Nahbereichsunterstützung",
-SEADTTS="Luftabwehr ausschalten",
-BOMBTTS="Bombardieren",
-PRECBOMBTTS="Präzisionsbombardieren",
-BAITTS="Luftunterstützung",
-ANTISHIPTTS="Anti-Schiff",
-INTERCEPTTS="Abfangen",
-BOMBRUNWAYTTS="Startbahn Bombardieren",
-HAVEACTIVETASK="Du hast einen aktiven Auftrag! Beende ihn zuerst!",
-PILOTJOINEDTASK="%s, %s hat Auftrag %s %03d angenommen",
-TASKNAME="%s Auftrag ID %03d",
-TASKNAMETTS="%s Auftrag ID %03d",
-THREATHIGH="hoch",
-THREATMEDIUM="mittel",
-THREATLOW="niedrig",
-THREATTEXT="%s\nGefahrstufe: %s\nZiele: %d\nKoord: %s",
-THREATTEXTTTS="%s, %s. Zielinformation zu %s. Gefahrstufe %s. Ziele %d. Zielposition %s.",
-MARKTASK="%s, %s, verstanden, Zielposition %03d auf der Karte markiert!",
-SMOKETASK="%s, %s, verstanden, Zielposition %03d mit Rauch markiert!",
-FLARETASK="%s, %s, verstanden, Zielposition %03d beleuchtet!",
-ABORTTASK="%s, an alle, %s hat Auftrag %s %03d abgebrochen!",
-UNKNOWN="Unbekannt",
-MENUTASKING=" Aufträge ",
-MENUACTIVE="Aktiver Auftrag",
-MENUINFO="Information",
-MENUMARK="Kartenmarkierung",
-MENUSMOKE="Rauchgranate",
-MENUFLARE="Leuchtgranate",
-MENUILLU="Feldbeleuchtung",
-MENUABORT="Abbrechen",
-MENUJOIN="Auftrag annehmen",
-MENUTASKINFO="Auftrag Briefing",
-MENUTASKNO="AuftragsNr",
-MENUNOTASKS="Momentan keine Aufträge verfügbar.",
-TASKCANCELLED="Auftrag #%03d %s wurde beendet!",
-TASKCANCELLEDTTS="%s, Auftrag %03d %s wurde beendet!",
-TASKSUCCESS="Auftrag #%03d %s erfolgreich!",
-TASKSUCCESSTTS="%s, Auftrag %03d %s erfolgreich!",
-TASKFAILED="Auftrag #%03d %s gescheitert!",
-TASKFAILEDTTS="%s, Auftrag %03d %s gescheitert!",
-TASKFAILEDREPLAN="Auftrag #%03d %s gescheitert! Neuplanung!",
-TASKFAILEDREPLANTTS="%s, Auftrag %03d %s gescheitert! Neuplanung!",
-TASKADDED="%s hat einen neuen Auftrag %s erstellt!",
-PILOTS="\nPilot(en): ",
-PILOTSTTS=". Pilot(en): ",
-YES="Ja",
-NO="Nein",
-NONE="Keine",
-POINTEROVERTARGET="%s, %s, Marker im Zielbereich für %03d, Laser an!",
-POINTERTARGETREPORT="\nMarker im Zielbereich: %s\nLaser an: %s",
-RECCETARGETREPORT="\nSpäher % im Zielbereich: %s\nLasing: %s",
-POINTERTARGETLASINGTTS=". Marker im Zielbereich, Laser is an.",
-TARGET="Ziel",
-FLASHON="%s - Richtungsangaben einblenden ist EIN!",
-FLASHOFF="%s - Richtungsangaben einblenden ist AUS!",
-FLASHMENU="Richtungsangaben Schalter",
-BRIEFING="Briefing",
-TARGETLOCATION="Zielposition",
-COORDINATE="Koordinate",
-INFANTRY="Infantrie",
-TECHNICAL="Technische",
-ARTILLERY="Artillerie",
-TANKS="Panzer",
-AIRDEFENSE="Flak",
-SAM="Luftabwehr",
-GROUP="Einheit",
-UNARMEDSHIP="Handelsschiff",
-LIGHTARMEDSHIP="Tender",
-CORVETTE="Korvette",
-FRIGATE="Fregatte",
-CRUISER="Kreuzer",
-DESTROYER="Zerstörer",
-CARRIER="Flugzeugträger",
-},
-}
-PLAYERTASKCONTROLLER.version="0.1.63"
-function PLAYERTASKCONTROLLER:New(Name,Coalition,Type,ClientFilter)
-local self=BASE:Inherit(self,FSM:New())
-self.Name=Name or"CentCom"
-self.Coalition=Coalition or coalition.side.BLUE
-self.CoalitionName=UTILS.GetCoalitionName(Coalition)
-self.Type=Type or PLAYERTASKCONTROLLER.Type.A2G
-self.usecluster=false
-if self.Type==PLAYERTASKCONTROLLER.Type.A2A then
-self.usecluster=true
-end
-self.ClusterRadius=0.5
-self.TargetRadius=500
-self.ClientFilter=ClientFilter
-self.TargetQueue=FIFO:New()
-self.TaskQueue=FIFO:New()
-self.TasksPerPlayer=FIFO:New()
-self.PrecisionTasks=FIFO:New()
-self.FlashPlayer={}
-self.AllowFlash=false
-self.lasttaskcount=0
-self.taskinfomenu=false
-self.activehasinfomenu=false
-self.MenuName=nil
-self.menuitemlimit=5
-self.holdmenutime=30
-self.MarkerReadOnly=false
-self.repeatonfailed=true
-self.repeattimes=5
-self.UseGroupNames=true
-self.customcallsigns={}
-self.ShortCallsign=true
-self.Keepnumber=false
-self.CallsignTranslations=nil
-self.noflaresmokemenu=false
-self.illumenu=false
-self.ShowMagnetic=true
-self.UseTypeNames=false
-self.IsClientSet=false
-if ClientFilter and type(ClientFilter)=="table"and ClientFilter.ClassName and ClientFilter.ClassName=="SET_CLIENT"then
-self.ClientSet=ClientFilter
-self.IsClientSet=true
-end
-if ClientFilter and not self.IsClientSet then
-self.ClientSet=SET_CLIENT:New():FilterCoalitions(string.lower(self.CoalitionName)):FilterActive(true):FilterPrefixes(ClientFilter):FilterStart()
-elseif not self.IsClientSet then
-self.ClientSet=SET_CLIENT:New():FilterCoalitions(string.lower(self.CoalitionName)):FilterActive(true):FilterStart()
-end
-self.ActiveClientSet=SET_CLIENT:New()
-self.lid=string.format("PlayerTaskController %s %s | ",self.Name,tostring(self.Type))
-self:_InitLocalization()
-self:SetStartState("Stopped")
-self:AddTransition("Stopped","Start","Running")
-self:AddTransition("*","Status","*")
-self:AddTransition("*","TaskAdded","*")
-self:AddTransition("*","TaskDone","*")
-self:AddTransition("*","TaskCancelled","*")
-self:AddTransition("*","TaskSuccess","*")
-self:AddTransition("*","TaskFailed","*")
-self:AddTransition("*","TaskProgress","*")
-self:AddTransition("*","TaskTargetSmoked","*")
-self:AddTransition("*","TaskTargetFlared","*")
-self:AddTransition("*","TaskTargetIlluminated","*")
-self:AddTransition("*","TaskRepeatOnFailed","*")
-self:AddTransition("*","PlayerJoinedTask","*")
-self:AddTransition("*","PlayerAbortedTask","*")
-self:AddTransition("*","Stop","Stopped")
-self:__Start(2)
-local starttime=math.random(5,10)
-self:__Status(starttime)
-self:I(self.lid..self.version.." Started.")
-return self
-end
-function PLAYERTASKCONTROLLER:EnableScoring(Scoring)
-self.Scoring=Scoring or SCORING:New(self.Name)
-return self
-end
-function PLAYERTASKCONTROLLER:DisableScoring()
-self.Scoring=nil
-return self
-end
-function PLAYERTASKCONTROLLER:_InitLocalization()
-self:T(self.lid.."_InitLocalization")
-self.gettext=TEXTANDSOUND:New("PLAYERTASKCONTROLLER","en")
-self.locale="en"
-for locale,table in pairs(self.Messages)do
-local Locale=string.lower(tostring(locale))
-self:T("**** Adding locale: "..Locale)
-for ID,Text in pairs(table)do
-self:T(string.format('Adding ID %s',tostring(ID)))
-self.gettext:AddEntry(Locale,tostring(ID),Text)
-end
-end
-return self
-end
-function PLAYERTASKCONTROLLER:SetEnableUseTypeNames()
-self:T(self.lid.."SetEnableUseTypeNames")
-self.UseTypeNames=true
-return self
-end
-function PLAYERTASKCONTROLLER:SetDisableUseTypeNames()
-self:T(self.lid.."SetDisableUseTypeNames")
-self.UseTypeNames=false
-return self
-end
-function PLAYERTASKCONTROLLER:SetAllowFlashDirection(OnOff)
-self:T(self.lid.."SetAllowFlashDirection")
-self.AllowFlash=OnOff
-return self
-end
-function PLAYERTASKCONTROLLER:SetDisableSmokeFlareTask()
-self:T(self.lid.."SetDisableSmokeFlareTask")
-self.noflaresmokemenu=true
-return self
-end
-function PLAYERTASKCONTROLLER:SetTransmitOnlyWithPlayers(Switch)
-self.TransmitOnlyWithPlayers=Switch
-if self.SRSQueue then
-self.SRSQueue:SetTransmitOnlyWithPlayers(Switch)
-end
-return self
-end
-function PLAYERTASKCONTROLLER:SetEnableSmokeFlareTask()
-self:T(self.lid.."SetEnableSmokeFlareTask")
-self.noflaresmokemenu=false
-return self
-end
-function PLAYERTASKCONTROLLER:SetEnableIlluminateTask()
-self:T(self.lid.."SetEnableSmokeFlareTask")
-self.illumenu=true
-return self
-end
-function PLAYERTASKCONTROLLER:SetDisableIlluminateTask()
-self:T(self.lid.."SetDisableIlluminateTask")
-self.illumenu=false
-return self
-end
-function PLAYERTASKCONTROLLER:SetInfoShowsCoordinate(OnOff,LLDDM)
-self:T(self.lid.."SetInfoShowsCoordinate")
-self.InfoHasCoordinate=OnOff
-self.InfoHasLLDDM=LLDDM
-return self
-end
-function PLAYERTASKCONTROLLER:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTranslations)
-if not ShortCallsign or ShortCallsign==false then
-self.ShortCallsign=false
-else
-self.ShortCallsign=true
-end
-self.Keepnumber=Keepnumber or false
-self.CallsignTranslations=CallsignTranslations
-return self
-end
-function PLAYERTASKCONTROLLER:_GetTextForSpeech(text)
-self:T(self.lid.."_GetTextForSpeech")
-text=string.gsub(text,"%d","%1 ")
-text=string.gsub(text,"^%s*","")
-text=string.gsub(text,"%s*$","")
-text=string.gsub(text," "," ")
-return text
-end
-function PLAYERTASKCONTROLLER:SetTaskRepetition(OnOff,Repeats)
-self:T(self.lid.."SetTaskRepetition")
-if OnOff then
-self.repeatonfailed=true
-self.repeattimes=Repeats or 5
-else
-self.repeatonfailed=false
-self.repeattimes=Repeats or 5
-end
-return self
-end
-function PLAYERTASKCONTROLLER:_SendMessageToClients(Text,Seconds)
-self:T(self.lid.."_SendMessageToClients")
-local seconds=Seconds or 10
-self.ClientSet:ForEachClient(
-function(Client)
-local m=MESSAGE:New(Text,seconds,"Tasking"):ToClient(Client)
-end
-)
-return self
-end
-function PLAYERTASKCONTROLLER:EnablePrecisionBombing(FlightGroup,LaserCode)
-self:T(self.lid.."EnablePrecisionBombing")
-if FlightGroup then
-if FlightGroup.ClassName and(FlightGroup.ClassName=="FLIGHTGROUP"or FlightGroup.ClassName=="ARMYGROUP")then
-self.LasingDrone=FlightGroup
-self.LasingDrone.playertask={}
-self.LasingDrone.playertask.busy=false
-self.LasingDrone.playertask.id=0
-self.precisionbombing=true
-self.LasingDrone:SetLaser(LaserCode)
-self.LaserCode=LaserCode or 1688
-self.LasingDroneTemplate=self.LasingDrone:_GetTemplate(true)
-if self.LasingDrone:IsFlightgroup()then
-local BullsCoordinate=COORDINATE:NewFromVec3(coalition.getMainRefPoint(self.Coalition))
-local Orbit=AUFTRAG:NewORBIT_CIRCLE(BullsCoordinate,10000,120)
-self.LasingDrone:AddMission(Orbit)
-end
-else
-self:E(self.lid.."No FLIGHTGROUP object passed or FLIGHTGROUP is not alive!")
-end
-else
-self.autolase=nil
-self.precisionbombing=false
-end
-return self
-end
-function PLAYERTASKCONTROLLER:EnableBuddyLasing(Recce)
-self:T(self.lid.."EnableBuddyLasing")
-self.buddylasing=true
-self.PlayerRecce=Recce
-return self
-end
-function PLAYERTASKCONTROLLER:DisableBuddyLasing()
-self:T(self.lid.."DisableBuddyLasing")
-self.buddylasing=false
-return self
-end
-function PLAYERTASKCONTROLLER:EnableMarkerOps(Tag)
-self:T(self.lid.."EnableMarkerOps")
-local tag=Tag or"TASK"
-local MarkerOps=MARKEROPS_BASE:New(tag,{"Name","Text"},true)
-local function Handler(Keywords,Coord,Text)
-if self.verbose then
-local m=MESSAGE:New(string.format("Target added from marker at: %s",Coord:ToStringA2G(nil,nil,self.ShowMagnetic)),15,"INFO"):ToAll()
-local m=MESSAGE:New(string.format("Text: %s",Text),15,"INFO"):ToAll()
-end
-local menuname=string.match(Text,"Name=(.+),")
-local freetext=string.match(Text,"Text=(.+)")
-if menuname then
-Coord.menuname=menuname
-if freetext then
-Coord.freetext=freetext
-end
-end
-self:AddTarget(Coord)
-end
-function MarkerOps:OnAfterMarkAdded(From,Event,To,Text,Keywords,Coord)
-Handler(Keywords,Coord,Text)
-end
-function MarkerOps:OnAfterMarkChanged(From,Event,To,Text,Keywords,Coord)
-Handler(Keywords,Coord,Text)
-end
-self.MarkerOps=MarkerOps
-return self
-end
-function PLAYERTASKCONTROLLER:_GetPlayerName(Client)
-self:T(self.lid.."_GetPlayerName")
-local playername=Client:GetPlayerName()
-local ttsplayername=nil
-if not self.customcallsigns[playername]then
-local playergroup=Client:GetGroup()
-ttsplayername=playergroup:GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
-local newplayername=self:_GetTextForSpeech(ttsplayername)
-self.customcallsigns[playername]=newplayername
-ttsplayername=newplayername
-else
-ttsplayername=self.customcallsigns[playername]
-end
-return playername,ttsplayername
-end
-function PLAYERTASKCONTROLLER:DisablePrecisionBombing(FlightGroup,LaserCode)
-self:T(self.lid.."DisablePrecisionBombing")
-self.autolase=nil
-self.precisionbombing=false
-return self
-end
-function PLAYERTASKCONTROLLER:EnableTaskInfoMenu()
-self:T(self.lid.."EnableTaskInfoMenu")
-self.taskinfomenu=true
-return self
-end
-function PLAYERTASKCONTROLLER:DisableTaskInfoMenu()
-self:T(self.lid.."DisableTaskInfoMenu")
-self.taskinfomenu=false
-return self
-end
-function PLAYERTASKCONTROLLER:SetMenuOptions(InfoMenu,ItemLimit,HoldTime)
-self:T(self.lid.."SetMenuOptions")
-self.activehasinfomenu=InfoMenu or false
-if self.activehasinfomenu then
-self:EnableTaskInfoMenu()
-end
-self.menuitemlimit=ItemLimit or 5
-self.holdmenutime=HoldTime or 30
-return self
-end
-function PLAYERTASKCONTROLLER:SetMarkerReadOnly()
-self:T(self.lid.."SetMarkerReadOnly")
-self.MarkerReadOnly=true
-return self
-end
-function PLAYERTASKCONTROLLER:SetMarkerDeleteable()
-self:T(self.lid.."SetMarkerDeleteable")
-self.MarkerReadOnly=false
-return self
-end
-function PLAYERTASKCONTROLLER:_EventHandler(EventData)
-self:T(self.lid.."_EventHandler: "..EventData.id)
-if EventData.id==EVENTS.PlayerLeaveUnit or EventData.id==EVENTS.Ejection or EventData.id==EVENTS.Crash or EventData.id==EVENTS.PilotDead then
-if EventData.IniPlayerName then
-self:T(self.lid.."Event for player: "..EventData.IniPlayerName)
-local text=""
-if self.TasksPerPlayer:HasUniqueID(EventData.IniPlayerName)then
-local task=self.TasksPerPlayer:PullByID(EventData.IniPlayerName)
-local Client=_DATABASE:FindClient(EventData.IniPlayerName)
-if Client then
-task:RemoveClient(Client)
-text=self.gettext:GetEntry("TASKABORT",self.locale)
-self.ActiveTaskMenuTemplate:ResetMenu(Client)
-self.JoinTaskMenuTemplate:ResetMenu(Client)
-else
-task:RemoveClient(nil,EventData.IniPlayerName)
-text=self.gettext:GetEntry("TASKABORT",self.locale)
-end
-else
-text=self.gettext:GetEntry("NOACTIVETASK",self.locale)
-end
-self:T(self.lid..text)
-end
-elseif EventData.id==EVENTS.PlayerEnterAircraft and EventData.IniCoalition==self.Coalition then
-if EventData.IniPlayerName and EventData.IniGroup then
-if self.IsClientSet and(not self.ClientSet:IsIncludeObject(CLIENT:FindByName(EventData.IniUnitName)))then
-self:T(self.lid.."Client not in SET: "..EventData.IniPlayerName)
-return self
-end
-self:T(self.lid.."Event for player: "..EventData.IniPlayerName)
-if self.UseSRS then
-local frequency=self.Frequency
-local freqtext=""
-if type(frequency)=="table"then
-freqtext=self.gettext:GetEntry("FREQUENCIES",self.locale)
-freqtext=freqtext..table.concat(frequency,", ")
-else
-local freqt=self.gettext:GetEntry("FREQUENCY",self.locale)
-freqtext=string.format(freqt,frequency)
-end
-local modulation=self.Modulation
-if type(modulation)=="table"then modulation=modulation[1]end
-modulation=UTILS.GetModulationName(modulation)
-local switchtext=self.gettext:GetEntry("BROADCAST",self.locale)
-local playername=EventData.IniPlayerName
-if EventData.IniGroup then
-if self.customcallsigns[playername]then
-self.customcallsigns[playername]=nil
-end
-playername=EventData.IniGroup:GetCustomCallSign(self.ShortCallsign,self.Keepnumber)
-end
-playername=self:_GetTextForSpeech(playername)
-local text=string.format(switchtext,playername,self.MenuName or self.Name,freqtext)
-self.SRSQueue:NewTransmission(text,nil,self.SRS,timer.getAbsTime()+60,2,{EventData.IniGroup},text,30,self.BCFrequency,self.BCModulation)
-end
-if EventData.IniPlayerName then
-local player=_DATABASE:FindClient(EventData.IniUnitName)
-self:_SwitchMenuForClient(player,"Info")
-end
-end
-end
-return self
-end
-function PLAYERTASKCONTROLLER:SetLocale(Locale)
-self:T(self.lid.."SetLocale")
-self.locale=Locale or"en"
-return self
-end
-function PLAYERTASKCONTROLLER:SuppressScreenOutput(OnOff)
-self:T(self.lid.."SuppressScreenOutput")
-self.NoScreenOutput=OnOff or false
-return self
-end
-function PLAYERTASKCONTROLLER:SetTargetRadius(Radius)
-self:T(self.lid.."SetTargetRadius")
-self.TargetRadius=Radius or 500
-return self
-end
-function PLAYERTASKCONTROLLER:SetClusterRadius(Radius)
-self:T(self.lid.."SetClusterRadius")
-self.ClusterRadius=Radius or 0.5
-self.usecluster=true
-return self
-end
-function PLAYERTASKCONTROLLER:CancelTask(Task)
-self:T(self.lid.."CancelTask")
-Task:__Cancel(-1)
-return self
-end
-function PLAYERTASKCONTROLLER:SwitchUseGroupNames(OnOff)
-self:T(self.lid.."SwitchUseGroupNames")
-if OnOff then
-self.UseGroupNames=true
-else
-self.UseGroupNames=false
-end
-return self
-end
-function PLAYERTASKCONTROLLER:SwitchMagenticAngles(OnOff)
-self:T(self.lid.."SwitchMagenticAngles")
-if OnOff then
-self.ShowMagnetic=true
-else
-self.ShowMagnetic=false
-end
-return self
-end
-function PLAYERTASKCONTROLLER:_GetAvailableTaskTypes()
-self:T(self.lid.."_GetAvailableTaskTypes")
-local tasktypes={}
-self.TaskQueue:ForEach(
-function(Task)
-local task=Task
-local type=Task.Type
-tasktypes[type]={}
-end
-)
-return tasktypes
-end
-function PLAYERTASKCONTROLLER:_GetTasksPerType()
-self:T(self.lid.."_GetTasksPerType")
-local tasktypes=self:_GetAvailableTaskTypes()
-local datatable=self.TaskQueue:GetDataTable()
-local threattable={}
-for _,_task in pairs(datatable)do
-local task=_task
-local threat=task.Target:GetThreatLevelMax()
-if not task:IsDone()then
-threattable[#threattable+1]={task=task,threat=threat}
-end
-end
-table.sort(threattable,function(k1,k2)return k1.threat>k2.threat end)
-for _id,_data in pairs(threattable)do
-local threat=_data.threat
-local task=_data.task
-local type=task.Type
-local name=task.Target:GetName()
-if not task:IsDone()then
-table.insert(tasktypes[type],task)
-end
-end
-return tasktypes
-end
-function PLAYERTASKCONTROLLER:_CheckTargetQueue()
-self:T(self.lid.."_CheckTargetQueue")
-if self.TargetQueue:Count()>0 then
-local object=self.TargetQueue:Pull()
-local target=TARGET:New(object)
-if object.menuname then
-target.menuname=object.menuname
-if object.freetext then
-target.freetext=object.freetext
-end
-end
-if object:IsInstanceOf("UNIT")or object:IsInstanceOf("GROUP")then
-if self.UseTypeNames and object:IsGround()then
-local threat=object:GetThreatLevel()
-local typekey="INFANTRY"
-if threat==0 or threat==2 then
-typekey="TECHNICAL"
-elseif threat==3 then
-typekey="ARTILLERY"
-elseif threat==4 or threat==5 then
-typekey="TANKS"
-elseif threat==6 or threat==7 then
-typekey="AIRDEFENSE"
-elseif threat>=8 then
-typekey="SAM"
-end
-local typename=self.gettext:GetEntry(typekey,self.locale)
-local gname=self.gettext:GetEntry("GROUP",self.locale)
-target.TypeName=string.format("%s %s",typename,gname)
-end
-if self.UseTypeNames and object:IsShip()then
-local threat=object:GetThreatLevel()
-local typekey="UNARMEDSHIP"
-if threat==1 then
-typekey="LIGHTARMEDSHIP"
-elseif threat==2 then
-typekey="CORVETTE"
-elseif threat==3 or threat==4 then
-typekey="FRIGATE"
-elseif threat==5 or threat==6 then
-typekey="CRUISER"
-elseif threat==7 or threat==8 then
-typekey="DESTROYER"
-elseif threat>=9 then
-typekey="CARRIER"
-end
-local typename=self.gettext:GetEntry(typekey,self.locale)
-target.TypeName=typename
-end
-end
-self:_AddTask(target)
-end
-return self
-end
-function PLAYERTASKCONTROLLER:_CheckTaskQueue()
-self:T(self.lid.."_CheckTaskQueue")
-if self.TaskQueue:Count()>0 then
-local tasks=self.TaskQueue:GetIDStack()
-for _id,_entry in pairs(tasks)do
-local data=_entry.data
-self:T("Looking at Task: "..data.PlayerTaskNr.." Type: "..data.Type.." State: "..data:GetState())
-if data:GetState()=="Done"or data:GetState()=="Stopped"then
-local task=self.TaskQueue:ReadByID(_id)
-local clientsattask=task.Clients:GetIDStackSorted()
-for _,_id in pairs(clientsattask)do
-self:T("*****Removing player ".._id)
-self.TasksPerPlayer:PullByID(_id)
-end
-local clients=task:GetClientObjects()
-for _,client in pairs(clients)do
-self:_RemoveMenuEntriesForTask(task,client)
-end
-for _,client in pairs(clients)do
-self:_SwitchMenuForClient(client,"Info",5)
-end
-local nexttasks={}
-if task.FinalState=="Success"then
-nexttasks=task.NextTaskSuccess
-elseif task.FinalState=="Failed"then
-nexttasks=task.NextTaskFailure
-end
-local clientlist,count=task:GetClientObjects()
-if count>0 then
-for _,_client in pairs(clientlist)do
-local client=_client
-local group=client:GetGroup()
-for _,task in pairs(nexttasks)do
-self:_JoinTask(task,true,group,client)
-end
-end
-end
-local TNow=timer.getAbsTime()
-if TNow-task.timestamp>5 then
-self:_RemoveMenuEntriesForTask(task)
-local task=self.TaskQueue:PullByID(_id)
-task=nil
-end
-end
-end
-end
-return self
-end
-function PLAYERTASKCONTROLLER:_CheckPrecisionTasks()
-self:T(self.lid.."_CheckPrecisionTasks")
-if self.PrecisionTasks:Count()>0 and self.precisionbombing then
-if not self.LasingDrone or self.LasingDrone:IsDead()then
-self:E(self.lid.."Lasing drone is dead ... creating a new one!")
-if self.LasingDrone then
-self.LasingDrone:_Respawn(1,nil,true)
-else
-local FG=FLIGHTGROUP:New(self.LasingDroneTemplate)
-FG:Activate()
-self:EnablePrecisionBombing(FG,self.LaserCode or 1688)
-end
-return self
-end
-if self.LasingDrone and self.LasingDrone:IsAlive()then
-if self.LasingDrone.playertask and(not self.LasingDrone.playertask.busy)then
-self:T(self.lid.."Sending lasing unit to target")
-local task=self.PrecisionTasks:Pull()
-self.LasingDrone.playertask.id=task.PlayerTaskNr
-self.LasingDrone.playertask.busy=true
-self.LasingDrone.playertask.inreach=false
-self.LasingDrone.playertask.reachmessage=false
-if self.LasingDrone:IsFlightgroup()then
-local auftrag=AUFTRAG:NewORBIT_CIRCLE(task.Target:GetCoordinate(),10000,120)
-local currmission=self.LasingDrone:GetMissionCurrent()
-self.LasingDrone:AddMission(auftrag)
-currmission:__Cancel(-2)
-elseif self.LasingDrone:IsArmygroup()then
-local tgtcoord=task.Target:GetCoordinate()
-local tgtzone=ZONE_RADIUS:New("ArmyGroup-"..math.random(1,10000),tgtcoord:GetVec2(),3000)
-local finalpos=nil
-for i=1,50 do
-finalpos=tgtzone:GetRandomCoordinate(2500,0,{land.SurfaceType.LAND,land.SurfaceType.ROAD,land.SurfaceType.SHALLOW_WATER})
-if finalpos then
-if finalpos:IsLOS(tgtcoord,0)then
-break
-end
-end
-end
-if finalpos then
-local auftrag=AUFTRAG:NewARMOREDGUARD(finalpos,"Off road")
-local currmission=self.LasingDrone:GetMissionCurrent()
-self.LasingDrone:AddMission(auftrag)
-if currmission then currmission:__Cancel(-2)end
-else
-self:E("***Could not find LOS position to post ArmyGroup for lasing!")
-self.LasingDrone.playertask.id=0
-self.LasingDrone.playertask.busy=false
-self.LasingDrone.playertask.inreach=false
-self.LasingDrone.playertask.reachmessage=false
-end
-end
-self.PrecisionTasks:Push(task,task.PlayerTaskNr)
-elseif self.LasingDrone.playertask and self.LasingDrone.playertask.busy then
-local task=self.PrecisionTasks:ReadByID(self.LasingDrone.playertask.id)
-self:T("Looking at Task: "..task.PlayerTaskNr.." Type: "..task.Type.." State: "..task:GetState())
-if(not task)or task:GetState()=="Done"or task:GetState()=="Stopped"then
-local task=self.PrecisionTasks:PullByID(self.LasingDrone.playertask.id)
-self:_CheckTaskQueue()
-task=nil
-if self.LasingDrone:IsLasing()then
-self.LasingDrone:__LaserOff(-1)
-end
-self.LasingDrone.playertask.busy=false
-self.LasingDrone.playertask.inreach=false
-self.LasingDrone.playertask.id=0
-self.LasingDrone.playertask.reachmessage=false
-self:T(self.lid.."Laser Off")
-else
-local dcoord=self.LasingDrone:GetCoordinate()
-local tcoord=task.Target:GetCoordinate()
-local dist=dcoord:Get2DDistance(tcoord)
-if dist<3000 and not self.LasingDrone:IsLasing()then
-self:T(self.lid.."Laser On")
-self.LasingDrone:__LaserOn(-1,tcoord)
-self.LasingDrone.playertask.inreach=true
-if not self.LasingDrone.playertask.reachmessage then
-self.LasingDrone.playertask.reachmessage=true
-local clients=task:GetClients()
-local text=""
-for _,playername in pairs(clients)do
-local pointertext=self.gettext:GetEntry("POINTEROVERTARGET",self.locale)
-local ttsplayername=playername
-if self.customcallsigns[playername]then
-ttsplayername=self.customcallsigns[playername]
-end
-text=string.format(pointertext,ttsplayername,self.MenuName or self.Name,task.PlayerTaskNr)
-if not self.NoScreenOutput then
-local client=nil
-self.ClientSet:ForEachClient(
-function(Client)
-if Client:GetPlayerName()==playername then client=Client end
-end
-)
-if client then
-local m=MESSAGE:New(text,15,"Tasking"):ToClient(client)
-end
-end
-end
-if self.UseSRS then
-self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2)
-end
-end
-end
-end
-end
-end
-end
-return self
-end
-function PLAYERTASKCONTROLLER:_CheckPlayerHasTask(PlayerName)
-self:T(self.lid.."_CheckPlayerHasTask")
-return self.TasksPerPlayer:HasUniqueID(PlayerName)
-end
-function PLAYERTASKCONTROLLER:AddTarget(Target)
-self:T(self.lid.."AddTarget")
-self.TargetQueue:Push(Target)
-return self
-end
-function PLAYERTASKCONTROLLER:_CheckTaskTypeAllowed(Type)
-self:T(self.lid.."_CheckTaskTypeAllowed")
-local Outcome=false
-if self.UseWhiteList then
-for _,_type in pairs(self.WhiteList)do
-if Type==_type then
-Outcome=true
-break
-end
-end
-else
-return true
-end
-return Outcome
-end
-function PLAYERTASKCONTROLLER:_CheckTaskTypeDisallowed(Type)
-self:T(self.lid.."_CheckTaskTypeDisallowed")
-local Outcome=false
-if self.UseBlackList then
-for _,_type in pairs(self.BlackList)do
-if Type==_type then
-Outcome=true
-break
-end
-end
-else
-return true
-end
-return Outcome
-end
-function PLAYERTASKCONTROLLER:SetTaskWhiteList(WhiteList)
-self:T(self.lid.."SetTaskWhiteList")
-self.WhiteList=WhiteList
-self.UseWhiteList=true
-return self
-end
-function PLAYERTASKCONTROLLER:SetTaskBlackList(BlackList)
-self:T(self.lid.."SetTaskBlackList")
-self.BlackList=BlackList
-self.UseBlackList=true
-return self
-end
-function PLAYERTASKCONTROLLER:SetSEADAttributes(Attributes)
-self:T(self.lid.."SetSEADAttributes")
-if type(Attributes)~="table"then
-Attributes={Attributes}
-end
-self.SeadAttributes=Attributes
-return self
-end
-function PLAYERTASKCONTROLLER:_IsAttributeSead(Attribute)
-self:T(self.lid.."_IsAttributeSead?")
-local IsSead=false
-for _,_attribute in pairs(self.SeadAttributes)do
-if Attribute==_attribute then
-IsSead=true
-break
-end
-end
-return IsSead
-end
-function PLAYERTASKCONTROLLER:_AddTask(Target)
-self:T(self.lid.."_AddTask")
-local cat=Target:GetCategory()
-local threat=Target:GetThreatLevelMax()
-local type=AUFTRAG.Type.CAS
-local ttstype=self.gettext:GetEntry("CASTTS",self.locale)
-if cat==TARGET.Category.GROUND then
-type=AUFTRAG.Type.CAS
-local targetobject=Target:GetObject()
-if targetobject:IsInstanceOf("UNIT")then
-self:T("SEAD Check UNIT")
-if targetobject:HasSEAD()then
-type=AUFTRAG.Type.SEAD
-ttstype=self.gettext:GetEntry("SEADTTS",self.locale)
-end
-elseif targetobject:IsInstanceOf("GROUP")then
-self:T("SEAD Check GROUP")
-local attribute=targetobject:GetAttribute()
-if self:_IsAttributeSead(attribute)then
-type=AUFTRAG.Type.SEAD
-ttstype=self.gettext:GetEntry("SEADTTS",self.locale)
-end
-elseif targetobject:IsInstanceOf("SET_GROUP")then
-self:T("SEAD Check SET_GROUP")
-targetobject:ForEachGroup(
-function(group)
-local attribute=group:GetAttribute()
-if self:_IsAttributeSead(attribute)then
-type=AUFTRAG.Type.SEAD
-ttstype=self.gettext:GetEntry("SEADTTS",self.locale)
-end
-end
-)
-elseif targetobject:IsInstanceOf("SET_UNIT")then
-self:T("SEAD Check SET_UNIT")
-targetobject:ForEachUnit(
-function(unit)
-if unit:HasSEAD()then
-type=AUFTRAG.Type.SEAD
-ttstype=self.gettext:GetEntry("SEADTTS",self.locale)
-end
-end
-)
-elseif targetobject:IsInstanceOf("SET_STATIC")or targetobject:IsInstanceOf("STATIC")then
-self:T("(PRECISION-)BOMBING SET_STATIC or STATIC")
-if self.precisionbombing then
-type=AUFTRAG.Type.PRECISIONBOMBING
-ttstype=self.gettext:GetEntry("PRECBOMBTTS",self.locale)
-else
-type=AUFTRAG.Type.BOMBING
-ttstype=self.gettext:GetEntry("BOMBTTS",self.locale)
-end
-end
-local targetcoord=Target:GetCoordinate()
-local targetvec2=targetcoord:GetVec2()
-local targetzone=ZONE_RADIUS:New(self.Name,targetvec2,self.TargetRadius)
-local coalition=targetobject:GetCoalitionName()or"Blue"
-coalition=string.lower(coalition)
-self:T("Target coalition is "..tostring(coalition))
-local filtercoalition="blue"
-if coalition=="blue"then filtercoalition="red"end
-local friendlyset=SET_GROUP:New():FilterCategoryGround():FilterCoalitions(filtercoalition):FilterZones({targetzone}):FilterOnce()
-if friendlyset:Count()==0 and type==AUFTRAG.Type.CAS then
-type=AUFTRAG.Type.BAI
-ttstype=self.gettext:GetEntry("BAITTS",self.locale)
-end
-if(type==AUFTRAG.Type.BAI or type==AUFTRAG.Type.CAS)and(self.precisionbombing or self.buddylasing)then
-if threat>2 and threat<7 then
-type=AUFTRAG.Type.PRECISIONBOMBING
-ttstype=self.gettext:GetEntry("PRECBOMBTTS",self.locale)
-end
-end
-elseif cat==TARGET.Category.NAVAL then
-type=AUFTRAG.Type.ANTISHIP
-ttstype=self.gettext:GetEntry("ANTISHIPTTS",self.locale)
-elseif cat==TARGET.Category.AIRCRAFT then
-type=AUFTRAG.Type.INTERCEPT
-ttstype=self.gettext:GetEntry("INTERCEPTTS",self.locale)
-elseif cat==TARGET.Category.AIRBASE then
-type=AUFTRAG.Type.BOMBRUNWAY
-ttstype=self.gettext:GetEntry("BOMBRUNWAYTTS",self.locale)
-elseif cat==TARGET.Category.COORDINATE or cat==TARGET.Category.ZONE then
-local zone=Target:GetObject()
-if cat==TARGET.Category.COORDINATE then
-zone=ZONE_RADIUS:New("TargetZone-"..math.random(1,10000),Target:GetVec2(),self.TargetRadius)
-end
-local enemies=self.CoalitionName=="Blue"and"red"or"blue"
-local enemysetg=SET_GROUP:New():FilterCoalitions(enemies):FilterCategoryGround():FilterActive(true):FilterZones({zone}):FilterOnce()
-local enemysets=SET_STATIC:New():FilterCoalitions(enemies):FilterZones({zone}):FilterOnce()
-local countg=enemysetg:Count()
-local counts=enemysets:Count()
-if countg>0 then
-if Target.menuname then
-enemysetg.menuname=Target.menuname
-if Target.freetext then
-enemysetg.freetext=Target.freetext
-end
-end
-self:AddTarget(enemysetg)
-end
-if counts>0 then
-if Target.menuname then
-enemysets.menuname=Target.menuname
-if Target.freetext then
-enemysets.freetext=Target.freetext
-end
-end
-self:AddTarget(enemysets)
-end
-return self
-end
-if self.UseWhiteList then
-if not self:_CheckTaskTypeAllowed(type)then
-return self
-end
-end
-if self.UseBlackList then
-if self:_CheckTaskTypeDisallowed(type)then
-return self
-end
-end
-local task=PLAYERTASK:New(type,Target,self.repeatonfailed,self.repeattimes,ttstype)
-if Target.menuname then
-task:SetMenuName(Target.menuname)
-if Target.freetext then
-task:AddFreetext(Target.freetext)
-end
-end
-task.coalition=self.Coalition
-task.TypeName=Target.TypeName
-if type==AUFTRAG.Type.BOMBRUNWAY then
-task:HandleEvent(EVENTS.Shot)
-function task:OnEventShot(EventData)
-local data=EventData
-local wcat=Object.getCategory(data.Weapon)
-local coord=data.IniUnit:GetCoordinate()or data.IniGroup:GetCoordinate()
-local vec2=coord:GetVec2()or{x=0,y=0}
-local coal=data.IniCoalition
-local afbzone=AIRBASE:FindByName(Target:GetName()):GetZone()
-local runways=AIRBASE:FindByName(Target:GetName()):GetRunways()or{}
-local inrunwayzone=false
-for _,_runway in pairs(runways)do
-local runway=_runway
-if runway.zone:IsVec2InZone(vec2)then
-inrunwayzone=true
-end
-end
-local inzone=afbzone:IsVec2InZone(vec2)
-if coal==task.coalition and(wcat==2 or wcat==3)and(inrunwayzone or inzone)then
-task:__Success(-20)
-end
-end
-end
-task:_SetController(self)
-self.TaskQueue:Push(task)
-self:__TaskAdded(10,task)
-return self
-end
-function PLAYERTASKCONTROLLER:AddPlayerTaskToQueue(PlayerTask,Silent,TaskFilter)
-self:T(self.lid.."AddPlayerTaskToQueue")
-if PlayerTask and PlayerTask.ClassName and PlayerTask.ClassName=="PLAYERTASK"then
-if TaskFilter then
-if self.UseWhiteList and(not self:_CheckTaskTypeAllowed(PlayerTask.Type))then
-return self
-end
-if self.UseBlackList and self:_CheckTaskTypeDisallowed(PlayerTask.Type)then
-return self
-end
-end
-PlayerTask:_SetController(self)
-PlayerTask:SetCoalition(self.Coalition)
-self.TaskQueue:Push(PlayerTask)
-if not Silent then
-self:__TaskAdded(10,PlayerTask)
-end
-else
-self:E(self.lid.."***** NO valid PAYERTASK object sent!")
-end
-return self
-end
-function PLAYERTASKCONTROLLER:_JoinTask(Task,Force,Group,Client)
-self:T({Force,Group,Client})
-self:T(self.lid.."_JoinTask")
-local force=false
-if type(Force)=="boolean"then
-force=Force
-end
-local playername,ttsplayername=self:_GetPlayerName(Client)
-if self.TasksPerPlayer:HasUniqueID(playername)and not force then
-if not self.NoScreenOutput then
-local text=self.gettext:GetEntry("HAVEACTIVETASK",self.locale)
-local m=MESSAGE:New(text,"10","Tasking"):ToClient(Client)
-end
-return self
-end
-local taskstate=Task:GetState()
-if not Task:IsDone()then
-if taskstate~="Executing"then
-Task:__Requested(-1)
-Task:__Executing(-2)
-end
-Task:AddClient(Client)
-local joined=self.gettext:GetEntry("PILOTJOINEDTASK",self.locale)
-local text=string.format(joined,ttsplayername,self.MenuName or self.Name,Task.TTSType,Task.PlayerTaskNr)
-self:T(self.lid..text)
-if not self.NoScreenOutput then
-self:_SendMessageToClients(text)
-end
-if self.UseSRS then
-self:T(self.lid..text)
-self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2)
-end
-self.TasksPerPlayer:Push(Task,playername)
-self:__PlayerJoinedTask(1,Group,Client,Task)
-self:_SwitchMenuForClient(Client,"Active",1)
-end
-if Task.Type==AUFTRAG.Type.PRECISIONBOMBING then
-if not self.PrecisionTasks:HasUniqueID(Task.PlayerTaskNr)then
-self.PrecisionTasks:Push(Task,Task.PlayerTaskNr)
-end
-end
-return self
-end
-function PLAYERTASKCONTROLLER:_SwitchFlashing(Group,Client)
-self:T(self.lid.."_SwitchFlashing")
-local playername,ttsplayername=self:_GetPlayerName(Client)
-if(not self.FlashPlayer[playername])or(self.FlashPlayer[playername]==false)then
-self.FlashPlayer[playername]=Client
-local flashtext=self.gettext:GetEntry("FLASHON",self.locale)
-local text=string.format(flashtext,ttsplayername)
-local m=MESSAGE:New(text,10,"Tasking"):ToClient(Client)
-else
-self.FlashPlayer[playername]=false
-local flashtext=self.gettext:GetEntry("FLASHOFF",self.locale)
-local text=string.format(flashtext,ttsplayername)
-local m=MESSAGE:New(text,10,"Tasking"):ToClient(Client)
-end
-return self
-end
-function PLAYERTASKCONTROLLER:_FlashInfo()
-self:T(self.lid.."_FlashInfo")
-for _playername,_client in pairs(self.FlashPlayer)do
-if _client and _client:IsAlive()then
-if self.TasksPerPlayer:HasUniqueID(_playername)then
-local task=self.TasksPerPlayer:ReadByID(_playername)
-local Coordinate=task.Target:GetCoordinate()
-local CoordText=""
-if self.Type~=PLAYERTASKCONTROLLER.Type.A2A then
-CoordText=Coordinate:ToStringA2G(_client,nil,self.ShowMagnetic)
-else
-CoordText=Coordinate:ToStringA2A(_client,nil,self.ShowMagnetic)
-end
-local targettxt=self.gettext:GetEntry("TARGET",self.locale)
-local text="Target: "..CoordText
-local m=MESSAGE:New(text,10,"Tasking"):ToClient(_client)
-end
-end
-end
-return self
-end
-function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Task,Group,Client)
-self:T(self.lid.."_ActiveTaskInfo")
-local playername,ttsplayername=self:_GetPlayerName(Client)
-local text=""
-local textTTS=""
-local task=nil
-if type(Task)~="string"then
-task=Task
-end
-if self.TasksPerPlayer:HasUniqueID(playername)or task then
-local task=task or self.TasksPerPlayer:ReadByID(playername)
-local tname=self.gettext:GetEntry("TASKNAME",self.locale)
-local ttsname=self.gettext:GetEntry("TASKNAMETTS",self.locale)
-local taskname=string.format(tname,task.Type,task.PlayerTaskNr)
-local ttstaskname=string.format(ttsname,task.TTSType,task.PlayerTaskNr)
-local Coordinate=task.Target:GetCoordinate()
-local CoordText=""
-local CoordTextLLDM=nil
-if self.Type~=PLAYERTASKCONTROLLER.Type.A2A then
-CoordText=Coordinate:ToStringA2G(Client,nil,self.ShowMagnetic)
-else
-CoordText=Coordinate:ToStringA2A(Client,nil,self.ShowMagnetic)
-end
-local ThreatLevel=task.Target:GetThreatLevelMax()
-local ThreatLevelText=self.gettext:GetEntry("THREATHIGH",self.locale)
-if ThreatLevel>3 and ThreatLevel<8 then
-ThreatLevelText=self.gettext:GetEntry("THREATMEDIUM",self.locale)
-elseif ThreatLevel<=3 then
-ThreatLevelText=self.gettext:GetEntry("THREATLOW",self.locale)
-end
-local targets=task.Target:CountTargets()or 0
-local clientlist,clientcount=task:GetClients()
-local ThreatGraph="["..string.rep("■",ThreatLevel)..string.rep("□",10-ThreatLevel).."]: "..ThreatLevel
-local ThreatLocaleText=self.gettext:GetEntry("THREATTEXT",self.locale)
-text=string.format(ThreatLocaleText,taskname,ThreatGraph,targets,CoordText)
-if task.Type==AUFTRAG.Type.PRECISIONBOMBING and self.precisionbombing then
-if self.LasingDrone and self.LasingDrone.playertask then
-local yes=self.gettext:GetEntry("YES",self.locale)
-local no=self.gettext:GetEntry("NO",self.locale)
-local inreach=self.LasingDrone.playertask.inreach==true and yes or no
-local islasing=self.LasingDrone:IsLasing()==true and yes or no
-local prectext=self.gettext:GetEntry("POINTERTARGETREPORT",self.locale)
-prectext=string.format(prectext,inreach,islasing)
-text=text..prectext
-end
-end
-if task.Type==AUFTRAG.Type.PRECISIONBOMBING and self.buddylasing then
-if self.PlayerRecce then
-local yes=self.gettext:GetEntry("YES",self.locale)
-local no=self.gettext:GetEntry("NO",self.locale)
-local reachdist=8000
-local inreach=false
-local pset=self.PlayerRecce.PlayerSet:GetAliveSet()
-for _,_player in pairs(pset)do
-local player=_player
-local pcoord=player:GetCoordinate()
-if pcoord:Get2DDistance(Coordinate)<=reachdist then
-inreach=true
-local callsign=player:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
-local playername=player:GetPlayerName()
-local islasing=no
-if self.PlayerRecce.CanLase[player:GetTypeName()]and self.PlayerRecce.AutoLase[playername]then
-islasing=yes
-end
-local inrtext=inreach==true and yes or no
-local prectext=self.gettext:GetEntry("RECCETARGETREPORT",self.locale)
-prectext=string.format(prectext,callsign,inrtext,islasing)
-text=text..prectext
-end
-end
-end
-elseif task.Type==AUFTRAG.Type.CTLD or task.Type==AUFTRAG.Type.CSAR then
-text=taskname
-textTTS=taskname
-local detail=task:GetFreetext()
-local detailTTS=task:GetFreetextTTS()
-local brieftxt=self.gettext:GetEntry("BRIEFING",self.locale)
-local locatxt=self.gettext:GetEntry("TARGETLOCATION",self.locale)
-text=text..string.format("\n%s: %s\n%s %s",brieftxt,detail,locatxt,CoordText)
-textTTS=textTTS..string.format("; %s: %s; %s %s",brieftxt,detailTTS,locatxt,CoordText)
-end
-local clienttxt=self.gettext:GetEntry("PILOTS",self.locale)
-if clientcount>0 then
-for _,_name in pairs(clientlist)do
-if self.customcallsigns[_name]then
-_name=self.customcallsigns[_name]
-_name=string.gsub(_name,"(%d) ","%1")
-end
-clienttxt=clienttxt.._name..", "
-end
-clienttxt=string.gsub(clienttxt,", $",".")
-else
-local keine=self.gettext:GetEntry("NONE",self.locale)
-clienttxt=clienttxt..keine
-end
-text=text..clienttxt
-textTTS=textTTS..clienttxt
-if self.InfoHasCoordinate then
-if self.InfoHasLLDDM then
-CoordTextLLDM=Coordinate:ToStringLLDDM()
-else
-CoordTextLLDM=Coordinate:ToStringLLDMS()
-end
-local locatxt=self.gettext:GetEntry("COORDINATE",self.locale)
-text=string.format("%s\n%s: %s",text,locatxt,CoordTextLLDM)
-end
-if task:HasFreetext()and not(task.Type==AUFTRAG.Type.CTLD or task.Type==AUFTRAG.Type.CSAR)then
-local brieftxt=self.gettext:GetEntry("BRIEFING",self.locale)
-text=text..string.format("\n%s: ",brieftxt)..task:GetFreetext()
-end
-if self.UseSRS then
-if string.find(CoordText," BR, ")then
-CoordText=string.gsub(CoordText," BR, "," Bee, Arr; ")
-end
-if self.ShowMagnetic then
-text=string.gsub(text,"°M|","° magnetic; ")
-end
-if string.find(CoordText,"MGRS")then
-local Text=string.gsub(CoordText,"MGRS ","")
-Text=string.gsub(Text,"%s+","")
-Text=string.gsub(Text,"([%a%d])","%1;")
-Text=string.gsub(Text,"0","zero")
-Text=string.gsub(Text,"9","niner")
-CoordText="MGRS;"..Text
-if self.PathToGoogleKey then
-CoordText=string.format("%s",CoordText)
-end
-end
-local ThreatLocaleTextTTS=self.gettext:GetEntry("THREATTEXTTTS",self.locale)
-local ttstext=string.format(ThreatLocaleTextTTS,ttsplayername,self.MenuName or self.Name,ttstaskname,ThreatLevelText,targets,CoordText)
-if task.Type==AUFTRAG.Type.PRECISIONBOMBING and self.precisionbombing then
-if self.LasingDrone.playertask.inreach and self.LasingDrone:IsLasing()then
-local lasingtext=self.gettext:GetEntry("POINTERTARGETLASINGTTS",self.locale)
-ttstext=ttstext..lasingtext
-end
-elseif task.Type==AUFTRAG.Type.CTLD or task.Type==AUFTRAG.Type.CSAR then
-ttstext=textTTS
-if string.find(ttstext," BR, ")then
-CoordText=string.gsub(ttstext," BR, "," Bee, Arr, ")
-end
-elseif task:HasFreetext()then
-local brieftxt=self.gettext:GetEntry("BRIEFING",self.locale)
-ttstext=ttstext..string.format("; %s: ",brieftxt)..task:GetFreetextTTS()
-end
-self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,2)
-end
-else
-text=self.gettext:GetEntry("NOACTIVETASK",self.locale)
-end
-if not self.NoScreenOutput then
-local m=MESSAGE:New(text,15,"Tasking"):ToClient(Client)
-end
-return self
-end
-function PLAYERTASKCONTROLLER:_MarkTask(Group,Client)
-self:T(self.lid.."_MarkTask")
-local playername,ttsplayername=self:_GetPlayerName(Client)
-local text=""
-if self.TasksPerPlayer:HasUniqueID(playername)then
-local task=self.TasksPerPlayer:ReadByID(playername)
-text=string.format("Task ID #%03d | Type: %s | Threat: %d",task.PlayerTaskNr,task.Type,task.Target:GetThreatLevelMax())
-task:MarkTargetOnF10Map(text,self.Coalition,self.MarkerReadOnly)
-local textmark=self.gettext:GetEntry("MARKTASK",self.locale)
-text=string.format(textmark,ttsplayername,self.MenuName or self.Name,task.PlayerTaskNr)
-self:T(self.lid..text)
-if self.UseSRS then
-self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2)
-end
-else
-text=self.gettext:GetEntry("NOACTIVETASK",self.locale)
-end
-if not self.NoScreenOutput then
-local m=MESSAGE:New(text,"10","Tasking"):ToClient(Client)
-end
-return self
-end
-function PLAYERTASKCONTROLLER:_SmokeTask(Group,Client)
-self:T(self.lid.."_SmokeTask")
-local playername,ttsplayername=self:_GetPlayerName(Client)
-local text=""
-if self.TasksPerPlayer:HasUniqueID(playername)then
-local task=self.TasksPerPlayer:ReadByID(playername)
-task:SmokeTarget()
-local textmark=self.gettext:GetEntry("SMOKETASK",self.locale)
-text=string.format(textmark,ttsplayername,self.MenuName or self.Name,task.PlayerTaskNr)
-self:T(self.lid..text)
-if self.UseSRS then
-self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2)
-end
-self:__TaskTargetSmoked(5,task)
-else
-text=self.gettext:GetEntry("NOACTIVETASK",self.locale)
-end
-if not self.NoScreenOutput then
-local m=MESSAGE:New(text,15,"Tasking"):ToClient(Client)
-end
-return self
-end
-function PLAYERTASKCONTROLLER:_FlareTask(Group,Client)
-self:T(self.lid.."_FlareTask")
-local playername,ttsplayername=self:_GetPlayerName(Client)
-local text=""
-if self.TasksPerPlayer:HasUniqueID(playername)then
-local task=self.TasksPerPlayer:ReadByID(playername)
-task:FlareTarget()
-local textmark=self.gettext:GetEntry("FLARETASK",self.locale)
-text=string.format(textmark,ttsplayername,self.MenuName or self.Name,task.PlayerTaskNr)
-self:T(self.lid..text)
-if self.UseSRS then
-self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2)
-end
-self:__TaskTargetFlared(5,task)
-else
-text=self.gettext:GetEntry("NOACTIVETASK",self.locale)
-end
-if not self.NoScreenOutput then
-local m=MESSAGE:New(text,15,"Tasking"):ToClient(Client)
-end
-return self
-end
-function PLAYERTASKCONTROLLER:_IlluminateTask(Group,Client)
-self:T(self.lid.."_IlluminateTask")
-local playername,ttsplayername=self:_GetPlayerName(Client)
-local text=""
-if self.TasksPerPlayer:HasUniqueID(playername)then
-local task=self.TasksPerPlayer:ReadByID(playername)
-task:FlareTarget()
-local textmark=self.gettext:GetEntry("FLARETASK",self.locale)
-text=string.format(textmark,ttsplayername,self.MenuName or self.Name,task.PlayerTaskNr)
-self:T(self.lid..text)
-if self.UseSRS then
-self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2)
-end
-self:__TaskTargetIlluminated(5,task)
-else
-text=self.gettext:GetEntry("NOACTIVETASK",self.locale)
-end
-if not self.NoScreenOutput then
-local m=MESSAGE:New(text,15,"Tasking"):ToClient(Client)
-end
-return self
-end
-function PLAYERTASKCONTROLLER:_AbortTask(Group,Client)
-self:T(self.lid.."_AbortTask")
-local playername,ttsplayername=self:_GetPlayerName(Client)
-local text=""
-if self.TasksPerPlayer:HasUniqueID(playername)then
-local task=self.TasksPerPlayer:PullByID(playername)
-task:ClientAbort(Client)
-local textmark=self.gettext:GetEntry("ABORTTASK",self.locale)
-text=string.format(textmark,self.MenuName or self.Name,ttsplayername,task.TTSType,task.PlayerTaskNr)
-self:T(self.lid..text)
-if self.UseSRS then
-self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2)
-end
-self:__PlayerAbortedTask(1,Group,Client,task)
-else
-text=self.gettext:GetEntry("NOACTIVETASK",self.locale)
-end
-if not self.NoScreenOutput then
-local m=MESSAGE:New(text,15,"Tasking"):ToClient(Client)
-end
-self:_SwitchMenuForClient(Client,"Info",1)
-return self
-end
-function PLAYERTASKCONTROLLER:_UpdateJoinMenuTemplate()
-self:T("_UpdateJoinMenuTemplate")
-if self.TaskQueue:Count()>0 then
-local taskpertype=self:_GetTasksPerType()
-local JoinMenu=self.JoinMenu
-local controller=self.JoinTaskMenuTemplate
-local actcontroller=self.ActiveTaskMenuTemplate
-local actinfomenu=self.ActiveInfoMenu
-if self.TaskQueue:Count()==0 and self.MenuNoTask==nil then
-local menunotasks=self.gettext:GetEntry("MENUNOTASKS",self.locale)
-self.MenuNoTask=controller:NewEntry(menunotasks,self.JoinMenu)
-controller:AddEntry(self.MenuNoTask)
-end
-if self.TaskQueue:Count()>0 and self.MenuNoTask~=nil then
-controller:DeleteGenericEntry(self.MenuNoTask)
-controller:DeleteF10Entry(self.MenuNoTask)
-self.MenuNoTask=nil
-end
-local maxn=self.menuitemlimit
-for _type,_ in pairs(taskpertype)do
-local found=controller:FindEntriesByText(_type)
-if#found==0 then
-local newentry=controller:NewEntry(_type,JoinMenu)
-controller:AddEntry(newentry)
-if self.JoinInfoMenu then
-local newentry=controller:NewEntry(_type,self.JoinInfoMenu)
-controller:AddEntry(newentry)
-end
-if actinfomenu then
-local newentry=actcontroller:NewEntry(_type,self.ActiveInfoMenu)
-actcontroller:AddEntry(newentry)
-end
-end
-end
-local typelist=self:_GetAvailableTaskTypes()
-for _tasktype,_data in pairs(typelist)do
-self:T("**** Building for TaskType: ".._tasktype)
-for _,_task in pairs(taskpertype[_tasktype])do
-_task=_task
-self:T("**** Building for Task: ".._task.Target:GetName())
-if _task.InMenu then
-self:T("**** Task already in Menu ".._task.Target:GetName())
-else
-local menutaskno=self.gettext:GetEntry("MENUTASKNO",self.locale)
-local text=string.format("%s %03d",menutaskno,_task.PlayerTaskNr)
-if self.UseGroupNames then
-local name=_task.Target:GetName()
-if name~="Unknown"then
-text=string.format("%s (%03d)",name,_task.PlayerTaskNr)
-end
-end
-local parenttable,number=controller:FindEntriesByText(_tasktype,JoinMenu)
-if number>0 then
-local Parent=parenttable[1]
-local matches,count=controller:FindEntriesByParent(Parent)
-self:T("***** Join Menu ".._tasktype.." # of entries: "..count)
-if count0 then
-local Parent=parenttable[1]
-local matches,count=controller:FindEntriesByParent(Parent)
-self:T("***** Join Info Menu ".._tasktype.." # of entries: "..count)
-if count0 then
-local Parent=parenttable[1]
-local matches,count=actcontroller:FindEntriesByParent(Parent)
-self:T("***** Active Info Menu ".._tasktype.." # of entries: "..count)
-if count0 and self.MenuNoTask~=nil then
-JoinTaskMenuTemplate:DeleteGenericEntry(self.MenuNoTask)
-self.MenuNoTask=nil
-end
-self.JoinTaskMenuTemplate=JoinTaskMenuTemplate
-return self
-end
-function PLAYERTASKCONTROLLER:_CreateActiveTaskMenuTemplate()
-self:T("_CreateActiveTaskMenuTemplate")
-local menuactive=self.gettext:GetEntry("MENUACTIVE",self.locale)
-local menuinfo=self.gettext:GetEntry("MENUINFO",self.locale)
-local menumark=self.gettext:GetEntry("MENUMARK",self.locale)
-local menusmoke=self.gettext:GetEntry("MENUSMOKE",self.locale)
-local menuflare=self.gettext:GetEntry("MENUFLARE",self.locale)
-local menuillu=self.gettext:GetEntry("MENUILLU",self.locale)
-local menuabort=self.gettext:GetEntry("MENUABORT",self.locale)
-local ActiveTaskMenuTemplate=CLIENTMENUMANAGER:New(self.ActiveClientSet,"ActiveTask")
-if not self.ActiveTopMenu then
-local taskings=self.gettext:GetEntry("MENUTASKING",self.locale)
-local longname=self.Name..taskings..self.Type
-local menuname=self.MenuName or longname
-self.ActiveTopMenu=ActiveTaskMenuTemplate:NewEntry(menuname,self.MenuParent)
-end
-if self.AllowFlash then
-local flashtext=self.gettext:GetEntry("FLASHMENU",self.locale)
-ActiveTaskMenuTemplate:NewEntry(flashtext,self.ActiveTopMenu,self._SwitchFlashing,self)
-end
-local active=ActiveTaskMenuTemplate:NewEntry(menuactive,self.ActiveTopMenu)
-ActiveTaskMenuTemplate:NewEntry(menuinfo,active,self._ActiveTaskInfo,self,"NONE")
-ActiveTaskMenuTemplate:NewEntry(menumark,active,self._MarkTask,self)
-if self.Type~=PLAYERTASKCONTROLLER.Type.A2A and self.noflaresmokemenu~=true then
-ActiveTaskMenuTemplate:NewEntry(menusmoke,active,self._SmokeTask,self)
-ActiveTaskMenuTemplate:NewEntry(menuflare,active,self._FlareTask,self)
-if self.illumenu then
-ActiveTaskMenuTemplate:NewEntry(menuillu,active,self._IlluminateTask,self)
-end
-end
-ActiveTaskMenuTemplate:NewEntry(menuabort,active,self._AbortTask,self)
-self.ActiveTaskMenuTemplate=ActiveTaskMenuTemplate
-if self.taskinfomenu and self.activehasinfomenu then
-local menutaskinfo=self.gettext:GetEntry("MENUTASKINFO",self.locale)
-self.ActiveInfoMenu=ActiveTaskMenuTemplate:NewEntry(menutaskinfo,self.ActiveTopMenu)
-end
-return self
-end
-function PLAYERTASKCONTROLLER:_SwitchMenuForClient(Client,MenuType,Delay)
-self:T(self.lid.."_SwitchMenuForClient")
-if Delay then
-self:ScheduleOnce(Delay,self._SwitchMenuForClient,self,Client,MenuType)
-return self
-end
-if MenuType=="Info"then
-self.ClientSet:AddClientsByName(Client:GetName())
-self.ActiveClientSet:Remove(Client:GetName(),true)
-self.ActiveTaskMenuTemplate:ResetMenu(Client)
-self.JoinTaskMenuTemplate:ResetMenu(Client)
-self.JoinTaskMenuTemplate:Propagate(Client)
-elseif MenuType=="Active"then
-self.ActiveClientSet:AddClientsByName(Client:GetName())
-self.ClientSet:Remove(Client:GetName(),true)
-self.ActiveTaskMenuTemplate:ResetMenu(Client)
-self.JoinTaskMenuTemplate:ResetMenu(Client)
-self.ActiveTaskMenuTemplate:Propagate(Client)
-else
-self:E(self.lid.."Unknown menu type in _SwitchMenuForClient:"..tostring(MenuType))
-end
-return self
-end
-function PLAYERTASKCONTROLLER:AddAgent(Recce)
-self:T(self.lid.."AddAgent")
-if self.Intel then
-self.Intel:AddAgent(Recce)
-else
-self:E(self.lid.."*****NO detection has been set up (yet)!")
-end
-return self
-end
-function PLAYERTASKCONTROLLER:AddAgentSet(RecceSet)
-self:T(self.lid.."AddAgentSet")
-if self.Intel then
-local Set=RecceSet:GetAliveSet()
-for _,_Recce in pairs(Set)do
-self.Intel:AddAgent(_Recce)
-end
-else
-self:E(self.lid.."*****NO detection has been set up (yet)!")
-end
-return self
-end
-function PLAYERTASKCONTROLLER:SwitchDetectStatics(OnOff)
-self:T(self.lid.."SwitchDetectStatics")
-if self.Intel then
-self.Intel:SetDetectStatics(OnOff)
-else
-self:E(self.lid.."***** NO detection has been set up (yet)!")
-end
-return self
-end
-function PLAYERTASKCONTROLLER:AddAcceptZone(AcceptZone)
-self:T(self.lid.."AddAcceptZone")
-if self.Intel then
-self.Intel:AddAcceptZone(AcceptZone)
-else
-self:E(self.lid.."*****NO detection has been set up (yet)!")
-end
-return self
-end
-function PLAYERTASKCONTROLLER:AddAcceptZoneSet(AcceptZoneSet)
-self:T(self.lid.."AddAcceptZoneSet")
-if self.Intel then
-self.Intel.acceptzoneset:AddSet(AcceptZoneSet)
-else
-self:E(self.lid.."*****NO detection has been set up (yet)!")
-end
-return self
-end
-function PLAYERTASKCONTROLLER:AddRejectZone(RejectZone)
-self:T(self.lid.."AddRejectZone")
-if self.Intel then
-self.Intel:AddRejectZone(RejectZone)
-else
-self:E(self.lid.."*****NO detection has been set up (yet)!")
-end
-return self
-end
-function PLAYERTASKCONTROLLER:AddRejectZoneSet(RejectZoneSet)
-self:T(self.lid.."AddRejectZoneSet")
-if self.Intel then
-self.Intel.rejectzoneset:AddSet(RejectZoneSet)
-else
-self:E(self.lid.."*****NO detection has been set up (yet)!")
-end
-return self
-end
-function PLAYERTASKCONTROLLER:RemoveAcceptZone(AcceptZone)
-self:T(self.lid.."RemoveAcceptZone")
-if self.Intel then
-self.Intel:RemoveAcceptZone(AcceptZone)
-else
-self:E(self.lid.."*****NO detection has been set up (yet)!")
-end
-return self
-end
-function PLAYERTASKCONTROLLER:RemoveRejectZoneSet(RejectZone)
-self:T(self.lid.."RemoveRejectZone")
-if self.Intel then
-self.Intel:RemoveRejectZone(RejectZone)
-else
-self:E(self.lid.."*****NO detection has been set up (yet)!")
-end
-return self
-end
-function PLAYERTASKCONTROLLER:SetMenuName(Name)
-self:T(self.lid.."SetMenuName: "..Name)
-self.MenuName=Name
-return self
-end
-function PLAYERTASKCONTROLLER:SetParentMenu(Menu)
-self:T(self.lid.."SetParentMenu")
-return self
-end
-function PLAYERTASKCONTROLLER:SetupIntel(RecceName)
-self:T(self.lid.."SetupIntel")
-self.RecceSet=SET_GROUP:New():FilterCoalitions(self.CoalitionName):FilterPrefixes(RecceName):FilterStart()
-self.Intel=INTEL:New(self.RecceSet,self.Coalition,self.Name.."-Intel")
-self.Intel:SetClusterAnalysis(true,false,false)
-self.Intel:SetClusterRadius(self.ClusterRadius or 0.5)
-self.Intel.statusupdate=25
-self.Intel:SetAcceptZones()
-self.Intel:SetRejectZones()
-if self.Type==PLAYERTASKCONTROLLER.Type.A2G or self.Type==PLAYERTASKCONTROLLER.Type.A2GS then
-self.Intel:SetDetectStatics(true)
-end
-self.Intel:__Start(2)
-local function NewCluster(Cluster)
-if not self.usecluster then return self end
-local cluster=Cluster
-local type=cluster.ctype
-self:T({type,self.Type})
-if(type==INTEL.Ctype.AIRCRAFT and self.Type==PLAYERTASKCONTROLLER.Type.A2A)or(type==INTEL.Ctype.NAVAL and(self.Type==PLAYERTASKCONTROLLER.Type.A2S or self.Type==PLAYERTASKCONTROLLER.Type.A2GS))then
-self:T("A2A or A2S")
-local contacts=cluster.Contacts
-local targetset=SET_GROUP:New()
-for _,_object in pairs(contacts)do
-local contact=_object
-self:T("Adding group: "..contact.groupname)
-targetset:AddGroup(contact.group,true)
-end
-self:AddTarget(targetset)
-elseif(type==INTEL.Ctype.GROUND or type==INTEL.Ctype.STRUCTURE)and(self.Type==PLAYERTASKCONTROLLER.Type.A2G or self.Type==PLAYERTASKCONTROLLER.Type.A2GS)then
-self:T("A2G")
-local contacts=cluster.Contacts
-local targetset=nil
-if type==INTEL.Ctype.GROUND then
-targetset=SET_GROUP:New()
-for _,_object in pairs(contacts)do
-local contact=_object
-self:T("Adding group: "..contact.groupname)
-targetset:AddGroup(contact.group,true)
-end
-elseif type==INTEL.Ctype.STRUCTURE then
-targetset=SET_STATIC:New()
-for _,_object in pairs(contacts)do
-local contact=_object
-self:T("Adding static: "..contact.groupname)
-targetset:AddStatic(contact.group)
-end
-end
-if targetset then
-self:AddTarget(targetset)
-end
-end
-end
-local function NewContact(Contact)
-if self.usecluster then return self end
-local contact=Contact
-local type=contact.ctype
-self:T({type,self.Type})
-if(type==INTEL.Ctype.AIRCRAFT and self.Type==PLAYERTASKCONTROLLER.Type.A2A)or(type==INTEL.Ctype.NAVAL and(self.Type==PLAYERTASKCONTROLLER.Type.A2S or self.Type==PLAYERTASKCONTROLLER.Type.A2GS))then
-self:T("A2A or A2S")
-self:T("Adding group: "..contact.groupname)
-self:AddTarget(contact.group)
-elseif(type==INTEL.Ctype.GROUND or type==INTEL.Ctype.STRUCTURE)and(self.Type==PLAYERTASKCONTROLLER.Type.A2G or self.Type==PLAYERTASKCONTROLLER.Type.A2GS)then
-self:T("A2G")
-self:T("Adding group: "..contact.groupname)
-self:AddTarget(contact.group)
-end
-end
-function self.Intel:OnAfterNewCluster(From,Event,To,Cluster)
-NewCluster(Cluster)
-end
-function self.Intel:OnAfterNewContact(From,Event,To,Contact)
-NewContact(Contact)
-end
-return self
-end
-function PLAYERTASKCONTROLLER:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,Coordinate)
-self:T(self.lid.."SetSRS")
-self.PathToSRS=PathToSRS or"C:\\Program Files\\DCS-SimpleRadio-Standalone"
-self.Gender=Gender or"male"
-self.Culture=Culture or"en-US"
-self.Port=Port or 5002
-self.Voice=Voice
-self.PathToGoogleKey=PathToGoogleKey
-self.Volume=Volume or 1.0
-self.UseSRS=true
-self.Frequency=Frequency or{127,251}
-self.BCFrequency=self.Frequency
-self.Modulation=Modulation or{radio.modulation.FM,radio.modulation.AM}
-self.BCModulation=self.Modulation
-self.SRS=MSRS:New(self.PathToSRS,self.Frequency,self.Modulation,self.Volume)
-self.SRS:SetCoalition(self.Coalition)
-self.SRS:SetLabel(self.MenuName or self.Name)
-self.SRS:SetGender(self.Gender)
-self.SRS:SetCulture(self.Culture)
-self.SRS:SetPort(self.Port)
-self.SRS:SetVoice(self.Voice)
-if self.PathToGoogleKey then
-self.SRS:SetGoogle(self.PathToGoogleKey)
-end
-if Coordinate then
-self.SRS:SetCoordinate(Coordinate)
-end
-self.SRSQueue=MSRSQUEUE:New(self.MenuName or self.Name)
-self.SRSQueue:SetTransmitOnlyWithPlayers(self.TransmitOnlyWithPlayers)
-return self
-end
-function PLAYERTASKCONTROLLER:SetSRSBroadcast(Frequency,Modulation)
-self:T(self.lid.."SetSRSBroadcast")
-if self.SRS then
-self.BCFrequency=Frequency
-self.BCModulation=Modulation
-end
-return self
-end
-function PLAYERTASKCONTROLLER:onafterStart(From,Event,To)
-self:T({From,Event,To})
-self:T(self.lid.."onafterStart")
-self:_CreateJoinMenuTemplate()
-self:_CreateActiveTaskMenuTemplate()
-self:HandleEvent(EVENTS.PlayerLeaveUnit,self._EventHandler)
-self:HandleEvent(EVENTS.Ejection,self._EventHandler)
-self:HandleEvent(EVENTS.Crash,self._EventHandler)
-self:HandleEvent(EVENTS.PilotDead,self._EventHandler)
-self:HandleEvent(EVENTS.PlayerEnterAircraft,self._EventHandler)
-self:SetEventPriority(5)
-return self
-end
-function PLAYERTASKCONTROLLER:onafterStatus(From,Event,To)
-self:T({From,Event,To})
-self:_CheckTargetQueue()
-self:_CheckTaskQueue()
-self:_CheckPrecisionTasks()
-if self.AllowFlash then
-self:_FlashInfo()
-end
-local targetcount=self.TargetQueue:Count()
-local taskcount=self.TaskQueue:Count()
-local playercount=self.ClientSet:CountAlive()
-local assignedtasks=self.TasksPerPlayer:Count()
-local enforcedmenu=false
-if taskcount~=self.lasttaskcount then
-self.lasttaskcount=taskcount
-if taskcount12 then clock=clock-12 end
-if clock==0 then clock=12 end
-end
-return clock
-end
-function PLAYERRECCE:SetLaserCodes(LaserCodes)
-self.LaserCodes=(type(LaserCodes)=="table")and LaserCodes or{LaserCodes}
-return self
-end
-function PLAYERRECCE:SetReferencePoint(Coordinate,Name)
-self.ReferencePoint=Coordinate
-self.RPName=Name
-if self.RPMarker then
-self.RPMarker:Remove()
-end
-local llddm=Coordinate:ToStringLLDDM()
-local lldms=Coordinate:ToStringLLDMS()
-local mgrs=Coordinate:ToStringMGRS()
-local text=string.format("%s RP %s\n%s\n%s\n%s",self.Name,Name,llddm,lldms,mgrs)
-self.RPMarker=MARKER:New(Coordinate,text)
-self.RPMarker:ReadOnly()
-self.RPMarker:ToCoalition(self.Coalition)
-return self
-end
-function PLAYERRECCE:SetPlayerTaskController(Controller)
-self.UseController=true
-self.Controller=Controller
-return self
-end
-function PLAYERRECCE:SetAttackSet(AttackSet)
-self.AttackSet=AttackSet
-return self
-end
-function PLAYERRECCE:_CameraOn(client,playername)
-local camera=true
-local unit=client
-if unit and unit:IsAlive()then
-local typename=unit:GetTypeName()
-if string.find(typename,"SA342")then
-local dcsunit=Unit.getByName(client:GetName())
-local vivihorizontal=dcsunit:getDrawArgumentValue(215)or 0
-if vivihorizontal<-0.7 or vivihorizontal>0.7 then
-camera=false
-end
-elseif string.find(typename,"Ka-50")then
-camera=true
-end
-end
-return camera
-end
-function PLAYERRECCE:_GetGazelleVivianneSight(Gazelle)
-self:T(self.lid.."GetGazelleVivianneSight")
-local unit=Gazelle
-if unit and unit:IsAlive()then
-local dcsunit=Unit.getByName(Gazelle:GetName())
-local vivihorizontal=dcsunit:getDrawArgumentValue(215)or 0
-local vivivertical=dcsunit:getDrawArgumentValue(216)or 0
-local vivioff=false
-if vivihorizontal<-0.67 then
-vivihorizontal=-0.67
-vivioff=false
-elseif vivihorizontal>0.67 then
-vivihorizontal=0.67
-vivioff=true
-return 0,0,0,false
-end
-vivivertical=vivivertical/1.10731
-local horizontalview=vivihorizontal*-180
-local verticalview=vivivertical*30
-local heading=unit:GetHeading()
-local viviheading=(heading+horizontalview)%360
-local maxview=self:_GetActualMaxLOSight(unit,viviheading,verticalview,vivioff)
-local factor=3.15
-self.GazelleViewFactors={
-[1]=1.18,
-[2]=1.32,
-[3]=1.46,
-[4]=1.62,
-[5]=1.77,
-[6]=1.85,
-[7]=2.05,
-[8]=2.05,
-[9]=2.3,
-[10]=2.3,
-[11]=2.27,
-[12]=2.27,
-[13]=2.43,
-}
-local lfac=UTILS.Round(maxview,-2)
-if lfac<=1300 then
-factor=3.15
-maxview=math.ceil((maxview*factor)/100)*100
-end
-if maxview>8000 then maxview=8000 end
-return viviheading,verticalview,maxview,not vivioff
-end
-return 0,0,0,false
-end
-function PLAYERRECCE:_GetActualMaxLOSight(unit,vheading,vnod,vivoff)
-self:T(self.lid.."_GetActualMaxLOSight")
-if vivoff then return 0 end
-local maxview=0
-if unit and unit:IsAlive()then
-local typename=unit:GetTypeName()
-maxview=self.MaxViewDistance[typename]or 8000
-local CamHeight=self.Cameraheight[typename]or 0
-if vnod<0 then
-local beta=90
-local gamma=math.floor(90-vnod)
-local alpha=math.floor(180-beta-gamma)
-local a=unit:GetHeight()-unit:GetCoordinate():GetLandHeight()+CamHeight
-local b=a/math.sin(math.rad(alpha))
-local c=b*math.sin(math.rad(gamma))
-maxview=c*1.2
-end
-end
-return math.abs(maxview)
-end
-function PLAYERRECCE:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTranslations)
-if not ShortCallsign or ShortCallsign==false then
-self.ShortCallsign=false
-else
-self.ShortCallsign=true
-end
-self.Keepnumber=Keepnumber or false
-self.CallsignTranslations=CallsignTranslations
-return self
-end
-function PLAYERRECCE:_GetViewZone(unit,vheading,minview,maxview,angle,camon,laser)
-self:T(self.lid.."_GetViewZone")
-local viewzone=nil
-if not camon then return nil end
-if unit and unit:IsAlive()then
-local unitname=unit:GetName()
-if not laser then
-local startpos=unit:GetCoordinate()
-local heading1=(vheading+angle)%360
-local heading2=(vheading-angle)%360
-local pos1=startpos:Translate(maxview,heading1)
-local pos2=startpos:Translate(maxview,heading2)
-local array={}
-table.insert(array,startpos:GetVec2())
-table.insert(array,pos1:GetVec2())
-table.insert(array,pos2:GetVec2())
-viewzone=ZONE_POLYGON:NewFromPointsArray(unitname,array)
-else
-local startp=unit:GetCoordinate()
-local heading1=(vheading+90)%360
-local heading2=(vheading-90)%360
-self:T({heading1,heading2})
-local startpos=startp:Translate(minview,vheading)
-local pos1=startpos:Translate(12.5,heading1)
-local pos2=startpos:Translate(12.5,heading2)
-local pos3=pos1:Translate(maxview,vheading)
-local pos4=pos2:Translate(maxview,vheading)
-local array={}
-table.insert(array,pos1:GetVec2())
-table.insert(array,pos2:GetVec2())
-table.insert(array,pos4:GetVec2())
-table.insert(array,pos3:GetVec2())
-viewzone=ZONE_POLYGON:NewFromPointsArray(unitname,array)
-end
-end
-return viewzone
-end
-function PLAYERRECCE:_GetKnownTargets(client)
-self:T(self.lid.."_GetKnownTargets")
-local finaltargets=SET_UNIT:New()
-local targets=self.TargetCache:GetDataTable()
-local playername=client:GetPlayerName()
-for _,_target in pairs(targets)do
-local targetdata=_target.PlayerRecceDetected
-if targetdata.playername==playername then
-finaltargets:Add(_target:GetName(),_target)
-end
-end
-return finaltargets,finaltargets:CountAlive()
-end
-function PLAYERRECCE:_CleanupTargetCache()
-self:T(self.lid.."_CleanupTargetCache")
-local cleancache=FIFO:New()
-self.TargetCache:ForEach(
-function(unit)
-local pull=false
-if unit and unit:IsAlive()and unit:GetLife()>1 then
-if unit.PlayerRecceDetected and unit.PlayerRecceDetected.timestamp then
-local TNow=timer.getTime()
-if TNow-unit.PlayerRecceDetected.timestamp>self.TForget then
-pull=true
-unit.PlayerRecceDetected=nil
-end
-else
-pull=true
-end
-else
-pull=true
-end
-if not pull then
-cleancache:Push(unit,unit:GetName())
-end
-end
-)
-self.TargetCache=nil
-self.TargetCache=cleancache
-return self
-end
-function PLAYERRECCE:_GetTargetSet(unit,camera,laser)
-self:T(self.lid.."_GetTargetSet")
-local finaltargets=SET_UNIT:New()
-local finalcount=0
-local minview=0
-local typename=unit:GetTypeName()
-local playername=unit:GetPlayerName()
-local maxview=self.MaxViewDistance[typename]or 5000
-local heading,nod,maxview,angle=0,30,8000,10
-local camon=false
-local name=unit:GetName()
-if string.find(typename,"SA342")and camera then
-heading,nod,maxview,camon=self:_GetGazelleVivianneSight(unit)
-angle=10
-maxview=self.MaxViewDistance[typename]or 5000
-elseif string.find(typename,"Ka-50")and camera then
-heading=unit:GetHeading()
-nod,maxview,camon=10,1000,true
-angle=10
-maxview=self.MaxViewDistance[typename]or 5000
-else
-heading=unit:GetHeading()
-nod,maxview,camon=10,1000,true
-angle=45
-end
-if laser then
-if not self.LaserFOV[playername]then
-minview=100
-maxview=2000
-self.LaserFOV[playername]={
-min=100,
-max=2000,
-}
-else
-minview=self.LaserFOV[playername].min
-maxview=self.LaserFOV[playername].max
-end
-end
-local zone=self:_GetViewZone(unit,heading,minview,maxview,angle,camon,laser)
-if zone then
-local redcoalition="red"
-if self.Coalition==coalition.side.RED then
-redcoalition="blue"
-end
-local startpos=unit:GetCoordinate()
-local targetset=SET_UNIT:New():FilterCategories("ground"):FilterActive(true):FilterZones({zone}):FilterCoalitions(redcoalition):FilterOnce()
-self:T("Prefilter Target Count = "..targetset:CountAlive())
-targetset:ForEach(
-function(_unit)
-local _unit=_unit
-local _unitpos=_unit:GetCoordinate()
-if startpos:IsLOS(_unitpos)and _unit:IsAlive()and _unit:GetLife()>1 then
-self:T("Adding to final targets: ".._unit:GetName())
-finaltargets:Add(_unit:GetName(),_unit)
-end
-end
-)
-finalcount=finaltargets:CountAlive()
-self:T(string.format("%s Unit: %s | Targets in view %s",self.lid,name,finalcount))
-end
-return finaltargets,finalcount,zone
-end
-function PLAYERRECCE:_GetHVTTarget(targetset)
-self:T(self.lid.."_GetHVTTarget")
-local unitsbythreat={}
-local minthreat=self.minthreatlevel or 0
-for _,_unit in pairs(targetset.Set)do
-local unit=_unit
-if unit and unit:IsAlive()and unit:GetLife()>1 then
-local threat=unit:GetThreatLevel()
-if threat>=minthreat then
-if unit:HasAttribute("RADAR_BAND1_FOR_ARM")or unit:HasAttribute("RADAR_BAND2_FOR_ARM")or unit:HasAttribute("Optical Tracker")then
-threat=11
-end
-table.insert(unitsbythreat,{unit,threat})
-end
-end
-end
-table.sort(unitsbythreat,function(a,b)
-local aNum=a[2]
-local bNum=b[2]
-return aNum>bNum
-end)
-if unitsbythreat[1]and unitsbythreat[1][1]then
-return unitsbythreat[1][1]
-else
-return nil
-end
-end
-function PLAYERRECCE:_LaseTarget(client,targetset)
-self:T(self.lid.."_LaseTarget")
-local target=self:_GetHVTTarget(targetset)
-local playername=client:GetPlayerName()
-local laser=nil
-if not self.LaserSpots[playername]then
-laser=SPOT:New(client)
-if not self.UnitLaserCodes[playername]then
-self.UnitLaserCodes[playername]=1688
-end
-laser.LaserCode=self.UnitLaserCodes[playername]or 1688
-self.LaserSpots[playername]=laser
-else
-laser=self.LaserSpots[playername]
-end
-if self.LaserTarget[playername]then
-local target=self.LaserTarget[playername]
-local oldtarget=target:GetObject()
-self:T("Targetstate: "..target:GetState())
-self:T("Laser State: "..tostring(laser:IsLasing()))
-if(not oldtarget)or targetset:IsNotInSet(oldtarget)or target:IsDead()or target:IsDestroyed()then
-laser:LaseOff()
-if target:IsDead()or target:IsDestroyed()or target:GetLife()<2 then
-self:__Shack(-1,client,oldtarget)
-else
-self:__TargetLOSLost(-1,client,oldtarget)
-end
-self.LaserTarget[playername]=nil
-oldtarget=nil
-self.LaserSpots[playername]=nil
-elseif oldtarget and laser and(not laser:IsLasing())then
-self:T("Switching laser back on ..")
-local lasercode=self.UnitLaserCodes[playername]or laser.LaserCode or 1688
-local lasingtime=self.lasingtime or 60
-laser:LaseOn(oldtarget,lasercode,lasingtime)
-else
-self:T("Target alive and laser is on!")
-end
-elseif(not laser:IsLasing())and target then
-local relativecam=self.LaserRelativePos[client:GetTypeName()]
-laser:SetRelativeStartPosition(relativecam)
-local lasercode=self.UnitLaserCodes[playername]or laser.LaserCode or 1688
-local lasingtime=self.lasingtime or 60
-laser:LaseOn(target,lasercode,lasingtime)
-self.LaserTarget[playername]=TARGET:New(target)
-self:__TargetLasing(-1,client,target,lasercode,lasingtime)
-end
-return self
-end
-function PLAYERRECCE:_SetClientLaserCode(client,group,playername,code)
-self:T(self.lid.."_SetClientLaserCode")
-self.UnitLaserCodes[playername]=code or 1688
-if self.ClientMenus[playername]then
-self.ClientMenus[playername]:Remove()
-self.ClientMenus[playername]=nil
-end
-self:_BuildMenus()
-return self
-end
-function PLAYERRECCE:_SwitchOnStation(client,group,playername)
-self:T(self.lid.."_SwitchOnStation")
-if not self.OnStation[playername]then
-self.OnStation[playername]=true
-self:__RecceOnStation(-1,client,playername)
-else
-self.OnStation[playername]=false
-self:__RecceOffStation(-1,client,playername)
-end
-if self.ClientMenus[playername]then
-self.ClientMenus[playername]:Remove()
-self.ClientMenus[playername]=nil
-end
-self:_BuildMenus(client)
-return self
-end
-function PLAYERRECCE:_SwitchSmoke(client,group,playername)
-self:T(self.lid.."_SwitchLasing")
-if not self.SmokeOwn[playername]then
-self.SmokeOwn[playername]=true
-MESSAGE:New("Smoke self is now ON",10,self.Name or"FACA"):ToClient(client)
-else
-self.SmokeOwn[playername]=false
-MESSAGE:New("Smoke self is now OFF",10,self.Name or"FACA"):ToClient(client)
-end
-if self.ClientMenus[playername]then
-self.ClientMenus[playername]:Remove()
-self.ClientMenus[playername]=nil
-end
-self:_BuildMenus(client)
-return self
-end
-function PLAYERRECCE:_SwitchLasing(client,group,playername)
-self:T(self.lid.."_SwitchLasing")
-if not self.AutoLase[playername]then
-self.AutoLase[playername]=true
-MESSAGE:New("Lasing is now ON",10,self.Name or"FACA"):ToClient(client)
-else
-self.AutoLase[playername]=false
-if self.LaserSpots[playername]then
-local laser=self.LaserSpots[playername]
-if laser:IsLasing()then
-laser:LaseOff()
-end
-self.LaserSpots[playername]=nil
-end
-MESSAGE:New("Lasing is now OFF",10,self.Name or"FACA"):ToClient(client)
-end
-if self.ClientMenus[playername]then
-self.ClientMenus[playername]:Remove()
-self.ClientMenus[playername]=nil
-end
-self:_BuildMenus(client)
-return self
-end
-function PLAYERRECCE:_SwitchLasingDist(client,group,playername,mindist,maxdist)
-self:T(self.lid.."_SwitchLasingDist")
-local mind=mindist or 100
-local maxd=maxdist or 2000
-if not self.LaserFOV[playername]then
-self.LaserFOV[playername]={
-min=mind,
-max=maxd,
-}
-else
-self.LaserFOV[playername].min=mind
-self.LaserFOV[playername].max=maxd
-end
-MESSAGE:New(string.format("Laser distance set to %d-%dm!",mindist,maxdist),10,"FACA"):ToClient(client)
-if self.ClientMenus[playername]then
-self.ClientMenus[playername]:Remove()
-self.ClientMenus[playername]=nil
-end
-self:_BuildMenus(client)
-return self
-end
-function PLAYERRECCE:_WIP(client,group,playername)
-self:T(self.lid.."_WIP")
-return self
-end
-function PLAYERRECCE:_SmokeTargets(client,group,playername)
-self:T(self.lid.."_SmokeTargets")
-local cameraset=self:_GetTargetSet(client,true)
-local visualset=self:_GetTargetSet(client,false)
-if cameraset:CountAlive()>0 or visualset:CountAlive()>0 then
-self:__TargetsSmoked(-1,client,playername,cameraset)
-else
-return self
-end
-local highsmoke=self.SmokeColor.highsmoke
-local medsmoke=self.SmokeColor.medsmoke
-local lowsmoke=self.SmokeColor.lowsmoke
-local lasersmoke=self.SmokeColor.lasersmoke
-local laser=self.LaserSpots[playername]
-if laser and laser.Target and laser.Target:IsAlive()then
-laser.Target:GetCoordinate():Smoke(lasersmoke)
-end
-local coord=visualset:GetCoordinate()
-if coord and self.smokeaveragetargetpos then
-coord:SetAtLandheight()
-coord:Smoke(medsmoke)
-else
-for _,_unit in pairs(visualset.Set)do
-local unit=_unit
-if unit and unit:IsAlive()then
-local coord=unit:GetCoordinate()
-local threat=unit:GetThreatLevel()
-if coord then
-local color=lowsmoke
-if threat>7 then
-color=highsmoke
-elseif threat>2 then
-color=medsmoke
-end
-coord:Smoke(color)
-end
-end
-end
-end
-if self.SmokeOwn[playername]then
-local cc=client:GetVec2()
-local lc=COORDINATE:NewFromVec2(cc,1)
-local color=self.SmokeColor.ownsmoke
-lc:Smoke(color)
-end
-return self
-end
-function PLAYERRECCE:_FlareTargets(client,group,playername)
-self:T(self.lid.."_FlareTargets")
-local cameraset=self:_GetTargetSet(client,true)
-local visualset=self:_GetTargetSet(client,false)
-cameraset:AddSet(visualset)
-if cameraset:CountAlive()>0 then
-self:__TargetsFlared(-1,client,playername,cameraset)
-end
-local highsmoke=self.FlareColor.highflare
-local medsmoke=self.FlareColor.medflare
-local lowsmoke=self.FlareColor.lowflare
-local lasersmoke=self.FlareColor.laserflare
-local laser=self.LaserSpots[playername]
-if laser and laser.Target and laser.Target:IsAlive()then
-laser.Target:GetCoordinate():Flare(lasersmoke)
-if cameraset:IsInSet(laser.Target)then
-cameraset:Remove(laser.Target:GetName(),true)
-end
-end
-for _,_unit in pairs(cameraset.Set)do
-local unit=_unit
-if unit and unit:IsAlive()then
-local coord=unit:GetCoordinate()
-local threat=unit:GetThreatLevel()
-if coord then
-local color=lowsmoke
-if threat>7 then
-color=highsmoke
-elseif threat>2 then
-color=medsmoke
-end
-coord:Flare(color)
-end
-end
-end
-return self
-end
-function PLAYERRECCE:_IlluTargets(client,group,playername)
-self:T(self.lid.."_IlluTargets")
-local totalset,count=self:_GetKnownTargets(client)
-if count>0 then
-local coord=totalset:GetCoordinate()
-coord.y=coord.y+200
-coord:IlluminationBomb(nil,1)
-self:__Illumination(1,client,playername,totalset)
-end
-return self
-end
-function PLAYERRECCE:_UploadTargets(client,group,playername)
-self:T(self.lid.."_UploadTargets")
-local totalset,count=self:_GetKnownTargets(client)
-if count>0 then
-self.Controller:AddTarget(totalset)
-self:__TargetReportSent(1,client,playername,totalset)
-end
-return self
-end
-function PLAYERRECCE:_ReportLaserTargets(client,group,playername)
-self:T(self.lid.."_ReportLaserTargets")
-local targetset,number=self:_GetTargetSet(client,true,true)
-if number>0 and self.AutoLase[playername]then
-local Settings=(client and _DATABASE:GetPlayerSettings(playername))or _SETTINGS
-local target=self:_GetHVTTarget(targetset)
-local ThreatLevel=target:GetThreatLevel()or 1
-local ThreatLevelText="high"
-if ThreatLevel>3 and ThreatLevel<8 then
-ThreatLevelText="medium"
-elseif ThreatLevel<=3 then
-ThreatLevelText="low"
-end
-local ThreatGraph="["..string.rep("■",ThreatLevel)..string.rep("□",10-ThreatLevel).."]: "..ThreatLevel
-local report=REPORT:New("Lasing Report")
-report:Add(string.rep("-",15))
-report:Add("Target type: "..target:GetTypeName()or"unknown")
-report:Add("Threat Level: "..ThreatGraph.." ("..ThreatLevelText..")")
-if not self.ReferencePoint then
-report:Add("Location: "..client:GetCoordinate():ToStringBULLS(self.Coalition,Settings))
-else
-report:Add("Location: "..client:GetCoordinate():ToStringFromRPShort(self.ReferencePoint,self.RPName,client,Settings))
-end
-report:Add("Laser Code: "..self.UnitLaserCodes[playername]or 1688)
-report:Add(string.rep("-",15))
-local text=report:Text()
-self:__TargetReport(1,client,targetset,target,text)
-else
-local report=REPORT:New("Lasing Report")
-report:Add(string.rep("-",15))
-report:Add("N O T A R G E T S")
-report:Add(string.rep("-",15))
-local text=report:Text()
-self:__TargetReport(1,client,nil,nil,text)
-end
-return self
-end
-function PLAYERRECCE:_ReportVisualTargets(client,group,playername)
-self:T(self.lid.."_ReportVisualTargets")
-local targetset,number=self:_GetKnownTargets(client)
-if number>0 then
-local Settings=(client and _DATABASE:GetPlayerSettings(playername))or _SETTINGS
-local ThreatLevel=targetset:CalculateThreatLevelA2G()
-local ThreatLevelText="high"
-if ThreatLevel>3 and ThreatLevel<8 then
-ThreatLevelText="medium"
-elseif ThreatLevel<=3 then
-ThreatLevelText="low"
-end
-local ThreatGraph="["..string.rep("■",ThreatLevel)..string.rep("□",10-ThreatLevel).."]: "..ThreatLevel
-local report=REPORT:New("Target Report")
-report:Add(string.rep("-",15))
-report:Add("Target count: "..number)
-report:Add("Threat Level: "..ThreatGraph.." ("..ThreatLevelText..")")
-if not self.ReferencePoint then
-report:Add("Location: "..client:GetCoordinate():ToStringBULLS(self.Coalition,Settings))
-else
-report:Add("Location: "..client:GetCoordinate():ToStringFromRPShort(self.ReferencePoint,self.RPName,client,Settings))
-end
-report:Add(string.rep("-",15))
-local text=report:Text()
-self:__TargetReport(1,client,targetset,nil,text)
-else
-local report=REPORT:New("Target Report")
-report:Add(string.rep("-",15))
-report:Add("N O T A R G E T S")
-report:Add(string.rep("-",15))
-local text=report:Text()
-self:__TargetReport(1,client,nil,nil,text)
-end
-return self
-end
-function PLAYERRECCE:_BuildMenus(Client)
-self:T(self.lid.."_BuildMenus")
-local clients=self.PlayerSet
-local clientset=clients:GetSetObjects()
-if Client then clientset={Client}end
-for _,_client in pairs(clientset)do
-local client=_client
-if client and client:IsAlive()then
-local playername=client:GetPlayerName()
-if not self.UnitLaserCodes[playername]then
-self:_SetClientLaserCode(nil,nil,playername,1688)
-end
-if self.SmokeOwn[playername]==nil then
-self.SmokeOwn[playername]=self.smokeownposition
-end
-local group=client:GetGroup()
-if not self.ClientMenus[playername]then
-local canlase=self.CanLase[client:GetTypeName()]
-self.ClientMenus[playername]=MENU_GROUP:New(group,self.MenuName or self.Name or"RECCE")
-local txtonstation=self.OnStation[playername]and"ON"or"OFF"
-local text=string.format("Switch On-Station (%s)",txtonstation)
-local onstationmenu=MENU_GROUP_COMMAND:New(group,text,self.ClientMenus[playername],self._SwitchOnStation,self,client,group,playername)
-if self.OnStation[playername]then
-local smoketopmenu=MENU_GROUP:New(group,"Visual Markers",self.ClientMenus[playername])
-local smokemenu=MENU_GROUP_COMMAND:New(group,"Smoke Targets",smoketopmenu,self._SmokeTargets,self,client,group,playername)
-local flaremenu=MENU_GROUP_COMMAND:New(group,"Flare Targets",smoketopmenu,self._FlareTargets,self,client,group,playername)
-local illumenu=MENU_GROUP_COMMAND:New(group,"Illuminate Area",smoketopmenu,self._IlluTargets,self,client,group,playername)
-local ownsm=self.SmokeOwn[playername]and"ON"or"OFF"
-local owntxt=string.format("Switch smoke self (%s)",ownsm)
-local ownsmoke=MENU_GROUP_COMMAND:New(group,owntxt,smoketopmenu,self._SwitchSmoke,self,client,group,playername)
-if canlase then
-local txtonstation=self.AutoLase[playername]and"ON"or"OFF"
-local text=string.format("Switch Lasing (%s)",txtonstation)
-local lasemenu=MENU_GROUP_COMMAND:New(group,text,self.ClientMenus[playername],self._SwitchLasing,self,client,group,playername)
-local lasedist=MENU_GROUP:New(group,"Set Laser Distance",self.ClientMenus[playername])
-local mindist=100
-local maxdist=2000
-if self.LaserFOV[playername]and self.LaserFOV[playername].max then
-maxdist=self.LaserFOV[playername].max
-end
-local laselist={}
-for i=2,8 do
-local dist1=(i*1000)-1000
-local dist2=i*1000
-dist1=dist1==1000 and 100 or dist1
-local text=string.format("%d-%dm",dist1,dist2)
-if dist2==maxdist then
-text=text.." (*)"
-end
-laselist[i]=MENU_GROUP_COMMAND:New(group,text,lasedist,self._SwitchLasingDist,self,client,group,playername,dist1,dist2)
-end
-end
-local targetmenu=MENU_GROUP:New(group,"Target Report",self.ClientMenus[playername])
-if canlase then
-local reportL=MENU_GROUP_COMMAND:New(group,"Laser Target",targetmenu,self._ReportLaserTargets,self,client,group,playername)
-end
-local reportV=MENU_GROUP_COMMAND:New(group,"Visual Targets",targetmenu,self._ReportVisualTargets,self,client,group,playername)
-if self.UseController then
-local text=string.format("Target Upload to %s",self.Controller.MenuName or self.Controller.Name)
-local upload=MENU_GROUP_COMMAND:New(group,text,targetmenu,self._UploadTargets,self,client,group,playername)
-end
-if canlase then
-local lasecodemenu=MENU_GROUP:New(group,"Set Laser Code",self.ClientMenus[playername])
-local codemenu={}
-for _,_code in pairs(self.LaserCodes)do
-if _code==self.UnitLaserCodes[playername]then
-_code=tostring(_code).."(*)"
-end
-codemenu[playername.._code]=MENU_GROUP_COMMAND:New(group,tostring(_code),lasecodemenu,self._SetClientLaserCode,self,client,group,playername,_code)
-end
-end
-end
-end
-end
-end
-return self
-end
-function PLAYERRECCE:_CheckNewTargets(targetset,client,playername)
-self:T(self.lid.."_CheckNewTargets")
-local tempset=SET_UNIT:New()
-targetset:ForEach(
-function(unit)
-if unit and unit:IsAlive()then
-self:T("Report unit: "..unit:GetName())
-if not unit.PlayerRecceDetected then
-self:T("New unit: "..unit:GetName())
-unit.PlayerRecceDetected={
-detected=true,
-recce=client,
-playername=playername,
-timestamp=timer.getTime()
-}
-tempset:Add(unit:GetName(),unit)
-if not self.TargetCache:HasUniqueID(unit:GetName())then
-self.TargetCache:Push(unit,unit:GetName())
-end
-end
-if unit.PlayerRecceDetected and unit.PlayerRecceDetected.timestamp then
-local TNow=timer.getTime()
-if TNow-unit.PlayerRecceDetected.timestamp>self.TForget then
-unit.PlayerRecceDetected={
-detected=true,
-recce=client,
-playername=playername,
-timestamp=timer.getTime()
-}
-if not self.TargetCache:HasUniqueID(unit:GetName())then
-self.TargetCache:Push(unit,unit:GetName())
-end
-tempset:Add(unit:GetName(),unit)
-end
-end
-end
-end
-)
-local targetsbyclock={}
-for i=1,12 do
-targetsbyclock[i]={}
-end
-tempset:ForEach(
-function(object)
-local obj=object
-local clock=self:_GetClockDirection(client,obj)
-table.insert(targetsbyclock[clock],obj)
-end
-)
-self:T("Known target Count: "..self.TargetCache:Count())
-if tempset:CountAlive()>0 then
-self:TargetDetected(targetsbyclock,client,playername)
-end
-return self
-end
-function PLAYERRECCE:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey)
-self:T(self.lid.."SetSRS")
-self.PathToSRS=PathToSRS or"C:\\Program Files\\DCS-SimpleRadio-Standalone"
-self.Gender=Gender or"male"
-self.Culture=Culture or"en-US"
-self.Port=Port or 5002
-self.Voice=Voice
-self.PathToGoogleKey=PathToGoogleKey
-self.Volume=Volume or 1.0
-self.UseSRS=true
-self.Frequency=Frequency or{127,251}
-self.BCFrequency=self.Frequency
-self.Modulation=Modulation or{radio.modulation.FM,radio.modulation.AM}
-self.BCModulation=self.Modulation
-self.SRS=MSRS:New(self.PathToSRS,self.Frequency,self.Modulation,self.Volume)
-self.SRS:SetCoalition(self.Coalition)
-self.SRS:SetLabel(self.MenuName or self.Name)
-self.SRS:SetGender(self.Gender)
-self.SRS:SetCulture(self.Culture)
-self.SRS:SetPort(self.Port)
-self.SRS:SetVoice(self.Voice)
-if self.PathToGoogleKey then
-self.SRS:SetGoogle(self.PathToGoogleKey)
-end
-self.SRSQueue=MSRSQUEUE:New(self.MenuName or self.Name)
-self.SRSQueue:SetTransmitOnlyWithPlayers(self.TransmitOnlyWithPlayers)
-return self
-end
-function PLAYERRECCE:SetTransmitOnlyWithPlayers(Switch)
-self.TransmitOnlyWithPlayers=Switch
-if self.SRSQueue then
-self.SRSQueue:SetTransmitOnlyWithPlayers(Switch)
-end
-return self
-end
-function PLAYERRECCE:SetMenuName(Name)
-self:T(self.lid.."SetMenuName: "..Name)
-self.MenuName=Name
-return self
-end
-function PLAYERRECCE:EnableSmokeOwnPosition()
-self:T(self.lid.."EnableSmokeOwnPosition")
-self.smokeownposition=true
-return self
-end
-function PLAYERRECCE:DisableSmokeOwnPosition()
-self:T(self.lid.."DisableSmokeOwnPosition")
-self.smokeownposition=false
-return self
-end
-function PLAYERRECCE:EnableSmokeAverageTargetPosition()
-self:T(self.lid.."ENableSmokeOwnPosition")
-self.smokeaveragetargetpos=true
-return self
-end
-function PLAYERRECCE:DisableSmokeAverageTargetPosition()
-self:T(self.lid.."DisableSmokeAverageTargetPosition")
-self.smokeaveragetargetpos=false
-return self
-end
-function PLAYERRECCE:_GetTextForSpeech(text)
-text=string.gsub(text,"%d","%1 ")
-text=string.gsub(text,"^%s*","")
-text=string.gsub(text,"%s*$","")
-text=string.gsub(text,"0","zero")
-text=string.gsub(text,"9","niner")
-text=string.gsub(text," "," ")
-return text
-end
-function PLAYERRECCE:onafterStatus(From,Event,To)
-self:T({From,Event,To})
-if not self.timestamp then
-self.timestamp=timer.getTime()
-else
-local tNow=timer.getTime()
-if tNow-self.timestamp>=60 then
-self:_CleanupTargetCache()
-self.timestamp=timer.getTime()
-end
-end
-self:_BuildMenus()
-self.PlayerSet:ForEachClient(
-function(Client)
-local client=Client
-local playername=client:GetPlayerName()
-local cameraison=self:_CameraOn(client,playername)
-if client and client:IsAlive()and self.OnStation[playername]then
-local targetset,targetcount,tzone=nil,0,nil
-local laserset,lzone=nil,nil
-local vistargetset,vistargetcount,viszone=nil,0,nil
-if cameraison then
-targetset,targetcount,tzone=self:_GetTargetSet(client,true)
-if targetset then
-if self.ViewZone[playername]then
-self.ViewZone[playername]:UndrawZone()
-end
-if self.debug and tzone then
-self.ViewZone[playername]=tzone:DrawZone(self.Coalition,{0,0,1},nil,nil,nil,1)
-end
-end
-self:T({targetcount=targetcount})
-end
-if self.AutoLase[playername]and cameraison then
-laserset,targetcount,lzone=self:_GetTargetSet(client,true,true)
-if targetcount>0 or self.LaserTarget[playername]then
-if self.CanLase[client:GetTypeName()]then
-self:_LaseTarget(client,laserset)
-end
-end
-if lzone then
-if self.ViewZoneLaser[playername]then
-self.ViewZoneLaser[playername]:UndrawZone()
-end
-if self.debug and tzone then
-self.ViewZoneLaser[playername]=lzone:DrawZone(self.Coalition,{0,1,0},nil,nil,nil,1)
-end
-end
-self:T({lasercount=targetcount})
-end
-vistargetset,vistargetcount,viszone=self:_GetTargetSet(client,false)
-if vistargetset then
-if self.ViewZoneVisual[playername]then
-self.ViewZoneVisual[playername]:UndrawZone()
-end
-if self.debug and viszone then
-self.ViewZoneVisual[playername]=viszone:DrawZone(self.Coalition,{1,0,0},nil,nil,nil,3)
-end
-end
-self:T({visualtargetcount=vistargetcount})
-if targetset then
-vistargetset:AddSet(targetset)
-end
-if laserset then
-vistargetset:AddSet(laserset)
-end
-if not cameraison and self.debug then
-if self.ViewZoneLaser[playername]then
-self.ViewZoneLaser[playername]:UndrawZone()
-end
-if self.ViewZone[playername]then
-self.ViewZone[playername]:UndrawZone()
-end
-end
-self:_CheckNewTargets(vistargetset,client,playername)
-end
-end
-)
-self:__Status(-10)
-return self
-end
-function PLAYERRECCE:onafterRecceOnStation(From,Event,To,Client,Playername)
-self:T({From,Event,To})
-local callsign=Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
-local coord=Client:GetCoordinate()
-local coordtext=coord:ToStringBULLS(self.Coalition)
-if self.ReferencePoint then
-local Settings=Client and _DATABASE:GetPlayerSettings(Playername)or _SETTINGS
-coordtext=coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,Client,Settings)
-end
-local text1="Party time!"
-local text2=string.format("All stations, FACA %s on station\nat %s!",callsign,coordtext)
-local text2tts=string.format("All stations, FACA %s on station at %s!",callsign,coordtext)
-text2tts=self:_GetTextForSpeech(text2tts)
-if self.debug then
-self:T(text2.."\n"..text2tts)
-end
-if self.UseSRS then
-local grp=Client:GetGroup()
-local coord=grp:GetCoordinate()
-if coord then
-self.SRS:SetCoordinate(coord)
-end
-self.SRSQueue:NewTransmission(text1,nil,self.SRS,nil,2)
-self.SRSQueue:NewTransmission(text2tts,nil,self.SRS,nil,3)
-MESSAGE:New(text2,10,self.Name or"FACA"):ToCoalition(self.Coalition)
-else
-MESSAGE:New(text1,10,self.Name or"FACA"):ToClient(Client)
-MESSAGE:New(text2,10,self.Name or"FACA"):ToCoalition(self.Coalition)
-end
-return self
-end
-function PLAYERRECCE:onafterRecceOffStation(From,Event,To,Client,Playername)
-self:T({From,Event,To})
-local callsign=Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
-local coord=Client:GetCoordinate()
-local coordtext=coord:ToStringBULLS(self.Coalition)
-if self.ReferencePoint then
-local Settings=Client and _DATABASE:GetPlayerSettings(Playername)or _SETTINGS
-coordtext=coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,Client,Settings)
-end
-local text=string.format("All stations, FACA %s leaving station\nat %s, good bye!",callsign,coordtext)
-local texttts=string.format("All stations, FACA %s leaving station at %s, good bye!",callsign,coordtext)
-texttts=self:_GetTextForSpeech(texttts)
-if self.debug then
-self:T(text.."\n"..texttts)
-end
-local text1="Going home!"
-if self.UseSRS then
-local grp=Client:GetGroup()
-local coord=grp:GetCoordinate()
-if coord then
-self.SRS:SetCoordinate(coord)
-end
-self.SRSQueue:NewTransmission(text1,nil,self.SRS,nil,2)
-self.SRSQueue:NewTransmission(texttts,nil,self.SRS,nil,3)
-MESSAGE:New(text,10,self.Name or"FACA"):ToCoalition(self.Coalition)
-else
-MESSAGE:New(text,10,self.Name or"FACA"):ToCoalition(self.Coalition)
-end
-return self
-end
-function PLAYERRECCE:onafterTargetDetected(From,Event,To,Targetsbyclock,Client,Playername)
-self:T({From,Event,To})
-local dunits="meters"
-local Settings=Client and _DATABASE:GetPlayerSettings(Playername)or _SETTINGS
-local clientcoord=Client:GetCoordinate()
-for i=1,12 do
-local targets=Targetsbyclock[i]
-local targetno=#targets
-if targetno==1 then
-local targetdistance=clientcoord:Get2DDistance(targets[1]:GetCoordinate())or 100
-local Threatlvl=targets[1]:GetThreatLevel()
-local ThreatTxt="Low"
-if Threatlvl>=7 then
-ThreatTxt="Medium"
-elseif Threatlvl>=3 then
-ThreatTxt="High"
-end
-if Settings:IsMetric()then
-targetdistance=UTILS.Round(targetdistance,-2)
-if targetdistance>=1000 then
-targetdistance=UTILS.Round(targetdistance/1000,0)
-dunits="kilometer"
-end
-else
-if UTILS.MetersToNM(targetdistance)>=1 then
-targetdistance=UTILS.Round(UTILS.MetersToNM(targetdistance),0)
-dunits="miles"
-else
-targetdistance=UTILS.Round(UTILS.MetersToFeet(targetdistance),-2)
-dunits="feet"
-end
-end
-local text=string.format("Target! %s! %s o\'clock, %d %s!",ThreatTxt,i,targetdistance,dunits)
-local ttstext=string.format("Target! %s! %s oh clock, %d %s!",ThreatTxt,i,targetdistance,dunits)
-if self.UseSRS then
-local grp=Client:GetGroup()
-if clientcoord then
-self.SRS:SetCoordinate(clientcoord)
-end
-self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1,{grp},text,10)
-else
-MESSAGE:New(text,10,self.Name or"FACA"):ToClient(Client)
-end
-elseif targetno>1 then
-local function GetNearest(TTable)
-local distance=10000000
-for _,_unit in pairs(TTable)do
-local dist=clientcoord:Get2DDistance(_unit:GetCoordinate())or 100
-if dist=1000 then
-targetdistance=UTILS.Round(targetdistance/1000,0)
-dunits="kilometer"
-end
-else
-if UTILS.MetersToNM(targetdistance)>=1 then
-targetdistance=UTILS.Round(UTILS.MetersToNM(targetdistance),0)
-dunits="miles"
-else
-targetdistance=UTILS.Round(UTILS.MetersToFeet(targetdistance),-2)
-dunits="feet"
-end
-end
-local text=string.format(" %d targets! %s o\'clock, %d %s!",targetno,i,targetdistance,dunits)
-local ttstext=string.format("%d targets! %s oh clock, %d %s!",targetno,i,targetdistance,dunits)
-if self.UseSRS then
-local grp=Client:GetGroup()
-local coord=grp:GetCoordinate()
-if coord then
-self.SRS:SetCoordinate(coord)
-end
-self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1,{grp},text,10)
-else
-MESSAGE:New(text,10,self.Name or"FACA"):ToClient(Client)
-end
-end
-end
-return self
-end
-function PLAYERRECCE:onafterIllumination(From,Event,To,Client,Playername,TargetSet)
-self:T({From,Event,To})
-local callsign=Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
-local coord=Client:GetCoordinate()
-local coordtext=coord:ToStringBULLS(self.Coalition)
-if self.AttackSet then
-for _,_client in pairs(self.AttackSet.Set)do
-local client=_client
-if client and client:IsAlive()then
-local Settings=client and _DATABASE:GetPlayerSettings(client:GetPlayerName())or _SETTINGS
-local coordtext=coord:ToStringA2G(client,Settings)
-if self.ReferencePoint then
-coordtext=coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,client,Settings)
-end
-local text=string.format("All stations, %s fired illumination\nat %s!",callsign,coordtext)
-MESSAGE:New(text,15,self.Name or"FACA"):ToClient(client)
-end
-end
-end
-local text="Sunshine!"
-local ttstext="Sunshine!"
-if self.UseSRS then
-local grp=Client:GetGroup()
-local coord=grp:GetCoordinate()
-if coord then
-self.SRS:SetCoordinate(coord)
-end
-self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1,{grp},text,10)
-else
-MESSAGE:New(text,10,self.Name or"FACA"):ToClient(Client)
-end
-return self
-end
-function PLAYERRECCE:onafterTargetsSmoked(From,Event,To,Client,Playername,TargetSet)
-self:T({From,Event,To})
-local callsign=Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
-local coord=Client:GetCoordinate()
-local coordtext=coord:ToStringBULLS(self.Coalition)
-if self.AttackSet then
-for _,_client in pairs(self.AttackSet.Set)do
-local client=_client
-if client and client:IsAlive()then
-local Settings=client and _DATABASE:GetPlayerSettings(client:GetPlayerName())or _SETTINGS
-local coordtext=coord:ToStringA2G(client,Settings)
-if self.ReferencePoint then
-coordtext=coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,client,Settings)
-end
-local text=string.format("All stations, %s smoked targets\nat %s!",callsign,coordtext)
-MESSAGE:New(text,15,self.Name or"FACA"):ToClient(client)
-end
-end
-end
-local text="Smoke on!"
-local ttstext="Smoke and Mirrors!"
-if self.UseSRS then
-local grp=Client:GetGroup()
-local coord=grp:GetCoordinate()
-if coord then
-self.SRS:SetCoordinate(coord)
-end
-self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1,{grp},text,10)
-else
-MESSAGE:New(text,10,self.Name or"FACA"):ToClient(Client)
-end
-return self
-end
-function PLAYERRECCE:onafterTargetsFlared(From,Event,To,Client,Playername,TargetSet)
-self:T({From,Event,To})
-local callsign=Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
-local coord=Client:GetCoordinate()
-local coordtext=coord:ToStringBULLS(self.Coalition)
-if self.AttackSet then
-for _,_client in pairs(self.AttackSet.Set)do
-local client=_client
-if client and client:IsAlive()then
-local Settings=client and _DATABASE:GetPlayerSettings(client:GetPlayerName())or _SETTINGS
-if self.ReferencePoint then
-coordtext=coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,client,Settings)
-end
-local coordtext=coord:ToStringA2G(client,Settings)
-local text=string.format("All stations, %s flared targets\nat %s!",callsign,coordtext)
-MESSAGE:New(text,15,self.Name or"FACA"):ToClient(client)
-end
-end
-end
-local text="Fireworks!"
-local ttstext="Fire works!"
-if self.UseSRS then
-local grp=Client:GetGroup()
-local coord=grp:GetCoordinate()
-if coord then
-self.SRS:SetCoordinate(coord)
-end
-self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1,{grp},text,10)
-else
-MESSAGE:New(text,10,self.Name or"FACA"):ToClient(Client)
-end
-return self
-end
-function PLAYERRECCE:onafterTargetLasing(From,Event,To,Client,Target,Lasercode,Lasingtime)
-self:T({From,Event,To})
-local callsign=Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
-local Settings=(Client and _DATABASE:GetPlayerSettings(Client:GetPlayerName()))or _SETTINGS
-local coord=Client:GetCoordinate()
-local coordtext=coord:ToStringBULLS(self.Coalition,Settings)
-if self.ReferencePoint then
-coordtext=coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,Client,Settings)
-end
-local targettype=Target:GetTypeName()
-if self.AttackSet then
-for _,_client in pairs(self.AttackSet.Set)do
-local client=_client
-if client and client:IsAlive()then
-local Settings=client and _DATABASE:GetPlayerSettings(client:GetPlayerName())or _SETTINGS
-if self.ReferencePoint then
-coordtext=coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,client,Settings)
-end
-local coordtext=coord:ToStringA2G(client,Settings)
-local text=string.format("All stations, %s lasing %s\nat %s!\nCode %d, Duration %d plus seconds!",callsign,targettype,coordtext,Lasercode,Lasingtime)
-MESSAGE:New(text,15,self.Name or"FACA"):ToClient(client)
-end
-end
-end
-local text="Lasing!"
-local ttstext="Laser on!"
-if self.UseSRS then
-local grp=Client:GetGroup()
-local coord=grp:GetCoordinate()
-if coord then
-self.SRS:SetCoordinate(coord)
-end
-self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1,{grp},text,10)
-else
-MESSAGE:New(text,10,self.Name or"FACA"):ToClient(Client)
-end
-return self
-end
-function PLAYERRECCE:onafterShack(From,Event,To,Client,Target)
-self:T({From,Event,To})
-local callsign=Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
-local Settings=(Client and _DATABASE:GetPlayerSettings(Client:GetPlayerName()))or _SETTINGS
-local coord=Client:GetCoordinate()
-local coordtext=coord:ToStringBULLS(self.Coalition,Settings)
-if self.ReferencePoint then
-coordtext=coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,Client,Settings)
-end
-local targettype="target"
-if self.AttackSet then
-for _,_client in pairs(self.AttackSet.Set)do
-local client=_client
-if client and client:IsAlive()then
-local Settings=client and _DATABASE:GetPlayerSettings(client:GetPlayerName())or _SETTINGS
-if self.ReferencePoint then
-coordtext=coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,client,Settings)
-end
-local coordtext=coord:ToStringA2G(client,Settings)
-local text=string.format("All stations, %s good hit on %s\nat %s!",callsign,targettype,coordtext)
-MESSAGE:New(text,15,self.Name or"FACA"):ToClient(client)
-end
-end
-end
-local text="Shack!"
-local ttstext="Shack!"
-if self.UseSRS then
-local grp=Client:GetGroup()
-local coord=grp:GetCoordinate()
-if coord then
-self.SRS:SetCoordinate(coord)
-end
-self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1)
-else
-MESSAGE:New(text,10,self.Name or"FACA"):ToClient(Client)
-end
-return self
-end
-function PLAYERRECCE:onafterTargetLOSLost(From,Event,To,Client,Target)
-self:T({From,Event,To})
-local callsign=Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
-local Settings=(Client and _DATABASE:GetPlayerSettings(Client:GetPlayerName()))or _SETTINGS
-local coord=Client:GetCoordinate()
-local coordtext=coord:ToStringBULLS(self.Coalition,Settings)
-if self.ReferencePoint then
-coordtext=coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,Client,Settings)
-end
-local targettype="target"
-if self.AttackSet then
-for _,_client in pairs(self.AttackSet.Set)do
-local client=_client
-if client and client:IsAlive()then
-local Settings=client and _DATABASE:GetPlayerSettings(client:GetPlayerName())or _SETTINGS
-if self.ReferencePoint then
-coordtext=coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,client,Settings)
-end
-local coordtext=coord:ToStringA2G(client,Settings)
-local text=string.format("All stations, %s lost sight of %s\nat %s!",callsign,targettype,coordtext)
-MESSAGE:New(text,15,self.Name or"FACA"):ToClient(client)
-end
-end
-end
-local text="Lost LOS!"
-local ttstext="Lost L O S!"
-if self.UseSRS then
-local grp=Client:GetGroup()
-local coord=grp:GetCoordinate()
-if coord then
-self.SRS:SetCoordinate(coord)
-end
-self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,1,{grp},text,10)
-else
-MESSAGE:New(text,10,self.Name or"FACA"):ToClient(Client)
-end
-return self
-end
-function PLAYERRECCE:onafterTargetReport(From,Event,To,Client,TargetSet,Target,Text)
-self:T({From,Event,To})
-MESSAGE:New(Text,45,self.Name or"FACA"):ToClient(Client)
-if self.AttackSet then
-for _,_client in pairs(self.AttackSet.Set)do
-local client=_client
-if client and client:IsAlive()and client~=Client then
-MESSAGE:New(Text,45,self.Name or"FACA"):ToClient(client)
-end
-end
-end
-return self
-end
-function PLAYERRECCE:onafterTargetReportSent(From,Event,To,Client,Playername,TargetSet)
-self:T({From,Event,To})
-local text="Upload completed!"
-if self.UseSRS then
-local grp=Client:GetGroup()
-local coord=grp:GetCoordinate()
-if coord then
-self.SRS:SetCoordinate(coord)
-end
-self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,1,{grp},text,10)
-else
-MESSAGE:New(text,10,self.Name or"FACA"):ToClient(Client)
-end
-return self
-end
-function PLAYERRECCE:onafterStop(From,Event,To)
-self:I({From,Event,To})
-self:UnHandleEvent(EVENTS.PlayerLeaveUnit)
-self:UnHandleEvent(EVENTS.Ejection)
-self:UnHandleEvent(EVENTS.Crash)
-self:UnHandleEvent(EVENTS.PilotDead)
-self:UnHandleEvent(EVENTS.PlayerEnterAircraft)
-return self
-end
-RECOVERYTANKER={
-ClassName="RECOVERYTANKER",
-Debug=false,
-lid=nil,
-carrier=nil,
-carriertype=nil,
-tankergroupname=nil,
-tanker=nil,
-airbase=nil,
-beacon=nil,
-TACANchannel=nil,
-TACANmode=nil,
-TACANmorse=nil,
-TACANon=nil,
-RadioFreq=nil,
-RadioModu=nil,
-altitude=nil,
-speed=nil,
-distStern=nil,
-distBow=nil,
-dTupdate=nil,
-Dupdate=nil,
-Hupdate=nil,
-Tupdate=nil,
-takeoff=nil,
-lowfuel=nil,
-respawn=nil,
-respawninair=nil,
-uncontrolledac=nil,
-orientation=nil,
-orientlast=nil,
-position=nil,
-alias=nil,
-uid=0,
-awacs=nil,
-callsignname=nil,
-callsignnumber=nil,
-modex=nil,
-eplrs=nil,
-recovery=nil,
-terminaltype=nil,
-unlimitedfuel=false,
-}
-_RECOVERYTANKERID=0
-RECOVERYTANKER.version="1.0.10"
-function RECOVERYTANKER:New(carrierunit,tankergroupname)
-local self=BASE:Inherit(self,FSM:New())
-if type(carrierunit)=="string"then
-self.carrier=UNIT:FindByName(carrierunit)
-else
-self.carrier=carrierunit
-end
-self.carriertype=self.carrier:GetTypeName()
-self.tankergroupname=tankergroupname
-_RECOVERYTANKERID=_RECOVERYTANKERID+1
-self.uid=_RECOVERYTANKERID
-self.carrier:SetState(self.carrier,string.format("RECOVERYTANKER_%d",self.uid),self)
-self.alias=string.format("%s_%s_%02d",self.carrier:GetName(),self.tankergroupname,_RECOVERYTANKERID)
-self.lid=string.format("RECOVERYTANKER %s | ",self.alias)
-self:SetAltitude()
-self:SetSpeed()
-self:SetRacetrackDistances()
-self:SetHomeBase(AIRBASE:FindByName(self.carrier:GetName()))
-self:SetTakeoffHot()
-self:SetLowFuelThreshold()
-self:SetRespawnOnOff()
-self:SetTACAN()
-self:SetRadio()
-self:SetPatternUpdateDistance()
-self:SetPatternUpdateHeading()
-self:SetPatternUpdateInterval()
-self:SetAWACS(false)
-self:SetRecoveryAirboss(false)
-self.terminaltype=AIRBASE.TerminalType.OpenMedOrBig
-if false then
-BASE:TraceOnOff(true)
-BASE:TraceClass(self.ClassName)
-BASE:TraceLevel(1)
-end
-self:SetStartState("Stopped")
-self:AddTransition("Stopped","Start","Running")
-self:AddTransition("*","RefuelStart","Refueling")
-self:AddTransition("*","RefuelStop","Running")
-self:AddTransition("*","Run","Running")
-self:AddTransition("Running","RTB","Returning")
-self:AddTransition("Returning","Returned","Returned")
-self:AddTransition("*","Status","*")
-self:AddTransition("Running","PatternUpdate","*")
-self:AddTransition("*","Stop","Stopped")
-return self
-end
-function RECOVERYTANKER:SetUnlimitedFuel(OnOff)
-self.unlimitedfuel=OnOff
-return self
-end
-function RECOVERYTANKER:SetSpeed(speed)
-self.speed=UTILS.KnotsToMps(speed or 274)
-return self
-end
-function RECOVERYTANKER:SetAltitude(altitude)
-self.altitude=UTILS.FeetToMeters(altitude or 6000)
-return self
-end
-function RECOVERYTANKER:SetRacetrackDistances(distbow,diststern)
-self.distBow=UTILS.NMToMeters(distbow or 10)
-self.distStern=-UTILS.NMToMeters(diststern or 4)
-return self
-end
-function RECOVERYTANKER:SetPatternUpdateInterval(interval)
-self.dTupdate=(interval or 10)*60
-return self
-end
-function RECOVERYTANKER:SetPatternUpdateDistance(distancechange)
-self.Dupdate=UTILS.NMToMeters(distancechange or 5)
-return self
-end
-function RECOVERYTANKER:SetPatternUpdateHeading(headingchange)
-self.Hupdate=headingchange or 5
-return self
-end
-function RECOVERYTANKER:SetLowFuelThreshold(fuelthreshold)
-self.lowfuel=fuelthreshold or 10
-return self
-end
-function RECOVERYTANKER:SetHomeBase(airbase,terminaltype)
-if type(airbase)=="string"then
-self.airbase=AIRBASE:FindByName(airbase)
-else
-self.airbase=airbase
-end
-if not self.airbase then
-self:E(self.lid.."ERROR: Airbase is nil!")
-end
-if terminaltype then
-self.terminaltype=terminaltype
-end
-return self
-end
-function RECOVERYTANKER:SetRecoveryAirboss(switch)
-if switch==true or switch==nil then
-self.recovery=true
-else
-self.recovery=false
-end
-return self
-end
-function RECOVERYTANKER:SetAWACS(switch,eplrs)
-if switch==nil or switch==true then
-self.awacs=true
-else
-self.awacs=false
-end
-if eplrs==nil or eplrs==true then
-self.eplrs=true
-else
-self.eplrs=false
-end
-return self
-end
-function RECOVERYTANKER:SetCallsign(callsignname,callsignnumber)
-self.callsignname=callsignname
-self.callsignnumber=callsignnumber
-return self
-end
-function RECOVERYTANKER:SetModex(modex)
-self.modex=modex
-return self
-end
-function RECOVERYTANKER:SetTakeoff(takeofftype)
-self.takeoff=takeofftype
-return self
-end
-function RECOVERYTANKER:SetTakeoffHot()
-self:SetTakeoff(SPAWN.Takeoff.Hot)
-return self
-end
-function RECOVERYTANKER:SetTakeoffCold()
-self:SetTakeoff(SPAWN.Takeoff.Cold)
-return self
-end
-function RECOVERYTANKER:SetTakeoffAir()
-self:SetTakeoff(SPAWN.Takeoff.Air)
-return self
-end
-function RECOVERYTANKER:SetRespawnOn()
-self.respawn=true
-return self
-end
-function RECOVERYTANKER:SetRespawnOff()
-self.respawn=false
-return self
-end
-function RECOVERYTANKER:SetRespawnOnOff(switch)
-if switch==nil or switch==true then
-self.respawn=true
-else
-self.respawn=false
-end
-return self
-end
-function RECOVERYTANKER:SetRespawnInAir()
-self.respawninair=true
-return self
-end
-function RECOVERYTANKER:SetUseUncontrolledAircraft()
-self.uncontrolledac=true
-return self
-end
-function RECOVERYTANKER:SetTACANoff()
-self.TACANon=false
-return self
-end
-function RECOVERYTANKER:SetTACAN(channel,morse,mode)
-self.TACANchannel=channel or 1
-self.TACANmode=mode or"Y"
-self.TACANmorse=morse or"TKR"
-self.TACANon=true
-return self
-end
-function RECOVERYTANKER:SetRadio(frequency,modulation)
-self.RadioFreq=frequency or 251
-self.RadioModu=modulation or"AM"
-return self
-end
-function RECOVERYTANKER:SetDebugModeON()
-self.Debug=true
-return self
-end
-function RECOVERYTANKER:SetDebugModeOFF()
-self.Debug=false
-return self
-end
-function RECOVERYTANKER:IsReturning()
-return self:is("Returning")
-end
-function RECOVERYTANKER:IsReturned()
-return self:is("Returned")
-end
-function RECOVERYTANKER:IsRunning()
-return self:is("Running")
-end
-function RECOVERYTANKER:IsRefueling()
-return self:is("Refueling")
-end
-function RECOVERYTANKER:IsStopped()
-return self:is("Stopped")
-end
-function RECOVERYTANKER:GetAlias()
-return self.alias
-end
-function RECOVERYTANKER:GetUnitName()
-local unit=self.tanker:GetUnit(1)
-if unit then
-return unit:GetName()
-end
-return nil
-end
-function RECOVERYTANKER:onafterStart(From,Event,To)
-self:I(string.format("Starting Recovery Tanker v%s for carrier unit %s of type %s for tanker group %s.",RECOVERYTANKER.version,self.carrier:GetName(),self.carriertype,self.tankergroupname))
-self:HandleEvent(EVENTS.EngineShutdown)
-self:HandleEvent(EVENTS.Land)
-self:HandleEvent(EVENTS.Refueling,self._RefuelingStart)
-self:HandleEvent(EVENTS.RefuelingStop,self._RefuelingStop)
-self:HandleEvent(EVENTS.Crash,self._OnEventCrashOrDead)
-self:HandleEvent(EVENTS.Dead,self._OnEventCrashOrDead)
-local Spawn=SPAWN:NewWithAlias(self.tankergroupname,self.alias)
-if self.unlimitedfuel then
-Spawn:OnSpawnGroup(
-function(grp)
-grp:CommandSetUnlimitedFuel(self.unlimitedfuel)
-end
-)
-end
-Spawn:InitRadioCommsOnOff(true)
-Spawn:InitRadioFrequency(self.RadioFreq)
-Spawn:InitRadioModulation(self.RadioModu)
-Spawn:InitModex(self.modex)
-if self.takeoff==SPAWN.Takeoff.Air then
-local hdg=self.carrier:GetHeading()
-local dist=-self.distStern+UTILS.NMToMeters(4)
-local Carrier=self.carrier:GetCoordinate():Translate(dist,hdg+190):SetAltitude(self.altitude)
-Spawn:InitHeading(hdg+10)
-self.tanker=Spawn:SpawnFromCoordinate(Carrier)
-else
-if self.uncontrolledac then
-self.tanker=GROUP:FindByName(self.tankergroupname)
-if self.tanker:IsAlive()then
-self.tanker:StartUncontrolled()
-else
-self:E(string.format("ERROR: No uncontrolled (alive) tanker group with name %s could be found!",self.tankergroupname))
-return
-end
-else
-self.tanker=Spawn:SpawnAtAirbase(self.airbase,self.takeoff,nil,self.terminaltype)
-end
-end
-self:ScheduleOnce(1,self._InitRoute,self,-self.distStern+UTILS.NMToMeters(3))
-if self.TACANon then
-self:_ActivateTACAN(2)
-end
-if self.callsignname then
-self.tanker:CommandSetCallsign(self.callsignname,self.callsignnumber,2)
-end
-if self.eplrs then
-self.tanker:CommandEPLRS(true,3)
-end
-self.orientation=self.carrier:GetOrientationX()
-self.orientlast=self.carrier:GetOrientationX()
-self.position=self.carrier:GetCoordinate()
-self:__Status(10)
-end
-function RECOVERYTANKER:onafterStatus(From,Event,To)
-local time=timer.getTime()
-if self.tanker and self.tanker:IsAlive()then
-local fuel=self.tanker:GetFuel()*100
-local life=self.tanker:GetUnit(1):GetLife()
-local life0=self.tanker:GetUnit(1):GetLife0()
-local lifeR=self.tanker:GetUnit(1):GetLifeRelative()
-local text=string.format("Recovery tanker %s: state=%s fuel=%.1f, life=%.1f/%.1f=%d",self.tanker:GetName(),self:GetState(),fuel,life,life0,lifeR*100)
-self:T(self.lid..text)
-MESSAGE:New(text,10):ToAllIf(self.Debug)
-if self:IsRunning()then
-if fuel100 then
-return
-end
-local text=string.format("Recovery tanker %s started refueling unit %s",self.tanker:GetName(),receiver:GetName())
-MESSAGE:New(text,10,"DEBUG"):ToAllIf(self.Debug)
-self:T(self.lid..text)
-self:RefuelStart(receiver)
-end
-end
-function RECOVERYTANKER:_RefuelingStop(EventData)
-if EventData and EventData.IniUnit and EventData.IniUnit:IsAlive()then
-local receiver=EventData.IniUnit
-local dist=receiver:GetCoordinate():Get2DDistance(self.tanker:GetCoordinate())
-if dist>100 then
-return
-end
-local text=string.format("Recovery tanker %s stopped refueling unit %s",self.tanker:GetName(),receiver:GetName())
-MESSAGE:New(text,10,"DEBUG"):ToAllIf(self.Debug)
-self:T(self.lid..text)
-self:RefuelStop(receiver)
-end
-end
-function RECOVERYTANKER:_OnEventCrashOrDead(EventData)
-self:F2({eventdata=EventData})
-if EventData and EventData.IniUnit then
-local unit=EventData.IniUnit
-local unitname=tostring(EventData.IniUnitName)
-if EventData.IniGroupName==self.tanker:GetName()then
-self:E(self.lid..string.format("Recovery tanker %s crashed!",unitname))
-self:Stop()
-if self.respawn then
-self:__Start(5)
-end
-end
-end
-end
-function RECOVERYTANKER:_InitPatternTaskFunction()
-local carriername=self.carrier:GetName()
-local DCSScript={}
-DCSScript[#DCSScript+1]=string.format('local mycarrier = UNIT:FindByName(\"%s\") ',carriername)
-DCSScript[#DCSScript+1]=string.format('local mytanker = mycarrier:GetState(mycarrier, \"RECOVERYTANKER_%d\") ',self.uid)
-DCSScript[#DCSScript+1]=string.format('mytanker:PatternUpdate()')
-local DCSTask=CONTROLLABLE.TaskWrappedAction(self,CONTROLLABLE.CommandDoScript(self,table.concat(DCSScript)))
-return DCSTask
-end
-function RECOVERYTANKER:_InitRoute(dist,delay)
-dist=dist or UTILS.NMToMeters(8)
-delay=delay or 1
-self:T(self.lid..string.format("Initializing route of recovery tanker %s.",self.tanker:GetName()))
-local Carrier=self.carrier:GetCoordinate()
-local hdg=self.carrier:GetHeading()
-local p=Carrier:Translate(dist,hdg+190):SetAltitude(self.altitude)
-local speed=self.tanker:GetSpeedMax()*0.8
-if self.Debug then
-p:MarkToAll(string.format("Enter Pattern WP: alt=%d ft, speed=%d kts",UTILS.MetersToFeet(self.altitude),speed*0.539957))
-end
-local task=self:_InitPatternTaskFunction()
-local wp={}
-if self.takeoff==SPAWN.Takeoff.Air then
-wp[#wp+1]=self.tanker:GetCoordinate():SetAltitude(self.altitude):WaypointAirTurningPoint(nil,speed,{},"Spawn Position")
-else
-wp[#wp+1]=Carrier:WaypointAirTakeOffParking()
-end
-wp[#wp+1]=p:WaypointAirTurningPoint(nil,speed,{task},"Enter Pattern")
-self.tanker:Route(wp,delay)
-self:__Run(1)
-self.Tupdate=nil
-end
-function RECOVERYTANKER:_CheckPatternUpdate(dt)
-local pos=self.carrier:GetCoordinate()
-local vNew=self.carrier:GetOrientationX()
-local vOld=self.orientation
-local vLast=self.orientlast
-vNew.y=0;vOld.y=0;vLast.y=0
-local deltaHeading=math.deg(math.acos(UTILS.VecDot(vNew,vOld)/UTILS.VecNorm(vNew)/UTILS.VecNorm(vOld)))
-local deltaLast=math.deg(math.acos(UTILS.VecDot(vNew,vLast)/UTILS.VecNorm(vNew)/UTILS.VecNorm(vLast)))
-self.orientlast=vNew
-local turning=deltaLast>=1
-if turning then
-self:T2(self.lid..string.format("Carrier is turning. Delta Heading = %.1f",deltaLast))
-end
-local Hchange=false
-if math.abs(deltaHeading)>=self.Hupdate then
-self:T(self.lid..string.format("Carrier heading changed by %d degrees. Turning=%s.",deltaHeading,tostring(turning)))
-Hchange=true
-end
-local dist=pos:Get2DDistance(self.position)
-local Dchange=false
-if dist>self.Dupdate then
-self:T(self.lid..string.format("Carrier position changed by %.1f NM. Turning=%s.",UTILS.MetersToNM(dist),tostring(turning)))
-Dchange=true
-end
-local update=false
-if self:IsRunning()and dt>self.dTupdate and not turning then
-if Hchange or Dchange then
-local text=string.format("Updating tanker %s pattern due to carrier position=%s or heading=%s change.",self.tanker:GetName(),tostring(Dchange),tostring(Hchange))
-MESSAGE:New(text,10,"DEBUG"):ToAllIf(self.Debug)
-self:T(self.lid..text)
-self.orientation=vNew
-self.position=pos
-update=true
-end
-end
-return update
-end
-function RECOVERYTANKER:_ActivateTACAN(delay)
-if delay and delay>0 then
-self:ScheduleOnce(delay,RECOVERYTANKER._ActivateTACAN,self)
-else
-local unit=self.tanker:GetUnit(1)
-if unit and unit:IsAlive()then
-local text=string.format("Activating TACAN beacon: channel=%d mode=%s, morse=%s.",self.TACANchannel,self.TACANmode,self.TACANmorse)
-MESSAGE:New(text,10,"DEBUG"):ToAllIf(self.Debug)
-self:T(self.lid..text)
-self.beacon=BEACON:New(unit)
-self.beacon:ActivateTACAN(self.TACANchannel,self.TACANmode,self.TACANmorse,true)
-else
-self:E(self.lid.."ERROR: Recovery tanker is not alive!")
-end
-end
-end
-function RECOVERYTANKER:_Pattern()
-local hdg=self.carrier:GetHeading()
-local alt=self.altitude
-local Carrier=self.carrier:GetCoordinate()
-local width=UTILS.NMToMeters(8)
-local p={}
-p[1]=self.tanker:GetCoordinate()
-p[2]=Carrier:SetAltitude(alt)
-p[3]=p[2]:Translate(self.distBow,hdg)
-p[4]=p[3]:Translate(width/math.sqrt(2),hdg-45)
-p[5]=p[3]:Translate(width,hdg-90)
-p[6]=p[5]:Translate(self.distStern-self.distBow,hdg)
-p[7]=p[2]:Translate(self.distStern,hdg)
-local wp={}
-for i=1,#p do
-local coord=p[i]
-coord:MarkToAll(string.format("Waypoint %d",i))
-table.insert(wp,coord:WaypointAirTurningPoint(nil,UTILS.MpsToKmph(self.speed)))
-end
-return wp
-end
-RESCUEHELO={
-ClassName="RESCUEHELO",
-Debug=false,
-lid=nil,
-carrier=nil,
-carriertype=nil,
-helogroupname=nil,
-helo=nil,
-airbase=nil,
-takeoff=nil,
-followset=nil,
-formation=nil,
-lowfuel=nil,
-altitude=nil,
-offsetX=nil,
-offsetZ=nil,
-rescuezone=nil,
-respawn=nil,
-respawninair=nil,
-uncontrolledac=nil,
-rescueon=nil,
-rescueduration=nil,
-rescuespeed=nil,
-rescuestopboat=nil,
-HeloFuel0=nil,
-rtb=nil,
-carrierstop=nil,
-alias=nil,
-uid=0,
-modex=nil,
-dtFollow=nil,
-}
-_RESCUEHELOID=0
-RESCUEHELO.version="1.1.0"
-function RESCUEHELO:New(carrierunit,helogroupname)
-local self=BASE:Inherit(self,FSM:New())
-if type(carrierunit)=="string"then
-self.carrier=UNIT:FindByName(carrierunit)
-else
-self.carrier=carrierunit
-end
-self.carriertype=self.carrier:GetTypeName()
-self.helogroupname=helogroupname
-_RESCUEHELOID=_RESCUEHELOID+1
-self.uid=_RESCUEHELOID
-self.carrier:SetState(self.carrier,string.format("RESCUEHELO_%d",self.uid),self)
-self.alias=string.format("%s_%s_%02d",self.carrier:GetName(),self.helogroupname,_RESCUEHELOID)
-self.lid=string.format("RESCUEHELO %s | ",self.alias)
-self:SetHomeBase(AIRBASE:FindByName(self.carrier:GetName()))
-self:SetTakeoffHot()
-self:SetLowFuelThreshold()
-self:SetAltitude()
-self:SetOffsetX()
-self:SetOffsetZ()
-self:SetRespawnOn()
-self:SetRescueOn()
-self:SetRescueZone()
-self:SetRescueHoverSpeed()
-self:SetRescueDuration()
-self:SetFollowTimeInterval()
-self:SetRescueStopBoatOff()
-self.rtb=false
-self.carrierstop=false
-if false then
-self.Debug=true
-BASE:TraceOnOff(true)
-BASE:TraceClass(self.ClassName)
-BASE:TraceLevel(1)
-end
-self:SetStartState("Stopped")
-self:AddTransition("Stopped","Start","Running")
-self:AddTransition("Running","Rescue","Rescuing")
-self:AddTransition("Running","RTB","Returning")
-self:AddTransition("Rescuing","RTB","Returning")
-self:AddTransition("Returning","Returned","Returned")
-self:AddTransition("Running","Run","Running")
-self:AddTransition("Returned","Run","Running")
-self:AddTransition("*","Status","*")
-self:AddTransition("*","Stop","Stopped")
-return self
-end
-function RESCUEHELO:SetLowFuelThreshold(threshold)
-self.lowfuel=threshold or 5
-return self
-end
-function RESCUEHELO:SetHomeBase(airbase)
-if type(airbase)=="string"then
-self.airbase=AIRBASE:FindByName(airbase)
-else
-self.airbase=airbase
-end
-if not self.airbase then
-self:E(self.lid.."ERROR: Airbase is nil!")
-end
-return self
-end
-function RESCUEHELO:SetRescueZone(radius)
-radius=UTILS.NMToMeters(radius or 15)
-self.rescuezone=ZONE_UNIT:New("Rescue Zone",self.carrier,radius)
-return self
-end
-function RESCUEHELO:SetRescueHoverSpeed(speed)
-self.rescuespeed=UTILS.KnotsToMps(speed or 5)
-return self
-end
-function RESCUEHELO:SetRescueDuration(duration)
-self.rescueduration=(duration or 5)*60
-return self
-end
-function RESCUEHELO:SetRescueOn()
-self.rescueon=true
-return self
-end
-function RESCUEHELO:SetRescueOff()
-self.rescueon=false
-return self
-end
-function RESCUEHELO:SetRescueStopBoatOn()
-self.rescuestopboat=true
-return self
-end
-function RESCUEHELO:SetRescueStopBoatOff()
-self.rescuestopboat=false
-return self
-end
-function RESCUEHELO:SetTakeoff(takeofftype)
-self.takeoff=takeofftype or SPAWN.Takeoff.Hot
-return self
-end
-function RESCUEHELO:SetTakeoffHot()
-self:SetTakeoff(SPAWN.Takeoff.Hot)
-return self
-end
-function RESCUEHELO:SetTakeoffCold()
-self:SetTakeoff(SPAWN.Takeoff.Cold)
-return self
-end
-function RESCUEHELO:SetTakeoffAir()
-self:SetTakeoff(SPAWN.Takeoff.Air)
-return self
-end
-function RESCUEHELO:SetAltitude(alt)
-self.altitude=alt or 70
-return self
-end
-function RESCUEHELO:SetOffsetX(distance)
-self.offsetX=distance or 200
-return self
-end
-function RESCUEHELO:SetOffsetZ(distance)
-self.offsetZ=distance or 240
-return self
-end
-function RESCUEHELO:SetRespawnOn()
-self.respawn=true
-return self
-end
-function RESCUEHELO:SetRespawnOff()
-self.respawn=false
-return self
-end
-function RESCUEHELO:SetRespawnOnOff(switch)
-if switch==nil or switch==true then
-self.respawn=true
-else
-self.respawn=false
-end
-return self
-end
-function RESCUEHELO:SetRespawnInAir()
-self.respawninair=true
-return self
-end
-function RESCUEHELO:SetModex(modex)
-self.modex=modex
-return self
-end
-function RESCUEHELO:SetFollowTimeInterval(dt)
-self.dtFollow=dt or 1.0
-return self
-end
-function RESCUEHELO:SetUseUncontrolledAircraft()
-self.uncontrolledac=true
-return self
-end
-function RESCUEHELO:SetDebugModeON()
-self.Debug=true
-return self
-end
-function RESCUEHELO:SetDebugModeOFF()
-self.Debug=false
-return self
-end
-function RESCUEHELO:IsReturning()
-return self:is("Returning")
-end
-function RESCUEHELO:IsRunning()
-return self:is("Running")
-end
-function RESCUEHELO:IsRescuing()
-return self:is("Rescuing")
-end
-function RESCUEHELO:IsStopped()
-return self:is("Stopped")
-end
-function RESCUEHELO:GetAlias()
-return self.alias
-end
-function RESCUEHELO:GetUnitName()
-local unit=self.helo:GetUnit(1)
-if unit then
-return unit:GetName()
-end
-return nil
-end
-function RESCUEHELO:OnEventLand(EventData)
-local group=EventData.IniGroup
-if group and group:IsAlive()then
-local groupname=group:GetName()
-if groupname==self.helo:GetName()then
-local airbase=nil
-local airbasename="unknown"
-if EventData.Place then
-airbase=EventData.Place
-airbasename=airbase:GetName()
-end
-local text=string.format("Rescue helo group %s landed at airbase %s.",groupname,airbasename)
-MESSAGE:New(text,10,"DEBUG"):ToAllIf(self.Debug)
-self:T(self.lid..text)
-if self:IsRescuing()then
-self:T(self.lid..string.format("Rescue helo %s returned from rescue operation.",groupname))
-end
-if self.takeoff==SPAWN.Takeoff.Air or self.respawninair then
-if not self:IsRescuing()then
-self:E(self.lid..string.format("WARNING: Rescue helo %s landed. This should not happen for Takeoff=Air or respawninair=true and no rescue operation in progress.",groupname))
-end
-end
-self:__Returned(3,airbase)
-end
-end
-end
-function RESCUEHELO:_OnEventCrashOrEject(EventData)
-self:F2({eventdata=EventData})
-if EventData and EventData.IniUnit then
-local unit=EventData.IniUnit
-local unitname=tostring(EventData.IniUnitName)
-if EventData.IniGroupName~=self.helo:GetName()then
-local text=string.format("Unit %s crashed or ejected.",unitname)
-MESSAGE:New(text,10,"DEBUG"):ToAllIf(self.Debug)
-self:T(self.lid..text)
-local Vec3=EventData.IniDCSUnit:getPoint()
-local coord=COORDINATE:NewFromVec3(Vec3)
-if coord and self.rescuezone:IsCoordinateInZone(coord)then
-if self.Debug then
-coord:MarkToCoalition(self.lid..string.format("Crash site of unit %s.",unitname),self.helo:GetCoalition())
-end
-local rightcoalition=EventData.IniGroup:GetCoalition()==self.helo:GetCoalition()
-if self:IsRunning()and self.rescueon and rightcoalition then
-self:Rescue(coord)
-end
-end
-else
-self:E(self.lid..string.format("Rescue helo %s crashed!",unitname))
-self:Stop()
-if self.respawn then
-self:__Start(5)
-end
-end
-end
-end
-function RESCUEHELO:onafterStart(From,Event,To)
-local text=string.format("Starting Rescue Helo Formation v%s for carrier unit %s of type %s.",RESCUEHELO.version,self.carrier:GetName(),self.carriertype)
-self:I(self.lid..text)
-self:HandleEvent(EVENTS.Land)
-self:HandleEvent(EVENTS.Crash,self._OnEventCrashOrEject)
-self:HandleEvent(EVENTS.Ejection,self._OnEventCrashOrEject)
-local delay=120
-local Spawn=SPAWN:NewWithAlias(self.helogroupname,self.alias)
-Spawn:InitModex(self.modex)
-if self.takeoff==SPAWN.Takeoff.Air then
-local hdg=self.carrier:GetHeading()
-local dist=UTILS.NMToMeters(0.2)
-local Carrier=self.carrier:GetCoordinate():Translate(dist,hdg):SetAltitude(math.max(100,self.altitude))
-Spawn:InitHeading(hdg)
-self.helo=Spawn:SpawnFromCoordinate(Carrier)
-delay=1
-else
-if self.uncontrolledac then
-self.helo=GROUP:FindByName(self.helogroupname)
-if self.helo and self.helo:IsAlive()then
-self.helo:StartUncontrolled()
-delay=60
-else
-self:E(string.format("ERROR: No uncontrolled (alive) rescue helo group with name %s could be found!",self.helogroupname))
-return
-end
-else
-self.helo=Spawn:SpawnAtAirbase(self.airbase,self.takeoff,nil,AIRBASE.TerminalType.HelicopterUsable)
-if self.takeoff==SPAWN.Takeoff.Runway then
-delay=5
-elseif self.takeoff==SPAWN.Takeoff.Hot then
-delay=30
-elseif self.takeoff==SPAWN.Takeoff.Cold then
-delay=60
-end
-end
-end
-self.followset=SET_GROUP:New()
-self.followset:AddGroup(self.helo)
-self.HeloFuel0=self.helo:GetFuel()
-self.formation=AI_FORMATION:New(self.carrier,self.followset,"Helo Formation with Carrier","Follow Carrier at given parameters.")
-self.formation:FormationCenterWing(-self.offsetX,50,math.abs(self.altitude),50,self.offsetZ,50)
-self.formation:SetFollowTimeInterval(self.dtFollow)
-self.formation:SetFlightModeFormation(self.helo)
-self.formation:__Start(delay)
-self:__Status(1)
-end
-function RESCUEHELO:onafterStatus(From,Event,To)
-local time=timer.getTime()
-if self.helo and self.helo:IsAlive()then
-local fuel=self.helo:GetFuel()*100
-local fuelrel=fuel/self.HeloFuel0
-local life=self.helo:GetUnit(1):GetLife()
-local life0=self.helo:GetUnit(1):GetLife0()
-local lifeR=self.helo:GetUnit(1):GetLifeRelative()
-local text=string.format("Rescue Helo %s: state=%s fuel=%.1f, rel.fuel=%.1f, life=%.1f/%.1f=%d",self.helo:GetName(),self:GetState(),fuel,fuelrel,life,life0,lifeR*100)
-MESSAGE:New(text,10,"DEBUG"):ToAllIf(self.Debug)
-self:T(self.lid..text)
-if self:IsRunning()then
-if fuel4 then self.ngrouping=4 end
-return self
-end
-function SQUADRON:SetParkingIDs(ParkingIDs)
-if type(ParkingIDs)~="table"then
-ParkingIDs={ParkingIDs}
-end
-self.parkingIDs=ParkingIDs
-return self
-end
-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
-elseif TakeoffType:lower()=="air"then
-self.takeoffType=COORDINATE.WaypointType.TurningPoint
-else
-self.takeoffType=COORDINATE.WaypointType.TakeOffParking
-end
-return self
-end
-function SQUADRON:SetTakeoffCold()
-self:SetTakeoffType("Cold")
-return self
-end
-function SQUADRON:SetTakeoffHot()
-self:SetTakeoffType("Hot")
-return self
-end
-function SQUADRON:SetTakeoffAir()
-self:SetTakeoffType("Air")
-return self
-end
-function SQUADRON:SetDespawnAfterLanding(Switch)
-if Switch then
-self.despawnAfterLanding=Switch
-else
-self.despawnAfterLanding=true
-end
-return self
-end
-function SQUADRON:SetDespawnAfterHolding(Switch)
-if Switch then
-self.despawnAfterHolding=Switch
-else
-self.despawnAfterHolding=true
-end
-return self
-end
-function SQUADRON:SetFuelLowThreshold(LowFuel)
-self.fuellow=LowFuel or 25
-return self
-end
-function SQUADRON:SetFuelLowRefuel(switch)
-if switch==false then
-self.fuellowRefuel=false
-else
-self.fuellowRefuel=true
-end
-return self
-end
-function SQUADRON:SetAirwing(Airwing)
-self.legion=Airwing
-return self
-end
-function SQUADRON:GetAirwing(Airwing)
-return self.legion
-end
-function SQUADRON:onafterStart(From,Event,To)
-local text=string.format("Starting SQUADRON",self.name)
-self:T(self.lid..text)
-self:__Status(-1)
-end
-function SQUADRON:onafterStatus(From,Event,To)
-if self.verbose>=1 then
-local fsmstate=self:GetState()
-local callsign=self.callsignName and UTILS.GetCallsignName(self.callsignName)or"N/A"
-local modex=self.modex and self.modex or-1
-local skill=self.skill and tostring(self.skill)or"N/A"
-local NassetsTot=#self.assets
-local NassetsInS=self:CountAssets(true)
-local NassetsQP=0;local NassetsP=0;local NassetsQ=0
-if self.legion then
-NassetsQP,NassetsP,NassetsQ=self.legion:CountAssetsOnMission(nil,self)
-end
-local text=string.format("%s [Type=%s, Call=%s, Modex=%d, Skill=%s]: Assets Total=%d, Stock=%d, Mission=%d [Active=%d, Queue=%d]",
-fsmstate,self.aircrafttype,callsign,modex,skill,NassetsTot,NassetsInS,NassetsQP,NassetsP,NassetsQ)
-self:I(self.lid..text)
-self:_CheckAssetStatus()
-end
-if not self:IsStopped()then
-self:__Status(-60)
-end
-end
-TARGET={
-ClassName="TARGET",
-verbose=0,
-lid=nil,
-targets={},
-targetcounter=0,
-life=0,
-life0=0,
-N0=0,
-Ntargets0=0,
-Ndestroyed=0,
-Ndead=0,
-elements={},
-casualties={},
-threatlevel0=0,
-conditionStart={},
-TStatus=30,
-}
-TARGET.ObjectType={
-GROUP="Group",
-UNIT="Unit",
-STATIC="Static",
-SCENERY="Scenery",
-COORDINATE="Coordinate",
-AIRBASE="Airbase",
-ZONE="Zone",
-OPSZONE="OpsZone"
-}
-TARGET.Category={
-AIRCRAFT="Aircraft",
-GROUND="Ground",
-NAVAL="Naval",
-AIRBASE="Airbase",
-COORDINATE="Coordinate",
-ZONE="Zone",
-}
-TARGET.ObjectStatus={
-ALIVE="Alive",
-DEAD="Dead",
-DAMAGED="Damaged",
-}
-_TARGETID=0
-TARGET.version="0.6.0"
-function TARGET:New(TargetObject)
-local self=BASE:Inherit(self,FSM:New())
-_TARGETID=_TARGETID+1
-self.uid=_TARGETID
-if TargetObject then
-self:AddObject(TargetObject)
-end
-self:SetPriority()
-self:SetImportance()
-self.TStatus=30
-self.lid=string.format("TARGET #%03d | ",_TARGETID)
-self:SetStartState("Stopped")
-self:AddTransition("Stopped","Start","Alive")
-self:AddTransition("*","Status","*")
-self:AddTransition("*","Stop","Stopped")
-self:AddTransition("*","ObjectDamaged","*")
-self:AddTransition("*","ObjectDestroyed","*")
-self:AddTransition("*","ObjectDead","*")
-self:AddTransition("*","Damaged","Damaged")
-self:AddTransition("*","Destroyed","Dead")
-self:AddTransition("*","Dead","Dead")
-self:__Start(-1)
-return self
-end
-function TARGET:AddObject(Object)
-if Object:IsInstanceOf("SET_GROUP")or
-Object:IsInstanceOf("SET_UNIT")or
-Object:IsInstanceOf("SET_STATIC")or
-Object:IsInstanceOf("SET_SCENERY")or
-Object:IsInstanceOf("SET_OPSGROUP")or
-Object:IsInstanceOf("SET_OPSZONE")then
-local set=Object
-for _,object in pairs(set.Set)do
-self:AddObject(object)
-end
-elseif Object:IsInstanceOf("SET_ZONE")then
-local set=Object
-set:SortByName()
-for index,ZoneName in pairs(set.Index)do
-local zone=set.Set[ZoneName]
-self:_AddObject(zone)
-end
-else
-if Object:IsInstanceOf("OPSGROUP")then
-self:_AddObject(Object:GetGroup())
-else
-self:_AddObject(Object)
-end
-end
-return self
-end
-function TARGET:SetPriority(Priority)
-self.prio=Priority or 50
-return self
-end
-function TARGET:SetImportance(Importance)
-self.importance=Importance
-return self
-end
-function TARGET:AddConditionStart(ConditionFunction,...)
-local condition={}
-condition.func=ConditionFunction
-condition.arg={}
-if arg then
-condition.arg=arg
-end
-table.insert(self.conditionStart,condition)
-return self
-end
-function TARGET:AddConditionStop(ConditionFunction,...)
-local condition={}
-condition.func=ConditionFunction
-condition.arg={}
-if arg then
-condition.arg=arg
-end
-table.insert(self.conditionStop,condition)
-return self
-end
-function TARGET:EvalConditionsAll(Conditions)
-for _,_condition in pairs(Conditions or{})do
-local condition=_condition
-local istrue=condition.func(unpack(condition.arg))
-if not istrue then
-return false
-end
-end
-return true
-end
-function TARGET:EvalConditionsAny(Conditions)
-for _,_condition in pairs(Conditions or{})do
-local condition=_condition
-local istrue=condition.func(unpack(condition.arg))
-if istrue then
-return true
-end
-end
-return false
-end
-function TARGET:AddResource(MissionType,Nmin,Nmax,Attributes,Properties)
-if Attributes and type(Attributes)~="table"then
-Attributes={Attributes}
-end
-if Properties and type(Properties)~="table"then
-Properties={Properties}
-end
-local resource={}
-resource.MissionType=MissionType
-resource.Nmin=Nmin or 1
-resource.Nmax=Nmax or 1
-resource.Attributes=Attributes or{}
-resource.Properties=Properties or{}
-self.resources=self.resources or{}
-table.insert(self.resources,resource)
-if self.verbose>10 then
-local text="Resource:"
-for _,_r in pairs(self.resources)do
-local r=_r
-text=text..string.format("\nmission=%s, Nmin=%d, Nmax=%d, attribute=%s, properties=%s",r.MissionType,r.Nmin,r.Nmax,tostring(r.Attributes[1]),tostring(r.Properties[1]))
-end
-self:I(self.lid..text)
-end
-return resource
-end
-function TARGET:IsAlive()
-for _,_target in pairs(self.targets)do
-local target=_target
-if target.Status==TARGET.ObjectStatus.ALIVE then
-return true
-end
-end
-return false
-end
-function TARGET:IsDestroyed()
-return self.isDestroyed
-end
-function TARGET:IsDead()
-local is=self:Is("Dead")
-return is
-end
-function TARGET:onafterStart(From,Event,To)
-self:T({From,Event,To})
-local text=string.format("Starting Target")
-self:T(self.lid..text)
-self:HandleEvent(EVENTS.Dead,self.OnEventUnitDeadOrLost)
-self:HandleEvent(EVENTS.UnitLost,self.OnEventUnitDeadOrLost)
-self:HandleEvent(EVENTS.RemoveUnit,self.OnEventUnitDeadOrLost)
-self:__Status(-1)
-return self
-end
-function TARGET:onafterStatus(From,Event,To)
-self:T({From,Event,To})
-local fsmstate=self:GetState()
-local damaged=false
-for i,_target in pairs(self.targets)do
-local target=_target
-local life=target.Life
-target.Life=self:GetTargetLife(target)
-if target.Life>target.Life0 then
-local delta=2*(target.Life-target.Life0)
-target.Life0=target.Life0+delta
-life=target.Life0
-self.life0=self.life0+delta
-end
-if target.Life object dead now for target object %s!",tostring(target.Name)))
-self:ObjectDead(target)
-damaged=true
-end
-end
-if damaged then
-self:Damaged()
-end
-if self.verbose>=1 then
-local text=string.format("%s: Targets=%d/%d Life=%.1f/%.1f Damage=%.1f",fsmstate,self:CountTargets(),self.N0,self:GetLife(),self:GetLife0(),self:GetDamage())
-if self:CountTargets()==0 or self:GetDamage()>=100 then
-text=text.." Dead!"
-elseif damaged then
-text=text.." Damaged!"
-end
-self:I(self.lid..text)
-end
-if self.verbose>=2 then
-local text="Target:"
-for i,_target in pairs(self.targets)do
-local target=_target
-local damage=(1-target.Life/target.Life0)*100
-text=text..string.format("\n[%d] %s %s %s: Life=%.1f/%.1f, Damage=%.1f",i,target.Type,target.Name,target.Status,target.Life,target.Life0,damage)
-end
-self:I(self.lid..text)
-end
-if self:CountTargets()==0 or self:GetDamage()>=100 then
-self:Dead()
-end
-if self:IsAlive()then
-self:__Status(-self.TStatus)
-end
-return self
-end
-function TARGET:onafterObjectDamaged(From,Event,To,Target)
-self:T({From,Event,To})
-self:T(self.lid..string.format("Object %s damaged",Target.Name))
-return self
-end
-function TARGET:onafterObjectDestroyed(From,Event,To,Target)
-self:T({From,Event,To})
-self:T(self.lid..string.format("Object %s destroyed",Target.Name))
-self.Ndestroyed=self.Ndestroyed+1
-self:ObjectDead(Target)
-return self
-end
-function TARGET:onafterObjectDead(From,Event,To,Target)
-self:T({From,Event,To})
-self:T(self.lid..string.format("Object %s dead",Target.Name))
-Target.Status=TARGET.ObjectStatus.DEAD
-self.Ndead=self.Ndead+1
-local dead=true
-for _,_target in pairs(self.targets)do
-local target=_target
-if target.Status==TARGET.ObjectStatus.ALIVE then
-dead=false
-end
-end
-if dead then
-if self.Ndestroyed==self.Ntargets0 then
-self.isDestroyed=true
-self:Destroyed()
-else
-self:Dead()
-end
-else
-self:Damaged()
-end
-return self
-end
-function TARGET:onafterDamaged(From,Event,To)
-self:T({From,Event,To})
-self:T(self.lid..string.format("TARGET damaged"))
-return self
-end
-function TARGET:onafterDestroyed(From,Event,To)
-self:T({From,Event,To})
-self:T(self.lid..string.format("TARGET destroyed"))
-self:Dead()
-return self
-end
-function TARGET:onafterDead(From,Event,To)
-self:T({From,Event,To})
-self:T(self.lid..string.format("TARGET dead"))
-return self
-end
-function TARGET:OnEventUnitDeadOrLost(EventData)
-local Name=EventData and EventData.IniUnitName or nil
-if self:IsElement(Name)and not self:IsCasualty(Name)then
-self:T(self.lid..string.format("EVENT ID=%d: Unit %s dead or lost!",EventData.id,tostring(Name)))
-table.insert(self.casualties,Name)
-local target=self:GetTargetByName(EventData.IniGroupName)
-if not target then
-target=self:GetTargetByName(EventData.IniUnitName)
-end
-if target then
-if EventData.id==EVENTS.RemoveUnit then
-target.Ndead=target.Ndead+1
-else
-target.Ndestroyed=target.Ndestroyed+1
-target.Ndead=target.Ndead+1
-end
-if target.Ndead==target.N0 then
-if target.Ndestroyed>=target.N0 then
-self:T2(self.lid..string.format("EVENT ID=%d: target %s dead/lost ==> destroyed",EventData.id,tostring(target.Name)))
-target.Life=0
-self:ObjectDestroyed(target)
-else
-self:T2(self.lid..string.format("EVENT ID=%d: target %s removed ==> dead",EventData.id,tostring(target.Name)))
-target.Life=0
-self:ObjectDead(target)
-end
-end
-end
-end
-return self
-end
-function TARGET:_AddObject(Object)
-local target={}
-target.N0=0
-target.Ndead=0
-target.Ndestroyed=0
-if Object:IsInstanceOf("GROUP")then
-local group=Object
-target.Type=TARGET.ObjectType.GROUP
-target.Name=group:GetName()
-target.Coordinate=group:GetCoordinate()
-local units=group:GetUnits()
-target.Life=0;target.Life0=0
-for _,_unit in pairs(units or{})do
-local unit=_unit
-local life=unit:GetLife()
-target.Life=target.Life+life
-target.Life0=target.Life0+math.max(unit:GetLife0(),life)
-self.threatlevel0=self.threatlevel0+unit:GetThreatLevel()
-table.insert(self.elements,unit:GetName())
-target.N0=target.N0+1
-end
-elseif Object:IsInstanceOf("UNIT")then
-local unit=Object
-target.Type=TARGET.ObjectType.UNIT
-target.Name=unit:GetName()
-target.Coordinate=unit:GetCoordinate()
-if unit then
-target.Life=unit:GetLife()
-target.Life0=math.max(unit:GetLife0(),target.Life)
-self.threatlevel0=self.threatlevel0+unit:GetThreatLevel()
-table.insert(self.elements,unit:GetName())
-target.N0=target.N0+1
-end
-elseif Object:IsInstanceOf("STATIC")then
-local static=Object
-target.Type=TARGET.ObjectType.STATIC
-target.Name=static:GetName()
-target.Coordinate=static:GetCoordinate()
-if static and static:IsAlive()then
-target.Life0=static:GetLife0()
-target.Life=static:GetLife()
-target.N0=target.N0+1
-table.insert(self.elements,target.Name)
-end
-elseif Object:IsInstanceOf("SCENERY")then
-local scenery=Object
-target.Type=TARGET.ObjectType.SCENERY
-target.Name=scenery:GetName()
-target.Coordinate=scenery:GetCoordinate()
-target.Life0=scenery:GetLife0()
-if target.Life0==0 then target.Life0=1 end
-target.Life=scenery:GetLife()
-target.N0=target.N0+1
-table.insert(self.elements,target.Name)
-elseif Object:IsInstanceOf("AIRBASE")then
-local airbase=Object
-target.Type=TARGET.ObjectType.AIRBASE
-target.Name=airbase:GetName()
-target.Coordinate=airbase:GetCoordinate()
-target.Life0=1
-target.Life=1
-target.N0=target.N0+1
-table.insert(self.elements,target.Name)
-elseif Object:IsInstanceOf("COORDINATE")then
-local coord=UTILS.DeepCopy(Object)
-target.Type=TARGET.ObjectType.COORDINATE
-target.Name=coord:ToStringMGRS()
-target.Coordinate=coord
-target.Life0=1
-target.Life=1
-elseif Object:IsInstanceOf("ZONE_BASE")then
-local zone=Object
-Object=zone
-target.Type=TARGET.ObjectType.ZONE
-target.Name=zone:GetName()
-target.Coordinate=zone:GetCoordinate()
-target.Life0=1
-target.Life=1
-elseif Object:IsInstanceOf("OPSZONE")then
-local zone=Object
-Object=zone
-target.Type=TARGET.ObjectType.OPSZONE
-target.Name=zone:GetName()
-target.Coordinate=zone:GetCoordinate()
-target.N0=target.N0+1
-target.Life0=1
-target.Life=1
-else
-self:E(self.lid.."ERROR: Unknown object type!")
-return nil
-end
-self.life=self.life+target.Life
-self.life0=self.life0+target.Life0
-self.N0=self.N0+target.N0
-self.Ntargets0=self.Ntargets0+1
-self.targetcounter=self.targetcounter+1
-target.ID=self.targetcounter
-target.Status=TARGET.ObjectStatus.ALIVE
-target.Object=Object
-table.insert(self.targets,target)
-if self.name==nil then
-self.name=self:GetTargetName(target)
-end
-if self.category==nil then
-self.category=self:GetTargetCategory(target)
-end
-return self
-end
-function TARGET:GetLife0()
-return self.life0
-end
-function TARGET:GetDamage()
-local life=self:GetLife()/self:GetLife0()
-local damage=1-life
-return damage*100
-end
-function TARGET:GetTargetLife(Target)
-if Target.Type==TARGET.ObjectType.GROUP then
-if Target.Object and Target.Object:IsAlive()then
-local units=Target.Object:GetUnits()
-local life=0
-for _,_unit in pairs(units or{})do
-local unit=_unit
-life=life+unit:GetLife()
-end
-return life
-else
-return 0
-end
-elseif Target.Type==TARGET.ObjectType.UNIT then
-local unit=Target.Object
-if unit and unit:IsAlive()then
-local life=unit:GetLife()
-return life
-else
-return 0
-end
-elseif Target.Type==TARGET.ObjectType.STATIC then
-if Target.Object and Target.Object:IsAlive()then
-local life=Target.Object:GetLife()
-return life
-else
-return 0
-end
-elseif Target.Type==TARGET.ObjectType.SCENERY then
-if Target.Object and Target.Object:IsAlive()then
-local life=Target.Object:GetLife()
-return life
-else
-return 0
-end
-elseif Target.Type==TARGET.ObjectType.AIRBASE then
-if Target.Status==TARGET.ObjectStatus.ALIVE then
-return 1
-else
-return 0
-end
-elseif Target.Type==TARGET.ObjectType.COORDINATE then
-return 1
-elseif Target.Type==TARGET.ObjectType.ZONE or Target.Type==TARGET.ObjectType.OPSZONE then
-return 1
-else
-self:E("ERROR: unknown target object type in GetTargetLife!")
-end
-return self
-end
-function TARGET:GetLife()
-local N=0
-for _,_target in pairs(self.targets)do
-local Target=_target
-N=N+self:GetTargetLife(Target)
-end
-return N
-end
-function TARGET:GetTargetThreatLevelMax(Target)
-if Target.Type==TARGET.ObjectType.GROUP then
-local group=Target.Object
-if group and group:IsAlive()then
-local tl=group:GetThreatLevel()
-return tl
-else
-return 0
-end
-elseif Target.Type==TARGET.ObjectType.UNIT then
-local unit=Target.Object
-if unit and unit:IsAlive()then
-local life=unit:GetThreatLevel()
-return life
-else
-return 0
-end
-elseif Target.Type==TARGET.ObjectType.STATIC then
-return 0
-elseif Target.Type==TARGET.ObjectType.SCENERY then
-return 0
-elseif Target.Type==TARGET.ObjectType.AIRBASE then
-return 0
-elseif Target.Type==TARGET.ObjectType.COORDINATE then
-return 0
-elseif Target.Type==TARGET.ObjectType.ZONE then
-return 0
-else
-self:E("ERROR: unknown target object type in GetTargetThreatLevel!")
-end
-return self
-end
-function TARGET:GetThreatLevelMax()
-local N=0
-for _,_target in pairs(self.targets)do
-local Target=_target
-local n=self:GetTargetThreatLevelMax(Target)
-if n>N then
-N=n
-end
-end
-return N
-end
-function TARGET:GetTargetVec2(Target)
-local vec3=self:GetTargetVec3(Target)
-if vec3 then
-return{x=vec3.x,y=vec3.z}
-end
-return nil
-end
-function TARGET:GetTargetVec3(Target,Average)
-if Target.Type==TARGET.ObjectType.GROUP then
-local object=Target.Object
-if object and object:IsAlive()then
-local vec3=object:GetVec3()
-if Average then
-vec3=object:GetAverageVec3()
-end
-if vec3 then
-return vec3
-else
-return nil
-end
-else
-return nil
-end
-elseif Target.Type==TARGET.ObjectType.UNIT then
-local object=Target.Object
-if object and object:IsAlive()then
-local vec3=object:GetVec3()
-return vec3
-else
-return nil
-end
-elseif Target.Type==TARGET.ObjectType.STATIC then
-local object=Target.Object
-if object and object:IsAlive()then
-local vec3=object:GetVec3()
-return vec3
-else
-return nil
-end
-elseif Target.Type==TARGET.ObjectType.SCENERY then
-local object=Target.Object
-if object then
-local vec3=object:GetVec3()
-return vec3
-else
-return nil
-end
-elseif Target.Type==TARGET.ObjectType.AIRBASE then
-local object=Target.Object
-local vec3=object:GetVec3()
-return vec3
-elseif Target.Type==TARGET.ObjectType.COORDINATE then
-local object=Target.Object
-local vec3={x=object.x,y=object.y,z=object.z}
-return vec3
-elseif Target.Type==TARGET.ObjectType.ZONE then
-local object=Target.Object
-local vec3=object:GetVec3()
-return vec3
-elseif Target.Type==TARGET.ObjectType.OPSZONE then
-local object=Target.Object
-local vec3=object:GetZone():GetVec3()
-return vec3
-end
-self:E(self.lid.."ERROR: Unknown TARGET type! Cannot get Vec3")
-end
-function TARGET:GetTargetHeading(Target)
-if Target.Type==TARGET.ObjectType.GROUP then
-local object=Target.Object
-if object and object:IsAlive()then
-local heading=object:GetHeading()
-if heading then
-return heading
-else
-return nil
-end
-else
-return nil
-end
-elseif Target.Type==TARGET.ObjectType.UNIT then
-local object=Target.Object
-if object and object:IsAlive()then
-local heading=object:GetHeading()
-return heading
-else
-return nil
-end
-elseif Target.Type==TARGET.ObjectType.STATIC then
-local object=Target.Object
-if object and object:IsAlive()then
-local heading=object:GetHeading()
-return heading
-else
-return nil
-end
-elseif Target.Type==TARGET.ObjectType.SCENERY then
-local object=Target.Object
-if object then
-local heading=object:GetHeading()
-return heading
-else
-return nil
-end
-elseif Target.Type==TARGET.ObjectType.AIRBASE then
-local object=Target.Object
-return 0
-elseif Target.Type==TARGET.ObjectType.COORDINATE then
-local object=Target.Object
-return 0
-elseif Target.Type==TARGET.ObjectType.ZONE or Target.Type==TARGET.ObjectType.OPSZONE then
-local object=Target.Object
-return 0
-end
-self:E(self.lid.."ERROR: Unknown TARGET type! Cannot get heading")
-end
-function TARGET:GetTargetCoordinate(Target,Average)
-if Target.Type==TARGET.ObjectType.COORDINATE then
-return Target.Object
-else
-local vec3=self:GetTargetVec3(Target,Average)
-if vec3 then
-Target.Coordinate.x=vec3.x
-Target.Coordinate.y=vec3.y
-Target.Coordinate.z=vec3.z
-end
-return Target.Coordinate
-end
-return nil
-end
-function TARGET:GetTargetName(Target)
-if Target.Type==TARGET.ObjectType.GROUP then
-if Target.Object and Target.Object:IsAlive()then
-return Target.Object:GetName()
-end
-elseif Target.Type==TARGET.ObjectType.UNIT then
-if Target.Object and Target.Object:IsAlive()then
-return Target.Object:GetName()
-end
-elseif Target.Type==TARGET.ObjectType.STATIC then
-if Target.Object and Target.Object:IsAlive()then
-return Target.Object:GetName()
-end
-elseif Target.Type==TARGET.ObjectType.AIRBASE then
-if Target.Status==TARGET.ObjectStatus.ALIVE then
-return Target.Object:GetName()
-end
-elseif Target.Type==TARGET.ObjectType.COORDINATE then
-local coord=Target.Object
-return coord:ToStringMGRS()
-elseif Target.Type==TARGET.ObjectType.ZONE then
-local Zone=Target.Object
-return Zone:GetName()
-elseif Target.Type==TARGET.ObjectType.SCENERY then
-local Zone=Target.Object
-return Zone:GetName()
-end
-return"Unknown"
-end
-function TARGET:GetName()
-local name=self.name or"Unknown"
-return name
-end
-function TARGET:GetVec2()
-for _,_target in pairs(self.targets)do
-local Target=_target
-local coordinate=self:GetTargetVec2(Target)
-if coordinate then
-return coordinate
-end
-end
-self:E(self.lid..string.format("ERROR: Cannot get Vec2 of target %s",self.name))
-return nil
-end
-function TARGET:GetVec3()
-for _,_target in pairs(self.targets)do
-local Target=_target
-local coordinate=self:GetTargetVec3(Target)
-if coordinate then
-return coordinate
-end
-end
-self:E(self.lid..string.format("ERROR: Cannot get Vec3 of target %s",self.name))
-return nil
-end
-function TARGET:GetCoordinate()
-for _,_target in pairs(self.targets)do
-local Target=_target
-local coordinate=self:GetTargetCoordinate(Target)
-if coordinate then
-return coordinate
-end
-end
-self:E(self.lid..string.format("ERROR: Cannot get coordinate of target %s",tostring(self.name)))
-return nil
-end
-function TARGET:GetAverageCoordinate()
-for _,_target in pairs(self.targets)do
-local Target=_target
-local coordinate=self:GetTargetCoordinate(Target,true)
-if coordinate then
-return coordinate
-end
-end
-self:E(self.lid..string.format("ERROR: Cannot get average coordinate of target %s",tostring(self.name)))
-return nil
-end
-function TARGET:GetHeading()
-for _,_target in pairs(self.targets)do
-local Target=_target
-local heading=self:GetTargetHeading(Target)
-if heading then
-return heading
-end
-end
-self:E(self.lid..string.format("ERROR: Cannot get heading of target %s",tostring(self.name)))
-return nil
-end
-function TARGET:GetCategory()
-return self.category
-end
-function TARGET:GetTargetCategory(Target)
-local category=nil
-if Target.Type==TARGET.ObjectType.GROUP then
-if Target.Object and Target.Object:IsAlive()~=nil then
-local group=Target.Object
-local cat=group:GetCategory()
-if cat==Group.Category.AIRPLANE or cat==Group.Category.HELICOPTER then
-category=TARGET.Category.AIRCRAFT
-elseif cat==Group.Category.GROUND or cat==Group.Category.TRAIN then
-category=TARGET.Category.GROUND
-elseif cat==Group.Category.SHIP then
-category=TARGET.Category.NAVAL
-end
-end
-elseif Target.Type==TARGET.ObjectType.UNIT then
-if Target.Object and Target.Object:IsAlive()~=nil then
-local unit=Target.Object
-local group=unit:GetGroup()
-local cat=group:GetCategory()
-if cat==Group.Category.AIRPLANE or cat==Group.Category.HELICOPTER then
-category=TARGET.Category.AIRCRAFT
-elseif cat==Group.Category.GROUND or cat==Group.Category.TRAIN then
-category=TARGET.Category.GROUND
-elseif cat==Group.Category.SHIP then
-category=TARGET.Category.NAVAL
-end
-end
-elseif Target.Type==TARGET.ObjectType.STATIC then
-return TARGET.Category.GROUND
-elseif Target.Type==TARGET.ObjectType.SCENERY then
-return TARGET.Category.GROUND
-elseif Target.Type==TARGET.ObjectType.AIRBASE then
-return TARGET.Category.AIRBASE
-elseif Target.Type==TARGET.ObjectType.COORDINATE then
-return TARGET.Category.COORDINATE
-elseif Target.Type==TARGET.ObjectType.ZONE then
-return TARGET.Category.ZONE
-elseif Target.Type==TARGET.ObjectType.OPSZONE then
-return TARGET.Category.OPSZONE
-else
-self:E("ERROR: unknown target category!")
-end
-return category
-end
-function TARGET:GetTargetCoalition(Target)
-local coal=coalition.side.NEUTRAL
-if Target.Type==TARGET.ObjectType.GROUP then
-if Target.Object and Target.Object:IsAlive()~=nil then
-local object=Target.Object
-coal=object:GetCoalition()
-end
-elseif Target.Type==TARGET.ObjectType.UNIT then
-if Target.Object and Target.Object:IsAlive()~=nil then
-local object=Target.Object
-coal=object:GetCoalition()
-end
-elseif Target.Type==TARGET.ObjectType.STATIC then
-local object=Target.Object
-coal=object:GetCoalition()
-elseif Target.Type==TARGET.ObjectType.SCENERY then
-elseif Target.Type==TARGET.ObjectType.AIRBASE then
-local object=Target.Object
-coal=object:GetCoalition()
-elseif Target.Type==TARGET.ObjectType.COORDINATE then
-elseif Target.Type==TARGET.ObjectType.ZONE then
-elseif Target.Type==TARGET.ObjectType.OPSZONE then
-local object=Target.Object
-coal=object:GetOwner()
-else
-self:E("ERROR: unknown target category!")
-end
-return coal
-end
-function TARGET:GetTargetByName(ObjectName)
-for _,_target in pairs(self.targets)do
-local target=_target
-if ObjectName==target.Name then
-return target
-end
-end
-return nil
-end
-function TARGET:GetObjective(RefCoordinate,Coalitions)
-if RefCoordinate then
-local dmin=math.huge
-local tmin=nil
-for _,_target in pairs(self.targets)do
-local target=_target
-if target.Status~=TARGET.ObjectStatus.DEAD and(Coalitions==nil or UTILS.IsInTable(UTILS.EnsureTable(Coalitions),self:GetTargetCoalition(target)))then
-local vec3=self:GetTargetVec3(target)
-local d=UTILS.VecDist3D(vec3,RefCoordinate)
-if d1 then
-if Coalitions==nil or UTILS.IsInTable(UTILS.EnsureTable(Coalitions),unit:GetCoalition())then
-N=N+1
-end
-end
-end
-elseif Target.Type==TARGET.ObjectType.UNIT then
-local target=Target.Object
-if target and target:IsAlive()~=nil and target:GetLife()>1 then
-if Coalitions==nil or UTILS.IsInTable(Coalitions,target:GetCoalition())then
-N=N+1
-end
-end
-elseif Target.Type==TARGET.ObjectType.STATIC then
-local target=Target.Object
-if target and target:IsAlive()then
-if Coalitions==nil or UTILS.IsInTable(Coalitions,target:GetCoalition())then
-N=N+1
-end
-end
-elseif Target.Type==TARGET.ObjectType.SCENERY then
-if Target.Status~=TARGET.ObjectStatus.DEAD then
-N=N+1
-end
-elseif Target.Type==TARGET.ObjectType.AIRBASE then
-local target=Target.Object
-if Target.Status==TARGET.ObjectStatus.ALIVE then
-if Coalitions==nil or UTILS.IsInTable(Coalitions,target:GetCoalition())then
-N=N+1
-end
-end
-elseif Target.Type==TARGET.ObjectType.COORDINATE then
-elseif Target.Type==TARGET.ObjectType.ZONE then
-elseif Target.Type==TARGET.ObjectType.OPSZONE then
-local target=Target.Object
-if Coalitions==nil or UTILS.IsInTable(Coalitions,target:GetOwner())then
-N=N+1
-end
-else
-self:E(self.lid.."ERROR: Unknown target type! Cannot count targets")
-end
-return N
-end
-function TARGET:CountTargets(Coalitions)
-local N=0
-for _,_target in pairs(self.targets)do
-local Target=_target
-N=N+self:CountObjectives(Target,Coalitions)
-end
-return N
-end
-function TARGET:IsElement(Name)
-if Name==nil then
-return false
-end
-for _,name in pairs(self.elements)do
-if name==Name then
-return true
-end
-end
-return false
-end
-function TARGET:IsCasualty(Name)
-if Name==nil then
-return false
-end
-for _,name in pairs(self.casualties)do
-if tostring(name)==tostring(Name)then
-return true
-end
-end
-return false
-end
-EASYGCICAP={
-ClassName="EASYGCICAP",
-overhead=0.75,
-capgrouping=2,
-airbasename=nil,
-airbase=nil,
-coalition="blue",
-alias=nil,
-wings={},
-Intel=nil,
-resurrection=900,
-capspeed=300,
-capalt=25000,
-capdir=45,
-capleg=15,
-capgrouping=2,
-maxinterceptsize=2,
-missionrange=100,
-noaltert5=4,
-ManagedAW={},
-ManagedSQ={},
-ManagedCP={},
-ManagedTK={},
-ManagedEWR={},
-ManagedREC={},
-MaxAliveMissions=8,
-debug=false,
-engagerange=50,
-repeatsonfailure=3,
-GoZoneSet=nil,
-NoGoZoneSet=nil,
-Monitor=false,
-TankerInvisible=true,
-CapFormation=nil,
-}
-EASYGCICAP.version="0.0.9"
-function EASYGCICAP:New(Alias,AirbaseName,Coalition,EWRName)
-local self=BASE:Inherit(self,FSM:New())
-self.alias=Alias or AirbaseName.." CAP Wing"
-self.coalitionname=string.lower(Coalition)or"blue"
-self.coalition=self.coaltitionname=="blue"and coalition.side.BLUE or coalition.side.RED
-self.wings={}
-self.EWRName=EWRName or self.coalitionname.." EWR"
-self.airbasename=AirbaseName
-self.airbase=AIRBASE:FindByName(self.airbasename)
-self.GoZoneSet=SET_ZONE:New()
-self.NoGoZoneSet=SET_ZONE:New()
-self.resurrection=900
-self.capspeed=300
-self.capalt=25000
-self.capdir=90
-self.capleg=15
-self.capgrouping=2
-self.missionrange=100
-self.noaltert5=2
-self.MaxAliveMissions=8
-self.engagerange=50
-self.repeatsonfailure=3
-self.Monitor=false
-self.TankerInvisible=true
-self.CapFormation=ENUMS.Formation.FixedWing.FingerFour.Group
-self.lid=string.format("EASYGCICAP %s | ",self.alias)
-self:SetStartState("Stopped")
-self:AddTransition("Stopped","Start","Running")
-self:AddTransition("Running","Stop","Stopped")
-self:AddTransition("*","Status","*")
-self:AddAirwing(self.airbasename,self.alias,self.CapZoneName)
-self:I(self.lid.."Created new instance (v"..self.version..")")
-self:__Start(math.random(6,12))
-return self
-end
-function EASYGCICAP:SetCAPFormation(Formation)
-self.CapFormation=Formation
-return self
-end
-function EASYGCICAP:SetTankerAndAWACSInvisible(Switch)
-self:T(self.lid.."SetTankerAndAWACSInvisible")
-self.TankerInvisible=Switch
-return self
-end
-function EASYGCICAP:SetMaxAliveMissions(Maxiumum)
-self:T(self.lid.."SetDefaultResurrection")
-self.MaxAliveMissions=Maxiumum or 8
-return self
-end
-function EASYGCICAP:SetDefaultResurrection(Seconds)
-self:T(self.lid.."SetDefaultResurrection")
-self.resurrection=Seconds or 900
-return self
-end
-function EASYGCICAP:SetDefaultRepeatOnFailure(Retries)
-self:T(self.lid.."SetDefaultRepeatOnFailure")
-self.repeatsonfailure=Retries or 3
-return self
-end
-function EASYGCICAP:SetDefaultCAPSpeed(Speed)
-self:T(self.lid.."SetDefaultSpeed")
-self.capspeed=Speed or 300
-return self
-end
-function EASYGCICAP:SetDefaultCAPAlt(Altitude)
-self:T(self.lid.."SetDefaultAltitude")
-self.capalt=Altitude or 25000
-return self
-end
-function EASYGCICAP:SetDefaultCAPDirection(Direction)
-self:T(self.lid.."SetDefaultDirection")
-self.capdir=Direction or 90
-return self
-end
-function EASYGCICAP:SetDefaultCAPLeg(Leg)
-self:T(self.lid.."SetDefaultLeg")
-self.capleg=Leg or 15
-return self
-end
-function EASYGCICAP:SetDefaultCAPGrouping(Grouping)
-self:T(self.lid.."SetDefaultCAPGrouping")
-self.capgrouping=Grouping or 2
-return self
-end
-function EASYGCICAP:SetDefaultMissionRange(Range)
-self:T(self.lid.."SetDefaultMissionRange")
-self.missionrange=Range or 100
-return self
-end
-function EASYGCICAP:SetDefaultNumberAlter5Standby(Airframes)
-self:T(self.lid.."SetDefaultNumberAlter5Standby")
-self.noaltert5=math.abs(Airframes)or 2
-return self
-end
-function EASYGCICAP:SetDefaultEngageRange(Range)
-self:T(self.lid.."SetDefaultNumberAlter5Standby")
-self.engagerange=Range or 50
-return self
-end
-function EASYGCICAP:AddAirwing(Airbasename,Alias)
-self:T(self.lid.."AddAirwing "..Airbasename)
-local AWEntry={}
-AWEntry.AirbaseName=Airbasename
-AWEntry.Alias=Alias
-self.ManagedAW[Airbasename]=AWEntry
-return self
-end
-function EASYGCICAP:_CreateAirwings()
-self:T(self.lid.."_CreateAirwings")
-for airbase,data in pairs(self.ManagedAW)do
-local wing=data
-local afb=wing.AirbaseName
-local alias=wing.Alias
-self:_AddAirwing(airbase,alias)
-end
-return self
-end
-function EASYGCICAP:_AddAirwing(Airbasename,Alias)
-self:T(self.lid.."_AddAirwing "..Airbasename)
-local CapFormation=self.CapFormation
-local CAP_Wing=AIRWING:New(Airbasename,Alias)
-CAP_Wing:SetVerbosityLevel(3)
-CAP_Wing:SetReportOff()
-CAP_Wing:SetMarker(false)
-CAP_Wing:SetAirbase(AIRBASE:FindByName(Airbasename))
-CAP_Wing:SetRespawnAfterDestroyed()
-CAP_Wing:SetNumberCAP(self.capgrouping)
-CAP_Wing:SetCapCloseRaceTrack(true)
-if CapFormation then
-CAP_Wing:SetCAPFormation(CapFormation)
-end
-if#self.ManagedTK>0 then
-CAP_Wing:SetNumberTankerBoom(1)
-CAP_Wing:SetNumberTankerProbe(1)
-end
-if#self.ManagedEWR>0 then
-CAP_Wing:SetNumberAWACS(1)
-end
-if#self.ManagedREC>0 then
-CAP_Wing:SetNumberRecon(1)
-end
-CAP_Wing:SetTakeoffHot()
-CAP_Wing:SetLowFuelThreshold(0.3)
-CAP_Wing.RandomAssetScore=math.random(50,100)
-CAP_Wing:Start()
-local Intel=self.Intel
-local TankerInvisible=self.TankerInvisible
-function CAP_Wing:OnAfterFlightOnMission(From,Event,To,Flightgroup,Mission)
-local flightgroup=Flightgroup
-flightgroup:SetDespawnAfterHolding()
-flightgroup:SetDestinationbase(AIRBASE:FindByName(Airbasename))
-flightgroup:GetGroup():CommandEPLRS(true,5)
-if Mission.type~=AUFTRAG.Type.TANKER and Mission.type~=AUFTRAG.Type.AWACS and Mission.type~=AUFTRAG.Type.RECON then
-flightgroup:SetDetection(true)
-flightgroup:SetEngageDetectedOn(self.engagerange,{"Air"},self.GoZoneSet,self.NoGoZoneSet)
-flightgroup:SetOutOfAAMRTB()
-if CapFormation then
-flightgroup:GetGroup():SetOption(AI.Option.Air.id.FORMATION,CapFormation)
-end
-end
-if Mission.type==AUFTRAG.Type.TANKER or Mission.type==AUFTRAG.Type.AWACS or Mission.type==AUFTRAG.Type.RECON then
-if TankerInvisible then
-flightgroup:GetGroup():SetCommandInvisible(true)
-end
-if Mission.type==AUFTRAG.Type.RECON then
-flightgroup:SetDetection(true)
-end
-end
-flightgroup:GetGroup():OptionROTEvadeFire()
-flightgroup:SetFuelLowRTB(true)
-Intel:AddAgent(flightgroup)
-function flightgroup:OnAfterHolding(From,Event,To)
-self:ClearToLand(5)
-end
-end
-if self.noaltert5>0 then
-local alert=AUFTRAG:NewALERT5(AUFTRAG.Type.INTERCEPT)
-alert:SetRequiredAssets(self.noaltert5)
-alert:SetRepeat(99)
-CAP_Wing:AddMission(alert)
-end
-self.wings[Airbasename]={CAP_Wing,AIRBASE:FindByName(Airbasename):GetZone(),Airbasename}
-return self
-end
-function EASYGCICAP:AddPatrolPointCAP(AirbaseName,Coordinate,Altitude,Speed,Heading,LegLength)
-self:T(self.lid.."AddPatrolPointCAP "..Coordinate:ToStringLLDDM())
-local EntryCAP={}
-EntryCAP.AirbaseName=AirbaseName
-EntryCAP.Coordinate=Coordinate
-EntryCAP.Altitude=Altitude or 25000
-EntryCAP.Speed=Speed or 300
-EntryCAP.Heading=Heading or 90
-EntryCAP.LegLength=LegLength or 15
-self.ManagedCP[#self.ManagedCP+1]=EntryCAP
-if self.debug then
-local mark=MARKER:New(Coordinate,self.lid.."Patrol Point"):ToAll()
-end
-return self
-end
-function EASYGCICAP:AddPatrolPointRecon(AirbaseName,Coordinate,Altitude,Speed,Heading,LegLength)
-self:T(self.lid.."AddPatrolPointRecon "..Coordinate:ToStringLLDDM())
-local EntryCAP={}
-EntryCAP.AirbaseName=AirbaseName
-EntryCAP.Coordinate=Coordinate
-EntryCAP.Altitude=Altitude or 25000
-EntryCAP.Speed=Speed or 300
-EntryCAP.Heading=Heading or 90
-EntryCAP.LegLength=LegLength or 15
-self.ManagedREC[#self.ManagedREC+1]=EntryCAP
-if self.debug then
-local mark=MARKER:New(Coordinate,self.lid.."Patrol Point Recon"):ToAll()
-end
-return self
-end
-function EASYGCICAP:AddPatrolPointTanker(AirbaseName,Coordinate,Altitude,Speed,Heading,LegLength)
-self:T(self.lid.."AddPatrolPointTanker "..Coordinate:ToStringLLDDM())
-local EntryCAP={}
-EntryCAP.AirbaseName=AirbaseName
-EntryCAP.Coordinate=Coordinate
-EntryCAP.Altitude=Altitude or 25000
-EntryCAP.Speed=Speed or 300
-EntryCAP.Heading=Heading or 90
-EntryCAP.LegLength=LegLength or 15
-self.ManagedTK[#self.ManagedTK+1]=EntryCAP
-if self.debug then
-local mark=MARKER:New(Coordinate,self.lid.."Patrol Point Tanker"):ToAll()
-end
-return self
-end
-function EASYGCICAP:AddPatrolPointAwacs(AirbaseName,Coordinate,Altitude,Speed,Heading,LegLength)
-self:T(self.lid.."AddPatrolPointAwacs "..Coordinate:ToStringLLDDM())
-local EntryCAP={}
-EntryCAP.AirbaseName=AirbaseName
-EntryCAP.Coordinate=Coordinate
-EntryCAP.Altitude=Altitude or 25000
-EntryCAP.Speed=Speed or 300
-EntryCAP.Heading=Heading or 90
-EntryCAP.LegLength=LegLength or 15
-self.ManagedEWR[#self.ManagedEWR+1]=EntryCAP
-if self.debug then
-local mark=MARKER:New(Coordinate,self.lid.."Patrol Point AWACS"):ToAll()
-end
-return self
-end
-function EASYGCICAP:_SetTankerPatrolPoints()
-self:T(self.lid.."_SetTankerPatrolPoints")
-for _,_data in pairs(self.ManagedTK)do
-local data=_data
-local Wing=self.wings[data.AirbaseName][1]
-local Coordinate=data.Coordinate
-local Altitude=data.Altitude
-local Speed=data.Speed
-local Heading=data.Heading
-local LegLength=data.LegLength
-Wing:AddPatrolPointTANKER(Coordinate,Altitude,Speed,Heading,LegLength)
-end
-return self
-end
-function EASYGCICAP:_SetAwacsPatrolPoints()
-self:T(self.lid.."_SetAwacsPatrolPoints")
-for _,_data in pairs(self.ManagedEWR)do
-local data=_data
-local Wing=self.wings[data.AirbaseName][1]
-local Coordinate=data.Coordinate
-local Altitude=data.Altitude
-local Speed=data.Speed
-local Heading=data.Heading
-local LegLength=data.LegLength
-Wing:AddPatrolPointAWACS(Coordinate,Altitude,Speed,Heading,LegLength)
-end
-return self
-end
-function EASYGCICAP:_SetCAPPatrolPoints()
-self:T(self.lid.."_SetCAPPatrolPoints")
-for _,_data in pairs(self.ManagedCP)do
-local data=_data
-local Wing=self.wings[data.AirbaseName][1]
-local Coordinate=data.Coordinate
-local Altitude=data.Altitude
-local Speed=data.Speed
-local Heading=data.Heading
-local LegLength=data.LegLength
-Wing:AddPatrolPointCAP(Coordinate,Altitude,Speed,Heading,LegLength)
-end
-return self
-end
-function EASYGCICAP:_SetReconPatrolPoints()
-self:T(self.lid.."_SetReconPatrolPoints")
-for _,_data in pairs(self.ManagedREC)do
-local data=_data
-local Wing=self.wings[data.AirbaseName][1]
-local Coordinate=data.Coordinate
-local Altitude=data.Altitude
-local Speed=data.Speed
-local Heading=data.Heading
-local LegLength=data.LegLength
-Wing:AddPatrolPointRecon(Coordinate,Altitude,Speed,Heading,LegLength)
-end
-return self
-end
-function EASYGCICAP:_CreateSquads()
-self:T(self.lid.."_CreateSquads")
-for name,data in pairs(self.ManagedSQ)do
-local squad=data
-local SquadName=name
-local TemplateName=squad.TemplateName
-local AirbaseName=squad.AirbaseName
-local AirFrames=squad.AirFrames
-local Skill=squad.Skill
-local Modex=squad.Modex
-local Livery=squad.Livery
-local Frequeny=squad.Frequency
-local Modulation=squad.Modulation
-local TACAN=squad.TACAN
-if squad.Tanker then
-self:_AddTankerSquadron(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery,Frequeny,Modulation,TACAN)
-elseif squad.AWACS then
-self:_AddAWACSSquadron(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery,Frequeny,Modulation)
-elseif squad.RECON then
-self:_AddReconSquadron(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery)
-else
-self:_AddSquadron(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery)
-end
-end
-return self
-end
-function EASYGCICAP:AddSquadron(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery)
-self:T(self.lid.."AddSquadron "..SquadName)
-local EntrySQ={}
-EntrySQ.TemplateName=TemplateName
-EntrySQ.SquadName=SquadName
-EntrySQ.AirbaseName=AirbaseName
-EntrySQ.AirFrames=AirFrames or 20
-EntrySQ.Skill=Skill or AI.Skill.AVERAGE
-EntrySQ.Modex=Modex or 402
-EntrySQ.Livery=Livery
-self.ManagedSQ[SquadName]=EntrySQ
-return self
-end
-function EASYGCICAP:AddReconSquadron(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery)
-self:T(self.lid.."AddReconSquadron "..SquadName)
-local EntrySQ={}
-EntrySQ.TemplateName=TemplateName
-EntrySQ.SquadName=SquadName
-EntrySQ.AirbaseName=AirbaseName
-EntrySQ.AirFrames=AirFrames or 20
-EntrySQ.Skill=Skill or AI.Skill.AVERAGE
-EntrySQ.Modex=Modex or 402
-EntrySQ.Livery=Livery
-EntrySQ.RECON=true
-self.ManagedSQ[SquadName]=EntrySQ
-return self
-end
-function EASYGCICAP:AddTankerSquadron(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery,Frequency,Modulation,TACAN)
-self:T(self.lid.."AddTankerSquadron "..SquadName)
-local EntrySQ={}
-EntrySQ.TemplateName=TemplateName
-EntrySQ.SquadName=SquadName
-EntrySQ.AirbaseName=AirbaseName
-EntrySQ.AirFrames=AirFrames or 20
-EntrySQ.Skill=Skill or AI.Skill.AVERAGE
-EntrySQ.Modex=Modex or 602
-EntrySQ.Livery=Livery
-EntrySQ.Frequency=Frequency
-EntrySQ.Modulation=Livery
-EntrySQ.TACAN=TACAN
-EntrySQ.Tanker=true
-self.ManagedSQ[SquadName]=EntrySQ
-return self
-end
-function EASYGCICAP:AddAWACSSquadron(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery,Frequency,Modulation)
-self:T(self.lid.."AddAWACSSquadron "..SquadName)
-local EntrySQ={}
-EntrySQ.TemplateName=TemplateName
-EntrySQ.SquadName=SquadName
-EntrySQ.AirbaseName=AirbaseName
-EntrySQ.AirFrames=AirFrames or 20
-EntrySQ.Skill=Skill or AI.Skill.AVERAGE
-EntrySQ.Modex=Modex or 702
-EntrySQ.Livery=Livery
-EntrySQ.Frequency=Frequency
-EntrySQ.Modulation=Livery
-EntrySQ.AWACS=true
-self.ManagedSQ[SquadName]=EntrySQ
-return self
-end
-function EASYGCICAP:_AddSquadron(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery,Frequency,Modulation)
-self:T(self.lid.."_AddSquadron "..SquadName)
-local Squadron_One=SQUADRON:New(TemplateName,AirFrames,SquadName)
-Squadron_One:AddMissionCapability({AUFTRAG.Type.CAP,AUFTRAG.Type.GCICAP,AUFTRAG.Type.INTERCEPT,AUFTRAG.Type.PATROLRACETRACK,AUFTRAG.Type.ALERT5})
-Squadron_One:SetFuelLowThreshold(0.3)
-Squadron_One:SetTurnoverTime(10,20)
-Squadron_One:SetModex(Modex)
-Squadron_One:SetLivery(Livery)
-Squadron_One:SetSkill(Skill or AI.Skill.AVERAGE)
-Squadron_One:SetMissionRange(self.missionrange)
-local wing=self.wings[AirbaseName][1]
-wing:AddSquadron(Squadron_One)
-wing:NewPayload(TemplateName,-1,{AUFTRAG.Type.CAP,AUFTRAG.Type.GCICAP,AUFTRAG.Type.INTERCEPT,AUFTRAG.Type.PATROLRACETRACK,AUFTRAG.Type.ALERT5},75)
-return self
-end
-function EASYGCICAP:_AddReconSquadron(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery)
-self:T(self.lid.."_AddReconSquadron "..SquadName)
-local Squadron_One=SQUADRON:New(TemplateName,AirFrames,SquadName)
-Squadron_One:AddMissionCapability({AUFTRAG.Type.RECON})
-Squadron_One:SetFuelLowThreshold(0.3)
-Squadron_One:SetTurnoverTime(10,20)
-Squadron_One:SetModex(Modex)
-Squadron_One:SetLivery(Livery)
-Squadron_One:SetSkill(Skill or AI.Skill.AVERAGE)
-Squadron_One:SetMissionRange(self.missionrange)
-local wing=self.wings[AirbaseName][1]
-wing:AddSquadron(Squadron_One)
-wing:NewPayload(TemplateName,-1,{AUFTRAG.Type.RECON},75)
-return self
-end
-function EASYGCICAP:_AddTankerSquadron(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery,Frequency,Modulation,TACAN)
-self:T(self.lid.."_AddTankerSquadron "..SquadName)
-local Squadron_One=SQUADRON:New(TemplateName,AirFrames,SquadName)
-Squadron_One:AddMissionCapability({AUFTRAG.Type.TANKER})
-Squadron_One:SetFuelLowThreshold(0.3)
-Squadron_One:SetTurnoverTime(10,20)
-Squadron_One:SetModex(Modex)
-Squadron_One:SetLivery(Livery)
-Squadron_One:SetSkill(Skill or AI.Skill.AVERAGE)
-Squadron_One:SetMissionRange(self.missionrange)
-Squadron_One:SetRadio(Frequency,Modulation)
-Squadron_One:AddTacanChannel(TACAN,TACAN)
-local wing=self.wings[AirbaseName][1]
-wing:AddSquadron(Squadron_One)
-wing:NewPayload(TemplateName,-1,{AUFTRAG.Type.TANKER},75)
-return self
-end
-function EASYGCICAP:_AddAWACSSquadron(TemplateName,SquadName,AirbaseName,AirFrames,Skill,Modex,Livery,Frequency,Modulation)
-self:T(self.lid.."_AddAWACSSquadron "..SquadName)
-local Squadron_One=SQUADRON:New(TemplateName,AirFrames,SquadName)
-Squadron_One:AddMissionCapability({AUFTRAG.Type.AWACS})
-Squadron_One:SetFuelLowThreshold(0.3)
-Squadron_One:SetTurnoverTime(10,20)
-Squadron_One:SetModex(Modex)
-Squadron_One:SetLivery(Livery)
-Squadron_One:SetSkill(Skill or AI.Skill.AVERAGE)
-Squadron_One:SetMissionRange(self.missionrange)
-Squadron_One:SetRadio(Frequency,Modulation)
-local wing=self.wings[AirbaseName][1]
-wing:AddSquadron(Squadron_One)
-wing:NewPayload(TemplateName,-1,{AUFTRAG.Type.AWACS},75)
-return self
-end
-function EASYGCICAP:AddAcceptZone(Zone)
-self:T(self.lid.."AddAcceptZone0")
-self.GoZoneSet:AddZone(Zone)
-return self
-end
-function EASYGCICAP:AddRejectZone(Zone)
-self:T(self.lid.."AddRejectZone")
-self.NoGoZoneSet:AddZone(Zone)
-return self
-end
-function EASYGCICAP:_StartIntel()
-self:T(self.lid.."_StartIntel")
-local BlueAir_DetectionSetGroup=SET_GROUP:New()
-BlueAir_DetectionSetGroup:FilterPrefixes({self.EWRName})
-BlueAir_DetectionSetGroup:FilterStart()
-local BlueIntel=INTEL:New(BlueAir_DetectionSetGroup,self.coalitionname,self.EWRName)
-BlueIntel:SetClusterAnalysis(true,false,false)
-BlueIntel:SetForgetTime(300)
-BlueIntel:SetAcceptZones(self.GoZoneSet)
-BlueIntel:SetRejectZones(self.NoGoZoneSet)
-BlueIntel:SetVerbosity(0)
-BlueIntel:Start()
-if self.debug then
-BlueIntel.debug=true
-end
-local overhead=self.overhead
-local capspeed=self.capspeed+100
-local capalt=self.capalt
-local maxsize=self.maxinterceptsize
-local repeatsonfailure=self.repeatsonfailure
-local wings=self.wings
-local ctlpts=self.ManagedCP
-local MaxAliveMissions=self.MaxAliveMissions*self.capgrouping
-local nogozoneset=self.NoGoZoneSet
-function BlueIntel:OnAfterNewCluster(From,Event,To,Cluster)
-if Cluster.ctype~=INTEL.Ctype.AIRCRAFT then return end
-local contact=self:GetHighestThreatContact(Cluster)
-local name=contact.groupname
-local threat=contact.threatlevel
-local position=self:CalcClusterFuturePosition(Cluster,300)
-local bestdistance=2000*1000
-local targetairwing=nil
-local targetawname=""
-local clustersize=self:ClusterCountUnits(Cluster)or 1
-local wingsize=math.abs(overhead*(clustersize+1))
-if wingsize>maxsize then wingsize=maxsize end
-local retrymission=true
-if Cluster.mission and(not Cluster.mission:IsOver())then
-retrymission=false
-end
-if(retrymission)and(wingsize>=1)then
-MESSAGE:New(string.format("**** %s Interceptors need wingsize %d",UTILS.GetCoalitionName(self.coalition),wingsize),15,"CAPGCI"):ToAllIf(self.debug):ToLog()
-for _,_data in pairs(wings)do
-local airwing=_data[1]
-local zone=_data[2]
-local zonecoord=zone:GetCoordinate()
-local name=_data[3]
-local distance=position:DistanceFromPointVec2(zonecoord)
-local airframes=airwing:CountAssets(true)
-if distance=wingsize then
-bestdistance=distance
-targetairwing=airwing
-targetawname=name
-end
-end
-for _,_data in pairs(ctlpts)do
-local data=_data
-local name=data.AirbaseName
-local zonecoord=data.Coordinate
-local airwing=wings[name][1]
-local distance=position:DistanceFromPointVec2(zonecoord)
-local airframes=airwing:CountAssets(true)
-if distance=wingsize then
-bestdistance=distance
-targetairwing=airwing
-targetawname=name
-end
-end
-local text=string.format("Closest Airwing is %s",targetawname)
-local m=MESSAGE:New(text,10,"CAPGCI"):ToAllIf(self.debug):ToLog()
-if targetairwing then
-local AssetCount=targetairwing:CountAssetsOnMission(MissionTypes,Cohort)
-self:T(self.lid.." Assets on Mission "..AssetCount)
-if AssetCount<=MaxAliveMissions then
-local repeats=repeatsonfailure
-local InterceptAuftrag=AUFTRAG:NewINTERCEPT(contact.group)
-:SetMissionRange(150)
-:SetPriority(1,true,1)
-:SetRequiredAssets(wingsize)
-:SetRepeatOnFailure(repeats)
-:SetMissionSpeed(UTILS.KnotsToAltKIAS(capspeed,capalt))
-:SetMissionAltitude(capalt)
-if nogozoneset:Count()>0 then
-InterceptAuftrag:AddConditionSuccess(
-function(group,zoneset)
-local success=false
-if group and group:IsAlive()then
-local coord=group:GetCoordinate()
-if coord and zoneset:IsCoordinateInZone(coord)then
-success=true
-end
-end
-return success
-end,
-contact.group,
-nogozoneset
-)
-end
-targetairwing:AddMission(InterceptAuftrag)
-Cluster.mission=InterceptAuftrag
-end
-else
-MESSAGE:New("**** Not enough airframes available or max mission limit reached!",15,"CAPGCI"):ToAllIf(self.debug):ToLog()
-end
-end
-end
-self.Intel=BlueIntel
-return self
-end
-function EASYGCICAP:onafterStart(From,Event,To)
-self:T({From,Event,To})
-self:_StartIntel()
-self:_CreateAirwings()
-self:_CreateSquads()
-self:_SetCAPPatrolPoints()
-self:_SetTankerPatrolPoints()
-self:_SetAwacsPatrolPoints()
-self:_SetReconPatrolPoints()
-self:__Status(-10)
-return self
-end
-function EASYGCICAP:onbeforeStatus(From,Event,To)
-self:T({From,Event,To})
-if self:GetState()=="Stopped"then return false end
-return self
-end
-function EASYGCICAP:onafterStatus(From,Event,To)
-self:T({From,Event,To})
-local function counttable(tbl)
-local count=0
-for _,_data in pairs(tbl)do
-count=count+1
-end
-return count
-end
-local wings=counttable(self.ManagedAW)
-local squads=counttable(self.ManagedSQ)
-local caps=counttable(self.ManagedCP)
-local assets=0
-local instock=0
-local capmission=0
-local interceptmission=0
-local reconmission=0
-local awacsmission=0
-local tankermission=0
-for _,_wing in pairs(self.wings)do
-local count=_wing[1]:CountAssetsOnMission(MissionTypes,Cohort)
-local count2=_wing[1]:CountAssets(true,MissionTypes,Attributes)
-capmission=capmission+_wing[1]:CountMissionsInQueue({AUFTRAG.Type.GCICAP,AUFTRAG.Type.PATROLRACETRACK})
-interceptmission=interceptmission+_wing[1]:CountMissionsInQueue({AUFTRAG.Type.INTERCEPT})
-reconmission=reconmission+_wing[1]:CountMissionsInQueue({AUFTRAG.Type.RECON})
-awacsmission=awacsmission+_wing[1]:CountMissionsInQueue({AUFTRAG.Type.AWACS})
-tankermission=tankermission+_wing[1]:CountMissionsInQueue({AUFTRAG.Type.TANKER})
-assets=assets+count
-instock=instock+count2
-end
-if self.Monitor then
-local threatcount=#self.Intel.Clusters or 0
-local text="GCICAP "..self.alias
-text=text.."\nWings: "..wings.."\nSquads: "..squads.."\nCapPoints: "..caps.."\nAssets on Mission: "..assets.."\nAssets in Stock: "..instock
-text=text.."\nThreats: "..threatcount
-text=text.."\nMissions: "..capmission+interceptmission
-text=text.."\n - CAP: "..capmission
-text=text.."\n - Intercept: "..interceptmission
-text=text.."\n - AWACS: "..awacsmission
-text=text.."\n - TANKER: "..tankermission
-text=text.."\n - Recon: "..reconmission
-MESSAGE:New(text,15,"GCICAP"):ToAll():ToLogIf(self.debug)
-end
-self:__Status(30)
-return self
-end
-function EASYGCICAP:onafterStop(From,Event,To)
-self:T({From,Event,To})
-self.Intel:Stop()
-return self
-end
-AI_BALANCER={
-ClassName="AI_BALANCER",
-PatrolZones={},
-AIGroups={},
-Earliest=5,
-Latest=60,
-}
-function AI_BALANCER:New(SetClient,SpawnAI)
-local self=BASE:Inherit(self,FSM_SET:New(SET_GROUP:New()))
-self:SetStartState("None")
-self:AddTransition("*","Monitor","Monitoring")
-self:AddTransition("*","Spawn","Spawning")
-self:AddTransition("Spawning","Spawned","Spawned")
-self:AddTransition("*","Destroy","Destroying")
-self:AddTransition("*","Return","Returning")
-self.SetClient=SetClient
-self.SetClient:FilterOnce()
-self.SpawnAI=SpawnAI
-self.SpawnQueue={}
-self.ToNearestAirbase=false
-self.ToHomeAirbase=false
-self:__Monitor(1)
-return self
-end
-function AI_BALANCER:InitSpawnInterval(Earliest,Latest)
-self.Earliest=Earliest
-self.Latest=Latest
-return self
-end
-function AI_BALANCER:ReturnToNearestAirbases(ReturnThresholdRange,ReturnAirbaseSet)
-self.ToNearestAirbase=true
-self.ReturnThresholdRange=ReturnThresholdRange
-self.ReturnAirbaseSet=ReturnAirbaseSet
-end
-function AI_BALANCER:ReturnToHomeAirbase(ReturnThresholdRange)
-self.ToHomeAirbase=true
-self.ReturnThresholdRange=ReturnThresholdRange
-end
-function AI_BALANCER:onenterSpawning(SetGroup,From,Event,To,ClientName)
-local AIGroup=self.SpawnAI:Spawn()
-if AIGroup then
-AIGroup:T({"Spawning new AIGroup",ClientName=ClientName})
-SetGroup:Remove(ClientName)
-SetGroup:Add(ClientName,AIGroup)
-self.SpawnQueue[ClientName]=nil
-self:Spawned(AIGroup)
-end
-end
-function AI_BALANCER:onenterDestroying(SetGroup,From,Event,To,ClientName,AIGroup)
-AIGroup:Destroy()
-SetGroup:Flush(self)
-SetGroup:Remove(ClientName)
-SetGroup:Flush(self)
-end
-function AI_BALANCER:onenterReturning(SetGroup,From,Event,To,AIGroup)
-local AIGroupTemplate=AIGroup:GetTemplate()
-if self.ToHomeAirbase==true then
-local WayPointCount=#AIGroupTemplate.route.points
-local SwitchWayPointCommand=AIGroup:CommandSwitchWayPoint(1,WayPointCount,1)
-AIGroup:SetCommand(SwitchWayPointCommand)
-AIGroup:MessageToRed("Returning to home base ...",30)
-else
-local PointVec2=POINT_VEC2:New(AIGroup:GetVec2().x,AIGroup:GetVec2().y)
-local ClosestAirbase=self.ReturnAirbaseSet:FindNearestAirbaseFromPointVec2(PointVec2)
-self:T(ClosestAirbase.AirbaseName)
-AIGroup:RouteRTB(ClosestAirbase)
-end
-end
-function AI_BALANCER:onenterMonitoring(SetGroup)
-self:T2({self.SetClient:Count()})
-self.SetClient:ForEachClient(
-function(Client)
-self:T3(Client.ClientName)
-local AIGroup=self.Set:Get(Client.UnitName)
-if AIGroup then self:T({AIGroup=AIGroup:GetName(),IsAlive=AIGroup:IsAlive()})end
-if Client:IsAlive()==true then
-if AIGroup and AIGroup:IsAlive()==true then
-if self.ToNearestAirbase==false and self.ToHomeAirbase==false then
-self:Destroy(Client.UnitName,AIGroup)
-else
-local PlayerInRange={Value=false}
-local RangeZone=ZONE_RADIUS:New('RangeZone',AIGroup:GetVec2(),self.ReturnThresholdRange)
-self:T2(RangeZone)
-_DATABASE:ForEachPlayerUnit(
-function(RangeTestUnit,RangeZone,AIGroup,PlayerInRange)
-self:T2({PlayerInRange,RangeTestUnit.UnitName,RangeZone.ZoneName})
-if RangeTestUnit:IsInZone(RangeZone)==true then
-self:T2("in zone")
-if RangeTestUnit:GetCoalition()~=AIGroup:GetCoalition()then
-self:T2("in range")
-PlayerInRange.Value=true
-end
-end
-end,
-function(RangeZone,AIGroup,PlayerInRange)
-if PlayerInRange.Value==false then
-self:Return(AIGroup)
-end
-end
-,RangeZone,AIGroup,PlayerInRange
-)
-end
-self.Set:Remove(Client.UnitName)
-end
-else
-if not AIGroup or not AIGroup:IsAlive()==true then
-self:T("Client "..Client.UnitName.." not alive.")
-self:T({Queue=self.SpawnQueue[Client.UnitName]})
-if not self.SpawnQueue[Client.UnitName]then
-self:__Spawn(math.random(self.Earliest,self.Latest),Client.UnitName)
-self.SpawnQueue[Client.UnitName]=true
-self:T("New AI Spawned for Client "..Client.UnitName)
-end
-end
-end
-return true
-end
-)
-self:__Monitor(10)
-end
-AI_AIR={
-ClassName="AI_AIR",
-}
-AI_AIR.TaskDelay=0.5
-function AI_AIR:New(AIGroup)
-local self=BASE:Inherit(self,FSM_CONTROLLABLE:New())
-self:SetControllable(AIGroup)
-self:SetStartState("Stopped")
-self:AddTransition("*","Queue","Queued")
-self:AddTransition("*","Start","Started")
-self:AddTransition("*","Stop","Stopped")
-self:AddTransition("*","Status","*")
-self:AddTransition("*","RTB","*")
-self:AddTransition("Patrolling","Refuel","Refuelling")
-self:AddTransition("*","Takeoff","Airborne")
-self:AddTransition("*","Return","Returning")
-self:AddTransition("*","Hold","Holding")
-self:AddTransition("*","Home","Home")
-self:AddTransition("*","LostControl","LostControl")
-self:AddTransition("*","Fuel","Fuel")
-self:AddTransition("*","Damaged","Damaged")
-self:AddTransition("*","Eject","*")
-self:AddTransition("*","Crash","Crashed")
-self:AddTransition("*","PilotDead","*")
-self.IdleCount=0
-self.RTBSpeedMaxFactor=0.6
-self.RTBSpeedMinFactor=0.5
-return self
-end
-function GROUP:OnEventTakeoff(EventData,Fsm)
-Fsm:Takeoff()
-self:UnHandleEvent(EVENTS.Takeoff)
-end
-function AI_AIR:SetDispatcher(Dispatcher)
-self.Dispatcher=Dispatcher
-end
-function AI_AIR:GetDispatcher()
-return self.Dispatcher
-end
-function AI_AIR:SetTargetDistance(Coordinate)
-local CurrentCoord=self.Controllable:GetCoordinate()
-self.TargetDistance=CurrentCoord:Get2DDistance(Coordinate)
-self.ClosestTargetDistance=(not self.ClosestTargetDistance or self.ClosestTargetDistance>self.TargetDistance)and self.TargetDistance or self.ClosestTargetDistance
-end
-function AI_AIR:ClearTargetDistance()
-self.TargetDistance=nil
-self.ClosestTargetDistance=nil
-end
-function AI_AIR:SetSpeed(PatrolMinSpeed,PatrolMaxSpeed)
-self:F2({PatrolMinSpeed,PatrolMaxSpeed})
-self.PatrolMinSpeed=PatrolMinSpeed
-self.PatrolMaxSpeed=PatrolMaxSpeed
-end
-function AI_AIR:SetRTBSpeed(RTBMinSpeed,RTBMaxSpeed)
-self:F({RTBMinSpeed,RTBMaxSpeed})
-self.RTBMinSpeed=RTBMinSpeed
-self.RTBMaxSpeed=RTBMaxSpeed
-end
-function AI_AIR:SetAltitude(PatrolFloorAltitude,PatrolCeilingAltitude)
-self:F2({PatrolFloorAltitude,PatrolCeilingAltitude})
-self.PatrolFloorAltitude=PatrolFloorAltitude
-self.PatrolCeilingAltitude=PatrolCeilingAltitude
-end
-function AI_AIR:SetHomeAirbase(HomeAirbase)
-self:F2({HomeAirbase})
-self.HomeAirbase=HomeAirbase
-end
-function AI_AIR:SetTanker(TankerName)
-self:F2({TankerName})
-self.TankerName=TankerName
-end
-function AI_AIR:SetDisengageRadius(DisengageRadius)
-self:F2({DisengageRadius})
-self.DisengageRadius=DisengageRadius
-end
-function AI_AIR:SetStatusOff()
-self:F2()
-self.CheckStatus=false
-end
-function AI_AIR:SetFuelThreshold(FuelThresholdPercentage,OutOfFuelOrbitTime)
-self.FuelThresholdPercentage=FuelThresholdPercentage
-self.OutOfFuelOrbitTime=OutOfFuelOrbitTime
-self.Controllable:OptionRTBBingoFuel(false)
-return self
-end
-function AI_AIR:SetDamageThreshold(PatrolDamageThreshold)
-self.PatrolManageDamage=true
-self.PatrolDamageThreshold=PatrolDamageThreshold
-return self
-end
-function AI_AIR:onafterStart(Controllable,From,Event,To)
-self:__Status(10)
-self:HandleEvent(EVENTS.PilotDead,self.OnPilotDead)
-self:HandleEvent(EVENTS.Crash,self.OnCrash)
-self:HandleEvent(EVENTS.Ejection,self.OnEjection)
-Controllable:OptionROEHoldFire()
-Controllable:OptionROTVertical()
-end
-function AI_AIR:onafterReturn(Controllable,From,Event,To)
-self:__RTB(self.TaskDelay)
-end
-function AI_AIR:onbeforeStatus()
-return self.CheckStatus
-end
-function AI_AIR:onafterStatus()
-if self.Controllable and self.Controllable:IsAlive()then
-local RTB=false
-local DistanceFromHomeBase=self.HomeAirbase:GetCoordinate():Get2DDistance(self.Controllable:GetCoordinate())
-if not self:Is("Holding")and not self:Is("Returning")then
-local DistanceFromHomeBase=self.HomeAirbase:GetCoordinate():Get2DDistance(self.Controllable:GetCoordinate())
-if DistanceFromHomeBase>self.DisengageRadius then
-self:I(self.Controllable:GetName().." is too far from home base, RTB!")
-self:Hold(300)
-RTB=false
-end
-end
-if not self:Is("Fuel")and not self:Is("Home")and not self:is("Refuelling")then
-local Fuel=self.Controllable:GetFuelMin()
-if Fuel=10 then
-if Damage~=InitialLife then
-self:Damaged()
-else
-self:I(self.Controllable:GetName().." control lost! ")
-self:LostControl()
-end
-else
-self.IdleCount=self.IdleCount+1
-end
-end
-else
-self.IdleCount=0
-end
-if RTB==true then
-self:__RTB(self.TaskDelay)
-end
-if not self:Is("Home")then
-self:__Status(10)
-end
-end
-end
-function AI_AIR.RTBRoute(AIGroup,Fsm)
-AIGroup:F({"AI_AIR.RTBRoute:",AIGroup:GetName()})
-if AIGroup:IsAlive()then
-Fsm:RTB()
-end
-end
-function AI_AIR.RTBHold(AIGroup,Fsm)
-AIGroup:F({"AI_AIR.RTBHold:",AIGroup:GetName()})
-if AIGroup:IsAlive()then
-Fsm:__RTB(Fsm.TaskDelay)
-Fsm:Return()
-local Task=AIGroup:TaskOrbitCircle(4000,400)
-AIGroup:SetTask(Task)
-end
-end
-function AI_AIR:SetRTBSpeedFactors(MinFactor,MaxFactor)
-self.RTBSpeedMaxFactor=MaxFactor or 0.6
-self.RTBSpeedMinFactor=MinFactor or 0.5
-return self
-end
-function AI_AIR:onafterRTB(AIGroup,From,Event,To)
-self:F({AIGroup,From,Event,To})
-if AIGroup and AIGroup:IsAlive()then
-self:T("Group "..AIGroup:GetName().." ... RTB! ( "..self:GetState().." )")
-self:ClearTargetDistance()
-AIGroup:OptionProhibitAfterburner(true)
-local EngageRoute={}
-local FromCoord=AIGroup:GetCoordinate()
-local ToTargetCoord=self.HomeAirbase:GetCoordinate()
-local ToTargetVec3=ToTargetCoord:GetVec3()
-ToTargetVec3.y=ToTargetCoord:GetLandHeight()+3000
-local ToTargetCoord2=COORDINATE:NewFromVec3(ToTargetVec3)
-if not self.RTBMinSpeed or not self.RTBMaxSpeed then
-local RTBSpeedMax=AIGroup:GetSpeedMax()
-local RTBSpeedMaxFactor=self.RTBSpeedMaxFactor or 0.6
-local RTBSpeedMinFactor=self.RTBSpeedMinFactor or 0.5
-self:SetRTBSpeed(RTBSpeedMax*RTBSpeedMinFactor,RTBSpeedMax*RTBSpeedMaxFactor)
-end
-local RTBSpeed=math.random(self.RTBMinSpeed,self.RTBMaxSpeed)
-local Distance=FromCoord:Get2DDistance(ToTargetCoord2)
-local ToAirbaseCoord=ToTargetCoord2
-if Distance<5000 then
-self:I("RTB and near the airbase!")
-self:Home()
-return
-end
-if not AIGroup:InAir()==true then
-self:I("Not anymore in the air, considered Home.")
-self:Home()
-return
-end
-local FromRTBRoutePoint=FromCoord:WaypointAir(
-self.PatrolAltType,
-POINT_VEC3.RoutePointType.TurningPoint,
-POINT_VEC3.RoutePointAction.TurningPoint,
-RTBSpeed,
-true
-)
-local ToRTBRoutePoint=ToAirbaseCoord:WaypointAir(
-self.PatrolAltType,
-POINT_VEC3.RoutePointType.TurningPoint,
-POINT_VEC3.RoutePointAction.TurningPoint,
-RTBSpeed,
-true
-)
-EngageRoute[#EngageRoute+1]=FromRTBRoutePoint
-EngageRoute[#EngageRoute+1]=ToRTBRoutePoint
-local Tasks={}
-Tasks[#Tasks+1]=AIGroup:TaskFunction("AI_AIR.RTBRoute",self)
-EngageRoute[#EngageRoute].task=AIGroup:TaskCombo(Tasks)
-AIGroup:OptionROEHoldFire()
-AIGroup:OptionROTEvadeFire()
-AIGroup:Route(EngageRoute,self.TaskDelay)
-end
-end
-function AI_AIR:onafterHome(AIGroup,From,Event,To)
-self:F({AIGroup,From,Event,To})
-self:I("Group "..self.Controllable:GetName().." ... Home! ( "..self:GetState().." )")
-if AIGroup and AIGroup:IsAlive()then
-end
-end
-function AI_AIR:onafterHold(AIGroup,From,Event,To,HoldTime)
-self:F({AIGroup,From,Event,To})
-self:I("Group "..self.Controllable:GetName().." ... Holding! ( "..self:GetState().." )")
-if AIGroup and AIGroup:IsAlive()then
-local OrbitTask=AIGroup:TaskOrbitCircle(math.random(self.PatrolFloorAltitude,self.PatrolCeilingAltitude),self.PatrolMinSpeed)
-local TimedOrbitTask=AIGroup:TaskControlled(OrbitTask,AIGroup:TaskCondition(nil,nil,nil,nil,HoldTime,nil))
-local RTBTask=AIGroup:TaskFunction("AI_AIR.RTBHold",self)
-local OrbitHoldTask=AIGroup:TaskOrbitCircle(4000,self.PatrolMinSpeed)
-AIGroup:SetTask(AIGroup:TaskCombo({TimedOrbitTask,RTBTask,OrbitHoldTask}),1)
-end
-end
-function AI_AIR.Resume(AIGroup,Fsm)
-AIGroup:I({"AI_AIR.Resume:",AIGroup:GetName()})
-if AIGroup:IsAlive()then
-Fsm:__RTB(Fsm.TaskDelay)
-end
-end
-function AI_AIR:onafterRefuel(AIGroup,From,Event,To)
-self:F({AIGroup,From,Event,To})
-if AIGroup and AIGroup:IsAlive()then
-local Tanker=GROUP:FindByName(self.TankerName)
-if Tanker and Tanker:IsAlive()and Tanker:IsAirPlane()then
-self:I("Group "..self.Controllable:GetName().." ... Refuelling! State="..self:GetState()..", Refuelling tanker "..self.TankerName)
-local RefuelRoute={}
-local FromRefuelCoord=AIGroup:GetCoordinate()
-local ToRefuelCoord=Tanker:GetCoordinate()
-local ToRefuelSpeed=math.random(self.PatrolMinSpeed,self.PatrolMaxSpeed)
-local FromRefuelRoutePoint=FromRefuelCoord:WaypointAir(self.PatrolAltType,POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,ToRefuelSpeed,true)
-local ToRefuelRoutePoint=Tanker:GetCoordinate():WaypointAir(self.PatrolAltType,POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,ToRefuelSpeed,true)
-self:F({ToRefuelSpeed=ToRefuelSpeed})
-RefuelRoute[#RefuelRoute+1]=FromRefuelRoutePoint
-RefuelRoute[#RefuelRoute+1]=ToRefuelRoutePoint
-AIGroup:OptionROEHoldFire()
-AIGroup:OptionROTEvadeFire()
-local classname=self:GetClassName()
-if classname=="AI_A2A_CAP"then
-classname="AI_AIR_PATROL"
-end
-env.info("FF refueling classname="..classname)
-local Tasks={}
-Tasks[#Tasks+1]=AIGroup:TaskRefueling()
-Tasks[#Tasks+1]=AIGroup:TaskFunction(classname..".Resume",self)
-RefuelRoute[#RefuelRoute].task=AIGroup:TaskCombo(Tasks)
-AIGroup:Route(RefuelRoute,self.TaskDelay)
-else
-self:RTB()
-end
-end
-end
-function AI_AIR:onafterDead()
-self:SetStatusOff()
-end
-function AI_AIR:OnCrash(EventData)
-if self.Controllable:IsAlive()and EventData.IniDCSGroupName==self.Controllable:GetName()then
-if#self.Controllable:GetUnits()==1 then
-self:__Crash(self.TaskDelay,EventData)
-end
-end
-end
-function AI_AIR:OnEjection(EventData)
-if self.Controllable:IsAlive()and EventData.IniDCSGroupName==self.Controllable:GetName()then
-self:__Eject(self.TaskDelay,EventData)
-end
-end
-function AI_AIR:OnPilotDead(EventData)
-if self.Controllable:IsAlive()and EventData.IniDCSGroupName==self.Controllable:GetName()then
-self:__PilotDead(self.TaskDelay,EventData)
-end
-end
-AI_AIR_PATROL={
-ClassName="AI_AIR_PATROL",
-}
-function AI_AIR_PATROL:New(AI_Air,AIGroup,PatrolZone,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,PatrolAltType)
-local self=BASE:Inherit(self,AI_Air)
-local SpeedMax=AIGroup:GetSpeedMax()
-self.PatrolZone=PatrolZone
-self.PatrolFloorAltitude=PatrolFloorAltitude or 1000
-self.PatrolCeilingAltitude=PatrolCeilingAltitude or 1500
-self.PatrolMinSpeed=PatrolMinSpeed or SpeedMax*0.5
-self.PatrolMaxSpeed=PatrolMaxSpeed or SpeedMax*0.75
-self.PatrolAltType=PatrolAltType or"RADIO"
-self:AddTransition({"Started","Airborne","Refuelling"},"Patrol","Patrolling")
-self:AddTransition("Patrolling","PatrolRoute","Patrolling")
-self:AddTransition("*","Reset","Patrolling")
-return self
-end
-function AI_AIR_PATROL:SetEngageRange(EngageRange)
-self:F2()
-if EngageRange then
-self.EngageRange=EngageRange
-else
-self.EngageRange=nil
-end
-end
-function AI_AIR_PATROL:SetRaceTrackPattern(LegMin,LegMax,HeadingMin,HeadingMax,DurationMin,DurationMax,CapCoordinates)
-self.racetrack=true
-self.racetracklegmin=LegMin or 10000
-self.racetracklegmax=LegMax or 15000
-self.racetrackheadingmin=HeadingMin or 0
-self.racetrackheadingmax=HeadingMax or 180
-self.racetrackdurationmin=DurationMin
-self.racetrackdurationmax=DurationMax
-if self.racetrackdurationmax and not self.racetrackdurationmin then
-self.racetrackdurationmin=self.racetrackdurationmax
-end
-self.racetrackcapcoordinates=CapCoordinates
-end
-function AI_AIR_PATROL:onafterPatrol(AIPatrol,From,Event,To)
-self:F2()
-self:ClearTargetDistance()
-self:__PatrolRoute(self.TaskDelay)
-AIPatrol:OnReSpawn(
-function(PatrolGroup)
-self:__Reset(self.TaskDelay)
-self:__PatrolRoute(self.TaskDelay)
-end
-)
-end
-function AI_AIR_PATROL.___PatrolRoute(AIPatrol,Fsm)
-AIPatrol:F({"AI_AIR_PATROL.___PatrolRoute:",AIPatrol:GetName()})
-if AIPatrol and AIPatrol:IsAlive()then
-Fsm:PatrolRoute()
-end
-end
-function AI_AIR_PATROL:onafterPatrolRoute(AIPatrol,From,Event,To)
-self:F2()
-if From=="RTB"then
-return
-end
-if AIPatrol and AIPatrol:IsAlive()then
-local PatrolRoute={}
-local CurrentCoord=AIPatrol:GetCoordinate()
-local altitude=math.random(self.PatrolFloorAltitude,self.PatrolCeilingAltitude)
-local ToTargetCoord=self.PatrolZone:GetRandomPointVec2()
-ToTargetCoord:SetAlt(altitude)
-self:SetTargetDistance(ToTargetCoord)
-local ToTargetSpeed=math.random(self.PatrolMinSpeed,self.PatrolMaxSpeed)
-local speedkmh=ToTargetSpeed
-local FromWP=CurrentCoord:WaypointAir(self.PatrolAltType or"RADIO",POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,ToTargetSpeed,true)
-PatrolRoute[#PatrolRoute+1]=FromWP
-if self.racetrack then
-local heading=math.random(self.racetrackheadingmin,self.racetrackheadingmax)
-local leg=math.random(self.racetracklegmin,self.racetracklegmax)
-local duration=self.racetrackdurationmin
-if self.racetrackdurationmax then
-duration=math.random(self.racetrackdurationmin,self.racetrackdurationmax)
-end
-local c0=self.PatrolZone:GetRandomCoordinate()
-if self.racetrackcapcoordinates and#self.racetrackcapcoordinates>0 then
-c0=self.racetrackcapcoordinates[math.random(#self.racetrackcapcoordinates)]
-end
-local c1=c0:SetAltitude(altitude)
-local c2=c1:Translate(leg,heading):SetAltitude(altitude)
-self:SetTargetDistance(c0)
-self:T(string.format("Patrol zone race track: v=%.1f knots, h=%.1f ft, heading=%03d, leg=%d m, t=%s sec",UTILS.KmphToKnots(speedkmh),UTILS.MetersToFeet(altitude),heading,leg,tostring(duration)))
-local taskOrbit=AIPatrol:TaskOrbit(c1,altitude,UTILS.KmphToMps(speedkmh),c2)
-local taskPatrol=AIPatrol:TaskFunction("AI_AIR_PATROL.___PatrolRoute",self)
-local taskCond=AIPatrol:TaskCondition(nil,nil,nil,nil,duration,nil)
-local taskCont=AIPatrol:TaskControlled(taskOrbit,taskCond)
-PatrolRoute[2]=c1:WaypointAirTurningPoint(self.PatrolAltType,speedkmh,{taskCont,taskPatrol},"CAP Orbit")
-else
-local ToWP=ToTargetCoord:WaypointAir(self.PatrolAltType,POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,ToTargetSpeed,true)
-PatrolRoute[#PatrolRoute+1]=ToWP
-local Tasks={}
-Tasks[#Tasks+1]=AIPatrol:TaskFunction("AI_AIR_PATROL.___PatrolRoute",self)
-PatrolRoute[#PatrolRoute].task=AIPatrol:TaskCombo(Tasks)
-end
-AIPatrol:OptionROEReturnFire()
-AIPatrol:OptionROTEvadeFire()
-AIPatrol:Route(PatrolRoute,self.TaskDelay)
-end
-end
-function AI_AIR_PATROL.Resume(AIPatrol,Fsm)
-AIPatrol:F({"AI_AIR_PATROL.Resume:",AIPatrol:GetName()})
-if AIPatrol and AIPatrol:IsAlive()then
-Fsm:__Reset(Fsm.TaskDelay)
-Fsm:__PatrolRoute(Fsm.TaskDelay)
-end
-end
-AI_AIR_ENGAGE={
-ClassName="AI_AIR_ENGAGE",
-}
-function AI_AIR_ENGAGE:New(AI_Air,AIGroup,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType)
-local self=BASE:Inherit(self,AI_Air)
-self.Accomplished=false
-self.Engaging=false
-local SpeedMax=AIGroup:GetSpeedMax()
-self.EngageMinSpeed=EngageMinSpeed or SpeedMax*0.5
-self.EngageMaxSpeed=EngageMaxSpeed or SpeedMax*0.75
-self.EngageFloorAltitude=EngageFloorAltitude or 1000
-self.EngageCeilingAltitude=EngageCeilingAltitude or 1500
-self.EngageAltType=EngageAltType or"RADIO"
-self:AddTransition({"Started","Engaging","Returning","Airborne","Patrolling"},"EngageRoute","Engaging")
-self:AddTransition({"Started","Engaging","Returning","Airborne","Patrolling"},"Engage","Engaging")
-self:AddTransition("Engaging","Fired","Engaging")
-self:AddTransition("*","Destroy","*")
-self:AddTransition("Engaging","Abort","Patrolling")
-self:AddTransition("Engaging","Accomplish","Patrolling")
-self:AddTransition({"Patrolling","Engaging"},"Refuel","Refuelling")
-return self
-end
-function AI_AIR_ENGAGE:onafterStart(AIGroup,From,Event,To)
-self:GetParent(self,AI_AIR_ENGAGE).onafterStart(self,AIGroup,From,Event,To)
-AIGroup:HandleEvent(EVENTS.Takeoff,nil,self)
-end
-function AI_AIR_ENGAGE:onafterEngage(AIGroup,From,Event,To)
-self:HandleEvent(EVENTS.Dead)
-end
-function AI_AIR_ENGAGE:onbeforeEngage(AIGroup,From,Event,To)
-if self.Accomplished==true then
-return false
-end
-return true
-end
-function AI_AIR_ENGAGE:onafterAbort(AIGroup,From,Event,To)
-AIGroup:ClearTasks()
-self:Return()
-end
-function AI_AIR_ENGAGE:onafterAccomplish(AIGroup,From,Event,To)
-self.Accomplished=true
-end
-function AI_AIR_ENGAGE:onafterDestroy(AIGroup,From,Event,To,EventData)
-if EventData.IniUnit then
-self.AttackUnits[EventData.IniUnit]=nil
-end
-end
-function AI_AIR_ENGAGE:OnEventDead(EventData)
-self:F({"EventDead",EventData})
-if EventData.IniDCSUnit then
-if self.AttackUnits and self.AttackUnits[EventData.IniUnit]then
-self:__Destroy(self.TaskDelay,EventData)
-end
-end
-end
-function AI_AIR_ENGAGE.___EngageRoute(AIGroup,Fsm,AttackSetUnit)
-Fsm:I(string.format("AI_AIR_ENGAGE.___EngageRoute: %s",tostring(AIGroup:GetName())))
-if AIGroup and AIGroup:IsAlive()then
-Fsm:__EngageRoute(Fsm.TaskDelay or 0.1,AttackSetUnit)
-end
-end
-function AI_AIR_ENGAGE:onafterEngageRoute(DefenderGroup,From,Event,To,AttackSetUnit)
-self:I({DefenderGroup,From,Event,To,AttackSetUnit})
-local DefenderGroupName=DefenderGroup:GetName()
-self.AttackSetUnit=AttackSetUnit
-local AttackCount=AttackSetUnit:CountAlive()
-if AttackCount>0 then
-if DefenderGroup:IsAlive()then
-local EngageAltitude=math.random(self.EngageFloorAltitude,self.EngageCeilingAltitude)
-local EngageSpeed=math.random(self.EngageMinSpeed,self.EngageMaxSpeed)
-local DefenderCoord=DefenderGroup:GetPointVec3()
-DefenderCoord:SetY(EngageAltitude)
-local TargetCoord=AttackSetUnit:GetFirst():GetPointVec3()
-TargetCoord:SetY(EngageAltitude)
-local TargetDistance=DefenderCoord:Get2DDistance(TargetCoord)
-local EngageDistance=(DefenderGroup:IsHelicopter()and 5000)or(DefenderGroup:IsAirPlane()and 10000)
-if TargetDistance<=EngageDistance*9 then
-self:__Engage(0.1,AttackSetUnit)
-else
-local EngageRoute={}
-local AttackTasks={}
-local FromWP=DefenderCoord:WaypointAir(self.PatrolAltType or"RADIO",POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,EngageSpeed,true)
-EngageRoute[#EngageRoute+1]=FromWP
-self:SetTargetDistance(TargetCoord)
-local FromEngageAngle=DefenderCoord:GetAngleDegrees(DefenderCoord:GetDirectionVec3(TargetCoord))
-local ToCoord=DefenderCoord:Translate(EngageDistance,FromEngageAngle,true)
-local ToWP=ToCoord:WaypointAir(self.PatrolAltType or"RADIO",POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,EngageSpeed,true)
-EngageRoute[#EngageRoute+1]=ToWP
-AttackTasks[#AttackTasks+1]=DefenderGroup:TaskFunction("AI_AIR_ENGAGE.___EngageRoute",self,AttackSetUnit)
-EngageRoute[#EngageRoute].task=DefenderGroup:TaskCombo(AttackTasks)
-DefenderGroup:OptionROEReturnFire()
-DefenderGroup:OptionROTEvadeFire()
-DefenderGroup:Route(EngageRoute,self.TaskDelay or 0.1)
-end
-end
-else
-self:I(DefenderGroupName..": No targets found -> Going RTB")
-self:Return()
-end
-end
-function AI_AIR_ENGAGE.___Engage(AIGroup,Fsm,AttackSetUnit)
-Fsm:I(string.format("AI_AIR_ENGAGE.___Engage: %s",tostring(AIGroup:GetName())))
-if AIGroup and AIGroup:IsAlive()then
-local delay=Fsm.TaskDelay or 0.1
-Fsm:__Engage(delay,AttackSetUnit)
-end
-end
-function AI_AIR_ENGAGE:onafterEngage(DefenderGroup,From,Event,To,AttackSetUnit)
-self:F({DefenderGroup,From,Event,To,AttackSetUnit})
-local DefenderGroupName=DefenderGroup:GetName()
-self.AttackSetUnit=AttackSetUnit
-local AttackCount=AttackSetUnit:CountAlive()
-self:T({AttackCount=AttackCount})
-if AttackCount>0 then
-if DefenderGroup and DefenderGroup:IsAlive()then
-local EngageAltitude=math.random(self.EngageFloorAltitude or 500,self.EngageCeilingAltitude or 1000)
-local EngageSpeed=math.random(self.EngageMinSpeed,self.EngageMaxSpeed)
-local DefenderCoord=DefenderGroup:GetPointVec3()
-DefenderCoord:SetY(EngageAltitude)
-local TargetCoord=AttackSetUnit:GetFirst():GetPointVec3()
-if not TargetCoord then
-self:Return()
-return
-end
-TargetCoord:SetY(EngageAltitude)
-local TargetDistance=DefenderCoord:Get2DDistance(TargetCoord)
-local EngageDistance=(DefenderGroup:IsHelicopter()and 5000)or(DefenderGroup:IsAirPlane()and 10000)
-local EngageRoute={}
-local AttackTasks={}
-local FromWP=DefenderCoord:WaypointAir(self.EngageAltType or"RADIO",POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,EngageSpeed,true)
-EngageRoute[#EngageRoute+1]=FromWP
-self:SetTargetDistance(TargetCoord)
-local FromEngageAngle=DefenderCoord:GetAngleDegrees(DefenderCoord:GetDirectionVec3(TargetCoord))
-local ToCoord=DefenderCoord:Translate(EngageDistance,FromEngageAngle,true)
-local ToWP=ToCoord:WaypointAir(self.EngageAltType or"RADIO",POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,EngageSpeed,true)
-EngageRoute[#EngageRoute+1]=ToWP
-if TargetDistance<=EngageDistance*9 then
-local AttackUnitTasks=self:CreateAttackUnitTasks(AttackSetUnit,DefenderGroup,EngageAltitude)
-if#AttackUnitTasks==0 then
-self:I(DefenderGroupName..": No valid targets found -> Going RTB")
-self:Return()
-return
-else
-local text=string.format("%s: Engaging targets at distance %.2f NM",DefenderGroupName,UTILS.MetersToNM(TargetDistance))
-self:I(text)
-DefenderGroup:OptionROEOpenFire()
-DefenderGroup:OptionROTEvadeFire()
-DefenderGroup:OptionKeepWeaponsOnThreat()
-AttackTasks[#AttackTasks+1]=DefenderGroup:TaskCombo(AttackUnitTasks)
-end
-end
-AttackTasks[#AttackTasks+1]=DefenderGroup:TaskFunction("AI_AIR_ENGAGE.___Engage",self,AttackSetUnit)
-EngageRoute[#EngageRoute].task=DefenderGroup:TaskCombo(AttackTasks)
-DefenderGroup:Route(EngageRoute,self.TaskDelay or 0.1)
-end
-else
-self:I(DefenderGroupName..": No targets found -> returning.")
-self:Return()
-return
-end
-end
-function AI_AIR_ENGAGE.Resume(AIEngage,Fsm)
-AIEngage:F({"Resume:",AIEngage:GetName()})
-if AIEngage and AIEngage:IsAlive()then
-Fsm:__Reset(Fsm.TaskDelay or 0.1)
-Fsm:__EngageRoute(Fsm.TaskDelay or 0.2,Fsm.AttackSetUnit)
-end
-end
-AI_A2A_PATROL={
-ClassName="AI_A2A_PATROL",
-}
-function AI_A2A_PATROL:New(AIPatrol,PatrolZone,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,PatrolAltType)
-local AI_Air=AI_AIR:New(AIPatrol)
-local AI_Air_Patrol=AI_AIR_PATROL:New(AI_Air,AIPatrol,PatrolZone,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,PatrolAltType)
-local self=BASE:Inherit(self,AI_Air_Patrol)
-self:SetFuelThreshold(.2,60)
-self:SetDamageThreshold(0.4)
-self:SetDisengageRadius(70000)
-self.PatrolZone=PatrolZone
-self.PatrolFloorAltitude=PatrolFloorAltitude
-self.PatrolCeilingAltitude=PatrolCeilingAltitude
-self.PatrolMinSpeed=PatrolMinSpeed
-self.PatrolMaxSpeed=PatrolMaxSpeed
-self.PatrolAltType=PatrolAltType or"BARO"
-self:AddTransition({"Started","Airborne","Refuelling"},"Patrol","Patrolling")
-self:AddTransition("Patrolling","Route","Patrolling")
-self:AddTransition("*","Reset","Patrolling")
-return self
-end
-function AI_A2A_PATROL:SetSpeed(PatrolMinSpeed,PatrolMaxSpeed)
-self:F2({PatrolMinSpeed,PatrolMaxSpeed})
-self.PatrolMinSpeed=PatrolMinSpeed
-self.PatrolMaxSpeed=PatrolMaxSpeed
-end
-function AI_A2A_PATROL:SetAltitude(PatrolFloorAltitude,PatrolCeilingAltitude)
-self:F2({PatrolFloorAltitude,PatrolCeilingAltitude})
-self.PatrolFloorAltitude=PatrolFloorAltitude
-self.PatrolCeilingAltitude=PatrolCeilingAltitude
-end
-function AI_A2A_PATROL:onafterPatrol(AIPatrol,From,Event,To)
-self:F2()
-self:ClearTargetDistance()
-self:__Route(1)
-AIPatrol:OnReSpawn(
-function(PatrolGroup)
-self:__Reset(1)
-self:__Route(5)
-end
-)
-end
-function AI_A2A_PATROL.PatrolRoute(AIPatrol,Fsm)
-AIPatrol:F({"AI_A2A_PATROL.PatrolRoute:",AIPatrol:GetName()})
-if AIPatrol and AIPatrol:IsAlive()then
-Fsm:Route()
-end
-end
-function AI_A2A_PATROL:onafterRoute(AIPatrol,From,Event,To)
-self:F2()
-if From=="RTB"then
-return
-end
-if AIPatrol and AIPatrol:IsAlive()then
-local PatrolRoute={}
-local CurrentCoord=AIPatrol:GetCoordinate()
-local altitude=math.random(self.PatrolFloorAltitude,self.PatrolCeilingAltitude)
-local speedkmh=math.random(self.PatrolMinSpeed,self.PatrolMaxSpeed)
-PatrolRoute[1]=CurrentCoord:WaypointAirTurningPoint(nil,speedkmh,{},"Current")
-if self.racetrack then
-local heading=math.random(self.racetrackheadingmin,self.racetrackheadingmax)
-local leg=math.random(self.racetracklegmin,self.racetracklegmax)
-local duration=self.racetrackdurationmin
-if self.racetrackdurationmax then
-duration=math.random(self.racetrackdurationmin,self.racetrackdurationmax)
-end
-local c0=self.PatrolZone:GetRandomCoordinate()
-if self.racetrackcapcoordinates and#self.racetrackcapcoordinates>0 then
-c0=self.racetrackcapcoordinates[math.random(#self.racetrackcapcoordinates)]
-end
-local c1=c0:SetAltitude(altitude)
-local c2=c1:Translate(leg,heading):SetAltitude(altitude)
-self:SetTargetDistance(c0)
-self:T(string.format("Patrol zone race track: v=%.1f knots, h=%.1f ft, heading=%03d, leg=%d m, t=%s sec",UTILS.KmphToKnots(speedkmh),UTILS.MetersToFeet(altitude),heading,leg,tostring(duration)))
-local taskOrbit=AIPatrol:TaskOrbit(c1,altitude,UTILS.KmphToMps(speedkmh),c2)
-local taskPatrol=AIPatrol:TaskFunction("AI_A2A_PATROL.PatrolRoute",self)
-local taskCond=AIPatrol:TaskCondition(nil,nil,nil,nil,duration,nil)
-local taskCont=AIPatrol:TaskControlled(taskOrbit,taskCond)
-PatrolRoute[2]=c1:WaypointAirTurningPoint(self.PatrolAltType,speedkmh,{taskCont,taskPatrol},"CAP Orbit")
-else
-local ToTargetCoord=self.PatrolZone:GetRandomCoordinate()
-ToTargetCoord:SetAltitude(altitude)
-self:SetTargetDistance(ToTargetCoord)
-local taskReRoute=AIPatrol:TaskFunction("AI_A2A_PATROL.PatrolRoute",self)
-PatrolRoute[2]=ToTargetCoord:WaypointAirTurningPoint(self.PatrolAltType,speedkmh,{taskReRoute},"Patrol Point")
-end
-AIPatrol:OptionROEReturnFire()
-AIPatrol:OptionROTEvadeFire()
-AIPatrol:Route(PatrolRoute,0.5)
-end
-end
-AI_A2A_CAP={
-ClassName="AI_A2A_CAP",
-}
-function AI_A2A_CAP:New2(AICap,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType,PatrolZone,PatrolMinSpeed,PatrolMaxSpeed,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolAltType)
-local AI_Air=AI_AIR:New(AICap)
-local AI_Air_Patrol=AI_AIR_PATROL:New(AI_Air,AICap,PatrolZone,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,PatrolAltType)
-local AI_Air_Engage=AI_AIR_ENGAGE:New(AI_Air_Patrol,AICap,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType)
-local self=BASE:Inherit(self,AI_Air_Engage)
-self:SetFuelThreshold(.2,60)
-self:SetDamageThreshold(0.4)
-self:SetDisengageRadius(70000)
-return self
-end
-function AI_A2A_CAP:New(AICap,PatrolZone,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,EngageMinSpeed,EngageMaxSpeed,PatrolAltType)
-return self:New2(AICap,EngageMinSpeed,EngageMaxSpeed,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolAltType,PatrolZone,PatrolMinSpeed,PatrolMaxSpeed,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolAltType)
-end
-function AI_A2A_CAP:onafterStart(AICap,From,Event,To)
-self:GetParent(self,AI_A2A_CAP).onafterStart(self,AICap,From,Event,To)
-AICap:HandleEvent(EVENTS.Takeoff,nil,self)
-end
-function AI_A2A_CAP:SetEngageZone(EngageZone)
-self:F2()
-if EngageZone then
-self.EngageZone=EngageZone
-else
-self.EngageZone=nil
-end
-end
-function AI_A2A_CAP:SetEngageRange(EngageRange)
-self:F2()
-if EngageRange then
-self.EngageRange=EngageRange
-else
-self.EngageRange=nil
-end
-end
-function AI_A2A_CAP:CreateAttackUnitTasks(AttackSetUnit,DefenderGroup,EngageAltitude)
-local AttackUnitTasks={}
-for AttackUnitID,AttackUnit in pairs(self.AttackSetUnit:GetSet())do
-local AttackUnit=AttackUnit
-if AttackUnit and AttackUnit:IsAlive()and AttackUnit:IsAir()then
-self:T({"Attacking Task:",AttackUnit:GetName(),AttackUnit:IsAlive(),AttackUnit:IsAir()})
-AttackUnitTasks[#AttackUnitTasks+1]=DefenderGroup:TaskAttackUnit(AttackUnit)
-end
-end
-return AttackUnitTasks
-end
-AI_A2A_GCI={
-ClassName="AI_A2A_GCI",
-}
-function AI_A2A_GCI:New2(AIIntercept,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType)
-local AI_Air=AI_AIR:New(AIIntercept)
-local AI_Air_Engage=AI_AIR_ENGAGE:New(AI_Air,AIIntercept,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType)
-local self=BASE:Inherit(self,AI_Air_Engage)
-self:SetFuelThreshold(.2,60)
-self:SetDamageThreshold(0.4)
-self:SetDisengageRadius(70000)
-return self
-end
-function AI_A2A_GCI:New(AIIntercept,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType)
-return self:New2(AIIntercept,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType)
-end
-function AI_A2A_GCI:onafterStart(AIIntercept,From,Event,To)
-self:GetParent(self,AI_A2A_GCI).onafterStart(self,AIIntercept,From,Event,To)
-end
-function AI_A2A_GCI:CreateAttackUnitTasks(AttackSetUnit,DefenderGroup,EngageAltitude)
-local AttackUnitTasks={}
-for AttackUnitID,AttackUnit in pairs(self.AttackSetUnit:GetSet())do
-local AttackUnit=AttackUnit
-self:T({"Attacking Unit:",AttackUnit:GetName(),AttackUnit:IsAlive(),AttackUnit:IsAir()})
-if AttackUnit:IsAlive()and AttackUnit:IsAir()then
-AttackUnitTasks[#AttackUnitTasks+1]=DefenderGroup:TaskAttackUnit(AttackUnit)
-end
-end
-return AttackUnitTasks
-end
-do
-AI_A2A_DISPATCHER={
-ClassName="AI_A2A_DISPATCHER",
-Detection=nil,
-}
-AI_A2A_DISPATCHER.Takeoff=GROUP.Takeoff
-AI_A2A_DISPATCHER.Landing={
-NearAirbase=1,
-AtRunway=2,
-AtEngineShutdown=3,
-}
-function AI_A2A_DISPATCHER:New(Detection)
-local self=BASE:Inherit(self,DETECTION_MANAGER:New(nil,Detection))
-self.Detection=Detection
-self.DefenderSquadrons={}
-self.DefenderSpawns={}
-self.DefenderTasks={}
-self.DefenderDefault={}
-self.SetSendPlayerMessages=false
-self.Detection:FilterCategories({Unit.Category.AIRPLANE,Unit.Category.HELICOPTER})
-self.Detection:SetRefreshTimeInterval(30)
-self:SetEngageRadius()
-self:SetGciRadius()
-self:SetIntercept(300)
-self:SetDisengageRadius(300000)
-self:SetDefaultTakeoff(AI_A2A_DISPATCHER.Takeoff.Air)
-self:SetDefaultTakeoffInAirAltitude(500)
-self:SetDefaultLanding(AI_A2A_DISPATCHER.Landing.NearAirbase)
-self:SetDefaultOverhead(1)
-self:SetDefaultGrouping(1)
-self:SetDefaultFuelThreshold(0.15,0)
-self:SetDefaultDamageThreshold(0.4)
-self:SetDefaultCapTimeInterval(180,600)
-self:SetDefaultCapLimit(1)
-self:AddTransition("Started","Assign","Started")
-self:AddTransition("*","CAP","*")
-self:AddTransition("*","GCI","*")
-self:AddTransition("*","ENGAGE","*")
-self:HandleEvent(EVENTS.Crash,self.OnEventCrashOrDead)
-self:HandleEvent(EVENTS.Dead,self.OnEventCrashOrDead)
-self:HandleEvent(EVENTS.Land)
-self:HandleEvent(EVENTS.EngineShutdown)
-self:HandleEvent(EVENTS.BaseCaptured)
-self:SetTacticalDisplay(false)
-self.DefenderCAPIndex=0
-self:__Start(5)
-return self
-end
-function AI_A2A_DISPATCHER:onafterStart(From,Event,To)
-self:GetParent(self,AI_A2A_DISPATCHER).onafterStart(self,From,Event,To)
-for SquadronName,_DefenderSquadron in pairs(self.DefenderSquadrons)do
-local DefenderSquadron=_DefenderSquadron
-DefenderSquadron.Resources={}
-if DefenderSquadron.ResourceCount then
-for Resource=1,DefenderSquadron.ResourceCount do
-self:ParkDefender(DefenderSquadron)
-end
-end
-end
-end
-function AI_A2A_DISPATCHER:ParkDefender(DefenderSquadron)
-local TemplateID=math.random(1,#DefenderSquadron.Spawn)
-local Spawn=DefenderSquadron.Spawn[TemplateID]
-Spawn:InitGrouping(1)
-local SpawnGroup
-if self:IsSquadronVisible(DefenderSquadron.Name)then
-local Grouping=DefenderSquadron.Grouping or self.DefenderDefault.Grouping
-Grouping=1
-Spawn:InitGrouping(Grouping)
-SpawnGroup=Spawn:SpawnAtAirbase(DefenderSquadron.Airbase,SPAWN.Takeoff.Cold)
-local GroupName=SpawnGroup:GetName()
-DefenderSquadron.Resources=DefenderSquadron.Resources or{}
-DefenderSquadron.Resources[TemplateID]=DefenderSquadron.Resources[TemplateID]or{}
-DefenderSquadron.Resources[TemplateID][GroupName]={}
-DefenderSquadron.Resources[TemplateID][GroupName]=SpawnGroup
-self.uncontrolled=self.uncontrolled or{}
-self.uncontrolled[DefenderSquadron.Name]=self.uncontrolled[DefenderSquadron.Name]or{}
-table.insert(self.uncontrolled[DefenderSquadron.Name],{group=SpawnGroup,name=GroupName,grouping=Grouping})
-end
-end
-function AI_A2A_DISPATCHER:OnEventBaseCaptured(EventData)
-local AirbaseName=EventData.PlaceName
-self:I("Captured "..AirbaseName)
-for SquadronName,Squadron in pairs(self.DefenderSquadrons)do
-if Squadron.AirbaseName==AirbaseName then
-Squadron.ResourceCount=-999
-Squadron.Captured=true
-self:I("Squadron "..SquadronName.." captured.")
-end
-end
-end
-function AI_A2A_DISPATCHER:OnEventCrashOrDead(EventData)
-self.Detection:ForgetDetectedUnit(EventData.IniUnitName)
-end
-function AI_A2A_DISPATCHER:OnEventLand(EventData)
-self:F("Landed")
-local DefenderUnit=EventData.IniUnit
-local Defender=EventData.IniGroup
-local Squadron=self:GetSquadronFromDefender(Defender)
-if Squadron then
-self:F({SquadronName=Squadron.Name})
-local LandingMethod=self:GetSquadronLanding(Squadron.Name)
-if LandingMethod==AI_A2A_DISPATCHER.Landing.AtRunway then
-local DefenderSize=Defender:GetSize()
-if DefenderSize==1 then
-self:RemoveDefenderFromSquadron(Squadron,Defender)
-end
-DefenderUnit:Destroy()
-self:ParkDefender(Squadron)
-return
-end
-if DefenderUnit:GetLife()~=DefenderUnit:GetLife0()then
-DefenderUnit:Destroy()
-return
-end
-end
-end
-function AI_A2A_DISPATCHER:OnEventEngineShutdown(EventData)
-local DefenderUnit=EventData.IniUnit
-local Defender=EventData.IniGroup
-local Squadron=self:GetSquadronFromDefender(Defender)
-if Squadron then
-self:F({SquadronName=Squadron.Name})
-local LandingMethod=self:GetSquadronLanding(Squadron.Name)
-if LandingMethod==AI_A2A_DISPATCHER.Landing.AtEngineShutdown and not DefenderUnit:InAir()then
-local DefenderSize=Defender:GetSize()
-if DefenderSize==1 then
-self:RemoveDefenderFromSquadron(Squadron,Defender)
-end
-DefenderUnit:Destroy()
-self:ParkDefender(Squadron)
-end
-end
-end
-function AI_A2A_DISPATCHER:SetEngageRadius(EngageRadius)
-self.Detection:SetFriendliesRange(EngageRadius or 100000)
-return self
-end
-function AI_A2A_DISPATCHER:SetDisengageRadius(DisengageRadius)
-self.DisengageRadius=DisengageRadius or 300000
-return self
-end
-function AI_A2A_DISPATCHER:SetGciRadius(GciRadius)
-self.GciRadius=GciRadius or 200000
-return self
-end
-function AI_A2A_DISPATCHER:SetBorderZone(BorderZone)
-self.Detection:SetAcceptZones(BorderZone)
-return self
-end
-function AI_A2A_DISPATCHER:SetTacticalDisplay(TacticalDisplay)
-self.TacticalDisplay=TacticalDisplay
-return self
-end
-function AI_A2A_DISPATCHER:SetDefaultDamageThreshold(DamageThreshold)
-self.DefenderDefault.DamageThreshold=DamageThreshold
-return self
-end
-function AI_A2A_DISPATCHER:SetDefaultCapTimeInterval(CapMinSeconds,CapMaxSeconds)
-self.DefenderDefault.CapMinSeconds=CapMinSeconds
-self.DefenderDefault.CapMaxSeconds=CapMaxSeconds
-return self
-end
-function AI_A2A_DISPATCHER:SetDefaultCapLimit(CapLimit)
-self.DefenderDefault.CapLimit=CapLimit
-return self
-end
-function AI_A2A_DISPATCHER:SetIntercept(InterceptDelay)
-self.DefenderDefault.InterceptDelay=InterceptDelay
-local Detection=self.Detection
-Detection:SetIntercept(true,InterceptDelay)
-return self
-end
-function AI_A2A_DISPATCHER:GetAIFriendliesNearBy(DetectedItem)
-local FriendliesNearBy=self.Detection:GetFriendliesDistance(DetectedItem)
-return FriendliesNearBy
-end
-function AI_A2A_DISPATCHER:GetDefenderTasks()
-return self.DefenderTasks or{}
-end
-function AI_A2A_DISPATCHER:GetDefenderTask(Defender)
-return self.DefenderTasks[Defender]
-end
-function AI_A2A_DISPATCHER:GetDefenderTaskFsm(Defender)
-return self:GetDefenderTask(Defender).Fsm
-end
-function AI_A2A_DISPATCHER:GetDefenderTaskTarget(Defender)
-return self:GetDefenderTask(Defender).Target
-end
-function AI_A2A_DISPATCHER:GetDefenderTaskSquadronName(Defender)
-return self:GetDefenderTask(Defender).SquadronName
-end
-function AI_A2A_DISPATCHER:ClearDefenderTask(Defender)
-if Defender and Defender:IsAlive()and self.DefenderTasks[Defender]then
-local Target=self.DefenderTasks[Defender].Target
-local Message="Clearing ("..self.DefenderTasks[Defender].Type..") "
-Message=Message..Defender:GetName()
-if Target then
-Message=Message..(Target and(" from "..Target.Index.." ["..Target.Set:Count().."]"))or""
-end
-self:F({Target=Message})
-end
-self.DefenderTasks[Defender]=nil
-return self
-end
-function AI_A2A_DISPATCHER:ClearDefenderTaskTarget(Defender)
-local DefenderTask=self:GetDefenderTask(Defender)
-if Defender and Defender:IsAlive()and DefenderTask then
-local Target=DefenderTask.Target
-local Message="Clearing ("..DefenderTask.Type..") "
-Message=Message..Defender:GetName()
-if Target then
-Message=Message..((Target and(" from "..Target.Index.." ["..Target.Set:Count().."]"))or"")
-end
-self:F({Target=Message})
-end
-if Defender and DefenderTask and DefenderTask.Target then
-DefenderTask.Target=nil
-end
-return self
-end
-function AI_A2A_DISPATCHER:SetDefenderTask(SquadronName,Defender,Type,Fsm,Target)
-self:F({SquadronName=SquadronName,Defender=Defender:GetName(),Type=Type,Target=Target})
-self.DefenderTasks[Defender]=self.DefenderTasks[Defender]or{}
-self.DefenderTasks[Defender].Type=Type
-self.DefenderTasks[Defender].Fsm=Fsm
-self.DefenderTasks[Defender].SquadronName=SquadronName
-if Target then
-self:SetDefenderTaskTarget(Defender,Target)
-end
-return self
-end
-function AI_A2A_DISPATCHER:SetDefenderTaskTarget(Defender,AttackerDetection)
-local Message="("..self.DefenderTasks[Defender].Type..") "
-Message=Message..Defender:GetName()
-Message=Message..((AttackerDetection and(" target "..AttackerDetection.Index.." ["..AttackerDetection.Set:Count().."]"))or"")
-self:F({AttackerDetection=Message})
-if AttackerDetection then
-self.DefenderTasks[Defender].Target=AttackerDetection
-end
-return self
-end
-function AI_A2A_DISPATCHER:SetSquadron(SquadronName,AirbaseName,TemplatePrefixes,ResourceCount)
-self.DefenderSquadrons[SquadronName]=self.DefenderSquadrons[SquadronName]or{}
-local DefenderSquadron=self.DefenderSquadrons[SquadronName]
-DefenderSquadron.Name=SquadronName
-DefenderSquadron.Airbase=AIRBASE:FindByName(AirbaseName)
-DefenderSquadron.AirbaseName=DefenderSquadron.Airbase:GetName()
-if not DefenderSquadron.Airbase then
-error("Cannot find airbase with name:"..AirbaseName)
-end
-DefenderSquadron.Spawn={}
-if type(TemplatePrefixes)=="string"then
-local SpawnTemplate=TemplatePrefixes
-self.DefenderSpawns[SpawnTemplate]=self.DefenderSpawns[SpawnTemplate]or SPAWN:New(SpawnTemplate)
-DefenderSquadron.Spawn[1]=self.DefenderSpawns[SpawnTemplate]
-else
-for TemplateID,SpawnTemplate in pairs(TemplatePrefixes)do
-self.DefenderSpawns[SpawnTemplate]=self.DefenderSpawns[SpawnTemplate]or SPAWN:New(SpawnTemplate)
-DefenderSquadron.Spawn[#DefenderSquadron.Spawn+1]=self.DefenderSpawns[SpawnTemplate]
-end
-end
-DefenderSquadron.ResourceCount=ResourceCount
-DefenderSquadron.TemplatePrefixes=TemplatePrefixes
-DefenderSquadron.Captured=false
-self:SetSquadronLanguage(SquadronName,"EN")
-self:F({Squadron={SquadronName,AirbaseName,TemplatePrefixes,ResourceCount}})
-return self
-end
-function AI_A2A_DISPATCHER:GetSquadron(SquadronName)
-local DefenderSquadron=self.DefenderSquadrons[SquadronName]
-if not DefenderSquadron then
-error("Unknown Squadron:"..SquadronName)
-end
-return DefenderSquadron
-end
-function AI_A2A_DISPATCHER:QuerySquadron(Squadron)
-local Squadron=self:GetSquadron(Squadron)
-if Squadron.ResourceCount then
-self:T2(string.format("%s = %s",Squadron.Name,Squadron.ResourceCount))
-return Squadron.ResourceCount
-end
-self:F({Squadron=Squadron.Name,SquadronResourceCount=Squadron.ResourceCount})
-return nil
-end
-function AI_A2A_DISPATCHER:SetSquadronVisible(SquadronName)
-self.DefenderSquadrons[SquadronName]=self.DefenderSquadrons[SquadronName]or{}
-local DefenderSquadron=self:GetSquadron(SquadronName)
-DefenderSquadron.Uncontrolled=true
-DefenderSquadron.Grouping=1
-local nfreeparking=DefenderSquadron.Airbase:GetFreeParkingSpotsNumber(AIRBASE.TerminalType.FighterAircraft,true)
-DefenderSquadron.ResourceCount=DefenderSquadron.ResourceCount or nfreeparking
-DefenderSquadron.ResourceCount=math.min(DefenderSquadron.ResourceCount,nfreeparking)
-for SpawnTemplate,_DefenderSpawn in pairs(self.DefenderSpawns)do
-local DefenderSpawn=_DefenderSpawn
-DefenderSpawn:InitUnControlled(true)
-end
-end
-function AI_A2A_DISPATCHER:IsSquadronVisible(SquadronName)
-self.DefenderSquadrons[SquadronName]=self.DefenderSquadrons[SquadronName]or{}
-local DefenderSquadron=self:GetSquadron(SquadronName)
-if DefenderSquadron then
-return DefenderSquadron.Uncontrolled==true
-end
-return nil
-end
-function AI_A2A_DISPATCHER:SetSquadronCap2(SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType,Zone,PatrolMinSpeed,PatrolMaxSpeed,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolAltType)
-self.DefenderSquadrons[SquadronName]=self.DefenderSquadrons[SquadronName]or{}
-self.DefenderSquadrons[SquadronName].Cap=self.DefenderSquadrons[SquadronName].Cap or{}
-local DefenderSquadron=self:GetSquadron(SquadronName)
-local Cap=self.DefenderSquadrons[SquadronName].Cap
-Cap.Name=SquadronName
-Cap.EngageMinSpeed=EngageMinSpeed
-Cap.EngageMaxSpeed=EngageMaxSpeed
-Cap.EngageFloorAltitude=EngageFloorAltitude
-Cap.EngageCeilingAltitude=EngageCeilingAltitude
-Cap.Zone=Zone
-Cap.PatrolMinSpeed=PatrolMinSpeed
-Cap.PatrolMaxSpeed=PatrolMaxSpeed
-Cap.PatrolFloorAltitude=PatrolFloorAltitude
-Cap.PatrolCeilingAltitude=PatrolCeilingAltitude
-Cap.PatrolAltType=PatrolAltType
-Cap.EngageAltType=EngageAltType
-self:SetSquadronCapInterval(SquadronName,self.DefenderDefault.CapLimit,self.DefenderDefault.CapMinSeconds,self.DefenderDefault.CapMaxSeconds,1)
-self:I({CAP={SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,Zone,PatrolMinSpeed,PatrolMaxSpeed,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolAltType,EngageAltType}})
-local RecceSet=self.Detection:GetDetectionSet()
-RecceSet:FilterPrefixes(DefenderSquadron.TemplatePrefixes)
-RecceSet:FilterStart()
-self.Detection:SetFriendlyPrefixes(DefenderSquadron.TemplatePrefixes)
-return self
-end
-function AI_A2A_DISPATCHER:SetSquadronCap(SquadronName,Zone,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,EngageMinSpeed,EngageMaxSpeed,AltType)
-return self:SetSquadronCap2(SquadronName,EngageMinSpeed,EngageMaxSpeed,PatrolFloorAltitude,PatrolCeilingAltitude,AltType,Zone,PatrolMinSpeed,PatrolMaxSpeed,PatrolFloorAltitude,PatrolCeilingAltitude,AltType)
-end
-function AI_A2A_DISPATCHER:SetSquadronCapInterval(SquadronName,CapLimit,LowInterval,HighInterval,Probability)
-self.DefenderSquadrons[SquadronName]=self.DefenderSquadrons[SquadronName]or{}
-self.DefenderSquadrons[SquadronName].Cap=self.DefenderSquadrons[SquadronName].Cap or{}
-local DefenderSquadron=self:GetSquadron(SquadronName)
-local Cap=self.DefenderSquadrons[SquadronName].Cap
-if Cap then
-Cap.LowInterval=LowInterval or 180
-Cap.HighInterval=HighInterval or 600
-Cap.Probability=Probability or 1
-Cap.CapLimit=CapLimit or 1
-Cap.Scheduler=Cap.Scheduler or SCHEDULER:New(self)
-local Scheduler=Cap.Scheduler
-local ScheduleID=Cap.ScheduleID
-local Variance=(Cap.HighInterval-Cap.LowInterval)/2
-local Repeat=Cap.LowInterval+Variance
-local Randomization=Variance/Repeat
-local Start=math.random(1,Cap.HighInterval)
-if ScheduleID then
-Scheduler:Stop(ScheduleID)
-end
-Cap.ScheduleID=Scheduler:Schedule(self,self.SchedulerCAP,{SquadronName},Start,Repeat,Randomization)
-else
-error("This squadron does not exist:"..SquadronName)
-end
-end
-function AI_A2A_DISPATCHER:GetCAPDelay(SquadronName)
-self.DefenderSquadrons[SquadronName]=self.DefenderSquadrons[SquadronName]or{}
-self.DefenderSquadrons[SquadronName].Cap=self.DefenderSquadrons[SquadronName].Cap or{}
-local DefenderSquadron=self:GetSquadron(SquadronName)
-local Cap=self.DefenderSquadrons[SquadronName].Cap
-if Cap then
-return math.random(Cap.LowInterval,Cap.HighInterval)
-else
-error("This squadron does not exist:"..SquadronName)
-end
-end
-function AI_A2A_DISPATCHER:CanCAP(SquadronName)
-self:F({SquadronName=SquadronName})
-self.DefenderSquadrons[SquadronName]=self.DefenderSquadrons[SquadronName]or{}
-self.DefenderSquadrons[SquadronName].Cap=self.DefenderSquadrons[SquadronName].Cap or{}
-local DefenderSquadron=self:GetSquadron(SquadronName)
-if DefenderSquadron.Captured==false then
-if(not DefenderSquadron.ResourceCount)or(DefenderSquadron.ResourceCount and DefenderSquadron.ResourceCount>0)then
-local Cap=DefenderSquadron.Cap
-if Cap then
-local CapCount=self:CountCapAirborne(SquadronName)
-self:F({CapCount=CapCount})
-if CapCount0)then
-local Gci=DefenderSquadron.Gci
-if Gci then
-return DefenderSquadron
-end
-end
-end
-return nil
-end
-function AI_A2A_DISPATCHER:SetSquadronGci2(SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType)
-self.DefenderSquadrons[SquadronName]=self.DefenderSquadrons[SquadronName]or{}
-self.DefenderSquadrons[SquadronName].Gci=self.DefenderSquadrons[SquadronName].Gci or{}
-local Intercept=self.DefenderSquadrons[SquadronName].Gci
-Intercept.Name=SquadronName
-Intercept.EngageMinSpeed=EngageMinSpeed
-Intercept.EngageMaxSpeed=EngageMaxSpeed
-Intercept.EngageFloorAltitude=EngageFloorAltitude
-Intercept.EngageCeilingAltitude=EngageCeilingAltitude
-Intercept.EngageAltType=EngageAltType
-self:I({GCI={SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType}})
-end
-function AI_A2A_DISPATCHER:SetSquadronGci(SquadronName,EngageMinSpeed,EngageMaxSpeed)
-self.DefenderSquadrons[SquadronName]=self.DefenderSquadrons[SquadronName]or{}
-self.DefenderSquadrons[SquadronName].Gci=self.DefenderSquadrons[SquadronName].Gci or{}
-local Intercept=self.DefenderSquadrons[SquadronName].Gci
-Intercept.Name=SquadronName
-Intercept.EngageMinSpeed=EngageMinSpeed
-Intercept.EngageMaxSpeed=EngageMaxSpeed
-self:F({GCI={SquadronName,EngageMinSpeed,EngageMaxSpeed}})
-end
-function AI_A2A_DISPATCHER:SetDefaultOverhead(Overhead)
-self.DefenderDefault.Overhead=Overhead
-return self
-end
-function AI_A2A_DISPATCHER:SetSquadronOverhead(SquadronName,Overhead)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-DefenderSquadron.Overhead=Overhead
-return self
-end
-function AI_A2A_DISPATCHER:SetDefaultGrouping(Grouping)
-self.DefenderDefault.Grouping=Grouping
-return self
-end
-function AI_A2A_DISPATCHER:SetSquadronGrouping(SquadronName,Grouping)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-DefenderSquadron.Grouping=Grouping
-return self
-end
-function AI_A2A_DISPATCHER:SetDefaultTakeoff(Takeoff)
-self.DefenderDefault.Takeoff=Takeoff
-return self
-end
-function AI_A2A_DISPATCHER:SetSquadronTakeoff(SquadronName,Takeoff)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-DefenderSquadron.Takeoff=Takeoff
-return self
-end
-function AI_A2A_DISPATCHER:GetDefaultTakeoff()
-return self.DefenderDefault.Takeoff
-end
-function AI_A2A_DISPATCHER:GetSquadronTakeoff(SquadronName)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-return DefenderSquadron.Takeoff or self.DefenderDefault.Takeoff
-end
-function AI_A2A_DISPATCHER:SetDefaultTakeoffInAir()
-self:SetDefaultTakeoff(AI_A2A_DISPATCHER.Takeoff.Air)
-return self
-end
-function AI_A2A_DISPATCHER:SetSendMessages(onoff)
-self.SetSendPlayerMessages=onoff
-end
-function AI_A2A_DISPATCHER:SetSquadronTakeoffInAir(SquadronName,TakeoffAltitude)
-self:SetSquadronTakeoff(SquadronName,AI_A2A_DISPATCHER.Takeoff.Air)
-if TakeoffAltitude then
-self:SetSquadronTakeoffInAirAltitude(SquadronName,TakeoffAltitude)
-end
-return self
-end
-function AI_A2A_DISPATCHER:SetDefaultTakeoffFromRunway()
-self:SetDefaultTakeoff(AI_A2A_DISPATCHER.Takeoff.Runway)
-return self
-end
-function AI_A2A_DISPATCHER:SetSquadronTakeoffFromRunway(SquadronName)
-self:SetSquadronTakeoff(SquadronName,AI_A2A_DISPATCHER.Takeoff.Runway)
-return self
-end
-function AI_A2A_DISPATCHER:SetDefaultTakeoffFromParkingHot()
-self:SetDefaultTakeoff(AI_A2A_DISPATCHER.Takeoff.Hot)
-return self
-end
-function AI_A2A_DISPATCHER:SetSquadronTakeoffFromParkingHot(SquadronName)
-self:SetSquadronTakeoff(SquadronName,AI_A2A_DISPATCHER.Takeoff.Hot)
-return self
-end
-function AI_A2A_DISPATCHER:SetDefaultTakeoffFromParkingCold()
-self:SetDefaultTakeoff(AI_A2A_DISPATCHER.Takeoff.Cold)
-return self
-end
-function AI_A2A_DISPATCHER:SetSquadronTakeoffFromParkingCold(SquadronName)
-self:SetSquadronTakeoff(SquadronName,AI_A2A_DISPATCHER.Takeoff.Cold)
-return self
-end
-function AI_A2A_DISPATCHER:SetDefaultTakeoffInAirAltitude(TakeoffAltitude)
-self.DefenderDefault.TakeoffAltitude=TakeoffAltitude
-return self
-end
-function AI_A2A_DISPATCHER:SetSquadronTakeoffInAirAltitude(SquadronName,TakeoffAltitude)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-DefenderSquadron.TakeoffAltitude=TakeoffAltitude
-return self
-end
-function AI_A2A_DISPATCHER:SetDefaultLanding(Landing)
-self.DefenderDefault.Landing=Landing
-return self
-end
-function AI_A2A_DISPATCHER:SetSquadronLanding(SquadronName,Landing)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-DefenderSquadron.Landing=Landing
-return self
-end
-function AI_A2A_DISPATCHER:GetDefaultLanding()
-return self.DefenderDefault.Landing
-end
-function AI_A2A_DISPATCHER:GetSquadronLanding(SquadronName)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-return DefenderSquadron.Landing or self.DefenderDefault.Landing
-end
-function AI_A2A_DISPATCHER:SetDefaultLandingNearAirbase()
-self:SetDefaultLanding(AI_A2A_DISPATCHER.Landing.NearAirbase)
-return self
-end
-function AI_A2A_DISPATCHER:SetSquadronLandingNearAirbase(SquadronName)
-self:SetSquadronLanding(SquadronName,AI_A2A_DISPATCHER.Landing.NearAirbase)
-return self
-end
-function AI_A2A_DISPATCHER:SetDefaultLandingAtRunway()
-self:SetDefaultLanding(AI_A2A_DISPATCHER.Landing.AtRunway)
-return self
-end
-function AI_A2A_DISPATCHER:SetSquadronLandingAtRunway(SquadronName)
-self:SetSquadronLanding(SquadronName,AI_A2A_DISPATCHER.Landing.AtRunway)
-return self
-end
-function AI_A2A_DISPATCHER:SetDefaultLandingAtEngineShutdown()
-self:SetDefaultLanding(AI_A2A_DISPATCHER.Landing.AtEngineShutdown)
-return self
-end
-function AI_A2A_DISPATCHER:SetSquadronLandingAtEngineShutdown(SquadronName)
-self:SetSquadronLanding(SquadronName,AI_A2A_DISPATCHER.Landing.AtEngineShutdown)
-return self
-end
-function AI_A2A_DISPATCHER:SetDefaultFuelThreshold(FuelThreshold)
-self.DefenderDefault.FuelThreshold=FuelThreshold
-return self
-end
-function AI_A2A_DISPATCHER:SetSquadronFuelThreshold(SquadronName,FuelThreshold)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-DefenderSquadron.FuelThreshold=FuelThreshold
-return self
-end
-function AI_A2A_DISPATCHER:SetDefaultTanker(TankerName)
-self.DefenderDefault.TankerName=TankerName
-return self
-end
-function AI_A2A_DISPATCHER:SetSquadronTanker(SquadronName,TankerName)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-DefenderSquadron.TankerName=TankerName
-return self
-end
-function AI_A2A_DISPATCHER:SetSquadronLanguage(SquadronName,Language)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-DefenderSquadron.Language=Language
-if DefenderSquadron.RadioQueue then
-DefenderSquadron.RadioQueue:SetLanguage(Language)
-end
-return self
-end
-function AI_A2A_DISPATCHER:SetSquadronRadioFrequency(SquadronName,RadioFrequency,RadioModulation,RadioPower)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-DefenderSquadron.RadioFrequency=RadioFrequency
-DefenderSquadron.RadioModulation=RadioModulation or radio.modulation.AM
-DefenderSquadron.RadioPower=RadioPower or 100
-if DefenderSquadron.RadioQueue then
-DefenderSquadron.RadioQueue:Stop()
-end
-DefenderSquadron.RadioQueue=nil
-DefenderSquadron.RadioQueue=RADIOSPEECH:New(DefenderSquadron.RadioFrequency,DefenderSquadron.RadioModulation)
-DefenderSquadron.RadioQueue.power=DefenderSquadron.RadioPower
-DefenderSquadron.RadioQueue:Start(0.5)
-DefenderSquadron.RadioQueue:SetLanguage(DefenderSquadron.Language)
-end
-function AI_A2A_DISPATCHER:AddDefenderToSquadron(Squadron,Defender,Size)
-self.Defenders=self.Defenders or{}
-local DefenderName=Defender:GetName()
-self.Defenders[DefenderName]=Squadron
-if Squadron.ResourceCount then
-Squadron.ResourceCount=Squadron.ResourceCount-Size
-end
-self:F({DefenderName=DefenderName,SquadronResourceCount=Squadron.ResourceCount})
-end
-function AI_A2A_DISPATCHER:RemoveDefenderFromSquadron(Squadron,Defender)
-self.Defenders=self.Defenders or{}
-local DefenderName=Defender:GetName()
-if Squadron.ResourceCount then
-Squadron.ResourceCount=Squadron.ResourceCount+Defender:GetSize()
-end
-self.Defenders[DefenderName]=nil
-self:F({DefenderName=DefenderName,SquadronResourceCount=Squadron.ResourceCount})
-end
-function AI_A2A_DISPATCHER:GetSquadronFromDefender(Defender)
-self.Defenders=self.Defenders or{}
-if Defender~=nil then
-local DefenderName=Defender:GetName()
-self:F({DefenderName=DefenderName})
-return self.Defenders[DefenderName]
-else
-return nil
-end
-end
-function AI_A2A_DISPATCHER:EvaluateSWEEP(DetectedItem)
-self:F({DetectedItem.ItemID})
-local DetectedSet=DetectedItem.Set
-local DetectedZone=DetectedItem.Zone
-if DetectedItem.IsDetected==false then
-local TargetSetUnit=SET_UNIT:New()
-TargetSetUnit:SetDatabase(DetectedSet)
-TargetSetUnit:FilterOnce()
-return TargetSetUnit
-end
-return nil
-end
-function AI_A2A_DISPATCHER:CountCapAirborne(SquadronName)
-local CapCount=0
-local DefenderSquadron=self.DefenderSquadrons[SquadronName]
-if DefenderSquadron then
-for AIGroup,DefenderTask in pairs(self:GetDefenderTasks())do
-if DefenderTask.SquadronName==SquadronName then
-if DefenderTask.Type=="CAP"then
-if AIGroup and AIGroup:IsAlive()then
-if DefenderTask.Fsm:Is("Patrolling")or DefenderTask.Fsm:Is("Engaging")or DefenderTask.Fsm:Is("Refuelling")or DefenderTask.Fsm:Is("Started")then
-CapCount=CapCount+1
-end
-end
-end
-end
-end
-end
-return CapCount
-end
-function AI_A2A_DISPATCHER:CountDefendersEngaged(AttackerDetection)
-local DefenderCount=0
-local DetectedSet=AttackerDetection.Set
-local DefenderTasks=self:GetDefenderTasks()
-for DefenderGroup,DefenderTask in pairs(DefenderTasks)do
-local Defender=DefenderGroup
-local DefenderTaskTarget=DefenderTask.Target
-local DefenderSquadronName=DefenderTask.SquadronName
-if DefenderTaskTarget and DefenderTaskTarget.Index==AttackerDetection.Index then
-local Squadron=self:GetSquadron(DefenderSquadronName)
-local SquadronOverhead=Squadron.Overhead or self.DefenderDefault.Overhead
-local DefenderSize=Defender:GetInitialSize()
-if DefenderSize then
-DefenderCount=DefenderCount+DefenderSize/SquadronOverhead
-self:F("Defender Group Name: "..Defender:GetName()..", Size: "..DefenderSize)
-else
-DefenderCount=0
-end
-end
-end
-self:F({DefenderCount=DefenderCount})
-return DefenderCount
-end
-function AI_A2A_DISPATCHER:CountDefendersToBeEngaged(AttackerDetection,DefenderCount)
-local Friendlies=nil
-local AttackerSet=AttackerDetection.Set
-local AttackerCount=AttackerSet:Count()
-local DefenderFriendlies=self:GetAIFriendliesNearBy(AttackerDetection)
-for FriendlyDistance,AIFriendly in UTILS.spairs(DefenderFriendlies or{})do
-if AttackerCount>DefenderCount then
-if AIFriendly then
-local classname=AIFriendly.ClassName or"No Class Name"
-local unitname=AIFriendly.IdentifiableName or"No Unit Name"
-end
-local Friendly=nil
-if AIFriendly and AIFriendly:IsAlive()then
-Friendly=AIFriendly:GetGroup()
-end
-if Friendly and Friendly:IsAlive()then
-local DefenderTask=self:GetDefenderTask(Friendly)
-if DefenderTask then
-if DefenderTask.Type=="CAP"or DefenderTask.Type=="GCI"then
-if DefenderTask.Target==nil then
-if DefenderTask.Fsm:Is("Returning")or DefenderTask.Fsm:Is("Patrolling")then
-Friendlies=Friendlies or{}
-Friendlies[Friendly]=Friendly
-DefenderCount=DefenderCount+Friendly:GetSize()
-self:F({Friendly=Friendly:GetName(),FriendlyDistance=FriendlyDistance})
-end
-end
-end
-end
-end
-else
-break
-end
-end
-return Friendlies
-end
-function AI_A2A_DISPATCHER:ResourceActivate(DefenderSquadron,DefendersNeeded)
-local SquadronName=DefenderSquadron.Name
-DefendersNeeded=DefendersNeeded or 4
-local DefenderGrouping=DefenderSquadron.Grouping or self.DefenderDefault.Grouping
-DefenderGrouping=(DefenderGrouping0 then
-local id=math.random(n)
-local Defender=self.uncontrolled[SquadronName][id].group
-Defender:StartUncontrolled()
-DefenderGrouping=self.uncontrolled[SquadronName][id].grouping
-self:AddDefenderToSquadron(DefenderSquadron,Defender,DefenderGrouping)
-table.remove(self.uncontrolled[SquadronName],id)
-return Defender,DefenderGrouping
-else
-return nil,0
-end
-local TemplateID=math.random(1,#DefenderSquadron.Spawn)
-else
-local Spawn=DefenderSquadron.Spawn[math.random(1,#DefenderSquadron.Spawn)]
-if DefenderGrouping then
-Spawn:InitGrouping(DefenderGrouping)
-else
-Spawn:InitGrouping()
-end
-local TakeoffMethod=self:GetSquadronTakeoff(SquadronName)
-local Defender=Spawn:SpawnAtAirbase(DefenderSquadron.Airbase,TakeoffMethod,DefenderSquadron.TakeoffAltitude or self.DefenderDefault.TakeoffAltitude)
-self:AddDefenderToSquadron(DefenderSquadron,Defender,DefenderGrouping)
-return Defender,DefenderGrouping
-end
-return nil,nil
-end
-function AI_A2A_DISPATCHER:onafterCAP(From,Event,To,SquadronName)
-self:F({SquadronName=SquadronName})
-self.DefenderSquadrons[SquadronName]=self.DefenderSquadrons[SquadronName]or{}
-self.DefenderSquadrons[SquadronName].Cap=self.DefenderSquadrons[SquadronName].Cap or{}
-local DefenderSquadron=self:CanCAP(SquadronName)
-if DefenderSquadron then
-local Cap=DefenderSquadron.Cap
-if Cap then
-local DefenderCAP,DefenderGrouping=self:ResourceActivate(DefenderSquadron)
-if DefenderCAP then
-local AI_A2A_Fsm=AI_A2A_CAP:New2(DefenderCAP,Cap.EngageMinSpeed,Cap.EngageMaxSpeed,Cap.EngageFloorAltitude,Cap.EngageCeilingAltitude,Cap.EngageAltType,Cap.Zone,Cap.PatrolMinSpeed,Cap.PatrolMaxSpeed,Cap.PatrolFloorAltitude,Cap.PatrolCeilingAltitude,Cap.PatrolAltType)
-AI_A2A_Fsm:SetDispatcher(self)
-AI_A2A_Fsm:SetHomeAirbase(DefenderSquadron.Airbase)
-AI_A2A_Fsm:SetFuelThreshold(DefenderSquadron.FuelThreshold or self.DefenderDefault.FuelThreshold,60)
-AI_A2A_Fsm:SetDamageThreshold(self.DefenderDefault.DamageThreshold)
-AI_A2A_Fsm:SetDisengageRadius(self.DisengageRadius)
-AI_A2A_Fsm:SetTanker(DefenderSquadron.TankerName or self.DefenderDefault.TankerName)
-if DefenderSquadron.Racetrack or self.DefenderDefault.Racetrack then
-AI_A2A_Fsm:SetRaceTrackPattern(DefenderSquadron.RacetrackLengthMin or self.DefenderDefault.RacetrackLengthMin,
-DefenderSquadron.RacetrackLengthMax or self.DefenderDefault.RacetrackLengthMax,
-DefenderSquadron.RacetrackHeadingMin or self.DefenderDefault.RacetrackHeadingMin,
-DefenderSquadron.RacetrackHeadingMax or self.DefenderDefault.RacetrackHeadingMax,
-DefenderSquadron.RacetrackDurationMin or self.DefenderDefault.RacetrackDurationMin,
-DefenderSquadron.RacetrackDurationMax or self.DefenderDefault.RacetrackDurationMax,
-DefenderSquadron.RacetrackCoordinates or self.DefenderDefault.RacetrackCoordinates)
-end
-AI_A2A_Fsm:Start()
-self:SetDefenderTask(SquadronName,DefenderCAP,"CAP",AI_A2A_Fsm)
-function AI_A2A_Fsm:onafterTakeoff(DefenderGroup,From,Event,To)
-if DefenderGroup and DefenderGroup:IsAlive()then
-self:F({"CAP Takeoff",DefenderGroup:GetName()})
-local DefenderName=DefenderGroup:GetCallsign()
-local Dispatcher=AI_A2A_Fsm:GetDispatcher()
-local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup)
-if Squadron then
-if self.SetSendPlayerMessages then
-Dispatcher:MessageToPlayers(Squadron,DefenderName.." Wheels up.",DefenderGroup)
-end
-AI_A2A_Fsm:__Patrol(2)
-end
-end
-end
-function AI_A2A_Fsm:onafterPatrolRoute(DefenderGroup,From,Event,To)
-if DefenderGroup and DefenderGroup:IsAlive()then
-self:F({"CAP PatrolRoute",DefenderGroup:GetName()})
-self:GetParent(self).onafterPatrolRoute(self,DefenderGroup,From,Event,To)
-local DefenderName=DefenderGroup:GetCallsign()
-local Dispatcher=self:GetDispatcher()
-local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup)
-if Squadron and self.SetSendPlayerMessages then
-Dispatcher:MessageToPlayers(Squadron,DefenderName..", patrolling.",DefenderGroup)
-end
-Dispatcher:ClearDefenderTaskTarget(DefenderGroup)
-end
-end
-function AI_A2A_Fsm:onafterRTB(DefenderGroup,From,Event,To)
-if DefenderGroup and DefenderGroup:IsAlive()then
-self:F({"CAP RTB",DefenderGroup:GetName()})
-self:GetParent(self).onafterRTB(self,DefenderGroup,From,Event,To)
-local DefenderName=DefenderGroup:GetCallsign()
-local Dispatcher=self:GetDispatcher()
-local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup)
-if Squadron and self.SetSendPlayerMessages then
-Dispatcher:MessageToPlayers(Squadron,DefenderName.." returning to base.",DefenderGroup)
-end
-Dispatcher:ClearDefenderTaskTarget(DefenderGroup)
-end
-end
-function AI_A2A_Fsm:onafterHome(Defender,From,Event,To,Action)
-if Defender and Defender:IsAlive()then
-self:F({"CAP Home",Defender:GetName()})
-self:GetParent(self).onafterHome(self,Defender,From,Event,To)
-local Dispatcher=self:GetDispatcher()
-local Squadron=Dispatcher:GetSquadronFromDefender(Defender)
-if Action and Action=="Destroy"then
-Dispatcher:RemoveDefenderFromSquadron(Squadron,Defender)
-Defender:Destroy()
-end
-if Dispatcher:GetSquadronLanding(Squadron.Name)==AI_A2A_DISPATCHER.Landing.NearAirbase then
-Dispatcher:RemoveDefenderFromSquadron(Squadron,Defender)
-Defender:Destroy()
-Dispatcher:ParkDefender(Squadron)
-end
-end
-end
-end
-end
-end
-end
-function AI_A2A_DISPATCHER:onafterENGAGE(From,Event,To,AttackerDetection,Defenders)
-self:F("ENGAGING Detection ID="..tostring(AttackerDetection.ID))
-if Defenders then
-for DefenderID,Defender in pairs(Defenders)do
-local Fsm=self:GetDefenderTaskFsm(Defender)
-Fsm:EngageRoute(AttackerDetection.Set)
-self:SetDefenderTaskTarget(Defender,AttackerDetection)
-end
-end
-end
-function AI_A2A_DISPATCHER:onafterGCI(From,Event,To,AttackerDetection,DefendersMissing,DefenderFriendlies)
-self:F("GCI Detection ID="..tostring(AttackerDetection.ID))
-self:F({From,Event,To,AttackerDetection.Index,DefendersMissing,DefenderFriendlies})
-local AttackerSet=AttackerDetection.Set
-local AttackerUnit=AttackerSet:GetFirst()
-if AttackerUnit and AttackerUnit:IsAlive()then
-local AttackerCount=AttackerSet:Count()
-local DefenderCount=0
-for DefenderID,DefenderGroup in pairs(DefenderFriendlies or{})do
-local Fsm=self:GetDefenderTaskFsm(DefenderGroup)
-Fsm:__EngageRoute(0.1,AttackerSet)
-self:SetDefenderTaskTarget(DefenderGroup,AttackerDetection)
-DefenderCount=DefenderCount+DefenderGroup:GetSize()
-end
-self:F({DefenderCount=DefenderCount,DefendersMissing=DefendersMissing})
-DefenderCount=DefendersMissing
-local ClosestDistance=0
-local ClosestDefenderSquadronName=nil
-local BreakLoop=false
-while(DefenderCount>0 and not BreakLoop)do
-self:F({DefenderSquadrons=self.DefenderSquadrons})
-for SquadronName,DefenderSquadron in pairs(self.DefenderSquadrons or{})do
-self:F({GCI=DefenderSquadron.Gci})
-for InterceptID,Intercept in pairs(DefenderSquadron.Gci or{})do
-self:F({DefenderSquadron})
-local SpawnCoord=DefenderSquadron.Airbase:GetCoordinate()
-local AttackerCoord=AttackerUnit:GetCoordinate()
-local InterceptCoord=AttackerDetection.InterceptCoord
-self:F({InterceptCoord=InterceptCoord})
-if InterceptCoord then
-local InterceptDistance=SpawnCoord:Get2DDistance(InterceptCoord)
-local AirbaseDistance=SpawnCoord:Get2DDistance(AttackerCoord)
-self:F({InterceptDistance=InterceptDistance,AirbaseDistance=AirbaseDistance,InterceptCoord=InterceptCoord})
-if ClosestDistance==0 or InterceptDistanceDefenderSquadron.ResourceCount then
-DefendersNeeded=DefenderSquadron.ResourceCount
-BreakLoop=true
-end
-while(DefendersNeeded>0)do
-local DefenderGCI,DefenderGrouping=self:ResourceActivate(DefenderSquadron,DefendersNeeded)
-DefendersNeeded=DefendersNeeded-DefenderGrouping
-if DefenderGCI then
-DefenderCount=DefenderCount-DefenderGrouping/DefenderOverhead
-local Fsm=AI_A2A_GCI:New2(DefenderGCI,Gci.EngageMinSpeed,Gci.EngageMaxSpeed,Gci.EngageFloorAltitude,Gci.EngageCeilingAltitude,Gci.EngageAltType)
-Fsm:SetDispatcher(self)
-Fsm:SetHomeAirbase(DefenderSquadron.Airbase)
-Fsm:SetFuelThreshold(DefenderSquadron.FuelThreshold or self.DefenderDefault.FuelThreshold,60)
-Fsm:SetDamageThreshold(self.DefenderDefault.DamageThreshold)
-Fsm:SetDisengageRadius(self.DisengageRadius)
-Fsm:Start()
-self:SetDefenderTask(ClosestDefenderSquadronName,DefenderGCI,"GCI",Fsm,AttackerDetection)
-function Fsm:onafterTakeoff(DefenderGroup,From,Event,To)
-self:F({"GCI Birth",DefenderGroup:GetName()})
-local DefenderName=DefenderGroup:GetCallsign()
-local Dispatcher=Fsm:GetDispatcher()
-local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup)
-local DefenderTarget=Dispatcher:GetDefenderTaskTarget(DefenderGroup)
-if DefenderTarget then
-if Squadron.Language=="EN"and self.SetSendPlayerMessages then
-Dispatcher:MessageToPlayers(Squadron,DefenderName.." wheels up.",DefenderGroup)
-elseif Squadron.Language=="RU"and self.SetSendPlayerMessages then
-Dispatcher:MessageToPlayers(Squadron,DefenderName.." колёса вверх.",DefenderGroup)
-end
-Fsm:EngageRoute(DefenderTarget.Set)
-end
-end
-function Fsm:onafterEngageRoute(DefenderGroup,From,Event,To,AttackSetUnit)
-self:F({"GCI Route",DefenderGroup:GetName()})
-local DefenderName=DefenderGroup:GetCallsign()
-local Dispatcher=Fsm:GetDispatcher()
-local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup)
-if Squadron and AttackSetUnit:Count()>0 then
-local FirstUnit=AttackSetUnit:GetFirst()
-local Coordinate=FirstUnit:GetCoordinate()
-if Squadron.Language=="EN"and self.SetSendPlayerMessages then
-Dispatcher:MessageToPlayers(Squadron,DefenderName..", intercepting bogeys at "..Coordinate:ToStringA2A(DefenderGroup,nil,Squadron.Language),DefenderGroup)
-elseif Squadron.Language=="RU"and self.SetSendPlayerMessages then
-Dispatcher:MessageToPlayers(Squadron,DefenderName..", перехватывая боги в "..Coordinate:ToStringA2A(DefenderGroup,nil,Squadron.Language),DefenderGroup)
-elseif Squadron.Language=="DE"and self.SetSendPlayerMessages then
-Dispatcher:MessageToPlayers(Squadron,DefenderName..", Eindringlinge abfangen bei"..Coordinate:ToStringA2A(DefenderGroup,nil,Squadron.Language),DefenderGroup)
-end
-end
-self:GetParent(Fsm).onafterEngageRoute(self,DefenderGroup,From,Event,To,AttackSetUnit)
-end
-function Fsm:onafterEngage(DefenderGroup,From,Event,To,AttackSetUnit)
-self:F({"GCI Engage",DefenderGroup:GetName()})
-local DefenderName=DefenderGroup:GetCallsign()
-local Dispatcher=Fsm:GetDispatcher()
-local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup)
-if Squadron and AttackSetUnit:Count()>0 then
-local FirstUnit=AttackSetUnit:GetFirst()
-local Coordinate=FirstUnit:GetCoordinate()
-if Squadron.Language=="EN"and self.SetSendPlayerMessages then
-Dispatcher:MessageToPlayers(Squadron,DefenderName..", engaging bogeys at "..Coordinate:ToStringA2A(DefenderGroup,nil,Squadron.Language),DefenderGroup)
-elseif Squadron.Language=="RU"and self.SetSendPlayerMessages then
-Dispatcher:MessageToPlayers(Squadron,DefenderName..", задействуя боги в "..Coordinate:ToStringA2A(DefenderGroup,nil,Squadron.Language),DefenderGroup)
-end
-end
-self:GetParent(Fsm).onafterEngage(self,DefenderGroup,From,Event,To,AttackSetUnit)
-end
-function Fsm:onafterRTB(DefenderGroup,From,Event,To)
-self:F({"GCI RTB",DefenderGroup:GetName()})
-self:GetParent(self).onafterRTB(self,DefenderGroup,From,Event,To)
-local DefenderName=DefenderGroup:GetCallsign()
-local Dispatcher=self:GetDispatcher()
-local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup)
-if Squadron then
-if Squadron.Language=="EN"and self.SetSendPlayerMessages then
-Dispatcher:MessageToPlayers(Squadron,DefenderName.." returning to base.",DefenderGroup)
-elseif Squadron.Language=="RU"and self.SetSendPlayerMessages then
-Dispatcher:MessageToPlayers(Squadron,DefenderName..", возвращение на базу.",DefenderGroup)
-end
-end
-Dispatcher:ClearDefenderTaskTarget(DefenderGroup)
-end
-function Fsm:onafterLostControl(Defender,From,Event,To)
-self:F({"GCI LostControl",Defender:GetName()})
-self:GetParent(self).onafterHome(self,Defender,From,Event,To)
-local Dispatcher=Fsm:GetDispatcher()
-local Squadron=Dispatcher:GetSquadronFromDefender(Defender)
-if Defender:IsAboveRunway()then
-Dispatcher:RemoveDefenderFromSquadron(Squadron,Defender)
-Defender:Destroy()
-end
-end
-function Fsm:onafterHome(DefenderGroup,From,Event,To,Action)
-self:F({"GCI Home",DefenderGroup:GetName()})
-self:GetParent(self).onafterHome(self,DefenderGroup,From,Event,To)
-local DefenderName=DefenderGroup:GetCallsign()
-local Dispatcher=self:GetDispatcher()
-local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup)
-if Squadron.Language=="EN"and self.SetSendPlayerMessages then
-Dispatcher:MessageToPlayers(Squadron,DefenderName.." landing at base.",DefenderGroup)
-elseif Squadron.Language=="RU"and self.SetSendPlayerMessages then
-Dispatcher:MessageToPlayers(Squadron,DefenderName..", посадка на базу.",DefenderGroup)
-end
-if Action and Action=="Destroy"then
-Dispatcher:RemoveDefenderFromSquadron(Squadron,DefenderGroup)
-DefenderGroup:Destroy()
-end
-if Dispatcher:GetSquadronLanding(Squadron.Name)==AI_A2A_DISPATCHER.Landing.NearAirbase then
-Dispatcher:RemoveDefenderFromSquadron(Squadron,DefenderGroup)
-DefenderGroup:Destroy()
-Dispatcher:ParkDefender(Squadron)
-end
-end
-end
-end
-end
-else
-BreakLoop=true
-break
-end
-else
-break
-end
-end
-end
-end
-function AI_A2A_DISPATCHER:EvaluateENGAGE(DetectedItem)
-self:F({DetectedItem.ItemID})
-local DefenderCount=self:CountDefendersEngaged(DetectedItem)
-local DefenderGroups=self:CountDefendersToBeEngaged(DetectedItem,DefenderCount)
-self:F({DefenderCount=DefenderCount})
-if DefenderGroups and DetectedItem.IsDetected==true then
-return DefenderGroups
-end
-return nil
-end
-function AI_A2A_DISPATCHER:EvaluateGCI(DetectedItem)
-self:F({DetectedItem.ItemID})
-local AttackerSet=DetectedItem.Set
-local AttackerCount=AttackerSet:Count()
-local DefenderCount=self:CountDefendersEngaged(DetectedItem)
-local DefendersMissing=AttackerCount-DefenderCount
-self:F({AttackerCount=AttackerCount,DefenderCount=DefenderCount,DefendersMissing=DefendersMissing})
-local Friendlies=self:CountDefendersToBeEngaged(DetectedItem,DefenderCount)
-if DetectedItem.IsDetected==true then
-return DefendersMissing,Friendlies
-end
-return nil,nil
-end
-function AI_A2A_DISPATCHER:Order(DetectedItem)
-local detection=self.Detection
-local ShortestDistance=999999999
-local AttackCoordinate=detection:GetDetectedItemCoordinate(DetectedItem)
-if AttackCoordinate then
-for DefenderSquadronName,DefenderSquadron in pairs(self.DefenderSquadrons)do
-self:T({DefenderSquadron=DefenderSquadron.Name})
-local Airbase=DefenderSquadron.Airbase
-local AirbaseCoordinate=Airbase:GetCoordinate()
-local EvaluateDistance=AttackCoordinate:Get2DDistance(AirbaseCoordinate)
-if EvaluateDistance<=ShortestDistance then
-ShortestDistance=EvaluateDistance
-end
-end
-end
-return ShortestDistance
-end
-function AI_A2A_DISPATCHER:ShowTacticalDisplay(Detection)
-local AreaMsg={}
-local TaskMsg={}
-local ChangeMsg={}
-local TaskReport=REPORT:New()
-local Report=REPORT:New("Tactical Overview:")
-local DefenderGroupCount=0
-for DetectedItemID,DetectedItem in UTILS.spairs(Detection:GetDetectedItems(),function(t,a,b)
-return self:Order(t[a])0 then
-self:F({DefendersMissing=DefendersMissing})
-self:GCI(DetectedItem,DefendersMissing,Friendlies)
-end
-end
-end
-if self.TacticalDisplay then
-self:ShowTacticalDisplay(Detection)
-end
-return true
-end
-end
-do
-function AI_A2A_DISPATCHER:GetPlayerFriendliesNearBy(DetectedItem)
-local DetectedSet=DetectedItem.Set
-local PlayersNearBy=self.Detection:GetPlayersNearBy(DetectedItem)
-local PlayerTypes={}
-local PlayersCount=0
-if PlayersNearBy then
-local DetectedTreatLevel=DetectedSet:CalculateThreatLevelA2G()
-for PlayerUnitName,PlayerUnitData in pairs(PlayersNearBy)do
-local PlayerUnit=PlayerUnitData
-local PlayerName=PlayerUnit:GetPlayerName()
-if PlayerUnit:IsAirPlane()and PlayerName~=nil then
-local FriendlyUnitThreatLevel=PlayerUnit:GetThreatLevel()
-PlayersCount=PlayersCount+1
-local PlayerType=PlayerUnit:GetTypeName()
-PlayerTypes[PlayerName]=PlayerType
-if DetectedTreatLevel0 then
-for PlayerName,PlayerType in pairs(PlayerTypes)do
-PlayerTypesReport:Add(string.format('"%s" in %s',PlayerName,PlayerType))
-end
-else
-PlayerTypesReport:Add("-")
-end
-return PlayersCount,PlayerTypesReport
-end
-function AI_A2A_DISPATCHER:GetFriendliesNearBy(DetectedItem)
-local DetectedSet=DetectedItem.Set
-local FriendlyUnitsNearBy=self.Detection:GetFriendliesNearBy(DetectedItem)
-local FriendlyTypes={}
-local FriendliesCount=0
-if FriendlyUnitsNearBy then
-local DetectedTreatLevel=DetectedSet:CalculateThreatLevelA2G()
-for FriendlyUnitName,FriendlyUnitData in pairs(FriendlyUnitsNearBy)do
-local FriendlyUnit=FriendlyUnitData
-if FriendlyUnit:IsAirPlane()then
-local FriendlyUnitThreatLevel=FriendlyUnit:GetThreatLevel()
-FriendliesCount=FriendliesCount+1
-local FriendlyType=FriendlyUnit:GetTypeName()
-FriendlyTypes[FriendlyType]=FriendlyTypes[FriendlyType]and(FriendlyTypes[FriendlyType]+1)or 1
-if DetectedTreatLevel0 then
-for FriendlyType,FriendlyTypeCount in pairs(FriendlyTypes)do
-FriendlyTypesReport:Add(string.format("%d of %s",FriendlyTypeCount,FriendlyType))
-end
-else
-FriendlyTypesReport:Add("-")
-end
-return FriendliesCount,FriendlyTypesReport
-end
-function AI_A2A_DISPATCHER:SchedulerCAP(SquadronName)
-self:CAP(SquadronName)
-end
-function AI_A2A_DISPATCHER:AddToSquadron(Squadron,Amount)
-local Squadron=self:GetSquadron(Squadron)
-if Squadron.ResourceCount then
-Squadron.ResourceCount=Squadron.ResourceCount+Amount
-end
-self:T({Squadron=Squadron.Name,SquadronResourceCount=Squadron.ResourceCount})
-end
-function AI_A2A_DISPATCHER:RemoveFromSquadron(Squadron,Amount)
-local Squadron=self:GetSquadron(Squadron)
-if Squadron.ResourceCount then
-Squadron.ResourceCount=Squadron.ResourceCount-Amount
-end
-self:T({Squadron=Squadron.Name,SquadronResourceCount=Squadron.ResourceCount})
-end
-end
-do
-AI_A2A_GCICAP={
-ClassName="AI_A2A_GCICAP",
-Detection=nil,
-}
-function AI_A2A_GCICAP:New(EWRPrefixes,TemplatePrefixes,CapPrefixes,CapLimit,GroupingRadius,EngageRadius,GciRadius,ResourceCount)
-local EWRSetGroup=SET_GROUP:New()
-EWRSetGroup:FilterPrefixes(EWRPrefixes)
-EWRSetGroup:FilterStart()
-local Detection=DETECTION_AREAS:New(EWRSetGroup,GroupingRadius or 30000)
-local self=BASE:Inherit(self,AI_A2A_DISPATCHER:New(Detection))
-self:SetEngageRadius(EngageRadius)
-self:SetGciRadius(GciRadius)
-local EWRFirst=EWRSetGroup:GetFirst()
-local EWRCoalition=EWRFirst:GetCoalition()
-local AirbaseNames={}
-for AirbaseID,AirbaseData in pairs(_DATABASE.AIRBASES)do
-local Airbase=AirbaseData
-local AirbaseName=Airbase:GetName()
-if Airbase:GetCoalition()==EWRCoalition then
-table.insert(AirbaseNames,AirbaseName)
-end
-end
-self.Templates=SET_GROUP:New():FilterPrefixes(TemplatePrefixes):FilterOnce()
-self:I({Airbases=AirbaseNames})
-self:I("Defining Templates for Airbases ...")
-for AirbaseID,AirbaseName in pairs(AirbaseNames)do
-local Airbase=_DATABASE:FindAirbase(AirbaseName)
-local AirbaseName=Airbase:GetName()
-local AirbaseCoord=Airbase:GetCoordinate()
-local AirbaseZone=ZONE_RADIUS:New("Airbase",AirbaseCoord:GetVec2(),3000)
-local Templates=nil
-self:I({Airbase=AirbaseName})
-for TemplateID,Template in pairs(self.Templates:GetSet())do
-local Template=Template
-local TemplateCoord=Template:GetCoordinate()
-if AirbaseZone:IsVec2InZone(TemplateCoord:GetVec2())then
-Templates=Templates or{}
-table.insert(Templates,Template:GetName())
-self:I({Template=Template:GetName()})
-end
-end
-if Templates then
-self:SetSquadron(AirbaseName,AirbaseName,Templates,ResourceCount)
-end
-end
-self.CAPTemplates=SET_GROUP:New()
-self.CAPTemplates:FilterPrefixes(CapPrefixes)
-self.CAPTemplates:FilterOnce()
-self:I("Setting up CAP ...")
-for CAPID,CAPTemplate in pairs(self.CAPTemplates:GetSet())do
-local CAPZone=ZONE_POLYGON:New(CAPTemplate:GetName(),CAPTemplate)
-local AirbaseDistance=99999999
-local AirbaseClosest=nil
-self:I({CAPZoneGroup=CAPID})
-for AirbaseID,AirbaseName in pairs(AirbaseNames)do
-local Airbase=_DATABASE:FindAirbase(AirbaseName)
-local AirbaseName=Airbase:GetName()
-local AirbaseCoord=Airbase:GetCoordinate()
-local Squadron=self.DefenderSquadrons[AirbaseName]
-if Squadron then
-local Distance=AirbaseCoord:Get2DDistance(CAPZone:GetCoordinate())
-self:I({AirbaseDistance=Distance})
-if Distance0)then
-local Patrol=DefenderSquadron[DefenseTaskType]
-if Patrol and Patrol.Patrol==true then
-local PatrolCount=self:CountPatrolAirborne(SquadronName,DefenseTaskType)
-self:F({PatrolCount=PatrolCount,PatrolLimit=Patrol.PatrolLimit,PatrolProbability=Patrol.Probability})
-if PatrolCount0)then
-if DefenderSquadron[DefenseTaskType]and(DefenderSquadron[DefenseTaskType].Defend==true)then
-return DefenderSquadron,DefenderSquadron[DefenseTaskType]
-end
-end
-end
-return nil
-end
-function AI_A2G_DISPATCHER:SetSquadronEngageLimit(SquadronName,EngageLimit,DefenseTaskType)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-local Defense=DefenderSquadron[DefenseTaskType]
-if Defense then
-Defense.EngageLimit=EngageLimit or 1
-else
-error("This squadron does not exist:"..SquadronName)
-end
-end
-function AI_A2G_DISPATCHER:SetSquadronSead2(SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-DefenderSquadron.SEAD=DefenderSquadron.SEAD or{}
-local Sead=DefenderSquadron.SEAD
-Sead.Name=SquadronName
-Sead.EngageMinSpeed=EngageMinSpeed
-Sead.EngageMaxSpeed=EngageMaxSpeed
-Sead.EngageFloorAltitude=EngageFloorAltitude or 500
-Sead.EngageCeilingAltitude=EngageCeilingAltitude or 1000
-Sead.EngageAltType=EngageAltType
-Sead.Defend=true
-self:I({SEAD={SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType}})
-return self
-end
-function AI_A2G_DISPATCHER:SetSquadronSead(SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude)
-return self:SetSquadronSead2(SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,"RADIO")
-end
-function AI_A2G_DISPATCHER:SetSquadronSeadEngageLimit(SquadronName,EngageLimit)
-self:SetSquadronEngageLimit(SquadronName,EngageLimit,"SEAD")
-end
-function AI_A2G_DISPATCHER:SetSquadronSeadPatrol2(SquadronName,Zone,PatrolMinSpeed,PatrolMaxSpeed,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolAltType,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-DefenderSquadron.SEAD=DefenderSquadron.SEAD or{}
-local SeadPatrol=DefenderSquadron.SEAD
-SeadPatrol.Name=SquadronName
-SeadPatrol.Zone=Zone
-SeadPatrol.PatrolFloorAltitude=PatrolFloorAltitude
-SeadPatrol.PatrolCeilingAltitude=PatrolCeilingAltitude
-SeadPatrol.EngageFloorAltitude=EngageFloorAltitude
-SeadPatrol.EngageCeilingAltitude=EngageCeilingAltitude
-SeadPatrol.PatrolMinSpeed=PatrolMinSpeed
-SeadPatrol.PatrolMaxSpeed=PatrolMaxSpeed
-SeadPatrol.EngageMinSpeed=EngageMinSpeed
-SeadPatrol.EngageMaxSpeed=EngageMaxSpeed
-SeadPatrol.PatrolAltType=PatrolAltType
-SeadPatrol.EngageAltType=EngageAltType
-SeadPatrol.Patrol=true
-self:SetSquadronPatrolInterval(SquadronName,self.DefenderDefault.PatrolLimit,self.DefenderDefault.PatrolMinSeconds,self.DefenderDefault.PatrolMaxSeconds,1,"SEAD")
-self:I({SEAD={Zone:GetName(),PatrolMinSpeed,PatrolMaxSpeed,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolAltType,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType}})
-end
-function AI_A2G_DISPATCHER:SetSquadronSeadPatrol(SquadronName,Zone,FloorAltitude,CeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,EngageMinSpeed,EngageMaxSpeed,AltType)
-self:SetSquadronSeadPatrol2(SquadronName,Zone,PatrolMinSpeed,PatrolMaxSpeed,FloorAltitude,CeilingAltitude,AltType,EngageMinSpeed,EngageMaxSpeed,FloorAltitude,CeilingAltitude,AltType)
-end
-function AI_A2G_DISPATCHER:SetSquadronCas2(SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-DefenderSquadron.CAS=DefenderSquadron.CAS or{}
-local Cas=DefenderSquadron.CAS
-Cas.Name=SquadronName
-Cas.EngageMinSpeed=EngageMinSpeed
-Cas.EngageMaxSpeed=EngageMaxSpeed
-Cas.EngageFloorAltitude=EngageFloorAltitude or 500
-Cas.EngageCeilingAltitude=EngageCeilingAltitude or 1000
-Cas.EngageAltType=EngageAltType
-Cas.Defend=true
-self:I({CAS={SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType}})
-return self
-end
-function AI_A2G_DISPATCHER:SetSquadronCas(SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude)
-return self:SetSquadronCas2(SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,"RADIO")
-end
-function AI_A2G_DISPATCHER:SetSquadronCasEngageLimit(SquadronName,EngageLimit)
-self:SetSquadronEngageLimit(SquadronName,EngageLimit,"CAS")
-end
-function AI_A2G_DISPATCHER:SetSquadronCasPatrol2(SquadronName,Zone,PatrolMinSpeed,PatrolMaxSpeed,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolAltType,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-DefenderSquadron.CAS=DefenderSquadron.CAS or{}
-local CasPatrol=DefenderSquadron.CAS
-CasPatrol.Name=SquadronName
-CasPatrol.Zone=Zone
-CasPatrol.PatrolFloorAltitude=PatrolFloorAltitude
-CasPatrol.PatrolCeilingAltitude=PatrolCeilingAltitude
-CasPatrol.EngageFloorAltitude=EngageFloorAltitude
-CasPatrol.EngageCeilingAltitude=EngageCeilingAltitude
-CasPatrol.PatrolMinSpeed=PatrolMinSpeed
-CasPatrol.PatrolMaxSpeed=PatrolMaxSpeed
-CasPatrol.EngageMinSpeed=EngageMinSpeed
-CasPatrol.EngageMaxSpeed=EngageMaxSpeed
-CasPatrol.PatrolAltType=PatrolAltType
-CasPatrol.EngageAltType=EngageAltType
-CasPatrol.Patrol=true
-self:SetSquadronPatrolInterval(SquadronName,self.DefenderDefault.PatrolLimit,self.DefenderDefault.PatrolMinSeconds,self.DefenderDefault.PatrolMaxSeconds,1,"CAS")
-self:I({CAS={Zone:GetName(),PatrolMinSpeed,PatrolMaxSpeed,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolAltType,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType}})
-end
-function AI_A2G_DISPATCHER:SetSquadronCasPatrol(SquadronName,Zone,FloorAltitude,CeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,EngageMinSpeed,EngageMaxSpeed,AltType)
-self:SetSquadronCasPatrol2(SquadronName,Zone,PatrolMinSpeed,PatrolMaxSpeed,FloorAltitude,CeilingAltitude,AltType,EngageMinSpeed,EngageMaxSpeed,FloorAltitude,CeilingAltitude,AltType)
-end
-function AI_A2G_DISPATCHER:SetSquadronBai2(SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-DefenderSquadron.BAI=DefenderSquadron.BAI or{}
-local Bai=DefenderSquadron.BAI
-Bai.Name=SquadronName
-Bai.EngageMinSpeed=EngageMinSpeed
-Bai.EngageMaxSpeed=EngageMaxSpeed
-Bai.EngageFloorAltitude=EngageFloorAltitude or 500
-Bai.EngageCeilingAltitude=EngageCeilingAltitude or 1000
-Bai.EngageAltType=EngageAltType
-Bai.Defend=true
-self:I({BAI={SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType}})
-return self
-end
-function AI_A2G_DISPATCHER:SetSquadronBai(SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude)
-return self:SetSquadronBai2(SquadronName,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,"RADIO")
-end
-function AI_A2G_DISPATCHER:SetSquadronBaiEngageLimit(SquadronName,EngageLimit)
-self:SetSquadronEngageLimit(SquadronName,EngageLimit,"BAI")
-end
-function AI_A2G_DISPATCHER:SetSquadronBaiPatrol2(SquadronName,Zone,PatrolMinSpeed,PatrolMaxSpeed,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolAltType,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-DefenderSquadron.BAI=DefenderSquadron.BAI or{}
-local BaiPatrol=DefenderSquadron.BAI
-BaiPatrol.Name=SquadronName
-BaiPatrol.Zone=Zone
-BaiPatrol.PatrolFloorAltitude=PatrolFloorAltitude
-BaiPatrol.PatrolCeilingAltitude=PatrolCeilingAltitude
-BaiPatrol.EngageFloorAltitude=EngageFloorAltitude
-BaiPatrol.EngageCeilingAltitude=EngageCeilingAltitude
-BaiPatrol.PatrolMinSpeed=PatrolMinSpeed
-BaiPatrol.PatrolMaxSpeed=PatrolMaxSpeed
-BaiPatrol.EngageMinSpeed=EngageMinSpeed
-BaiPatrol.EngageMaxSpeed=EngageMaxSpeed
-BaiPatrol.PatrolAltType=PatrolAltType
-BaiPatrol.EngageAltType=EngageAltType
-BaiPatrol.Patrol=true
-self:SetSquadronPatrolInterval(SquadronName,self.DefenderDefault.PatrolLimit,self.DefenderDefault.PatrolMinSeconds,self.DefenderDefault.PatrolMaxSeconds,1,"BAI")
-self:I({BAI={Zone:GetName(),PatrolMinSpeed,PatrolMaxSpeed,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolAltType,EngageMinSpeed,EngageMaxSpeed,EngageFloorAltitude,EngageCeilingAltitude,EngageAltType}})
-end
-function AI_A2G_DISPATCHER:SetSquadronBaiPatrol(SquadronName,Zone,FloorAltitude,CeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,EngageMinSpeed,EngageMaxSpeed,AltType)
-self:SetSquadronBaiPatrol2(SquadronName,Zone,PatrolMinSpeed,PatrolMaxSpeed,FloorAltitude,CeilingAltitude,AltType,EngageMinSpeed,EngageMaxSpeed,FloorAltitude,CeilingAltitude,AltType)
-end
-function AI_A2G_DISPATCHER:SetDefaultOverhead(Overhead)
-self.DefenderDefault.Overhead=Overhead
-return self
-end
-function AI_A2G_DISPATCHER:SetSquadronOverhead(SquadronName,Overhead)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-DefenderSquadron.Overhead=Overhead
-return self
-end
-function AI_A2G_DISPATCHER:GetSquadronOverhead(SquadronName)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-return DefenderSquadron.Overhead or self.DefenderDefault.Overhead
-end
-function AI_A2G_DISPATCHER:SetDefaultGrouping(Grouping)
-self.DefenderDefault.Grouping=Grouping
-return self
-end
-function AI_A2G_DISPATCHER:SetSquadronGrouping(SquadronName,Grouping)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-DefenderSquadron.Grouping=Grouping
-return self
-end
-function AI_A2G_DISPATCHER:SetSquadronEngageProbability(SquadronName,EngageProbability)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-DefenderSquadron.EngageProbability=EngageProbability
-return self
-end
-function AI_A2G_DISPATCHER:SetDefaultTakeoff(Takeoff)
-self.DefenderDefault.Takeoff=Takeoff
-return self
-end
-function AI_A2G_DISPATCHER:SetSquadronTakeoff(SquadronName,Takeoff)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-DefenderSquadron.Takeoff=Takeoff
-return self
-end
-function AI_A2G_DISPATCHER:GetDefaultTakeoff()
-return self.DefenderDefault.Takeoff
-end
-function AI_A2G_DISPATCHER:GetSquadronTakeoff(SquadronName)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-return DefenderSquadron.Takeoff or self.DefenderDefault.Takeoff
-end
-function AI_A2G_DISPATCHER:SetDefaultTakeoffInAir()
-self:SetDefaultTakeoff(AI_A2G_DISPATCHER.Takeoff.Air)
-return self
-end
-function AI_A2G_DISPATCHER:SetSquadronTakeoffInAir(SquadronName,TakeoffAltitude)
-self:SetSquadronTakeoff(SquadronName,AI_A2G_DISPATCHER.Takeoff.Air)
-if TakeoffAltitude then
-self:SetSquadronTakeoffInAirAltitude(SquadronName,TakeoffAltitude)
-end
-return self
-end
-function AI_A2G_DISPATCHER:SetDefaultTakeoffFromRunway()
-self:SetDefaultTakeoff(AI_A2G_DISPATCHER.Takeoff.Runway)
-return self
-end
-function AI_A2G_DISPATCHER:SetSquadronTakeoffFromRunway(SquadronName)
-self:SetSquadronTakeoff(SquadronName,AI_A2G_DISPATCHER.Takeoff.Runway)
-return self
-end
-function AI_A2G_DISPATCHER:SetDefaultTakeoffFromParkingHot()
-self:SetDefaultTakeoff(AI_A2G_DISPATCHER.Takeoff.Hot)
-return self
-end
-function AI_A2G_DISPATCHER:SetSquadronTakeoffFromParkingHot(SquadronName)
-self:SetSquadronTakeoff(SquadronName,AI_A2G_DISPATCHER.Takeoff.Hot)
-return self
-end
-function AI_A2G_DISPATCHER:SetDefaultTakeoffFromParkingCold()
-self:SetDefaultTakeoff(AI_A2G_DISPATCHER.Takeoff.Cold)
-return self
-end
-function AI_A2G_DISPATCHER:SetSquadronTakeoffFromParkingCold(SquadronName)
-self:SetSquadronTakeoff(SquadronName,AI_A2G_DISPATCHER.Takeoff.Cold)
-return self
-end
-function AI_A2G_DISPATCHER:SetDefaultTakeoffInAirAltitude(TakeoffAltitude)
-self.DefenderDefault.TakeoffAltitude=TakeoffAltitude
-return self
-end
-function AI_A2G_DISPATCHER:SetSquadronTakeoffInAirAltitude(SquadronName,TakeoffAltitude)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-DefenderSquadron.TakeoffAltitude=TakeoffAltitude
-return self
-end
-function AI_A2G_DISPATCHER:SetDefaultLanding(Landing)
-self.DefenderDefault.Landing=Landing
-return self
-end
-function AI_A2G_DISPATCHER:SetSquadronLanding(SquadronName,Landing)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-DefenderSquadron.Landing=Landing
-return self
-end
-function AI_A2G_DISPATCHER:GetDefaultLanding()
-return self.DefenderDefault.Landing
-end
-function AI_A2G_DISPATCHER:GetSquadronLanding(SquadronName)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-return DefenderSquadron.Landing or self.DefenderDefault.Landing
-end
-function AI_A2G_DISPATCHER:SetDefaultLandingNearAirbase()
-self:SetDefaultLanding(AI_A2G_DISPATCHER.Landing.NearAirbase)
-return self
-end
-function AI_A2G_DISPATCHER:SetSquadronLandingNearAirbase(SquadronName)
-self:SetSquadronLanding(SquadronName,AI_A2G_DISPATCHER.Landing.NearAirbase)
-return self
-end
-function AI_A2G_DISPATCHER:SetDefaultLandingAtRunway()
-self:SetDefaultLanding(AI_A2G_DISPATCHER.Landing.AtRunway)
-return self
-end
-function AI_A2G_DISPATCHER:SetSquadronLandingAtRunway(SquadronName)
-self:SetSquadronLanding(SquadronName,AI_A2G_DISPATCHER.Landing.AtRunway)
-return self
-end
-function AI_A2G_DISPATCHER:SetDefaultLandingAtEngineShutdown()
-self:SetDefaultLanding(AI_A2G_DISPATCHER.Landing.AtEngineShutdown)
-return self
-end
-function AI_A2G_DISPATCHER:SetSquadronLandingAtEngineShutdown(SquadronName)
-self:SetSquadronLanding(SquadronName,AI_A2G_DISPATCHER.Landing.AtEngineShutdown)
-return self
-end
-function AI_A2G_DISPATCHER:SetDefaultFuelThreshold(FuelThreshold)
-self.DefenderDefault.FuelThreshold=FuelThreshold
-return self
-end
-function AI_A2G_DISPATCHER:SetSquadronFuelThreshold(SquadronName,FuelThreshold)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-DefenderSquadron.FuelThreshold=FuelThreshold
-return self
-end
-function AI_A2G_DISPATCHER:SetDefaultTanker(TankerName)
-self.DefenderDefault.TankerName=TankerName
-return self
-end
-function AI_A2G_DISPATCHER:SetSquadronTanker(SquadronName,TankerName)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-DefenderSquadron.TankerName=TankerName
-return self
-end
-function AI_A2G_DISPATCHER:SetSquadronRadioFrequency(SquadronName,RadioFrequency,RadioModulation,RadioPower)
-local DefenderSquadron=self:GetSquadron(SquadronName)
-DefenderSquadron.RadioFrequency=RadioFrequency
-DefenderSquadron.RadioModulation=RadioModulation or radio.modulation.AM
-DefenderSquadron.RadioPower=RadioPower or 100
-if DefenderSquadron.RadioQueue then
-DefenderSquadron.RadioQueue:Stop()
-end
-DefenderSquadron.RadioQueue=nil
-DefenderSquadron.RadioQueue=RADIOSPEECH:New(DefenderSquadron.RadioFrequency,DefenderSquadron.RadioModulation)
-DefenderSquadron.RadioQueue.power=DefenderSquadron.RadioPower
-DefenderSquadron.RadioQueue:Start(0.5)
-DefenderSquadron.RadioQueue:SetLanguage(DefenderSquadron.Language)
-end
-function AI_A2G_DISPATCHER:AddDefenderToSquadron(Squadron,Defender,Size)
-self.Defenders=self.Defenders or{}
-local DefenderName=Defender:GetName()
-self.Defenders[DefenderName]=Squadron
-if Squadron.ResourceCount then
-Squadron.ResourceCount=Squadron.ResourceCount-Size
-end
-self:F({DefenderName=DefenderName,SquadronResourceCount=Squadron.ResourceCount})
-end
-function AI_A2G_DISPATCHER:RemoveDefenderFromSquadron(Squadron,Defender)
-self.Defenders=self.Defenders or{}
-local DefenderName=Defender:GetName()
-if Squadron.ResourceCount then
-Squadron.ResourceCount=Squadron.ResourceCount+Defender:GetSize()
-end
-self.Defenders[DefenderName]=nil
-self:F({DefenderName=DefenderName,SquadronResourceCount=Squadron.ResourceCount})
-end
-function AI_A2G_DISPATCHER:GetSquadronFromDefender(Defender)
-self.Defenders=self.Defenders or{}
-local DefenderName=Defender:GetName()
-self:F({DefenderName=DefenderName})
-return self.Defenders[DefenderName]
-end
-function AI_A2G_DISPATCHER:CountPatrolAirborne(SquadronName,DefenseTaskType)
-local PatrolCount=0
-local DefenderSquadron=self.DefenderSquadrons[SquadronName]
-if DefenderSquadron then
-for AIGroup,DefenderTask in pairs(self:GetDefenderTasks())do
-if DefenderTask.SquadronName==SquadronName then
-if DefenderTask.Type==DefenseTaskType then
-if AIGroup:IsAlive()then
-if DefenderTask.Fsm:Is("Patrolling")or DefenderTask.Fsm:Is("Engaging")or DefenderTask.Fsm:Is("Refuelling")
-or DefenderTask.Fsm:Is("Started")then
-PatrolCount=PatrolCount+1
-end
-end
-end
-end
-end
-end
-return PatrolCount
-end
-function AI_A2G_DISPATCHER:CountDefendersEngaged(AttackerDetection,AttackerCount)
-local DefendersEngaged=0
-local DefendersTotal=0
-local AttackerSet=AttackerDetection.Set
-local DefendersMissing=AttackerCount
-local DefenderTasks=self:GetDefenderTasks()
-for DefenderGroup,DefenderTask in pairs(DefenderTasks)do
-local Defender=DefenderGroup
-local DefenderTaskTarget=DefenderTask.Target
-local DefenderSquadronName=DefenderTask.SquadronName
-local DefenderSize=DefenderTask.Size
-if DefenderTask.Target then
-self:F("Defender Group Name: "..Defender:GetName()..", Size: "..DefenderSize)
-DefendersTotal=DefendersTotal+DefenderSize
-if DefenderTaskTarget and DefenderTaskTarget.Index==AttackerDetection.Index then
-local SquadronOverhead=self:GetSquadronOverhead(DefenderSquadronName)
-self:F({SquadronOverhead=SquadronOverhead})
-if DefenderSize then
-DefendersEngaged=DefendersEngaged+DefenderSize
-DefendersMissing=DefendersMissing-DefenderSize/SquadronOverhead
-self:F("Defender Group Name: "..Defender:GetName()..", Size: "..DefenderSize)
-else
-DefendersEngaged=0
-end
-end
-end
-end
-for QueueID,QueueItem in pairs(self.DefenseQueue)do
-local QueueItem=QueueItem
-if QueueItem.AttackerDetection and QueueItem.AttackerDetection.ItemID==AttackerDetection.ItemID then
-DefendersMissing=DefendersMissing-QueueItem.DefendersNeeded/QueueItem.DefenderSquadron.Overhead
-self:F({QueueItemName=QueueItem.Defense,QueueItem_ItemID=QueueItem.AttackerDetection.ItemID,DetectedItem=AttackerDetection.ItemID,DefendersMissing=DefendersMissing})
-end
-end
-self:F({DefenderCount=DefendersEngaged})
-return DefendersTotal,DefendersEngaged,DefendersMissing
-end
-function AI_A2G_DISPATCHER:CountDefenders(AttackerDetection,DefenderCount,DefenderTaskType)
-local Friendlies=nil
-local AttackerSet=AttackerDetection.Set
-local AttackerCount=AttackerSet:Count()
-local DefenderFriendlies=self:GetDefenderFriendliesNearBy(AttackerDetection)
-for FriendlyDistance,DefenderFriendlyUnit in UTILS.spairs(DefenderFriendlies or{})do
-if AttackerCount>DefenderCount then
-local FriendlyGroup=DefenderFriendlyUnit:GetGroup()
-if FriendlyGroup and FriendlyGroup:IsAlive()then
-local DefenderTask=self:GetDefenderTask(FriendlyGroup)
-if DefenderTask then
-if DefenderTaskType==DefenderTask.Type then
-if DefenderTask.Target==nil then
-if DefenderTask.Fsm:Is("Returning")
-or DefenderTask.Fsm:Is("Patrolling")then
-Friendlies=Friendlies or{}
-Friendlies[FriendlyGroup]=FriendlyGroup
-DefenderCount=DefenderCount+FriendlyGroup:GetSize()
-self:F({Friendly=FriendlyGroup:GetName(),FriendlyDistance=FriendlyDistance})
-end
-end
-end
-end
-end
-else
-break
-end
-end
-return Friendlies
-end
-function AI_A2G_DISPATCHER:ResourceActivate(DefenderSquadron,DefendersNeeded)
-local SquadronName=DefenderSquadron.Name
-DefendersNeeded=DefendersNeeded or 4
-local DefenderGrouping=DefenderSquadron.Grouping or self.DefenderDefault.Grouping
-DefenderGrouping=(DefenderGroupingDefenderGrouping then
-break
-end
-end
-if DefenderPatrolTemplate then
-local TakeoffMethod=self:GetSquadronTakeoff(SquadronName)
-local SpawnGroup=GROUP:Register(DefenderName)
-DefenderPatrolTemplate.lateActivation=nil
-DefenderPatrolTemplate.uncontrolled=nil
-local Takeoff=self:GetSquadronTakeoff(SquadronName)
-DefenderPatrolTemplate.route.points[1].type=GROUPTEMPLATE.Takeoff[Takeoff][1]
-DefenderPatrolTemplate.route.points[1].action=GROUPTEMPLATE.Takeoff[Takeoff][2]
-local Defender=_DATABASE:Spawn(DefenderPatrolTemplate)
-self:AddDefenderToSquadron(DefenderSquadron,Defender,DefenderGrouping)
-Defender:Activate()
-return Defender,DefenderGrouping
-end
-else
-local Spawn=DefenderSquadron.Spawn[math.random(1,#DefenderSquadron.Spawn)]
-if DefenderGrouping then
-Spawn:InitGrouping(DefenderGrouping)
-else
-Spawn:InitGrouping()
-end
-local TakeoffMethod=self:GetSquadronTakeoff(SquadronName)
-local Defender=Spawn:SpawnAtAirbase(DefenderSquadron.Airbase,TakeoffMethod,DefenderSquadron.TakeoffAltitude or self.DefenderDefault.TakeoffAltitude)
-self:AddDefenderToSquadron(DefenderSquadron,Defender,DefenderGrouping)
-return Defender,DefenderGrouping
-end
-return nil,nil
-end
-function AI_A2G_DISPATCHER:onafterPatrol(From,Event,To,SquadronName,DefenseTaskType)
-local DefenderSquadron,Patrol=self:CanPatrol(SquadronName,DefenseTaskType)
-if DefenderSquadron then
-local DefendersNeeded
-local DefendersGrouping=(DefenderSquadron.Grouping or self.DefenderDefault.Grouping)
-if DefenderSquadron.ResourceCount==nil then
-DefendersNeeded=DefendersGrouping
-else
-if DefenderSquadron.ResourceCount>=DefendersGrouping then
-DefendersNeeded=DefendersGrouping
-else
-DefendersNeeded=DefenderSquadron.ResourceCount
-end
-end
-if Patrol then
-self:ResourceQueue(true,DefenderSquadron,DefendersNeeded,Patrol,DefenseTaskType,nil,SquadronName)
-end
-end
-end
-function AI_A2G_DISPATCHER:ResourceQueue(Patrol,DefenderSquadron,DefendersNeeded,Defense,DefenseTaskType,AttackerDetection,SquadronName)
-self:F({DefenderSquadron,DefendersNeeded,Defense,DefenseTaskType,AttackerDetection,SquadronName})
-local DefenseQueueItem={}
-DefenseQueueItem.Patrol=Patrol
-DefenseQueueItem.DefenderSquadron=DefenderSquadron
-DefenseQueueItem.DefendersNeeded=DefendersNeeded
-DefenseQueueItem.Defense=Defense
-DefenseQueueItem.DefenseTaskType=DefenseTaskType
-DefenseQueueItem.AttackerDetection=AttackerDetection
-DefenseQueueItem.SquadronName=SquadronName
-table.insert(self.DefenseQueue,DefenseQueueItem)
-self:F({QueueItems=#self.DefenseQueue})
-end
-function AI_A2G_DISPATCHER:ResourceTakeoff()
-for DefenseQueueID,DefenseQueueItem in pairs(self.DefenseQueue)do
-self:F({DefenseQueueID})
-end
-for SquadronName,Squadron in pairs(self.DefenderSquadrons)do
-if#self.DefenseQueue>0 then
-self:F({SquadronName,Squadron.Name,Squadron.TakeoffTime,Squadron.TakeoffInterval,timer.getTime()})
-local DefenseQueueItem=self.DefenseQueue[1]
-self:F({DefenderSquadron=DefenseQueueItem.DefenderSquadron})
-if DefenseQueueItem.SquadronName==SquadronName then
-if Squadron.TakeoffTime+Squadron.TakeoffInterval0 then
-local FirstUnit=AttackSetUnit:GetFirst()
-local Coordinate=FirstUnit:GetCoordinate()
-if self.SetSendPlayerMessages then
-Dispatcher:MessageToPlayers(Squadron,DefenderName..", moving on to ground target at "..Coordinate:ToStringA2G(DefenderGroup),DefenderGroup)
-end
-end
-end
-function AI_A2G_Fsm:OnAfterEngage(DefenderGroup,From,Event,To,AttackSetUnit)
-self:F({"Engage Route",DefenderGroup:GetName()})
-local DefenderName=DefenderGroup:GetCallsign()
-local Dispatcher=AI_A2G_Fsm:GetDispatcher()
-local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup)
-local FirstUnit=AttackSetUnit:GetFirst()
-if FirstUnit then
-local Coordinate=FirstUnit:GetCoordinate()
-if self.SetSendPlayerMessages then
-Dispatcher:MessageToPlayers(Squadron,DefenderName..", engaging ground target at "..Coordinate:ToStringA2G(DefenderGroup),DefenderGroup)
-end
-end
-end
-function AI_A2G_Fsm:onafterRTB(DefenderGroup,From,Event,To)
-self:F({"RTB",DefenderGroup:GetName()})
-self:GetParent(self).onafterRTB(self,DefenderGroup,From,Event,To)
-local DefenderName=DefenderGroup:GetCallsign()
-local Dispatcher=self:GetDispatcher()
-local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup)
-if self.SetSendPlayerMessages then
-Dispatcher:MessageToPlayers(Squadron,DefenderName..", returning to base.",DefenderGroup)
-end
-Dispatcher:ClearDefenderTaskTarget(DefenderGroup)
-end
-function AI_A2G_Fsm:onafterLostControl(DefenderGroup,From,Event,To)
-self:F({"LostControl",DefenderGroup:GetName()})
-self:GetParent(self).onafterHome(self,DefenderGroup,From,Event,To)
-local DefenderName=DefenderGroup:GetCallsign()
-local Dispatcher=AI_A2G_Fsm:GetDispatcher()
-local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup)
-if self.SetSendPlayerMessages then
-Dispatcher:MessageToPlayers(Squadron,DefenderName..", lost control.")
-end
-if DefenderGroup:IsAboveRunway()then
-Dispatcher:RemoveDefenderFromSquadron(Squadron,DefenderGroup)
-DefenderGroup:Destroy()
-end
-end
-function AI_A2G_Fsm:onafterHome(DefenderGroup,From,Event,To,Action)
-self:F({"Home",DefenderGroup:GetName()})
-self:GetParent(self).onafterHome(self,DefenderGroup,From,Event,To)
-local DefenderName=DefenderGroup:GetCallsign()
-local Dispatcher=self:GetDispatcher()
-local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup)
-if self.SetSendPlayerMessages then
-Dispatcher:MessageToPlayers(Squadron,DefenderName..", landing at base.",DefenderGroup)
-end
-if Action and Action=="Destroy"then
-Dispatcher:RemoveDefenderFromSquadron(Squadron,DefenderGroup)
-DefenderGroup:Destroy()
-end
-if Dispatcher:GetSquadronLanding(Squadron.Name)==AI_A2G_DISPATCHER.Landing.NearAirbase then
-Dispatcher:RemoveDefenderFromSquadron(Squadron,DefenderGroup)
-DefenderGroup:Destroy()
-Dispatcher:ResourcePark(Squadron,DefenderGroup)
-end
-end
-end
-end
-function AI_A2G_DISPATCHER:ResourceEngage(DefenderSquadron,DefendersNeeded,Defense,DefenseTaskType,AttackerDetection,SquadronName)
-self:F({DefenderSquadron=DefenderSquadron})
-self:F({DefendersNeeded=DefendersNeeded})
-self:F({Defense=Defense})
-self:F({DefenseTaskType=DefenseTaskType})
-self:F({AttackerDetection=AttackerDetection})
-self:F({SquadronName=SquadronName})
-local DefenderGroup,DefenderGrouping=self:ResourceActivate(DefenderSquadron,DefendersNeeded)
-if DefenderGroup then
-local AI_A2G_ENGAGE={SEAD=AI_A2G_SEAD,BAI=AI_A2G_BAI,CAS=AI_A2G_CAS}
-local AI_A2G_Fsm=AI_A2G_ENGAGE[DefenseTaskType]:New(DefenderGroup,Defense.EngageMinSpeed,Defense.EngageMaxSpeed,Defense.EngageFloorAltitude,Defense.EngageCeilingAltitude,Defense.EngageAltType)
-AI_A2G_Fsm:SetDispatcher(self)
-AI_A2G_Fsm:SetHomeAirbase(DefenderSquadron.Airbase)
-AI_A2G_Fsm:SetFuelThreshold(DefenderSquadron.FuelThreshold or self.DefenderDefault.FuelThreshold,60)
-AI_A2G_Fsm:SetDamageThreshold(self.DefenderDefault.DamageThreshold)
-AI_A2G_Fsm:SetDisengageRadius(self.DisengageRadius)
-AI_A2G_Fsm:Start()
-self:SetDefenderTask(SquadronName,DefenderGroup,DefenseTaskType,AI_A2G_Fsm,AttackerDetection,DefenderGrouping)
-function AI_A2G_Fsm:onafterTakeoff(DefenderGroup,From,Event,To)
-self:F({"Defender Birth",DefenderGroup:GetName()})
-local DefenderName=DefenderGroup:GetCallsign()
-local Dispatcher=AI_A2G_Fsm:GetDispatcher()
-local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup)
-local DefenderTarget=Dispatcher:GetDefenderTaskTarget(DefenderGroup)
-self:F({DefenderTarget=DefenderTarget})
-if DefenderTarget then
-if self.SetSendPlayerMessages then
-Dispatcher:MessageToPlayers(Squadron,DefenderName..", wheels up.",DefenderGroup)
-end
-AI_A2G_Fsm:EngageRoute(DefenderTarget.Set)
-end
-end
-function AI_A2G_Fsm:onafterEngageRoute(DefenderGroup,From,Event,To,AttackSetUnit)
-self:F({"Engage Route",DefenderGroup:GetName()})
-local DefenderName=DefenderGroup:GetCallsign()
-local Dispatcher=AI_A2G_Fsm:GetDispatcher()
-local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup)
-if Squadron then
-local FirstUnit=AttackSetUnit:GetFirst()
-local Coordinate=FirstUnit:GetCoordinate()
-if self.SetSendPlayerMessages then
-Dispatcher:MessageToPlayers(Squadron,DefenderName..", on route to ground target at "..Coordinate:ToStringA2G(DefenderGroup),DefenderGroup)
-end
-end
-self:GetParent(self).onafterEngageRoute(self,DefenderGroup,From,Event,To,AttackSetUnit)
-end
-function AI_A2G_Fsm:OnAfterEngage(DefenderGroup,From,Event,To,AttackSetUnit)
-self:F({"Engage Route",DefenderGroup:GetName()})
-local DefenderName=DefenderGroup:GetCallsign()
-local Dispatcher=AI_A2G_Fsm:GetDispatcher()
-local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup)
-local FirstUnit=AttackSetUnit:GetFirst()
-if FirstUnit then
-local Coordinate=FirstUnit:GetCoordinate()
-if self.SetSendPlayerMessages then
-Dispatcher:MessageToPlayers(Squadron,DefenderName..", engaging ground target at "..Coordinate:ToStringA2G(DefenderGroup),DefenderGroup)
-end
-end
-end
-function AI_A2G_Fsm:onafterRTB(DefenderGroup,From,Event,To)
-self:F({"Defender RTB",DefenderGroup:GetName()})
-local DefenderName=DefenderGroup:GetCallsign()
-local Dispatcher=self:GetDispatcher()
-local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup)
-if self.SetSendPlayerMessages then
-Dispatcher:MessageToPlayers(Squadron,DefenderName..", returning to base.",DefenderGroup)
-end
-self:GetParent(self).onafterRTB(self,DefenderGroup,From,Event,To)
-Dispatcher:ClearDefenderTaskTarget(DefenderGroup)
-end
-function AI_A2G_Fsm:onafterLostControl(DefenderGroup,From,Event,To)
-self:F({"Defender LostControl",DefenderGroup:GetName()})
-self:GetParent(self).onafterHome(self,DefenderGroup,From,Event,To)
-local DefenderName=DefenderGroup:GetCallsign()
-local Dispatcher=AI_A2G_Fsm:GetDispatcher()
-local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup)
-if self.SetSendPlayerMessages then
-Dispatcher:MessageToPlayers(Squadron,"Squadron "..Squadron.Name..", "..DefenderName.." lost control.")
-end
-if DefenderGroup:IsAboveRunway()then
-Dispatcher:RemoveDefenderFromSquadron(Squadron,DefenderGroup)
-DefenderGroup:Destroy()
-end
-end
-function AI_A2G_Fsm:onafterHome(DefenderGroup,From,Event,To,Action)
-self:F({"Defender Home",DefenderGroup:GetName()})
-self:GetParent(self).onafterHome(self,DefenderGroup,From,Event,To)
-local DefenderName=DefenderGroup:GetCallsign()
-local Dispatcher=self:GetDispatcher()
-local Squadron=Dispatcher:GetSquadronFromDefender(DefenderGroup)
-if self.SetSendPlayerMessages then
-Dispatcher:MessageToPlayers(Squadron,DefenderName..", landing at base.",DefenderGroup)
-end
-if Action and Action=="Destroy"then
-Dispatcher:RemoveDefenderFromSquadron(Squadron,DefenderGroup)
-DefenderGroup:Destroy()
-end
-if Dispatcher:GetSquadronLanding(Squadron.Name)==AI_A2G_DISPATCHER.Landing.NearAirbase then
-Dispatcher:RemoveDefenderFromSquadron(Squadron,DefenderGroup)
-DefenderGroup:Destroy()
-Dispatcher:ResourcePark(Squadron,DefenderGroup)
-end
-end
-end
-end
-function AI_A2G_DISPATCHER:onafterEngage(From,Event,To,AttackerDetection,Defenders)
-if Defenders then
-for DefenderID,Defender in pairs(Defenders or{})do
-local Fsm=self:GetDefenderTaskFsm(Defender)
-Fsm:Engage(AttackerDetection.Set)
-self:SetDefenderTaskTarget(Defender,AttackerDetection)
-end
-end
-end
-function AI_A2G_DISPATCHER:HasDefenseLine(DefenseCoordinate,DetectedItem)
-local AttackCoordinate=self.Detection:GetDetectedItemCoordinate(DetectedItem)
-local EvaluateDistance=AttackCoordinate:Get2DDistance(DefenseCoordinate)
-local c1=DefenseCoordinate
-local c2=AttackCoordinate
-local a=c1.z-c2.z
-local b=c2.x-c1.x
-local c=c1.x*c2.z-c2.x*c1.z
-local ok=true
-for AttackItemID,CheckAttackItem in pairs(self.Detection:GetDetectedItems())do
-if AttackItemID~=DetectedItem.ID then
-local CheckAttackCoordinate=self.Detection:GetDetectedItemCoordinate(CheckAttackItem)
-local x=CheckAttackCoordinate.x
-local y=CheckAttackCoordinate.z
-local r=5000
-local IntersectDistance=(math.abs(a*x+b*y+c))/math.sqrt(a*a+b*b)
-self:F({IntersectDistance=IntersectDistance,x=x,y=y})
-local IntersectAttackDistance=CheckAttackCoordinate:Get2DDistance(DefenseCoordinate)
-self:F({IntersectAttackDistance=IntersectAttackDistance,EvaluateDistance=EvaluateDistance})
-if IntersectDistance0 and not BreakLoop)do
-self:F({DefenderSquadrons=self.DefenderSquadrons})
-for SquadronName,DefenderSquadron in UTILS.rpairs(self.DefenderSquadrons or{})do
-if DefenderSquadron[DefenseTaskType]then
-local AirbaseCoordinate=DefenderSquadron.Airbase:GetCoordinate()
-local AttackerCoord=AttackerUnit:GetCoordinate()
-local InterceptCoord=DetectedItem.InterceptCoord
-self:F({InterceptCoord=InterceptCoord})
-if InterceptCoord then
-local InterceptDistance=AirbaseCoordinate:Get2DDistance(InterceptCoord)
-local AirbaseDistance=AirbaseCoordinate:Get2DDistance(AttackerCoord)
-self:F({InterceptDistance=InterceptDistance,AirbaseDistance=AirbaseDistance,InterceptCoord=InterceptCoord})
-if AirbaseDistance<=self.DefenseRadius then
-local HasDefenseLine=self:HasDefenseLine(AirbaseCoordinate,DetectedItem)
-if HasDefenseLine==true then
-local EngageProbability=(DefenderSquadron.EngageProbability or 1)
-local Probability=math.random()
-if Probability=DefendersLimit then
-DefendersNeeded=0
-BreakLoop=true
-else
-if DefendersTotal+DefendersNeeded>DefendersLimit then
-DefendersNeeded=DefendersLimit-DefendersTotal
-end
-end
-end
-if DefenderSquadron.ResourceCount and DefendersNeeded>DefenderSquadron.ResourceCount then
-DefendersNeeded=DefenderSquadron.ResourceCount
-BreakLoop=true
-end
-while(DefendersNeeded>0)do
-self:ResourceQueue(false,DefenderSquadron,DefendersNeeded,Defense,DefenseTaskType,DetectedItem,EngageSquadronName)
-DefendersNeeded=DefendersNeeded-DefenderGrouping
-DefenderCount=DefenderCount-DefenderGrouping/DefenderOverhead
-end
-else
-BreakLoop=true
-break
-end
-else
-break
-end
-end
-end
-end
-function AI_A2G_DISPATCHER:Evaluate_SEAD(DetectedItem)
-self:F({DetectedItem.ItemID})
-local AttackerSet=DetectedItem.Set
-local AttackerCount=AttackerSet:HasSEAD()
-if(AttackerCount>0)then
-local DefendersTotal,DefendersEngaged,DefendersMissing=self:CountDefendersEngaged(DetectedItem,AttackerCount)
-self:F({AttackerCount=AttackerCount,DefendersTotal=DefendersTotal,DefendersEngaged=DefendersEngaged,DefendersMissing=DefendersMissing})
-local DefenderGroups=self:CountDefenders(DetectedItem,DefendersEngaged,"SEAD")
-if DetectedItem.IsDetected==true then
-return DefendersTotal,DefendersEngaged,DefendersMissing,DefenderGroups
-end
-end
-return 0,0,0
-end
-function AI_A2G_DISPATCHER:Evaluate_CAS(DetectedItem)
-self:F({DetectedItem.ItemID})
-local AttackerSet=DetectedItem.Set
-local AttackerCount=AttackerSet:Count()
-local AttackerRadarCount=AttackerSet:HasSEAD()
-local IsFriendliesNearBy=self.Detection:IsFriendliesNearBy(DetectedItem,Unit.Category.GROUND_UNIT)
-local IsCas=(AttackerRadarCount==0)and(IsFriendliesNearBy==true)
-if IsCas==true then
-local DefendersTotal,DefendersEngaged,DefendersMissing=self:CountDefendersEngaged(DetectedItem,AttackerCount)
-self:F({AttackerCount=AttackerCount,DefendersTotal=DefendersTotal,DefendersEngaged=DefendersEngaged,DefendersMissing=DefendersMissing})
-local DefenderGroups=self:CountDefenders(DetectedItem,DefendersEngaged,"CAS")
-if DetectedItem.IsDetected==true then
-return DefendersTotal,DefendersEngaged,DefendersMissing,DefenderGroups
-end
-end
-return 0,0,0
-end
-function AI_A2G_DISPATCHER:Evaluate_BAI(DetectedItem)
-self:F({DetectedItem.ItemID})
-local AttackerSet=DetectedItem.Set
-local AttackerCount=AttackerSet:Count()
-local AttackerRadarCount=AttackerSet:HasSEAD()
-local IsFriendliesNearBy=self.Detection:IsFriendliesNearBy(DetectedItem,Unit.Category.GROUND_UNIT)
-local IsBai=(AttackerRadarCount==0)and(IsFriendliesNearBy==false)
-if IsBai==true then
-local DefendersTotal,DefendersEngaged,DefendersMissing=self:CountDefendersEngaged(DetectedItem,AttackerCount)
-self:F({AttackerCount=AttackerCount,DefendersTotal=DefendersTotal,DefendersEngaged=DefendersEngaged,DefendersMissing=DefendersMissing})
-local DefenderGroups=self:CountDefenders(DetectedItem,DefendersEngaged,"BAI")
-if DetectedItem.IsDetected==true then
-return DefendersTotal,DefendersEngaged,DefendersMissing,DefenderGroups
-end
-end
-return 0,0,0
-end
-function AI_A2G_DISPATCHER:Keys(DetectedItem)
-self:F({DetectedItem=DetectedItem})
-local AttackCoordinate=self.Detection:GetDetectedItemCoordinate(DetectedItem)
-local ShortestDistance=999999999
-for DefenseCoordinateName,DefenseCoordinate in pairs(self.DefenseCoordinates)do
-local DefenseCoordinate=DefenseCoordinate
-local EvaluateDistance=AttackCoordinate:Get2DDistance(DefenseCoordinate)
-if EvaluateDistance<=ShortestDistance then
-ShortestDistance=EvaluateDistance
-end
-end
-return ShortestDistance
-end
-function AI_A2G_DISPATCHER:Order(DetectedItem)
-local AttackCoordinate=self.Detection:GetDetectedItemCoordinate(DetectedItem)
-local ShortestDistance=999999999
-for DefenseCoordinateName,DefenseCoordinate in pairs(self.DefenseCoordinates)do
-local DefenseCoordinate=DefenseCoordinate
-local EvaluateDistance=AttackCoordinate:Get2DDistance(DefenseCoordinate)
-if EvaluateDistance<=ShortestDistance then
-ShortestDistance=EvaluateDistance
-end
-end
-return ShortestDistance
-end
-function AI_A2G_DISPATCHER:ShowTacticalDisplay(Detection)
-local AreaMsg={}
-local TaskMsg={}
-local ChangeMsg={}
-local TaskReport=REPORT:New()
-local DefenseTotal=0
-local Report=REPORT:New("\nTactical Overview")
-local DefenderGroupCount=0
-local DefendersTotal=0
-for DetectedItemID,DetectedItem in UTILS.spairs(Detection:GetDetectedItems(),function(t,a,b)return self:Order(t[a])0 then
-self:F({DefendersTotal=DefendersTotal,DefendersEngaged=DefendersEngaged,DefendersMissing=DefendersMissing})
-self:Defend(DetectedItem,DefendersTotal,DefendersEngaged,DefendersMissing,Friendlies,"SEAD")
-end
-end
-do
-local DefendersTotal,DefendersEngaged,DefendersMissing,Friendlies=self:Evaluate_CAS(DetectedItem)
-if DefendersMissing>0 then
-self:F({DefendersTotal=DefendersTotal,DefendersEngaged=DefendersEngaged,DefendersMissing=DefendersMissing})
-self:Defend(DetectedItem,DefendersTotal,DefendersEngaged,DefendersMissing,Friendlies,"CAS")
-end
-end
-do
-local DefendersTotal,DefendersEngaged,DefendersMissing,Friendlies=self:Evaluate_BAI(DetectedItem)
-if DefendersMissing>0 then
-self:F({DefendersTotal=DefendersTotal,DefendersEngaged=DefendersEngaged,DefendersMissing=DefendersMissing})
-self:Defend(DetectedItem,DefendersTotal,DefendersEngaged,DefendersMissing,Friendlies,"BAI")
-end
-end
-end
-for Defender,DefenderTask in pairs(self:GetDefenderTasks())do
-local Defender=Defender
-if DefenderTask.Target and DefenderTask.Target.Index==DetectedItem.Index then
-DefenseTotal=DefenseTotal+1
-end
-end
-for DefenseQueueID,DefenseQueueItem in pairs(self.DefenseQueue)do
-local DefenseQueueItem=DefenseQueueItem
-if DefenseQueueItem.AttackerDetection and DefenseQueueItem.AttackerDetection.Index and DefenseQueueItem.AttackerDetection.Index==DetectedItem.Index then
-DefenseTotal=DefenseTotal+1
-end
-end
-if self.TacticalDisplay then
-local ThreatLevel=DetectedItem.Set:CalculateThreatLevelA2G()
-Report:Add(string.format(" - %1s%s ( %4s ): ( #%d - %4s ) %s",(DetectedItem.IsDetected==true)and"!"or" ",DetectedItem.ItemID,DetectedItem.Index,DetectedItem.Set:Count(),DetectedItem.Type or" --- ",string.rep("■",ThreatLevel)))
-for Defender,DefenderTask in pairs(self:GetDefenderTasks())do
-local Defender=Defender
-if DefenderTask.Target and DefenderTask.Target.Index==DetectedItem.Index then
-if Defender:IsAlive()then
-DefenderGroupCount=DefenderGroupCount+1
-local Fuel=Defender:GetFuelMin()*100
-local Damage=Defender:GetLife()/Defender:GetLife0()*100
-Report:Add(string.format(" - %s ( %s - %s ): ( #%d ) F: %3d, D:%3d - %s",
-Defender:GetName(),
-DefenderTask.Type,
-DefenderTask.Fsm:GetState(),
-Defender:GetSize(),
-Fuel,
-Damage,
-Defender:HasTask()==true and"Executing"or"Idle"))
-end
-end
-end
-end
-end
-end
-if self.TacticalDisplay then
-Report:Add("\n - No Targets:")
-local TaskCount=0
-for Defender,DefenderTask in pairs(self:GetDefenderTasks())do
-TaskCount=TaskCount+1
-local Defender=Defender
-if not DefenderTask.Target then
-if Defender:IsAlive()then
-local DefenderHasTask=Defender:HasTask()
-local Fuel=Defender:GetFuelMin()*100
-local Damage=Defender:GetLife()/Defender:GetLife0()*100
-DefenderGroupCount=DefenderGroupCount+1
-Report:Add(string.format(" - %s ( %s - %s ): ( #%d ) F: %3d, D:%3d - %s",
-Defender:GetName(),
-DefenderTask.Type,
-DefenderTask.Fsm:GetState(),
-Defender:GetSize(),
-Fuel,
-Damage,
-Defender:HasTask()==true and"Executing"or"Idle"))
-end
-end
-end
-Report:Add(string.format("\n - %d Tasks - %d Defender Groups",TaskCount,DefenderGroupCount))
-Report:Add(string.format("\n - %d Queued Aircraft Launches",#self.DefenseQueue))
-for DefenseQueueID,DefenseQueueItem in pairs(self.DefenseQueue)do
-local DefenseQueueItem=DefenseQueueItem
-Report:Add(string.format(" - %s - %s",DefenseQueueItem.SquadronName,DefenseQueueItem.DefenderSquadron.TakeoffTime,DefenseQueueItem.DefenderSquadron.TakeoffInterval))
-end
-Report:Add(string.format("\n - Squadron Resources: ",#self.DefenseQueue))
-for DefenderSquadronName,DefenderSquadron in pairs(self.DefenderSquadrons)do
-Report:Add(string.format(" - %s - %s",DefenderSquadronName,DefenderSquadron.ResourceCount and tostring(DefenderSquadron.ResourceCount)or"n/a"))
-end
-self:F(Report:Text("\n"))
-trigger.action.outText(Report:Text("\n"),25)
-end
-return true
-end
-end
-do
-function AI_A2G_DISPATCHER:GetPlayerFriendliesNearBy(DetectedItem)
-local DetectedSet=DetectedItem.Set
-local PlayersNearBy=self.Detection:GetPlayersNearBy(DetectedItem)
-local PlayerTypes={}
-local PlayersCount=0
-if PlayersNearBy then
-local DetectedThreatLevel=DetectedSet:CalculateThreatLevelA2G()
-for PlayerUnitName,PlayerUnitData in pairs(PlayersNearBy)do
-local PlayerUnit=PlayerUnitData
-local PlayerName=PlayerUnit:GetPlayerName()
-if PlayerUnit:IsAirPlane()and PlayerName~=nil then
-local FriendlyUnitThreatLevel=PlayerUnit:GetThreatLevel()
-PlayersCount=PlayersCount+1
-local PlayerType=PlayerUnit:GetTypeName()
-PlayerTypes[PlayerName]=PlayerType
-if DetectedThreatLevel0 then
-for PlayerName,PlayerType in pairs(PlayerTypes)do
-PlayerTypesReport:Add(string.format('"%s" in %s',PlayerName,PlayerType))
-end
-else
-PlayerTypesReport:Add("-")
-end
-return PlayersCount,PlayerTypesReport
-end
-function AI_A2G_DISPATCHER:GetFriendliesNearBy(DetectedItem)
-local DetectedSet=DetectedItem.Set
-local FriendlyUnitsNearBy=self.Detection:GetFriendliesNearBy(DetectedItem)
-local FriendlyTypes={}
-local FriendliesCount=0
-if FriendlyUnitsNearBy then
-local DetectedThreatLevel=DetectedSet:CalculateThreatLevelA2G()
-for FriendlyUnitName,FriendlyUnitData in pairs(FriendlyUnitsNearBy)do
-local FriendlyUnit=FriendlyUnitData
-if FriendlyUnit:IsAirPlane()then
-local FriendlyUnitThreatLevel=FriendlyUnit:GetThreatLevel()
-FriendliesCount=FriendliesCount+1
-local FriendlyType=FriendlyUnit:GetTypeName()
-FriendlyTypes[FriendlyType]=FriendlyTypes[FriendlyType]and(FriendlyTypes[FriendlyType]+1)or 1
-if DetectedThreatLevel0 then
-for FriendlyType,FriendlyTypeCount in pairs(FriendlyTypes)do
-FriendlyTypesReport:Add(string.format("%d of %s",FriendlyTypeCount,FriendlyType))
-end
-else
-FriendlyTypesReport:Add("-")
-end
-return FriendliesCount,FriendlyTypesReport
-end
-function AI_A2G_DISPATCHER:SchedulerPatrol(SquadronName)
-local PatrolTaskTypes={"SEAD","CAS","BAI"}
-local PatrolTaskType=PatrolTaskTypes[math.random(1,3)]
-self:Patrol(SquadronName,PatrolTaskType)
-end
-function AI_A2G_DISPATCHER:SetSendMessages(onoff)
-self.SetSendPlayerMessages=onoff
-end
-end
-function AI_A2G_DISPATCHER:AddToSquadron(Squadron,Amount)
-local Squadron=self:GetSquadron(Squadron)
-if Squadron.ResourceCount then
-Squadron.ResourceCount=Squadron.ResourceCount+Amount
-end
-self:T({Squadron=Squadron.Name,SquadronResourceCount=Squadron.ResourceCount})
-end
-function AI_A2G_DISPATCHER:RemoveFromSquadron(Squadron,Amount)
-local Squadron=self:GetSquadron(Squadron)
-if Squadron.ResourceCount then
-Squadron.ResourceCount=Squadron.ResourceCount-Amount
-end
-self:T({Squadron=Squadron.Name,SquadronResourceCount=Squadron.ResourceCount})
-end
-AI_PATROL_ZONE={
-ClassName="AI_PATROL_ZONE",
-}
-function AI_PATROL_ZONE:New(PatrolZone,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,PatrolAltType)
-local self=BASE:Inherit(self,FSM_CONTROLLABLE:New())
-self.PatrolZone=PatrolZone
-self.PatrolFloorAltitude=PatrolFloorAltitude
-self.PatrolCeilingAltitude=PatrolCeilingAltitude
-self.PatrolMinSpeed=PatrolMinSpeed
-self.PatrolMaxSpeed=PatrolMaxSpeed
-self.PatrolAltType=PatrolAltType or"BARO"
-self:SetRefreshTimeInterval(30)
-self.CheckStatus=true
-self:ManageFuel(.2,60)
-self:ManageDamage(1)
-self.DetectedUnits={}
-self:SetStartState("None")
-self:AddTransition("*","Stop","Stopped")
-self:AddTransition("None","Start","Patrolling")
-self:AddTransition("Patrolling","Route","Patrolling")
-self:AddTransition("*","Status","*")
-self:AddTransition("*","Detect","*")
-self:AddTransition("*","Detected","*")
-self:AddTransition("*","RTB","Returning")
-self:AddTransition("*","Reset","Patrolling")
-self:AddTransition("*","Eject","*")
-self:AddTransition("*","Crash","Crashed")
-self:AddTransition("*","PilotDead","*")
-return self
-end
-function AI_PATROL_ZONE:SetSpeed(PatrolMinSpeed,PatrolMaxSpeed)
-self:F2({PatrolMinSpeed,PatrolMaxSpeed})
-self.PatrolMinSpeed=PatrolMinSpeed
-self.PatrolMaxSpeed=PatrolMaxSpeed
-end
-function AI_PATROL_ZONE:SetAltitude(PatrolFloorAltitude,PatrolCeilingAltitude)
-self:F2({PatrolFloorAltitude,PatrolCeilingAltitude})
-self.PatrolFloorAltitude=PatrolFloorAltitude
-self.PatrolCeilingAltitude=PatrolCeilingAltitude
-end
-function AI_PATROL_ZONE:SetDetectionOn()
-self:F2()
-self.DetectOn=true
-end
-function AI_PATROL_ZONE:SetDetectionOff()
-self:F2()
-self.DetectOn=false
-end
-function AI_PATROL_ZONE:SetStatusOff()
-self:F2()
-self.CheckStatus=false
-end
-function AI_PATROL_ZONE:SetDetectionActivated()
-self:F2()
-self:ClearDetectedUnits()
-self.DetectActivated=true
-self:__Detect(-self.DetectInterval)
-end
-function AI_PATROL_ZONE:SetDetectionDeactivated()
-self:F2()
-self:ClearDetectedUnits()
-self.DetectActivated=false
-end
-function AI_PATROL_ZONE:SetRefreshTimeInterval(Seconds)
-self:F2()
-if Seconds then
-self.DetectInterval=Seconds
-else
-self.DetectInterval=30
-end
-end
-function AI_PATROL_ZONE:SetDetectionZone(DetectionZone)
-self:F2()
-if DetectionZone then
-self.DetectZone=DetectionZone
-else
-self.DetectZone=nil
-end
-end
-function AI_PATROL_ZONE:GetDetectedUnits()
-self:F2()
-return self.DetectedUnits
-end
-function AI_PATROL_ZONE:ClearDetectedUnits()
-self:F2()
-self.DetectedUnits={}
-end
-function AI_PATROL_ZONE:ManageFuel(PatrolFuelThresholdPercentage,PatrolOutOfFuelOrbitTime)
-self.PatrolFuelThresholdPercentage=PatrolFuelThresholdPercentage
-self.PatrolOutOfFuelOrbitTime=PatrolOutOfFuelOrbitTime
-return self
-end
-function AI_PATROL_ZONE:ManageDamage(PatrolDamageThreshold)
-self.PatrolManageDamage=true
-self.PatrolDamageThreshold=PatrolDamageThreshold
-return self
-end
-function AI_PATROL_ZONE:onafterStart(Controllable,From,Event,To)
-self:F2()
-self:__Route(1)
-self:__Status(60)
-self:SetDetectionActivated()
-self:HandleEvent(EVENTS.PilotDead,self.OnPilotDead)
-self:HandleEvent(EVENTS.Crash,self.OnCrash)
-self:HandleEvent(EVENTS.Ejection,self.OnEjection)
-Controllable:OptionROEHoldFire()
-Controllable:OptionROTVertical()
-self.Controllable:OnReSpawn(
-function(PatrolGroup)
-self:T("ReSpawn")
-self:__Reset(1)
-self:__Route(5)
-end
-)
-self:SetDetectionOn()
-end
-function AI_PATROL_ZONE:onbeforeDetect(Controllable,From,Event,To)
-return self.DetectOn and self.DetectActivated
-end
-function AI_PATROL_ZONE:onafterDetect(Controllable,From,Event,To)
-local Detected=false
-local DetectedTargets=Controllable:GetDetectedTargets()
-for TargetID,Target in pairs(DetectedTargets or{})do
-local TargetObject=Target.object
-if TargetObject and TargetObject:isExist()and TargetObject.id_<50000000 then
-local TargetUnit=UNIT:Find(TargetObject)
-if TargetUnit and TargetUnit:IsAlive()then
-local TargetUnitName=TargetUnit:GetName()
-if self.DetectionZone then
-if TargetUnit:IsInZone(self.DetectionZone)then
-self:T({"Detected ",TargetUnit})
-if self.DetectedUnits[TargetUnit]==nil then
-self.DetectedUnits[TargetUnit]=true
-end
-Detected=true
-end
-else
-if self.DetectedUnits[TargetUnit]==nil then
-self.DetectedUnits[TargetUnit]=true
-end
-Detected=true
-end
-end
-end
-end
-self:__Detect(-self.DetectInterval)
-if Detected==true then
-self:__Detected(1.5)
-end
-end
-function AI_PATROL_ZONE:_NewPatrolRoute(AIControllable)
-local PatrolZone=AIControllable:GetState(AIControllable,"PatrolZone")
-PatrolZone:__Route(1)
-end
-function AI_PATROL_ZONE:onafterRoute(Controllable,From,Event,To)
-self:F2()
-if From=="RTB"then
-return
-end
-local life=self.Controllable:GetLife()or 0
-if self.Controllable:IsAlive()and life>1 then
-local PatrolRoute={}
-if self.Controllable:InAir()==false then
-self:T("Not in the air, finding route path within PatrolZone")
-local CurrentVec2=self.Controllable:GetVec2()
-if not CurrentVec2 then return end
-local CurrentAltitude=self.Controllable:GetAltitude()
-local CurrentPointVec3=POINT_VEC3:New(CurrentVec2.x,CurrentAltitude,CurrentVec2.y)
-local ToPatrolZoneSpeed=self.PatrolMaxSpeed
-local CurrentRoutePoint=CurrentPointVec3:WaypointAir(
-self.PatrolAltType,
-POINT_VEC3.RoutePointType.TakeOffParking,
-POINT_VEC3.RoutePointAction.FromParkingArea,
-ToPatrolZoneSpeed,
-true
-)
-PatrolRoute[#PatrolRoute+1]=CurrentRoutePoint
-else
-self:T("In the air, finding route path within PatrolZone")
-local CurrentVec2=self.Controllable:GetVec2()
-if not CurrentVec2 then return end
-local CurrentAltitude=self.Controllable:GetAltitude()
-local CurrentPointVec3=POINT_VEC3:New(CurrentVec2.x,CurrentAltitude,CurrentVec2.y)
-local ToPatrolZoneSpeed=self.PatrolMaxSpeed
-local CurrentRoutePoint=CurrentPointVec3:WaypointAir(
-self.PatrolAltType,
-POINT_VEC3.RoutePointType.TurningPoint,
-POINT_VEC3.RoutePointAction.TurningPoint,
-ToPatrolZoneSpeed,
-true
-)
-PatrolRoute[#PatrolRoute+1]=CurrentRoutePoint
-end
-local ToTargetVec2=self.PatrolZone:GetRandomVec2()
-self:T2(ToTargetVec2)
-local ToTargetAltitude=math.random(self.PatrolFloorAltitude,self.PatrolCeilingAltitude)
-local ToTargetSpeed=math.random(self.PatrolMinSpeed,self.PatrolMaxSpeed)
-self:T2({self.PatrolMinSpeed,self.PatrolMaxSpeed,ToTargetSpeed})
-local ToTargetPointVec3=POINT_VEC3:New(ToTargetVec2.x,ToTargetAltitude,ToTargetVec2.y)
-local ToTargetRoutePoint=ToTargetPointVec3:WaypointAir(
-self.PatrolAltType,
-POINT_VEC3.RoutePointType.TurningPoint,
-POINT_VEC3.RoutePointAction.TurningPoint,
-ToTargetSpeed,
-true
-)
-PatrolRoute[#PatrolRoute+1]=ToTargetRoutePoint
-self.Controllable:WayPointInitialize(PatrolRoute)
-self.Controllable:SetState(self.Controllable,"PatrolZone",self)
-self.Controllable:WayPointFunction(#PatrolRoute,1,"AI_PATROL_ZONE:_NewPatrolRoute")
-self.Controllable:WayPointExecute(1,2)
-end
-end
-function AI_PATROL_ZONE:onbeforeStatus()
-return self.CheckStatus
-end
-function AI_PATROL_ZONE:onafterStatus()
-self:F2()
-if self.Controllable and self.Controllable:IsAlive()then
-local RTB=false
-local Fuel=self.Controllable:GetFuelMin()
-if Fuel Engaging')
-self:__Engage(1)
-end
-end
-end
-function AI_CAP_ZONE:onafterAbort(Controllable,From,Event,To)
-Controllable:ClearTasks()
-self:__Route(1)
-end
-function AI_CAP_ZONE:onafterEngage(Controllable,From,Event,To)
-if Controllable and Controllable:IsAlive()then
-local EngageRoute={}
-local CurrentVec2=self.Controllable:GetVec2()
-if not CurrentVec2 then return self end
-local CurrentAltitude=self.Controllable:GetAltitude()
-local CurrentPointVec3=POINT_VEC3:New(CurrentVec2.x,CurrentAltitude,CurrentVec2.y)
-local ToEngageZoneSpeed=self.PatrolMaxSpeed
-local CurrentRoutePoint=CurrentPointVec3:WaypointAir(
-self.PatrolAltType,
-POINT_VEC3.RoutePointType.TurningPoint,
-POINT_VEC3.RoutePointAction.TurningPoint,
-ToEngageZoneSpeed,
-true
-)
-EngageRoute[#EngageRoute+1]=CurrentRoutePoint
-local ToTargetVec2=self.PatrolZone:GetRandomVec2()
-self:T2(ToTargetVec2)
-local ToTargetAltitude=math.random(self.EngageFloorAltitude,self.EngageCeilingAltitude)
-local ToTargetSpeed=math.random(self.PatrolMinSpeed,self.PatrolMaxSpeed)
-self:T2({self.PatrolMinSpeed,self.PatrolMaxSpeed,ToTargetSpeed})
-local ToTargetPointVec3=POINT_VEC3:New(ToTargetVec2.x,ToTargetAltitude,ToTargetVec2.y)
-local ToPatrolRoutePoint=ToTargetPointVec3:WaypointAir(
-self.PatrolAltType,
-POINT_VEC3.RoutePointType.TurningPoint,
-POINT_VEC3.RoutePointAction.TurningPoint,
-ToTargetSpeed,
-true
-)
-EngageRoute[#EngageRoute+1]=ToPatrolRoutePoint
-Controllable:OptionROEOpenFire()
-Controllable:OptionROTEvadeFire()
-local AttackTasks={}
-for DetectedUnit,Detected in pairs(self.DetectedUnits)do
-local DetectedUnit=DetectedUnit
-self:T({DetectedUnit,DetectedUnit:IsAlive(),DetectedUnit:IsAir()})
-if DetectedUnit:IsAlive()and DetectedUnit:IsAir()then
-if self.EngageZone then
-if DetectedUnit:IsInZone(self.EngageZone)then
-self:F({"Within Zone and Engaging ",DetectedUnit})
-AttackTasks[#AttackTasks+1]=Controllable:TaskAttackUnit(DetectedUnit)
-end
-else
-if self.EngageRange then
-if DetectedUnit:GetPointVec3():Get2DDistance(Controllable:GetPointVec3())<=self.EngageRange then
-self:F({"Within Range and Engaging",DetectedUnit})
-AttackTasks[#AttackTasks+1]=Controllable:TaskAttackUnit(DetectedUnit)
-end
-else
-AttackTasks[#AttackTasks+1]=Controllable:TaskAttackUnit(DetectedUnit)
-end
-end
-else
-self.DetectedUnits[DetectedUnit]=nil
-end
-end
-if#AttackTasks==0 then
-self:F("No targets found -> Going back to Patrolling")
-self:__Abort(1)
-self:__Route(1)
-self:SetDetectionActivated()
-else
-AttackTasks[#AttackTasks+1]=Controllable:TaskFunction("AI_CAP_ZONE.EngageRoute",self)
-EngageRoute[1].task=Controllable:TaskCombo(AttackTasks)
-self:SetDetectionDeactivated()
-end
-Controllable:Route(EngageRoute,0.5)
-end
-end
-function AI_CAP_ZONE:onafterAccomplish(Controllable,From,Event,To)
-self.Accomplished=true
-self:SetDetectionOff()
-end
-function AI_CAP_ZONE:onafterDestroy(Controllable,From,Event,To,EventData)
-if EventData.IniUnit then
-self.DetectedUnits[EventData.IniUnit]=nil
-end
-end
-function AI_CAP_ZONE:OnEventDead(EventData)
-self:F({"EventDead",EventData})
-if EventData.IniDCSUnit then
-if self.DetectedUnits and self.DetectedUnits[EventData.IniUnit]then
-self:__Destroy(1,EventData)
-end
-end
-end
-AI_CAS_ZONE={
-ClassName="AI_CAS_ZONE",
-}
-function AI_CAS_ZONE:New(PatrolZone,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,EngageZone,PatrolAltType)
-local self=BASE:Inherit(self,AI_PATROL_ZONE:New(PatrolZone,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,PatrolAltType))
-self.EngageZone=EngageZone
-self.Accomplished=false
-self:SetDetectionZone(self.EngageZone)
-self:AddTransition({"Patrolling","Engaging"},"Engage","Engaging")
-self:AddTransition("Engaging","Target","Engaging")
-self:AddTransition("Engaging","Fired","Engaging")
-self:AddTransition("*","Destroy","*")
-self:AddTransition("Engaging","Abort","Patrolling")
-self:AddTransition("Engaging","Accomplish","Patrolling")
-return self
-end
-function AI_CAS_ZONE:SetEngageZone(EngageZone)
-self:F2()
-if EngageZone then
-self.EngageZone=EngageZone
-else
-self.EngageZone=nil
-end
-end
-function AI_CAS_ZONE:onafterStart(Controllable,From,Event,To)
-self:GetParent(self).onafterStart(self,Controllable,From,Event,To)
-self:HandleEvent(EVENTS.Dead)
-self:SetDetectionDeactivated()
-end
-function AI_CAS_ZONE.EngageRoute(EngageGroup,Fsm)
-EngageGroup:F({"AI_CAS_ZONE.EngageRoute:",EngageGroup:GetName()})
-if EngageGroup:IsAlive()then
-Fsm:__Engage(1,Fsm.EngageSpeed,Fsm.EngageAltitude,Fsm.EngageWeaponExpend,Fsm.EngageAttackQty,Fsm.EngageDirection)
-end
-end
-function AI_CAS_ZONE:onbeforeEngage(Controllable,From,Event,To)
-if self.Accomplished==true then
-return false
-end
-end
-function AI_CAS_ZONE:onafterTarget(Controllable,From,Event,To)
-if Controllable:IsAlive()then
-local AttackTasks={}
-for DetectedUnit,Detected in pairs(self.DetectedUnits)do
-local DetectedUnit=DetectedUnit
-if DetectedUnit:IsAlive()then
-if DetectedUnit:IsInZone(self.EngageZone)then
-if Detected==true then
-self:F({"Target: ",DetectedUnit})
-self.DetectedUnits[DetectedUnit]=false
-local AttackTask=Controllable:TaskAttackUnit(DetectedUnit,false,self.EngageWeaponExpend,self.EngageAttackQty,self.EngageDirection,self.EngageAltitude,nil)
-self.Controllable:PushTask(AttackTask,1)
-end
-end
-else
-self.DetectedUnits[DetectedUnit]=nil
-end
-end
-self:__Target(-10)
-end
-end
-function AI_CAS_ZONE:onafterAbort(Controllable,From,Event,To)
-Controllable:ClearTasks()
-self:__Route(1)
-end
-function AI_CAS_ZONE:onafterEngage(Controllable,From,Event,To,
-EngageSpeed,
-EngageAltitude,
-EngageWeaponExpend,
-EngageAttackQty,
-EngageDirection)
-self:F("onafterEngage")
-self.EngageSpeed=EngageSpeed or 400
-self.EngageAltitude=EngageAltitude or 2000
-self.EngageWeaponExpend=EngageWeaponExpend
-self.EngageAttackQty=EngageAttackQty
-self.EngageDirection=EngageDirection
-if Controllable:IsAlive()then
-Controllable:OptionROEOpenFire()
-Controllable:OptionROTVertical()
-local EngageRoute={}
-local CurrentVec2=self.Controllable:GetVec2()
-local CurrentAltitude=self.Controllable:GetAltitude()
-local CurrentPointVec3=POINT_VEC3:New(CurrentVec2.x,CurrentAltitude,CurrentVec2.y)
-local ToEngageZoneSpeed=self.PatrolMaxSpeed
-local CurrentRoutePoint=CurrentPointVec3:WaypointAir(
-self.PatrolAltType,
-POINT_VEC3.RoutePointType.TurningPoint,
-POINT_VEC3.RoutePointAction.TurningPoint,
-self.EngageSpeed,
-true
-)
-EngageRoute[#EngageRoute+1]=CurrentRoutePoint
-local AttackTasks={}
-for DetectedUnit,Detected in pairs(self.DetectedUnits)do
-local DetectedUnit=DetectedUnit
-self:T(DetectedUnit)
-if DetectedUnit:IsAlive()then
-if DetectedUnit:IsInZone(self.EngageZone)then
-self:F({"Engaging ",DetectedUnit})
-AttackTasks[#AttackTasks+1]=Controllable:TaskAttackUnit(DetectedUnit,
-true,
-EngageWeaponExpend,
-EngageAttackQty,
-EngageDirection
-)
-end
-else
-self.DetectedUnits[DetectedUnit]=nil
-end
-end
-AttackTasks[#AttackTasks+1]=Controllable:TaskFunction("AI_CAS_ZONE.EngageRoute",self)
-EngageRoute[#EngageRoute].task=Controllable:TaskCombo(AttackTasks)
-local ToTargetVec2=self.EngageZone:GetRandomVec2()
-self:T2(ToTargetVec2)
-local ToTargetPointVec3=POINT_VEC3:New(ToTargetVec2.x,self.EngageAltitude,ToTargetVec2.y)
-local ToTargetRoutePoint=ToTargetPointVec3:WaypointAir(
-self.PatrolAltType,
-POINT_VEC3.RoutePointType.TurningPoint,
-POINT_VEC3.RoutePointAction.TurningPoint,
-self.EngageSpeed,
-true
-)
-EngageRoute[#EngageRoute+1]=ToTargetRoutePoint
-Controllable:Route(EngageRoute,0.5)
-self:SetRefreshTimeInterval(2)
-self:SetDetectionActivated()
-self:__Target(-2)
-end
-end
-function AI_CAS_ZONE:onafterAccomplish(Controllable,From,Event,To)
-self.Accomplished=true
-self:SetDetectionDeactivated()
-end
-function AI_CAS_ZONE:onafterDestroy(Controllable,From,Event,To,EventData)
-if EventData.IniUnit then
-self.DetectedUnits[EventData.IniUnit]=nil
-end
-end
-function AI_CAS_ZONE:OnEventDead(EventData)
-self:F({"EventDead",EventData})
-if EventData.IniDCSUnit then
-if self.DetectedUnits and self.DetectedUnits[EventData.IniUnit]then
-self:__Destroy(1,EventData)
-end
-end
-end
-AI_BAI_ZONE={
-ClassName="AI_BAI_ZONE",
-}
-function AI_BAI_ZONE:New(PatrolZone,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,EngageZone,PatrolAltType)
-local self=BASE:Inherit(self,AI_PATROL_ZONE:New(PatrolZone,PatrolFloorAltitude,PatrolCeilingAltitude,PatrolMinSpeed,PatrolMaxSpeed,PatrolAltType))
-self.EngageZone=EngageZone
-self.Accomplished=false
-self:SetDetectionZone(self.EngageZone)
-self:SearchOn()
-self:AddTransition({"Patrolling","Engaging"},"Engage","Engaging")
-self:AddTransition("Engaging","Target","Engaging")
-self:AddTransition("Engaging","Fired","Engaging")
-self:AddTransition("*","Destroy","*")
-self:AddTransition("Engaging","Abort","Patrolling")
-self:AddTransition("Engaging","Accomplish","Patrolling")
-return self
-end
-function AI_BAI_ZONE:SetEngageZone(EngageZone)
-self:F2()
-if EngageZone then
-self.EngageZone=EngageZone
-else
-self.EngageZone=nil
-end
-end
-function AI_BAI_ZONE:SearchOnOff(Search)
-self.Search=Search
-return self
-end
-function AI_BAI_ZONE:SearchOff()
-self:SearchOnOff(false)
-return self
-end
-function AI_BAI_ZONE:SearchOn()
-self:SearchOnOff(true)
-return self
-end
-function AI_BAI_ZONE:onafterStart(Controllable,From,Event,To)
-self:GetParent(self).onafterStart(self,Controllable,From,Event,To)
-self:HandleEvent(EVENTS.Dead)
-self:SetDetectionDeactivated()
-end
-function _NewEngageRoute(AIControllable)
-AIControllable:T("NewEngageRoute")
-local EngageZone=AIControllable:GetState(AIControllable,"EngageZone")
-EngageZone:__Engage(1,EngageZone.EngageSpeed,EngageZone.EngageAltitude,EngageZone.EngageWeaponExpend,EngageZone.EngageAttackQty,EngageZone.EngageDirection)
-end
-function AI_BAI_ZONE:onbeforeEngage(Controllable,From,Event,To)
-if self.Accomplished==true then
-return false
-end
-end
-function AI_BAI_ZONE:onafterTarget(Controllable,From,Event,To)
-self:F({"onafterTarget",self.Search,Controllable:IsAlive()})
-if Controllable:IsAlive()then
-local AttackTasks={}
-if self.Search==true then
-for DetectedUnit,Detected in pairs(self.DetectedUnits)do
-local DetectedUnit=DetectedUnit
-if DetectedUnit:IsAlive()then
-if DetectedUnit:IsInZone(self.EngageZone)then
-if Detected==true then
-self:F({"Target: ",DetectedUnit})
-self.DetectedUnits[DetectedUnit]=false
-local AttackTask=Controllable:TaskAttackUnit(DetectedUnit,false,self.EngageWeaponExpend,self.EngageAttackQty,self.EngageDirection,self.EngageAltitude,nil)
-self.Controllable:PushTask(AttackTask,1)
-end
-end
-else
-self.DetectedUnits[DetectedUnit]=nil
-end
-end
-else
-self:F("Attack zone")
-local AttackTask=Controllable:TaskAttackMapObject(
-self.EngageZone:GetPointVec2():GetVec2(),
-true,
-self.EngageWeaponExpend,
-self.EngageAttackQty,
-self.EngageDirection,
-self.EngageAltitude
-)
-self.Controllable:PushTask(AttackTask,1)
-end
-self:__Target(-10)
-end
-end
-function AI_BAI_ZONE:onafterAbort(Controllable,From,Event,To)
-Controllable:ClearTasks()
-self:__Route(1)
-end
-function AI_BAI_ZONE:onafterEngage(Controllable,From,Event,To,
-EngageSpeed,
-EngageAltitude,
-EngageWeaponExpend,
-EngageAttackQty,
-EngageDirection)
-self:F("onafterEngage")
-self.EngageSpeed=EngageSpeed or 400
-self.EngageAltitude=EngageAltitude or 2000
-self.EngageWeaponExpend=EngageWeaponExpend
-self.EngageAttackQty=EngageAttackQty
-self.EngageDirection=EngageDirection
-if Controllable:IsAlive()then
-local EngageRoute={}
-local CurrentVec2=self.Controllable:GetVec2()
-local CurrentAltitude=self.Controllable:GetAltitude()
-local CurrentPointVec3=POINT_VEC3:New(CurrentVec2.x,CurrentAltitude,CurrentVec2.y)
-local ToEngageZoneSpeed=self.PatrolMaxSpeed
-local CurrentRoutePoint=CurrentPointVec3:WaypointAir(
-self.PatrolAltType,
-POINT_VEC3.RoutePointType.TurningPoint,
-POINT_VEC3.RoutePointAction.TurningPoint,
-self.EngageSpeed,
-true
-)
-EngageRoute[#EngageRoute+1]=CurrentRoutePoint
-local AttackTasks={}
-if self.Search==true then
-for DetectedUnitID,DetectedUnitData in pairs(self.DetectedUnits)do
-local DetectedUnit=DetectedUnitData
-self:T(DetectedUnit)
-if DetectedUnit:IsAlive()then
-if DetectedUnit:IsInZone(self.EngageZone)then
-self:F({"Engaging ",DetectedUnit})
-AttackTasks[#AttackTasks+1]=Controllable:TaskBombing(
-DetectedUnit:GetPointVec2():GetVec2(),
-true,
-EngageWeaponExpend,
-EngageAttackQty,
-EngageDirection,
-EngageAltitude
-)
-end
-else
-self.DetectedUnits[DetectedUnit]=nil
-end
-end
-else
-self:F("Attack zone")
-AttackTasks[#AttackTasks+1]=Controllable:TaskAttackMapObject(
-self.EngageZone:GetPointVec2():GetVec2(),
-true,
-EngageWeaponExpend,
-EngageAttackQty,
-EngageDirection,
-EngageAltitude
-)
-end
-EngageRoute[#EngageRoute].task=Controllable:TaskCombo(AttackTasks)
-local ToTargetVec2=self.EngageZone:GetRandomVec2()
-self:T2(ToTargetVec2)
-local ToTargetPointVec3=POINT_VEC3:New(ToTargetVec2.x,self.EngageAltitude,ToTargetVec2.y)
-local ToTargetRoutePoint=ToTargetPointVec3:WaypointAir(
-self.PatrolAltType,
-POINT_VEC3.RoutePointType.TurningPoint,
-POINT_VEC3.RoutePointAction.TurningPoint,
-self.EngageSpeed,
-true
-)
-EngageRoute[#EngageRoute+1]=ToTargetRoutePoint
-Controllable:OptionROEOpenFire()
-Controllable:OptionROTVertical()
-Controllable:WayPointInitialize(EngageRoute)
-Controllable:SetState(Controllable,"EngageZone",self)
-Controllable:WayPointFunction(#EngageRoute,1,"_NewEngageRoute")
-Controllable:WayPointExecute(1)
-self:SetRefreshTimeInterval(2)
-self:SetDetectionActivated()
-self:__Target(-2)
-end
-end
-function AI_BAI_ZONE:onafterAccomplish(Controllable,From,Event,To)
-self.Accomplished=true
-self:SetDetectionDeactivated()
-end
-function AI_BAI_ZONE:onafterDestroy(Controllable,From,Event,To,EventData)
-if EventData.IniUnit then
-self.DetectedUnits[EventData.IniUnit]=nil
-end
-end
-function AI_BAI_ZONE:OnEventDead(EventData)
-self:F({"EventDead",EventData})
-if EventData.IniDCSUnit then
-if self.DetectedUnits and self.DetectedUnits[EventData.IniUnit]then
-self:__Destroy(1,EventData)
-end
-end
-end
-AI_FORMATION={
-ClassName="AI_FORMATION",
-FollowName=nil,
-FollowUnit=nil,
-FollowGroupSet=nil,
-FollowMode=1,
-MODE={
-FOLLOW=1,
-MISSION=2,
-},
-FollowScheduler=nil,
-OptionROE=AI.Option.Air.val.ROE.OPEN_FIRE,
-OptionReactionOnThreat=AI.Option.Air.val.REACTION_ON_THREAT.ALLOW_ABORT_MISSION,
-dtFollow=0.5,
-}
-AI_FORMATION.__Enum={}
-AI_FORMATION.__Enum.Formation={
-None=0,
-Mission=1,
-Line=2,
-Trail=3,
-Stack=4,
-LeftLine=5,
-RightLine=6,
-LeftWing=7,
-RightWing=8,
-Vic=9,
-Box=10,
-}
-AI_FORMATION.__Enum.Mode={
-Mission="M",
-Formation="F",
-Attack="A",
-Reconnaissance="R",
-}
-AI_FORMATION.__Enum.ReportType={
-Airborne="*",
-Airborne="A",
-GroundRadar="R",
-Ground="G",
-}
-function AI_FORMATION:New(FollowUnit,FollowGroupSet,FollowName,FollowBriefing)
-local self=BASE:Inherit(self,FSM_SET:New(FollowGroupSet))
-self:F({FollowUnit,FollowGroupSet,FollowName})
-self.FollowUnit=FollowUnit
-self.FollowGroupSet=FollowGroupSet
-self.FollowGroupSet:ForEachGroup(
-function(FollowGroup)
-FollowGroup:SetState(self,"Mode",self.__Enum.Mode.Formation)
-end
-)
-self:SetFlightModeFormation()
-self:SetFlightRandomization(2)
-self:SetStartState("None")
-self:AddTransition("*","Stop","Stopped")
-self:AddTransition({"None","Stopped"},"Start","Following")
-self:AddTransition("*","FormationLine","*")
-self:AddTransition("*","FormationTrail","*")
-self:AddTransition("*","FormationStack","*")
-self:AddTransition("*","FormationLeftLine","*")
-self:AddTransition("*","FormationRightLine","*")
-self:AddTransition("*","FormationLeftWing","*")
-self:AddTransition("*","FormationRightWing","*")
-self:AddTransition("*","FormationCenterWing","*")
-self:AddTransition("*","FormationVic","*")
-self:AddTransition("*","FormationBox","*")
-self:AddTransition("*","Follow","Following")
-self:FormationLeftLine(500,0,250,250)
-self.FollowName=FollowName
-self.FollowBriefing=FollowBriefing
-self.CT1=0
-self.GT1=0
-self.FollowMode=AI_FORMATION.MODE.MISSION
-return self
-end
-function AI_FORMATION:SetFollowTimeInterval(dt)
-self.dtFollow=dt or 0.5
-return self
-end
-function AI_FORMATION:TestSmokeDirectionVector(SmokeDirection)
-self.SmokeDirectionVector=(SmokeDirection==true)and true or false
-return self
-end
-function AI_FORMATION:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,ZStart,ZSpace,Formation)
-self:F({FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,ZStart,ZSpace,Formation})
-XStart=XStart or self.XStart
-XSpace=XSpace or self.XSpace
-YStart=YStart or self.YStart
-YSpace=YSpace or self.YSpace
-ZStart=ZStart or self.ZStart
-ZSpace=ZSpace or self.ZSpace
-FollowGroupSet:Flush(self)
-local FollowSet=FollowGroupSet:GetSet()
-local i=1
-for FollowID,FollowGroup in pairs(FollowSet)do
-local PointVec3=POINT_VEC3:New()
-PointVec3:SetX(XStart+i*XSpace)
-PointVec3:SetY(YStart+i*YSpace)
-PointVec3:SetZ(ZStart+i*ZSpace)
-local Vec3=PointVec3:GetVec3()
-FollowGroup:SetState(self,"FormationVec3",Vec3)
-i=i+1
-FollowGroup:SetState(FollowGroup,"Formation",Formation)
-end
-return self
-end
-function AI_FORMATION:onafterFormationTrail(FollowGroupSet,From,Event,To,XStart,XSpace,YStart)
-self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,0,0,self.__Enum.Formation.Trail)
-return self
-end
-function AI_FORMATION:onafterFormationStack(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace)
-self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,0,0,self.__Enum.Formation.Stack)
-return self
-end
-function AI_FORMATION:onafterFormationLeftLine(FollowGroupSet,From,Event,To,XStart,YStart,ZStart,ZSpace)
-self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,0,YStart,0,-ZStart,-ZSpace,self.__Enum.Formation.LeftLine)
-return self
-end
-function AI_FORMATION:onafterFormationRightLine(FollowGroupSet,From,Event,To,XStart,YStart,ZStart,ZSpace)
-self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,0,YStart,0,ZStart,ZSpace,self.__Enum.Formation.RightLine)
-return self
-end
-function AI_FORMATION:onafterFormationLeftWing(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,ZStart,ZSpace)
-self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,-ZStart,-ZSpace,self.__Enum.Formation.LeftWing)
-return self
-end
-function AI_FORMATION:onafterFormationRightWing(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,ZStart,ZSpace)
-self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,ZStart,ZSpace,self.__Enum.Formation.RightWing)
-return self
-end
-function AI_FORMATION:onafterFormationCenterWing(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,ZStart,ZSpace)
-local FollowSet=FollowGroupSet:GetSet()
-local i=0
-for FollowID,FollowGroup in pairs(FollowSet)do
-local PointVec3=POINT_VEC3:New()
-local Side=(i%2==0)and 1 or-1
-local Row=i/2+1
-PointVec3:SetX(XStart+Row*XSpace)
-PointVec3:SetY(YStart)
-PointVec3:SetZ(Side*(ZStart+i*ZSpace))
-local Vec3=PointVec3:GetVec3()
-FollowGroup:SetState(self,"FormationVec3",Vec3)
-i=i+1
-FollowGroup:SetState(FollowGroup,"Formation",self.__Enum.Formation.Vic)
-end
-return self
-end
-function AI_FORMATION:onafterFormationVic(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,ZStart,ZSpace)
-self:onafterFormationCenterWing(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,ZStart,ZSpace)
-return self
-end
-function AI_FORMATION:onafterFormationBox(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,ZStart,ZSpace,ZLevels)
-local FollowSet=FollowGroupSet:GetSet()
-local i=0
-for FollowID,FollowGroup in pairs(FollowSet)do
-local PointVec3=POINT_VEC3:New()
-local ZIndex=i%ZLevels
-local XIndex=math.floor(i/ZLevels)
-local YIndex=math.floor(i/ZLevels)
-PointVec3:SetX(XStart+XIndex*XSpace)
-PointVec3:SetY(YStart+YIndex*YSpace)
-PointVec3:SetZ(-ZStart-(ZSpace*ZLevels/2)+ZSpace*ZIndex)
-local Vec3=PointVec3:GetVec3()
-FollowGroup:SetState(self,"FormationVec3",Vec3)
-i=i+1
-FollowGroup:SetState(FollowGroup,"Formation",self.__Enum.Formation.Box)
-end
-return self
-end
-function AI_FORMATION:SetFlightRandomization(FlightRandomization)
-self.FlightRandomization=FlightRandomization
-return self
-end
-function AI_FORMATION:GetFlightMode(FollowGroup)
-if FollowGroup then
-FollowGroup:SetState(FollowGroup,"PreviousMode",FollowGroup:GetState(FollowGroup,"Mode"))
-FollowGroup:SetState(FollowGroup,"Mode",self.__Enum.Mode.Mission)
-end
-return FollowGroup:GetState(FollowGroup,"Mode")
-end
-function AI_FORMATION:SetFlightModeMission(FollowGroup)
-if FollowGroup then
-FollowGroup:SetState(FollowGroup,"PreviousMode",FollowGroup:GetState(FollowGroup,"Mode"))
-FollowGroup:SetState(FollowGroup,"Mode",self.__Enum.Mode.Mission)
-else
-self.FollowGroupSet:ForSomeGroupAlive(
-function(FollowGroup)
-FollowGroup:SetState(FollowGroup,"PreviousMode",FollowGroup:GetState(FollowGroup,"Mode"))
-FollowGroup:SetState(FollowGroup,"Mode",self.__Enum.Mode.Mission)
-end
-)
-end
-return self
-end
-function AI_FORMATION:SetFlightModeAttack(FollowGroup)
-if FollowGroup then
-FollowGroup:SetState(FollowGroup,"PreviousMode",FollowGroup:GetState(FollowGroup,"Mode"))
-FollowGroup:SetState(FollowGroup,"Mode",self.__Enum.Mode.Attack)
-else
-self.FollowGroupSet:ForSomeGroupAlive(
-function(FollowGroup)
-FollowGroup:SetState(FollowGroup,"PreviousMode",FollowGroup:GetState(FollowGroup,"Mode"))
-FollowGroup:SetState(FollowGroup,"Mode",self.__Enum.Mode.Attack)
-end
-)
-end
-return self
-end
-function AI_FORMATION:SetFlightModeFormation(FollowGroup)
-if FollowGroup then
-FollowGroup:SetState(FollowGroup,"PreviousMode",FollowGroup:GetState(FollowGroup,"Mode"))
-FollowGroup:SetState(FollowGroup,"Mode",self.__Enum.Mode.Formation)
-else
-self.FollowGroupSet:ForSomeGroupAlive(
-function(FollowGroup)
-FollowGroup:SetState(FollowGroup,"PreviousMode",FollowGroup:GetState(FollowGroup,"Mode"))
-FollowGroup:SetState(FollowGroup,"Mode",self.__Enum.Mode.Formation)
-end
-)
-end
-return self
-end
-function AI_FORMATION:onafterStop(FollowGroupSet,From,Event,To)
-self:E("Stopping formation.")
-end
-function AI_FORMATION:onbeforeFollow(FollowGroupSet,From,Event,To)
-if From=="Stopped"then
-return false
-end
-return true
-end
-function AI_FORMATION:onenterFollowing(FollowGroupSet)
-if self.FollowUnit:IsAlive()then
-local ClientUnit=self.FollowUnit
-local CT1,CT2,CV1,CV2
-CT1=ClientUnit:GetState(self,"CT1")
-local CuVec3=ClientUnit:GetVec3()
-if CT1==nil or CT1==0 then
-ClientUnit:SetState(self,"CV1",CuVec3)
-ClientUnit:SetState(self,"CT1",timer.getTime())
-else
-CT1=ClientUnit:GetState(self,"CT1")
-CT2=timer.getTime()
-CV1=ClientUnit:GetState(self,"CV1")
-CV2=CuVec3
-ClientUnit:SetState(self,"CT1",CT2)
-ClientUnit:SetState(self,"CV1",CV2)
-end
-for _,_group in pairs(FollowGroupSet:GetSet())do
-local group=_group
-if group and group:IsAlive()then
-self:FollowMe(group,ClientUnit,CT1,CV1,CT2,CV2)
-end
-end
-self:__Follow(-self.dtFollow)
-end
-end
-function AI_FORMATION:FollowMe(FollowGroup,ClientUnit,CT1,CV1,CT2,CV2)
-if FollowGroup:GetState(FollowGroup,"Mode")==self.__Enum.Mode.Formation and not self:Is("Stopped")then
-self:T({Mode=FollowGroup:GetState(FollowGroup,"Mode")})
-FollowGroup:OptionROTEvadeFire()
-FollowGroup:OptionROEReturnFire()
-local GroupUnit=FollowGroup:GetUnit(1)
-local GuVec3=GroupUnit:GetVec3()
-local FollowFormation=FollowGroup:GetState(self,"FormationVec3")
-if FollowFormation then
-local FollowDistance=FollowFormation.x
-local GT1=GroupUnit:GetState(self,"GT1")
-if CT1==nil or CT1==0 or GT1==nil or GT1==0 then
-GroupUnit:SetState(self,"GV1",GuVec3)
-GroupUnit:SetState(self,"GT1",timer.getTime())
-else
-local CD=((CV2.x-CV1.x)^2+(CV2.y-CV1.y)^2+(CV2.z-CV1.z)^2)^0.5
-local CT=CT2-CT1
-local CS=(3600/CT)*(CD/1000)/3.6
-local CDv={x=CV2.x-CV1.x,y=CV2.y-CV1.y,z=CV2.z-CV1.z}
-local Ca=math.atan2(CDv.x,CDv.z)
-local GT1=GroupUnit:GetState(self,"GT1")
-local GT2=timer.getTime()
-local GV1=GroupUnit:GetState(self,"GV1")
-local GV2=GuVec3
-GV2.x=GV2.x+math.random(-self.FlightRandomization/2,self.FlightRandomization/2)
-GV2.y=GV2.y+math.random(-self.FlightRandomization/2,self.FlightRandomization/2)
-GV2.z=GV2.z+math.random(-self.FlightRandomization/2,self.FlightRandomization/2)
-GroupUnit:SetState(self,"GT1",GT2)
-GroupUnit:SetState(self,"GV1",GV2)
-local GD=((GV2.x-GV1.x)^2+(GV2.y-GV1.y)^2+(GV2.z-GV1.z)^2)^0.5
-local GT=GT2-GT1
-local GDv={x=GV2.x-CV1.x,y=GV2.y-CV1.y,z=GV2.z-CV1.z}
-local Alpha_T=math.atan2(GDv.x,GDv.z)-math.atan2(CDv.x,CDv.z)
-local Alpha_R=(Alpha_T<0)and Alpha_T+2*math.pi or Alpha_T
-local Position=math.cos(Alpha_R)
-local GD=((GDv.x)^2+(GDv.z)^2)^0.5
-local Distance=GD*Position+-CS*0.5
-local GV={x=GV2.x-CV2.x,y=GV2.y-CV2.y,z=GV2.z-CV2.z}
-local GH2={x=GV2.x,y=CV2.y+FollowFormation.y,z=GV2.z}
-local alpha=math.atan2(GV.x,GV.z)
-local GVx=FollowFormation.z*math.cos(Ca)+FollowFormation.x*math.sin(Ca)
-local GVz=FollowFormation.x*math.cos(Ca)-FollowFormation.z*math.sin(Ca)
-local Inclination=(Distance+FollowFormation.x)/10
-if Inclination<-30 then
-Inclination=-30
-end
-local CVI={
-x=CV2.x+CS*10*math.sin(Ca),
-y=GH2.y+Inclination,
-y=GH2.y,
-z=CV2.z+CS*10*math.cos(Ca),
-}
-local DV={x=CV2.x-CVI.x,y=CV2.y-CVI.y,z=CV2.z-CVI.z}
-local DVu={x=DV.x/FollowDistance,y=DV.y,z=DV.z/FollowDistance}
-local GDV={x=CVI.x,y=CVI.y,z=CVI.z}
-local ADDx=FollowFormation.x*math.cos(alpha)-FollowFormation.z*math.sin(alpha)
-local ADDz=FollowFormation.z*math.cos(alpha)+FollowFormation.x*math.sin(alpha)
-local GDV_Formation={
-x=GDV.x-GVx,
-y=GDV.y,
-z=GDV.z-GVz
-}
-if self.SmokeDirectionVector==true then
-trigger.action.smoke(GDV,trigger.smokeColor.Green)
-trigger.action.smoke(GDV_Formation,trigger.smokeColor.White)
-end
-local Time=120
-local Speed=-(Distance+FollowFormation.x)/Time
-if Distance>-10000 then
-Speed=-(Distance+FollowFormation.x)/60
-end
-if Distance>-2500 then
-Speed=-(Distance+FollowFormation.x)/20
-end
-local GS=Speed+CS
-FollowGroup:RouteToVec3(GDV_Formation,GS)
-end
-end
-end
-end
-AI_ESCORT={
-ClassName="AI_ESCORT",
-EscortName=nil,
-EscortUnit=nil,
-EscortGroup=nil,
-EscortMode=1,
-Targets={},
-FollowScheduler=nil,
-ReportTargets=true,
-OptionROE=AI.Option.Air.val.ROE.OPEN_FIRE,
-OptionReactionOnThreat=AI.Option.Air.val.REACTION_ON_THREAT.ALLOW_ABORT_MISSION,
-SmokeDirectionVector=false,
-TaskPoints={}
-}
-AI_ESCORT.Detection=nil
-function AI_ESCORT:New(EscortUnit,EscortGroupSet,EscortName,EscortBriefing)
-local self=BASE:Inherit(self,AI_FORMATION:New(EscortUnit,EscortGroupSet,EscortName,EscortBriefing))
-self:F({EscortUnit,EscortGroupSet})
-self.PlayerUnit=self.FollowUnit
-self.PlayerGroup=self.FollowUnit:GetGroup()
-self.EscortName=EscortName
-self.EscortGroupSet=EscortGroupSet
-self.EscortGroupSet:SetSomeIteratorLimit(8)
-self.EscortBriefing=EscortBriefing
-self.Menu={}
-self.Menu.HoldAtEscortPosition=self.Menu.HoldAtEscortPosition or{}
-self.Menu.HoldAtLeaderPosition=self.Menu.HoldAtLeaderPosition or{}
-self.Menu.Flare=self.Menu.Flare or{}
-self.Menu.Smoke=self.Menu.Smoke or{}
-self.Menu.Targets=self.Menu.Targets or{}
-self.Menu.ROE=self.Menu.ROE or{}
-self.Menu.ROT=self.Menu.ROT or{}
-self.FollowDistance=100
-self.CT1=0
-self.GT1=0
-EscortGroupSet:ForEachGroup(
-function(EscortGroup)
-if not self.PlayerUnit._EscortGroups then
-self.PlayerUnit._EscortGroups={}
-end
-if not self.PlayerUnit._EscortGroups[EscortGroup:GetName()]then
-self.PlayerUnit._EscortGroups[EscortGroup:GetName()]={}
-self.PlayerUnit._EscortGroups[EscortGroup:GetName()].EscortGroup=EscortGroup
-self.PlayerUnit._EscortGroups[EscortGroup:GetName()].EscortName=self.EscortName
-self.PlayerUnit._EscortGroups[EscortGroup:GetName()].Detection=self.Detection
-end
-end
-)
-self:SetFlightReportType(self.__Enum.ReportType.All)
-return self
-end
-function AI_ESCORT:_InitFlightMenus()
-self:SetFlightMenuJoinUp()
-self:SetFlightMenuFormation("Trail")
-self:SetFlightMenuFormation("Stack")
-self:SetFlightMenuFormation("LeftLine")
-self:SetFlightMenuFormation("RightLine")
-self:SetFlightMenuFormation("LeftWing")
-self:SetFlightMenuFormation("RightWing")
-self:SetFlightMenuFormation("Vic")
-self:SetFlightMenuFormation("Box")
-self:SetFlightMenuHoldAtEscortPosition()
-self:SetFlightMenuHoldAtLeaderPosition()
-self:SetFlightMenuFlare()
-self:SetFlightMenuSmoke()
-self:SetFlightMenuROE()
-self:SetFlightMenuROT()
-self:SetFlightMenuTargets()
-self:SetFlightMenuReportType()
-end
-function AI_ESCORT:_InitEscortMenus(EscortGroup)
-EscortGroup.EscortMenu=MENU_GROUP:New(self.PlayerGroup,EscortGroup:GetCallsign(),self.MainMenu)
-self:SetEscortMenuJoinUp(EscortGroup)
-self:SetEscortMenuResumeMission(EscortGroup)
-self:SetEscortMenuHoldAtEscortPosition(EscortGroup)
-self:SetEscortMenuHoldAtLeaderPosition(EscortGroup)
-self:SetEscortMenuFlare(EscortGroup)
-self:SetEscortMenuSmoke(EscortGroup)
-self:SetEscortMenuROE(EscortGroup)
-self:SetEscortMenuROT(EscortGroup)
-self:SetEscortMenuTargets(EscortGroup)
-end
-function AI_ESCORT:_InitEscortRoute(EscortGroup)
-EscortGroup.MissionRoute=EscortGroup:GetTaskRoute()
-end
-function AI_ESCORT:onafterStart(EscortGroupSet)
-self:F()
-EscortGroupSet:ForEachGroup(
-function(EscortGroup)
-EscortGroup:WayPointInitialize()
-EscortGroup:OptionROTVertical()
-EscortGroup:OptionROEOpenFire()
-end
-)
-local LeaderEscort=EscortGroupSet:GetFirst()
-if LeaderEscort then
-local Report=REPORT:New("Escort reporting:")
-Report:Add("Joining Up "..EscortGroupSet:GetUnitTypeNames():Text(", ").." from "..LeaderEscort:GetCoordinate():ToString(self.PlayerUnit))
-LeaderEscort:MessageTypeToGroup(Report:Text(),MESSAGE.Type.Information,self.PlayerUnit)
-end
-self.Detection=DETECTION_AREAS:New(EscortGroupSet,5000)
-self.Detection:InitDetectVisual(true)
-self.Detection:InitDetectIRST(true)
-self.Detection:InitDetectOptical(true)
-self.Detection:InitDetectRadar(true)
-self.Detection:InitDetectRWR(true)
-self.Detection:SetAcceptRange(100000)
-self.Detection:__Start(30)
-self.MainMenu=MENU_GROUP:New(self.PlayerGroup,self.EscortName)
-self.FlightMenu=MENU_GROUP:New(self.PlayerGroup,"Flight",self.MainMenu)
-self:_InitFlightMenus()
-self.EscortGroupSet:ForSomeGroupAlive(
-function(EscortGroup)
-self:_InitEscortMenus(EscortGroup)
-self:_InitEscortRoute(EscortGroup)
-self:SetFlightModeFormation(EscortGroup)
-function EscortGroup:OnEventDeadOrCrash(EventData)
-self:F({"EventDead",EventData})
-self.EscortMenu:Remove()
-end
-EscortGroup:HandleEvent(EVENTS.Dead,EscortGroup.OnEventDeadOrCrash)
-EscortGroup:HandleEvent(EVENTS.Crash,EscortGroup.OnEventDeadOrCrash)
-end
-)
-end
-function AI_ESCORT:onafterStop(EscortGroupSet)
-self:F()
-EscortGroupSet:ForEachGroup(
-function(EscortGroup)
-EscortGroup:WayPointInitialize()
-EscortGroup:OptionROTVertical()
-EscortGroup:OptionROEOpenFire()
-end
-)
-self.Detection:Stop()
-self.MainMenu:Remove()
-end
-function AI_ESCORT:SetDetection(Detection)
-self.Detection=Detection
-self.EscortGroup.Detection=self.Detection
-self.PlayerUnit._EscortGroups[self.EscortGroup:GetName()].Detection=self.EscortGroup.Detection
-Detection:__Start(1)
-end
-function AI_ESCORT:TestSmokeDirectionVector(SmokeDirection)
-self.SmokeDirectionVector=(SmokeDirection==true)and true or false
-end
-function AI_ESCORT:MenusHelicopters(XStart,XSpace,YStart,YSpace,ZStart,ZSpace,ZLevels)
-self:F()
-self.XStart=XStart or 50
-self.XSpace=XSpace or 50
-self.YStart=YStart or 50
-self.YSpace=YSpace or 50
-self.ZStart=ZStart or 50
-self.ZSpace=ZSpace or 50
-self.ZLevels=ZLevels or 10
-self:MenuJoinUp()
-self:MenuFormationTrail(self.XStart,self.XSpace,self.YStart)
-self:MenuFormationStack(self.XStart,self.XSpace,self.YStart,self.YSpace)
-self:MenuFormationLeftLine(self.XStart,self.YStart,self.ZStart,self.ZSpace)
-self:MenuFormationRightLine(self.XStart,self.YStart,self.ZStart,self.ZSpace)
-self:MenuFormationLeftWing(self.XStart,self.XSpace,self.YStart,self.ZStart,self.ZSpace)
-self:MenuFormationRightWing(self.XStart,self.XSpace,self.YStart,self.ZStart,self.ZSpace)
-self:MenuFormationVic(self.XStart,self.XSpace,self.YStart,self.YSpace,self.ZStart,self.ZSpace)
-self:MenuFormationBox(self.XStart,self.XSpace,self.YStart,self.YSpace,self.ZStart,self.ZSpace,self.ZLevels)
-self:MenuHoldAtEscortPosition(30)
-self:MenuHoldAtEscortPosition(100)
-self:MenuHoldAtEscortPosition(500)
-self:MenuHoldAtLeaderPosition(30,500)
-self:MenuFlare()
-self:MenuSmoke()
-self:MenuTargets(60)
-self:MenuAssistedAttack()
-self:MenuROE()
-self:MenuROT()
-return self
-end
-function AI_ESCORT:MenusAirplanes(XStart,XSpace,YStart,YSpace,ZStart,ZSpace,ZLevels)
-self:F()
-self.XStart=XStart or 50
-self.XSpace=XSpace or 50
-self.YStart=YStart or 50
-self.YSpace=YSpace or 50
-self.ZStart=ZStart or 50
-self.ZSpace=ZSpace or 50
-self.ZLevels=ZLevels or 10
-self:MenuJoinUp()
-self:MenuFormationTrail(self.XStart,self.XSpace,self.YStart)
-self:MenuFormationStack(self.XStart,self.XSpace,self.YStart,self.YSpace)
-self:MenuFormationLeftLine(self.XStart,self.YStart,self.ZStart,self.ZSpace)
-self:MenuFormationRightLine(self.XStart,self.YStart,self.ZStart,self.ZSpace)
-self:MenuFormationLeftWing(self.XStart,self.XSpace,self.YStart,self.ZStart,self.ZSpace)
-self:MenuFormationRightWing(self.XStart,self.XSpace,self.YStart,self.ZStart,self.ZSpace)
-self:MenuFormationVic(self.XStart,self.XSpace,self.YStart,self.YSpace,self.ZStart,self.ZSpace)
-self:MenuFormationBox(self.XStart,self.XSpace,self.YStart,self.YSpace,self.ZStart,self.ZSpace,self.ZLevels)
-self:MenuHoldAtEscortPosition(1000,500)
-self:MenuHoldAtLeaderPosition(1000,500)
-self:MenuFlare()
-self:MenuSmoke()
-self:MenuTargets(60)
-self:MenuAssistedAttack()
-self:MenuROE()
-self:MenuROT()
-return self
-end
-function AI_ESCORT:SetFlightMenuFormation(Formation)
-local FormationID="Formation"..Formation
-local MenuFormation=self.Menu[FormationID]
-if MenuFormation then
-local Arguments=MenuFormation.Arguments
-local FlightMenuFormation=MENU_GROUP:New(self.PlayerGroup,"Formation",self.MainMenu)
-local MenuFlightFormationID=MENU_GROUP_COMMAND:New(self.PlayerGroup,Formation,FlightMenuFormation,
-function(self,Formation,...)
-self.EscortGroupSet:ForSomeGroupAlive(
-function(EscortGroup,self,Formation,Arguments)
-if EscortGroup:IsAir()then
-self:E({FormationID=FormationID})
-self[FormationID](self,unpack(Arguments))
-end
-end,self,Formation,Arguments
-)
-end,self,Formation,Arguments
-)
-end
-return self
-end
-function AI_ESCORT:MenuFormation(Formation,...)
-local FormationID="Formation"..Formation
-self.Menu[FormationID]=self.Menu[FormationID]or{}
-self.Menu[FormationID].Arguments=arg
-end
-function AI_ESCORT:MenuFormationTrail(XStart,XSpace,YStart)
-self:MenuFormation("Trail",XStart,XSpace,YStart)
-return self
-end
-function AI_ESCORT:MenuFormationStack(XStart,XSpace,YStart,YSpace)
-self:MenuFormation("Stack",XStart,XSpace,YStart,YSpace)
-return self
-end
-function AI_ESCORT:MenuFormationLeftLine(XStart,YStart,ZStart,ZSpace)
-self:MenuFormation("LeftLine",XStart,YStart,ZStart,ZSpace)
-return self
-end
-function AI_ESCORT:MenuFormationRightLine(XStart,YStart,ZStart,ZSpace)
-self:MenuFormation("RightLine",XStart,YStart,ZStart,ZSpace)
-return self
-end
-function AI_ESCORT:MenuFormationLeftWing(XStart,XSpace,YStart,ZStart,ZSpace)
-self:MenuFormation("LeftWing",XStart,XSpace,YStart,ZStart,ZSpace)
-return self
-end
-function AI_ESCORT:MenuFormationRightWing(XStart,XSpace,YStart,ZStart,ZSpace)
-self:MenuFormation("RightWing",XStart,XSpace,YStart,ZStart,ZSpace)
-return self
-end
-function AI_ESCORT:MenuFormationCenterWing(XStart,XSpace,YStart,YSpace,ZStart,ZSpace)
-self:MenuFormation("CenterWing",XStart,XSpace,YStart,YSpace,ZStart,ZSpace)
-return self
-end
-function AI_ESCORT:MenuFormationVic(XStart,XSpace,YStart,YSpace,ZStart,ZSpace)
-self:MenuFormation("Vic",XStart,XSpace,YStart,YSpace,ZStart,ZSpace)
-return self
-end
-function AI_ESCORT:MenuFormationBox(XStart,XSpace,YStart,YSpace,ZStart,ZSpace,ZLevels)
-self:MenuFormation("Box",XStart,XSpace,YStart,YSpace,ZStart,ZSpace,ZLevels)
-return self
-end
-function AI_ESCORT:SetFlightMenuJoinUp()
-if self.Menu.JoinUp==true then
-local FlightMenuReportNavigation=MENU_GROUP:New(self.PlayerGroup,"Navigation",self.FlightMenu)
-local FlightMenuJoinUp=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Join Up",FlightMenuReportNavigation,AI_ESCORT._FlightJoinUp,self)
-end
-end
-function AI_ESCORT:SetEscortMenuJoinUp(EscortGroup)
-if self.Menu.JoinUp==true then
-if EscortGroup:IsAir()then
-local EscortGroupName=EscortGroup:GetName()
-local EscortMenuReportNavigation=MENU_GROUP:New(self.PlayerGroup,"Navigation",EscortGroup.EscortMenu)
-local EscortMenuJoinUp=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Join Up",EscortMenuReportNavigation,AI_ESCORT._JoinUp,self,EscortGroup)
-end
-end
-end
-function AI_ESCORT:MenuJoinUp()
-self.Menu.JoinUp=true
-return self
-end
-function AI_ESCORT:SetFlightMenuHoldAtEscortPosition()
-for _,MenuHoldAtEscortPosition in pairs(self.Menu.HoldAtEscortPosition or{})do
-local FlightMenuReportNavigation=MENU_GROUP:New(self.PlayerGroup,"Navigation",self.FlightMenu)
-local FlightMenuHoldPosition=MENU_GROUP_COMMAND
-:New(
-self.PlayerGroup,
-MenuHoldAtEscortPosition.MenuText,
-FlightMenuReportNavigation,
-AI_ESCORT._FlightHoldPosition,
-self,
-nil,
-MenuHoldAtEscortPosition.Height,
-MenuHoldAtEscortPosition.Speed
-)
-end
-return self
-end
-function AI_ESCORT:SetEscortMenuHoldAtEscortPosition(EscortGroup)
-for _,HoldAtEscortPosition in pairs(self.Menu.HoldAtEscortPosition or{})do
-if EscortGroup:IsAir()then
-local EscortGroupName=EscortGroup:GetName()
-local EscortMenuReportNavigation=MENU_GROUP:New(self.PlayerGroup,"Navigation",EscortGroup.EscortMenu)
-local EscortMenuHoldPosition=MENU_GROUP_COMMAND
-:New(
-self.PlayerGroup,
-HoldAtEscortPosition.MenuText,
-EscortMenuReportNavigation,
-AI_ESCORT._HoldPosition,
-self,
-EscortGroup,
-EscortGroup,
-HoldAtEscortPosition.Height,
-HoldAtEscortPosition.Speed
-)
-end
-end
-return self
-end
-function AI_ESCORT:MenuHoldAtEscortPosition(Height,Speed,MenuTextFormat)
-self:F({Height,Speed,MenuTextFormat})
-if not Height then
-Height=30
-end
-if not Speed then
-Speed=0
-end
-local MenuText=""
-if not MenuTextFormat then
-if Speed==0 then
-MenuText=string.format("Hold at %d meter",Height)
-else
-MenuText=string.format("Hold at %d meter at %d",Height,Speed)
-end
-else
-if Speed==0 then
-MenuText=string.format(MenuTextFormat,Height)
-else
-MenuText=string.format(MenuTextFormat,Height,Speed)
-end
-end
-self.Menu.HoldAtEscortPosition=self.Menu.HoldAtEscortPosition or{}
-self.Menu.HoldAtEscortPosition[#self.Menu.HoldAtEscortPosition+1]={}
-self.Menu.HoldAtEscortPosition[#self.Menu.HoldAtEscortPosition].Height=Height
-self.Menu.HoldAtEscortPosition[#self.Menu.HoldAtEscortPosition].Speed=Speed
-self.Menu.HoldAtEscortPosition[#self.Menu.HoldAtEscortPosition].MenuText=MenuText
-return self
-end
-function AI_ESCORT:SetFlightMenuHoldAtLeaderPosition()
-for _,MenuHoldAtLeaderPosition in pairs(self.Menu.HoldAtLeaderPosition or{})do
-local FlightMenuReportNavigation=MENU_GROUP:New(self.PlayerGroup,"Navigation",self.FlightMenu)
-local FlightMenuHoldAtLeaderPosition=MENU_GROUP_COMMAND
-:New(
-self.PlayerGroup,
-MenuHoldAtLeaderPosition.MenuText,
-FlightMenuReportNavigation,
-AI_ESCORT._FlightHoldPosition,
-self,
-self.PlayerGroup,
-MenuHoldAtLeaderPosition.Height,
-MenuHoldAtLeaderPosition.Speed
-)
-end
-return self
-end
-function AI_ESCORT:SetEscortMenuHoldAtLeaderPosition(EscortGroup)
-for _,HoldAtLeaderPosition in pairs(self.Menu.HoldAtLeaderPosition or{})do
-if EscortGroup:IsAir()then
-local EscortGroupName=EscortGroup:GetName()
-local EscortMenuReportNavigation=MENU_GROUP:New(self.PlayerGroup,"Navigation",EscortGroup.EscortMenu)
-local EscortMenuHoldAtLeaderPosition=MENU_GROUP_COMMAND
-:New(
-self.PlayerGroup,
-HoldAtLeaderPosition.MenuText,
-EscortMenuReportNavigation,
-AI_ESCORT._HoldPosition,
-self,
-self.PlayerGroup,
-EscortGroup,
-HoldAtLeaderPosition.Height,
-HoldAtLeaderPosition.Speed
-)
-end
-end
-return self
-end
-function AI_ESCORT:MenuHoldAtLeaderPosition(Height,Speed,MenuTextFormat)
-self:F({Height,Speed,MenuTextFormat})
-if not Height then
-Height=30
-end
-if not Speed then
-Speed=0
-end
-local MenuText=""
-if not MenuTextFormat then
-if Speed==0 then
-MenuText=string.format("Rejoin and hold at %d meter",Height)
-else
-MenuText=string.format("Rejoin and hold at %d meter at %d",Height,Speed)
-end
-else
-if Speed==0 then
-MenuText=string.format(MenuTextFormat,Height)
-else
-MenuText=string.format(MenuTextFormat,Height,Speed)
-end
-end
-self.Menu.HoldAtLeaderPosition=self.Menu.HoldAtLeaderPosition or{}
-self.Menu.HoldAtLeaderPosition[#self.Menu.HoldAtLeaderPosition+1]={}
-self.Menu.HoldAtLeaderPosition[#self.Menu.HoldAtLeaderPosition].Height=Height
-self.Menu.HoldAtLeaderPosition[#self.Menu.HoldAtLeaderPosition].Speed=Speed
-self.Menu.HoldAtLeaderPosition[#self.Menu.HoldAtLeaderPosition].MenuText=MenuText
-return self
-end
-function AI_ESCORT:MenuScanForTargets(Height,Seconds,MenuTextFormat)
-self:F({Height,Seconds,MenuTextFormat})
-if self.EscortGroup:IsAir()then
-if not self.EscortMenuScan then
-self.EscortMenuScan=MENU_GROUP:New(self.PlayerGroup,"Scan for targets",self.EscortMenu)
-end
-if not Height then
-Height=100
-end
-if not Seconds then
-Seconds=30
-end
-local MenuText=""
-if not MenuTextFormat then
-if Seconds==0 then
-MenuText=string.format("At %d meter",Height)
-else
-MenuText=string.format("At %d meter for %d seconds",Height,Seconds)
-end
-else
-if Seconds==0 then
-MenuText=string.format(MenuTextFormat,Height)
-else
-MenuText=string.format(MenuTextFormat,Height,Seconds)
-end
-end
-if not self.EscortMenuScanForTargets then
-self.EscortMenuScanForTargets={}
-end
-self.EscortMenuScanForTargets[#self.EscortMenuScanForTargets+1]=MENU_GROUP_COMMAND
-:New(
-self.PlayerGroup,
-MenuText,
-self.EscortMenuScan,
-AI_ESCORT._ScanTargets,
-self,
-30
-)
-end
-return self
-end
-function AI_ESCORT:SetFlightMenuFlare()
-for _,MenuFlare in pairs(self.Menu.Flare or{})do
-local FlightMenuReportNavigation=MENU_GROUP:New(self.PlayerGroup,"Navigation",self.FlightMenu)
-local FlightMenuFlare=MENU_GROUP:New(self.PlayerGroup,MenuFlare.MenuText,FlightMenuReportNavigation)
-local FlightMenuFlareGreenFlight=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release green flare",FlightMenuFlare,AI_ESCORT._FlightFlare,self,FLARECOLOR.Green,"Released a green flare!")
-local FlightMenuFlareRedFlight=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release red flare",FlightMenuFlare,AI_ESCORT._FlightFlare,self,FLARECOLOR.Red,"Released a red flare!")
-local FlightMenuFlareWhiteFlight=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release white flare",FlightMenuFlare,AI_ESCORT._FlightFlare,self,FLARECOLOR.White,"Released a white flare!")
-local FlightMenuFlareYellowFlight=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release yellow flare",FlightMenuFlare,AI_ESCORT._FlightFlare,self,FLARECOLOR.Yellow,"Released a yellow flare!")
-end
-return self
-end
-function AI_ESCORT:SetEscortMenuFlare(EscortGroup)
-for _,MenuFlare in pairs(self.Menu.Flare or{})do
-if EscortGroup:IsAir()then
-local EscortGroupName=EscortGroup:GetName()
-local EscortMenuReportNavigation=MENU_GROUP:New(self.PlayerGroup,"Navigation",EscortGroup.EscortMenu)
-local EscortMenuFlare=MENU_GROUP:New(self.PlayerGroup,MenuFlare.MenuText,EscortMenuReportNavigation)
-local EscortMenuFlareGreen=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release green flare",EscortMenuFlare,AI_ESCORT._Flare,self,EscortGroup,FLARECOLOR.Green,"Released a green flare!")
-local EscortMenuFlareRed=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release red flare",EscortMenuFlare,AI_ESCORT._Flare,self,EscortGroup,FLARECOLOR.Red,"Released a red flare!")
-local EscortMenuFlareWhite=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release white flare",EscortMenuFlare,AI_ESCORT._Flare,self,EscortGroup,FLARECOLOR.White,"Released a white flare!")
-local EscortMenuFlareYellow=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release yellow flare",EscortMenuFlare,AI_ESCORT._Flare,self,EscortGroup,FLARECOLOR.Yellow,"Released a yellow flare!")
-end
-end
-return self
-end
-function AI_ESCORT:MenuFlare(MenuTextFormat)
-self:F()
-local MenuText=""
-if not MenuTextFormat then
-MenuText="Flare"
-else
-MenuText=MenuTextFormat
-end
-self.Menu.Flare=self.Menu.Flare or{}
-self.Menu.Flare[#self.Menu.Flare+1]={}
-self.Menu.Flare[#self.Menu.Flare].MenuText=MenuText
-return self
-end
-function AI_ESCORT:SetFlightMenuSmoke()
-for _,MenuSmoke in pairs(self.Menu.Smoke or{})do
-local FlightMenuReportNavigation=MENU_GROUP:New(self.PlayerGroup,"Navigation",self.FlightMenu)
-local FlightMenuSmoke=MENU_GROUP:New(self.PlayerGroup,MenuSmoke.MenuText,FlightMenuReportNavigation)
-local FlightMenuSmokeGreenFlight=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release green smoke",FlightMenuSmoke,AI_ESCORT._FlightSmoke,self,SMOKECOLOR.Green,"Releasing green smoke!")
-local FlightMenuSmokeRedFlight=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release red smoke",FlightMenuSmoke,AI_ESCORT._FlightSmoke,self,SMOKECOLOR.Red,"Releasing red smoke!")
-local FlightMenuSmokeWhiteFlight=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release white smoke",FlightMenuSmoke,AI_ESCORT._FlightSmoke,self,SMOKECOLOR.White,"Releasing white smoke!")
-local FlightMenuSmokeOrangeFlight=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release orange smoke",FlightMenuSmoke,AI_ESCORT._FlightSmoke,self,SMOKECOLOR.Orange,"Releasing orange smoke!")
-local FlightMenuSmokeBlueFlight=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release blue smoke",FlightMenuSmoke,AI_ESCORT._FlightSmoke,self,SMOKECOLOR.Blue,"Releasing blue smoke!")
-end
-return self
-end
-function AI_ESCORT:SetEscortMenuSmoke(EscortGroup)
-for _,MenuSmoke in pairs(self.Menu.Smoke or{})do
-if EscortGroup:IsAir()then
-local EscortGroupName=EscortGroup:GetName()
-local EscortMenuReportNavigation=MENU_GROUP:New(self.PlayerGroup,"Navigation",EscortGroup.EscortMenu)
-local EscortMenuSmoke=MENU_GROUP:New(self.PlayerGroup,MenuSmoke.MenuText,EscortMenuReportNavigation)
-local EscortMenuSmokeGreen=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release green smoke",EscortMenuSmoke,AI_ESCORT._Smoke,self,EscortGroup,SMOKECOLOR.Green,"Releasing green smoke!")
-local EscortMenuSmokeRed=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release red smoke",EscortMenuSmoke,AI_ESCORT._Smoke,self,EscortGroup,SMOKECOLOR.Red,"Releasing red smoke!")
-local EscortMenuSmokeWhite=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release white smoke",EscortMenuSmoke,AI_ESCORT._Smoke,self,EscortGroup,SMOKECOLOR.White,"Releasing white smoke!")
-local EscortMenuSmokeOrange=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release orange smoke",EscortMenuSmoke,AI_ESCORT._Smoke,self,EscortGroup,SMOKECOLOR.Orange,"Releasing orange smoke!")
-local EscortMenuSmokeBlue=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Release blue smoke",EscortMenuSmoke,AI_ESCORT._Smoke,self,EscortGroup,SMOKECOLOR.Blue,"Releasing blue smoke!")
-end
-end
-return self
-end
-function AI_ESCORT:MenuSmoke(MenuTextFormat)
-self:F()
-local MenuText=""
-if not MenuTextFormat then
-MenuText="Smoke"
-else
-MenuText=MenuTextFormat
-end
-self.Menu.Smoke=self.Menu.Smoke or{}
-self.Menu.Smoke[#self.Menu.Smoke+1]={}
-self.Menu.Smoke[#self.Menu.Smoke].MenuText=MenuText
-return self
-end
-function AI_ESCORT:SetFlightMenuReportType()
-local FlightMenuReportTargets=MENU_GROUP:New(self.PlayerGroup,"Report targets",self.FlightMenu)
-local MenuStamp=FlightMenuReportTargets:GetStamp()
-local FlightReportType=self:GetFlightReportType()
-if FlightReportType~=self.__Enum.ReportType.All then
-local FlightMenuReportTargetsAll=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Report all targets",FlightMenuReportTargets,AI_ESCORT._FlightSwitchReportTypeAll,self)
-:SetTag("ReportType")
-:SetStamp(MenuStamp)
-end
-if FlightReportType==self.__Enum.ReportType.All or FlightReportType~=self.__Enum.ReportType.Airborne then
-local FlightMenuReportTargetsAirborne=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Report airborne targets",FlightMenuReportTargets,AI_ESCORT._FlightSwitchReportTypeAirborne,self)
-:SetTag("ReportType")
-:SetStamp(MenuStamp)
-end
-if FlightReportType==self.__Enum.ReportType.All or FlightReportType~=self.__Enum.ReportType.GroundRadar then
-local FlightMenuReportTargetsGroundRadar=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Report gound radar targets",FlightMenuReportTargets,AI_ESCORT._FlightSwitchReportTypeGroundRadar,self)
-:SetTag("ReportType")
-:SetStamp(MenuStamp)
-end
-if FlightReportType==self.__Enum.ReportType.All or FlightReportType~=self.__Enum.ReportType.Ground then
-local FlightMenuReportTargetsGround=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Report ground targets",FlightMenuReportTargets,AI_ESCORT._FlightSwitchReportTypeGround,self)
-:SetTag("ReportType")
-:SetStamp(MenuStamp)
-end
-FlightMenuReportTargets:RemoveSubMenus(MenuStamp,"ReportType")
-end
-function AI_ESCORT:SetFlightMenuTargets()
-local FlightMenuReportTargets=MENU_GROUP:New(self.PlayerGroup,"Report targets",self.FlightMenu)
-local FlightMenuReportTargetsNow=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Report targets now!",FlightMenuReportTargets,AI_ESCORT._FlightReportNearbyTargetsNow,self)
-local FlightMenuReportTargetsOn=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Report targets on",FlightMenuReportTargets,AI_ESCORT._FlightSwitchReportNearbyTargets,self,true)
-local FlightMenuReportTargetsOff=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Report targets off",FlightMenuReportTargets,AI_ESCORT._FlightSwitchReportNearbyTargets,self,false)
-self.FlightMenuAttack=MENU_GROUP:New(self.PlayerGroup,"Attack targets",self.FlightMenu)
-local FlightMenuAttackNearby=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Attack nearest targets",self.FlightMenuAttack,AI_ESCORT._FlightAttackNearestTarget,self):SetTag("Attack")
-local FlightMenuAttackNearbyAir=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Attack nearest airborne targets",self.FlightMenuAttack,AI_ESCORT._FlightAttackNearestTarget,self,self.__Enum.ReportType.Air):SetTag("Attack")
-local FlightMenuAttackNearbyGround=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Attack nearest ground targets",self.FlightMenuAttack,AI_ESCORT._FlightAttackNearestTarget,self,self.__Enum.ReportType.Ground):SetTag("Attack")
-for _,MenuTargets in pairs(self.Menu.Targets or{})do
-MenuTargets.FlightReportTargetsScheduler=SCHEDULER:New(self,self._FlightReportTargetsScheduler,{},MenuTargets.Interval,MenuTargets.Interval)
-end
-return self
-end
-function AI_ESCORT:SetEscortMenuTargets(EscortGroup)
-for _,MenuTargets in pairs(self.Menu.Targets or{}or{})do
-if EscortGroup:IsAir()then
-local EscortGroupName=EscortGroup:GetName()
-EscortGroup.EscortMenuReportNearbyTargetsNow=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Report targets",EscortGroup.EscortMenu,AI_ESCORT._ReportNearbyTargetsNow,self,EscortGroup,true)
-EscortGroup.ReportTargetsScheduler=SCHEDULER:New(self,self._ReportTargetsScheduler,{EscortGroup},1,MenuTargets.Interval)
-EscortGroup.ResumeScheduler=SCHEDULER:New(self,self._ResumeScheduler,{EscortGroup},1,60)
-end
-end
-return self
-end
-function AI_ESCORT:MenuTargets(Seconds)
-self:F({Seconds})
-if not Seconds then
-Seconds=30
-end
-self.Menu.Targets=self.Menu.Targets or{}
-self.Menu.Targets[#self.Menu.Targets+1]={}
-self.Menu.Targets[#self.Menu.Targets].Interval=Seconds
-return self
-end
-function AI_ESCORT:MenuAssistedAttack()
-self:F()
-self.EscortGroupSet:ForSomeGroupAlive(
-function(EscortGroup)
-if not EscortGroup:IsAir()then
-self.EscortMenuTargetAssistance=MENU_GROUP:New(self.PlayerGroup,"Request assistance from",EscortGroup.EscortMenu)
-end
-end
-)
-return self
-end
-function AI_ESCORT:SetFlightMenuROE()
-for _,MenuROE in pairs(self.Menu.ROE or{})do
-local FlightMenuROE=MENU_GROUP:New(self.PlayerGroup,"Rule Of Engagement",self.FlightMenu)
-local FlightMenuROEHoldFire=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Hold fire",FlightMenuROE,AI_ESCORT._FlightROEHoldFire,self,"Holding weapons!")
-local FlightMenuROEReturnFire=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Return fire",FlightMenuROE,AI_ESCORT._FlightROEReturnFire,self,"Returning fire!")
-local FlightMenuROEOpenFire=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Open Fire",FlightMenuROE,AI_ESCORT._FlightROEOpenFire,self,"Open fire at designated targets!")
-local FlightMenuROEWeaponFree=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Engage all targets",FlightMenuROE,AI_ESCORT._FlightROEWeaponFree,self,"Engaging all targets!")
-end
-return self
-end
-function AI_ESCORT:SetEscortMenuROE(EscortGroup)
-for _,MenuROE in pairs(self.Menu.ROE or{})do
-if EscortGroup:IsAir()then
-local EscortGroupName=EscortGroup:GetName()
-local EscortMenuROE=MENU_GROUP:New(self.PlayerGroup,"Rule Of Engagement",EscortGroup.EscortMenu)
-if EscortGroup:OptionROEHoldFirePossible()then
-local EscortMenuROEHoldFire=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Hold fire",EscortMenuROE,AI_ESCORT._ROE,self,EscortGroup,EscortGroup.OptionROEHoldFire,"Holding weapons!")
-end
-if EscortGroup:OptionROEReturnFirePossible()then
-local EscortMenuROEReturnFire=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Return fire",EscortMenuROE,AI_ESCORT._ROE,self,EscortGroup,EscortGroup.OptionROEReturnFire,"Returning fire!")
-end
-if EscortGroup:OptionROEOpenFirePossible()then
-EscortGroup.EscortMenuROEOpenFire=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Open Fire",EscortMenuROE,AI_ESCORT._ROE,self,EscortGroup,EscortGroup.OptionROEOpenFire,"Opening fire on designated targets!!")
-end
-if EscortGroup:OptionROEWeaponFreePossible()then
-EscortGroup.EscortMenuROEWeaponFree=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Engage all targets",EscortMenuROE,AI_ESCORT._ROE,self,EscortGroup,EscortGroup.OptionROEWeaponFree,"Opening fire on targets of opportunity!")
-end
-end
-end
-return self
-end
-function AI_ESCORT:MenuROE()
-self:F()
-self.Menu.ROE=self.Menu.ROE or{}
-self.Menu.ROE[#self.Menu.ROE+1]={}
-return self
-end
-function AI_ESCORT:SetFlightMenuROT()
-for _,MenuROT in pairs(self.Menu.ROT or{})do
-local FlightMenuROT=MENU_GROUP:New(self.PlayerGroup,"Reaction On Threat",self.FlightMenu)
-local FlightMenuROTNoReaction=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Fight until death",FlightMenuROT,AI_ESCORT._FlightROTNoReaction,self,"Fighting until death!")
-local FlightMenuROTPassiveDefense=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Use flares, chaff and jammers",FlightMenuROT,AI_ESCORT._FlightROTPassiveDefense,self,"Defending using jammers, chaff and flares!")
-local FlightMenuROTEvadeFire=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Open fire",FlightMenuROT,AI_ESCORT._FlightROTEvadeFire,self,"Evading on enemy fire!")
-local FlightMenuROTVertical=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Avoid radar and evade fire",FlightMenuROT,AI_ESCORT._FlightROTVertical,self,"Evading on enemy fire with vertical manoeuvres!")
-end
-return self
-end
-function AI_ESCORT:SetEscortMenuROT(EscortGroup)
-for _,MenuROT in pairs(self.Menu.ROT or{})do
-if EscortGroup:IsAir()then
-local EscortGroupName=EscortGroup:GetName()
-local EscortMenuROT=MENU_GROUP:New(self.PlayerGroup,"Reaction On Threat",EscortGroup.EscortMenu)
-if not EscortGroup.EscortMenuEvasion then
-if EscortGroup:OptionROTNoReactionPossible()then
-local EscortMenuEvasionNoReaction=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Fight until death",EscortMenuROT,AI_ESCORT._ROT,self,EscortGroup,EscortGroup.OptionROTNoReaction,"Fighting until death!")
-end
-if EscortGroup:OptionROTPassiveDefensePossible()then
-local EscortMenuEvasionPassiveDefense=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Use flares, chaff and jammers",EscortMenuROT,AI_ESCORT._ROT,self,EscortGroup,EscortGroup.OptionROTPassiveDefense,"Defending using jammers, chaff and flares!")
-end
-if EscortGroup:OptionROTEvadeFirePossible()then
-local EscortMenuEvasionEvadeFire=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Open fire",EscortMenuROT,AI_ESCORT._ROT,self,EscortGroup,EscortGroup.OptionROTEvadeFire,"Evading on enemy fire!")
-end
-if EscortGroup:OptionROTVerticalPossible()then
-local EscortMenuOptionEvasionVertical=MENU_GROUP_COMMAND:New(self.PlayerGroup,"Avoid radar and evade fire",EscortMenuROT,AI_ESCORT._ROT,self,EscortGroup,EscortGroup.OptionROTVertical,"Evading on enemy fire with vertical manoeuvres!")
-end
-end
-end
-end
-return self
-end
-function AI_ESCORT:MenuROT(MenuTextFormat)
-self:F(MenuTextFormat)
-self.Menu.ROT=self.Menu.ROT or{}
-self.Menu.ROT[#self.Menu.ROT+1]={}
-return self
-end
-function AI_ESCORT:SetEscortMenuResumeMission(EscortGroup)
-self:F()
-if EscortGroup:IsAir()then
-local EscortGroupName=EscortGroup:GetName()
-EscortGroup.EscortMenuResumeMission=MENU_GROUP:New(self.PlayerGroup,"Resume from",EscortGroup.EscortMenu)
-end
-return self
-end
-function AI_ESCORT:_HoldPosition(OrbitGroup,EscortGroup,OrbitHeight,OrbitSeconds)
-local EscortUnit=self.PlayerUnit
-local OrbitUnit=OrbitGroup:GetUnit(1)
-self:SetFlightModeMission(EscortGroup)
-local PointFrom={}
-local GroupVec3=EscortGroup:GetUnit(1):GetVec3()
-PointFrom={}
-PointFrom.x=GroupVec3.x
-PointFrom.y=GroupVec3.z
-PointFrom.speed=250
-PointFrom.type=AI.Task.WaypointType.TURNING_POINT
-PointFrom.alt=GroupVec3.y
-PointFrom.alt_type=AI.Task.AltitudeType.BARO
-local OrbitPoint=OrbitUnit:GetVec2()
-local PointTo={}
-PointTo.x=OrbitPoint.x
-PointTo.y=OrbitPoint.y
-PointTo.speed=250
-PointTo.type=AI.Task.WaypointType.TURNING_POINT
-PointTo.alt=OrbitHeight
-PointTo.alt_type=AI.Task.AltitudeType.BARO
-PointTo.task=EscortGroup:TaskOrbitCircleAtVec2(OrbitPoint,OrbitHeight,0)
-local Points={PointFrom,PointTo}
-EscortGroup:OptionROEHoldFire()
-EscortGroup:OptionROTPassiveDefense()
-EscortGroup:SetTask(EscortGroup:TaskRoute(Points),1)
-EscortGroup:MessageTypeToGroup("Orbiting at current location.",MESSAGE.Type.Information,EscortUnit:GetGroup())
-end
-function AI_ESCORT:_FlightHoldPosition(OrbitGroup,OrbitHeight,OrbitSeconds)
-local EscortUnit=self.PlayerUnit
-self.EscortGroupSet:ForEachGroupAlive(
-function(EscortGroup,OrbitGroup)
-if EscortGroup:IsAir()then
-if OrbitGroup==nil then
-OrbitGroup=EscortGroup
-end
-self:_HoldPosition(OrbitGroup,EscortGroup,OrbitHeight,OrbitSeconds)
-end
-end,OrbitGroup
-)
-end
-function AI_ESCORT:_JoinUp(EscortGroup)
-local EscortUnit=self.PlayerUnit
-self:SetFlightModeFormation(EscortGroup)
-EscortGroup:MessageTypeToGroup("Joining up!",MESSAGE.Type.Information,EscortUnit:GetGroup())
-end
-function AI_ESCORT:_FlightJoinUp()
-self.EscortGroupSet:ForEachGroupAlive(
-function(EscortGroup)
-if EscortGroup:IsAir()then
-self:_JoinUp(EscortGroup)
-end
-end
-)
-end
-function AI_ESCORT:_EscortFormationTrail(EscortGroup,XStart,XSpace,YStart)
-self:FormationTrail(XStart,XSpace,YStart)
-end
-function AI_ESCORT:_FlightFormationTrail(XStart,XSpace,YStart)
-self.EscortGroupSet:ForEachGroupAlive(
-function(EscortGroup)
-if EscortGroup:IsAir()then
-self:_EscortFormationTrail(EscortGroup,XStart,XSpace,YStart)
-end
-end
-)
-end
-function AI_ESCORT:_EscortFormationStack(EscortGroup,XStart,XSpace,YStart,YSpace)
-self:FormationStack(XStart,XSpace,YStart,YSpace)
-end
-function AI_ESCORT:_FlightFormationStack(XStart,XSpace,YStart,YSpace)
-self.EscortGroupSet:ForEachGroupAlive(
-function(EscortGroup)
-if EscortGroup:IsAir()then
-self:_EscortFormationStack(EscortGroup,XStart,XSpace,YStart,YSpace)
-end
-end
-)
-end
-function AI_ESCORT:_Flare(EscortGroup,Color,Message)
-local EscortUnit=self.PlayerUnit
-EscortGroup:GetUnit(1):Flare(Color)
-EscortGroup:MessageTypeToGroup(Message,MESSAGE.Type.Information,EscortUnit:GetGroup())
-end
-function AI_ESCORT:_FlightFlare(Color,Message)
-self.EscortGroupSet:ForEachGroupAlive(
-function(EscortGroup)
-if EscortGroup:IsAir()then
-self:_Flare(EscortGroup,Color,Message)
-end
-end
-)
-end
-function AI_ESCORT:_Smoke(EscortGroup,Color,Message)
-local EscortUnit=self.PlayerUnit
-EscortGroup:GetUnit(1):Smoke(Color)
-EscortGroup:MessageTypeToGroup(Message,MESSAGE.Type.Information,EscortUnit:GetGroup())
-end
-function AI_ESCORT:_FlightSmoke(Color,Message)
-self.EscortGroupSet:ForEachGroupAlive(
-function(EscortGroup)
-if EscortGroup:IsAir()then
-self:_Smoke(EscortGroup,Color,Message)
-end
-end
-)
-end
-function AI_ESCORT:_ReportNearbyTargetsNow(EscortGroup)
-local EscortUnit=self.PlayerUnit
-self:_ReportTargetsScheduler(EscortGroup)
-end
-function AI_ESCORT:_FlightReportNearbyTargetsNow()
-self:_FlightReportTargetsScheduler()
-end
-function AI_ESCORT:_FlightSwitchReportNearbyTargets(ReportTargets)
-self.EscortGroupSet:ForEachGroupAlive(
-function(EscortGroup)
-if EscortGroup:IsAir()then
-self:_EscortSwitchReportNearbyTargets(EscortGroup,ReportTargets)
-end
-end
-)
-end
-function AI_ESCORT:SetFlightReportType(ReportType)
-self.FlightReportType=ReportType
-end
-function AI_ESCORT:GetFlightReportType()
-return self.FlightReportType
-end
-function AI_ESCORT:_FlightSwitchReportTypeAll()
-self:SetFlightReportType(self.__Enum.ReportType.All)
-self:SetFlightMenuReportType()
-local EscortGroup=self.EscortGroupSet:GetFirst()
-EscortGroup:MessageTypeToGroup("Reporting all targets.",MESSAGE.Type.Information,self.PlayerGroup)
-end
-function AI_ESCORT:_FlightSwitchReportTypeAirborne()
-self:SetFlightReportType(self.__Enum.ReportType.Airborne)
-self:SetFlightMenuReportType()
-local EscortGroup=self.EscortGroupSet:GetFirst()
-EscortGroup:MessageTypeToGroup("Reporting airborne targets.",MESSAGE.Type.Information,self.PlayerGroup)
-end
-function AI_ESCORT:_FlightSwitchReportTypeGroundRadar()
-self:SetFlightReportType(self.__Enum.ReportType.Ground)
-self:SetFlightMenuReportType()
-local EscortGroup=self.EscortGroupSet:GetFirst()
-EscortGroup:MessageTypeToGroup("Reporting ground radar targets.",MESSAGE.Type.Information,self.PlayerGroup)
-end
-function AI_ESCORT:_FlightSwitchReportTypeGround()
-self:SetFlightReportType(self.__Enum.ReportType.Ground)
-self:SetFlightMenuReportType()
-local EscortGroup=self.EscortGroupSet:GetFirst()
-EscortGroup:MessageTypeToGroup("Reporting ground targets.",MESSAGE.Type.Information,self.PlayerGroup)
-end
-function AI_ESCORT:_ScanTargets(ScanDuration)
-local EscortGroup=self.EscortGroup
-local EscortUnit=self.PlayerUnit
-self.FollowScheduler:Stop(self.FollowSchedule)
-if EscortGroup:IsHelicopter()then
-EscortGroup:PushTask(
-EscortGroup:TaskControlled(
-EscortGroup:TaskOrbitCircle(200,20),
-EscortGroup:TaskCondition(nil,nil,nil,nil,ScanDuration,nil)
-),1)
-elseif EscortGroup:IsAirPlane()then
-EscortGroup:PushTask(
-EscortGroup:TaskControlled(
-EscortGroup:TaskOrbitCircle(1000,500),
-EscortGroup:TaskCondition(nil,nil,nil,nil,ScanDuration,nil)
-),1)
-end
-EscortGroup:MessageToClient("Scanning targets for "..ScanDuration.." seconds.",ScanDuration,EscortUnit)
-if self.EscortMode==AI_ESCORT.MODE.FOLLOW then
-self.FollowScheduler:Start(self.FollowSchedule)
-end
-end
-function AI_ESCORT.___Resume(EscortGroup,self)
-self:F({self=self})
-local PlayerGroup=self.PlayerGroup
-EscortGroup:OptionROEHoldFire()
-EscortGroup:OptionROTVertical()
-EscortGroup:SetState(EscortGroup,"Mode",EscortGroup:GetState(EscortGroup,"PreviousMode"))
-if EscortGroup:GetState(EscortGroup,"Mode")==self.__Enum.Mode.Mission then
-EscortGroup:MessageTypeToGroup("Resuming route.",MESSAGE.Type.Information,PlayerGroup)
-else
-EscortGroup:MessageTypeToGroup("Rejoining formation.",MESSAGE.Type.Information,PlayerGroup)
-end
-end
-function AI_ESCORT:_ResumeMission(EscortGroup,WayPoint)
-self:SetFlightModeMission(EscortGroup)
-local WayPoints=EscortGroup.MissionRoute
-self:T(WayPoint,WayPoints)
-for WayPointIgnore=1,WayPoint do
-table.remove(WayPoints,1)
-end
-EscortGroup:SetTask(EscortGroup:TaskRoute(WayPoints),1)
-EscortGroup:MessageTypeToGroup("Resuming mission from waypoint ",MESSAGE.Type.Information,self.PlayerGroup)
-end
-function AI_ESCORT:_AttackTarget(EscortGroup,DetectedItem)
-self:F(EscortGroup)
-self:SetFlightModeAttack(EscortGroup)
-if EscortGroup:IsAir()then
-EscortGroup:OptionROEOpenFire()
-EscortGroup:OptionROTVertical()
-EscortGroup:SetState(EscortGroup,"Escort",self)
-local DetectedSet=self.Detection:GetDetectedItemSet(DetectedItem)
-local Tasks={}
-local AttackUnitTasks={}
-DetectedSet:ForEachUnit(
-function(DetectedUnit,Tasks)
-if DetectedUnit:IsAlive()then
-AttackUnitTasks[#AttackUnitTasks+1]=EscortGroup:TaskAttackUnit(DetectedUnit)
-end
-end,Tasks
-)
-Tasks[#Tasks+1]=EscortGroup:TaskCombo(AttackUnitTasks)
-Tasks[#Tasks+1]=EscortGroup:TaskFunction("AI_ESCORT.___Resume",self)
-EscortGroup:PushTask(
-EscortGroup:TaskCombo(
-Tasks
-),1
-)
-else
-local DetectedSet=self.Detection:GetDetectedItemSet(DetectedItem)
-local Tasks={}
-DetectedSet:ForEachUnit(
-function(DetectedUnit,Tasks)
-if DetectedUnit:IsAlive()then
-Tasks[#Tasks+1]=EscortGroup:TaskFireAtPoint(DetectedUnit:GetVec2(),50)
-end
-end,Tasks
-)
-EscortGroup:PushTask(
-EscortGroup:TaskCombo(
-Tasks
-),1
-)
-end
-local DetectedTargetsReport=REPORT:New("Engaging target:\n")
-local DetectedItemReportSummary=self.Detection:DetectedItemReportSummary(DetectedItem,self.PlayerGroup,_DATABASE:GetPlayerSettings(self.PlayerUnit:GetPlayerName()))
-local ReportSummary=DetectedItemReportSummary:Text(", ")
-DetectedTargetsReport:AddIndent(ReportSummary,"-")
-EscortGroup:MessageTypeToGroup(DetectedTargetsReport:Text(),MESSAGE.Type.Information,self.PlayerGroup)
-end
-function AI_ESCORT:_FlightAttackTarget(DetectedItem)
-self.EscortGroupSet:ForEachGroupAlive(
-function(EscortGroup,DetectedItem)
-if EscortGroup:IsAir()then
-self:_AttackTarget(EscortGroup,DetectedItem)
-end
-end,DetectedItem
-)
-end
-function AI_ESCORT:_FlightAttackNearestTarget(TargetType)
-self.Detection:Detect()
-self:_FlightReportTargetsScheduler()
-local EscortGroup=self.EscortGroupSet:GetFirst()
-local AttackDetectedItem=nil
-local DetectedItems=self.Detection:GetDetectedItems()
-for DetectedItemIndex,DetectedItem in UTILS.spairs(DetectedItems,function(t,a,b)return self:Distance(self.PlayerUnit,t[a])0
-local HasAir=DetectedItemSet:HasAirUnits()>0
-local FlightReportType=self:GetFlightReportType()
-if(TargetType and TargetType==self.__Enum.ReportType.Ground and HasGround)or
-(TargetType and TargetType==self.__Enum.ReportType.Air and HasAir)or
-(TargetType==nil)then
-AttackDetectedItem=DetectedItem
-break
-end
-end
-if AttackDetectedItem then
-self:_FlightAttackTarget(AttackDetectedItem)
-else
-EscortGroup:MessageTypeToGroup("Nothing to attack!",MESSAGE.Type.Information,self.PlayerGroup)
-end
-end
-function AI_ESCORT:_AssistTarget(EscortGroup,DetectedItem)
-local EscortUnit=self.PlayerUnit
-local DetectedSet=self.Detection:GetDetectedItemSet(DetectedItem)
-local Tasks={}
-DetectedSet:ForEachUnit(
-function(DetectedUnit,Tasks)
-if DetectedUnit:IsAlive()then
-Tasks[#Tasks+1]=EscortGroup:TaskFireAtPoint(DetectedUnit:GetVec2(),50)
-end
-end,Tasks
-)
-EscortGroup:SetTask(
-EscortGroup:TaskCombo(
-Tasks
-),1
-)
-EscortGroup:MessageTypeToGroup("Assisting attack!",MESSAGE.Type.Information,EscortUnit:GetGroup())
-end
-function AI_ESCORT:_ROE(EscortGroup,EscortROEFunction,EscortROEMessage)
-pcall(function()EscortROEFunction(EscortGroup)end)
-EscortGroup:MessageTypeToGroup(EscortROEMessage,MESSAGE.Type.Information,self.PlayerGroup)
-end
-function AI_ESCORT:_FlightROEHoldFire(EscortROEMessage)
-self.EscortGroupSet:ForEachGroupAlive(
-function(EscortGroup)
-self:_ROE(EscortGroup,EscortGroup.OptionROEHoldFire,EscortROEMessage)
-end
-)
-end
-function AI_ESCORT:_FlightROEOpenFire(EscortROEMessage)
-self.EscortGroupSet:ForEachGroupAlive(
-function(EscortGroup)
-self:_ROE(EscortGroup,EscortGroup.OptionROEOpenFire,EscortROEMessage)
-end
-)
-end
-function AI_ESCORT:_FlightROEReturnFire(EscortROEMessage)
-self.EscortGroupSet:ForEachGroupAlive(
-function(EscortGroup)
-self:_ROE(EscortGroup,EscortGroup.OptionROEReturnFire,EscortROEMessage)
-end
-)
-end
-function AI_ESCORT:_FlightROEWeaponFree(EscortROEMessage)
-self.EscortGroupSet:ForEachGroupAlive(
-function(EscortGroup)
-self:_ROE(EscortGroup,EscortGroup.OptionROEWeaponFree,EscortROEMessage)
-end
-)
-end
-function AI_ESCORT:_ROT(EscortGroup,EscortROTFunction,EscortROTMessage)
-pcall(function()EscortROTFunction(EscortGroup)end)
-EscortGroup:MessageTypeToGroup(EscortROTMessage,MESSAGE.Type.Information,self.PlayerGroup)
-end
-function AI_ESCORT:_FlightROTNoReaction(EscortROTMessage)
-self.EscortGroupSet:ForEachGroupAlive(
-function(EscortGroup)
-self:_ROT(EscortGroup,EscortGroup.OptionROTNoReaction,EscortROTMessage)
-end
-)
-end
-function AI_ESCORT:_FlightROTPassiveDefense(EscortROTMessage)
-self.EscortGroupSet:ForEachGroupAlive(
-function(EscortGroup)
-self:_ROT(EscortGroup,EscortGroup.OptionROTPassiveDefense,EscortROTMessage)
-end
-)
-end
-function AI_ESCORT:_FlightROTEvadeFire(EscortROTMessage)
-self.EscortGroupSet:ForEachGroupAlive(
-function(EscortGroup)
-self:_ROT(EscortGroup,EscortGroup.OptionROTEvadeFire,EscortROTMessage)
-end
-)
-end
-function AI_ESCORT:_FlightROTVertical(EscortROTMessage)
-self.EscortGroupSet:ForEachGroupAlive(
-function(EscortGroup)
-self:_ROT(EscortGroup,EscortGroup.OptionROTVertical,EscortROTMessage)
-end
-)
-end
-function AI_ESCORT:RegisterRoute()
-self:F()
-local EscortGroup=self.EscortGroup
-local TaskPoints=EscortGroup:GetTaskRoute()
-self:T(TaskPoints)
-return TaskPoints
-end
-function AI_ESCORT:_ResumeScheduler(EscortGroup)
-self:F(EscortGroup:GetName())
-if EscortGroup:IsAlive()and self.PlayerUnit:IsAlive()then
-local EscortGroupName=EscortGroup:GetCallsign()
-if EscortGroup.EscortMenuResumeMission then
-EscortGroup.EscortMenuResumeMission:RemoveSubMenus()
-local TaskPoints=EscortGroup.MissionRoute
-for WayPointID,WayPoint in pairs(TaskPoints)do
-local EscortVec3=EscortGroup:GetVec3()
-local Distance=((WayPoint.x-EscortVec3.x)^2+
-(WayPoint.y-EscortVec3.z)^2
-)^0.5/1000
-MENU_GROUP_COMMAND:New(self.PlayerGroup,"Waypoint "..WayPointID.." at "..string.format("%.2f",Distance).."km",EscortGroup.EscortMenuResumeMission,AI_ESCORT._ResumeMission,self,EscortGroup,WayPointID)
-end
-end
-end
-end
-function AI_ESCORT:Distance(PlayerUnit,DetectedItem)
-local DetectedCoordinate=self.Detection:GetDetectedItemCoordinate(DetectedItem)
-local PlayerCoordinate=PlayerUnit:GetCoordinate()
-return DetectedCoordinate:Get3DDistance(PlayerCoordinate)
-end
-function AI_ESCORT:_ReportTargetsScheduler(EscortGroup,Report)
-self:F(EscortGroup:GetName())
-if EscortGroup:IsAlive()and self.PlayerUnit:IsAlive()then
-local EscortGroupName=EscortGroup:GetCallsign()
-local DetectedTargetsReport=REPORT:New("Reporting targets:\n")
-if EscortGroup.EscortMenuTargetAssistance then
-EscortGroup.EscortMenuTargetAssistance:RemoveSubMenus()
-end
-local DetectedItems=self.Detection:GetDetectedItems()
-local ClientEscortTargets=self.Detection
-local TimeUpdate=timer.getTime()
-local EscortMenuAttackTargets=MENU_GROUP:New(self.PlayerGroup,"Attack targets",EscortGroup.EscortMenu)
-local DetectedTargets=false
-for DetectedItemIndex,DetectedItem in UTILS.spairs(DetectedItems,function(t,a,b)return self:Distance(self.PlayerUnit,t[a])0
-local HasGroundRadar=HasGround and DetectedItemSet:HasRadar()>0
-local HasAir=DetectedItemSet:HasAirUnits()>0
-local FlightReportType=self:GetFlightReportType()
-if(FlightReportType==self.__Enum.ReportType.All)or
-(FlightReportType==self.__Enum.ReportType.Airborne and HasAir)or
-(FlightReportType==self.__Enum.ReportType.Ground and HasGround)or
-(FlightReportType==self.__Enum.ReportType.GroundRadar and HasGroundRadar)then
-DetectedTargets=true
-local DetectedMenu=self.Detection:DetectedItemReportMenu(DetectedItem,EscortGroup,_DATABASE:GetPlayerSettings(self.PlayerUnit:GetPlayerName())):Text("\n")
-local DetectedItemReportSummary=self.Detection:DetectedItemReportSummary(DetectedItem,EscortGroup,_DATABASE:GetPlayerSettings(self.PlayerUnit:GetPlayerName()))
-local ReportSummary=DetectedItemReportSummary:Text(", ")
-DetectedTargetsReport:AddIndent(ReportSummary,"-")
-if EscortGroup:IsAir()then
-MENU_GROUP_COMMAND:New(self.PlayerGroup,
-DetectedMenu,
-EscortMenuAttackTargets,
-AI_ESCORT._AttackTarget,
-self,
-EscortGroup,
-DetectedItem
-):SetTag("Escort"):SetTime(TimeUpdate)
-else
-if self.EscortMenuTargetAssistance then
-local MenuTargetAssistance=MENU_GROUP:New(self.PlayerGroup,EscortGroupName,EscortGroup.EscortMenuTargetAssistance)
-MENU_GROUP_COMMAND:New(self.PlayerGroup,
-DetectedMenu,
-MenuTargetAssistance,
-AI_ESCORT._AssistTarget,
-self,
-EscortGroup,
-DetectedItem
-)
-end
-end
-end
-end
-EscortMenuAttackTargets:RemoveSubMenus(TimeUpdate,"Escort")
-if Report then
-if DetectedTargets then
-EscortGroup:MessageTypeToGroup(DetectedTargetsReport:Text("\n"),MESSAGE.Type.Information,self.PlayerGroup)
-else
-EscortGroup:MessageTypeToGroup("No targets detected.",MESSAGE.Type.Information,self.PlayerGroup)
-end
-end
-return true
-end
-return false
-end
-function AI_ESCORT:_FlightReportTargetsScheduler()
-self:F("FlightReportTargetScheduler")
-local EscortGroup=self.EscortGroupSet:GetFirst()
-local DetectedTargetsReport=REPORT:New("Reporting your targets:\n")
-if EscortGroup and(self.PlayerUnit:IsAlive()and EscortGroup:IsAlive())then
-local TimeUpdate=timer.getTime()
-local DetectedItems=self.Detection:GetDetectedItems()
-local DetectedTargets=false
-local ClientEscortTargets=self.Detection
-for DetectedItemIndex,DetectedItem in UTILS.spairs(DetectedItems,function(t,a,b)return self:Distance(self.PlayerUnit,t[a])0
-local HasGroundRadar=HasGround and DetectedItemSet:HasRadar()>0
-local HasAir=DetectedItemSet:HasAirUnits()>0
-local FlightReportType=self:GetFlightReportType()
-if(FlightReportType==self.__Enum.ReportType.All)or
-(FlightReportType==self.__Enum.ReportType.Airborne and HasAir)or
-(FlightReportType==self.__Enum.ReportType.Ground and HasGround)or
-(FlightReportType==self.__Enum.ReportType.GroundRadar and HasGroundRadar)then
-DetectedTargets=true
-local DetectedItemReportMenu=self.Detection:DetectedItemReportMenu(DetectedItem,self.PlayerGroup,_DATABASE:GetPlayerSettings(self.PlayerUnit:GetPlayerName()))
-local ReportMenuText=DetectedItemReportMenu:Text(", ")
-MENU_GROUP_COMMAND:New(self.PlayerGroup,
-ReportMenuText,
-self.FlightMenuAttack,
-AI_ESCORT._FlightAttackTarget,
-self,
-DetectedItem
-):SetTag("Flight"):SetTime(TimeUpdate)
-local DetectedItemReportSummary=self.Detection:DetectedItemReportSummary(DetectedItem,self.PlayerGroup,_DATABASE:GetPlayerSettings(self.PlayerUnit:GetPlayerName()))
-local ReportSummary=DetectedItemReportSummary:Text(", ")
-DetectedTargetsReport:AddIndent(ReportSummary,"-")
-end
-end
-self.FlightMenuAttack:RemoveSubMenus(TimeUpdate,"Flight")
-if DetectedTargets then
-EscortGroup:MessageTypeToGroup(DetectedTargetsReport:Text("\n"),MESSAGE.Type.Information,self.PlayerGroup)
-end
-return true
-end
-return false
-end
-AI_ESCORT_REQUEST={
-ClassName="AI_ESCORT_REQUEST",
-}
-function AI_ESCORT_REQUEST:New(EscortUnit,EscortSpawn,EscortAirbase,EscortName,EscortBriefing)
-local EscortGroupSet=SET_GROUP:New():FilterDeads():FilterCrashes()
-local self=BASE:Inherit(self,AI_ESCORT:New(EscortUnit,EscortGroupSet,EscortName,EscortBriefing))
-self.EscortGroupSet=EscortGroupSet
-self.EscortSpawn=EscortSpawn
-self.EscortAirbase=EscortAirbase
-self.LeaderGroup=self.PlayerUnit:GetGroup()
-self.Detection=DETECTION_AREAS:New(self.EscortGroupSet,5000)
-self.Detection:__Start(30)
-self.SpawnMode=self.__Enum.Mode.Mission
-return self
-end
-function AI_ESCORT_REQUEST:SpawnEscort()
-local EscortGroup=self.EscortSpawn:SpawnAtAirbase(self.EscortAirbase,SPAWN.Takeoff.Hot)
-self:ScheduleOnce(0.1,
-function(EscortGroup)
-EscortGroup:OptionROTVertical()
-EscortGroup:OptionROEHoldFire()
-self.EscortGroupSet:AddGroup(EscortGroup)
-local LeaderEscort=self.EscortGroupSet:GetFirst()
-local Report=REPORT:New()
-Report:Add("Joining Up "..self.EscortGroupSet:GetUnitTypeNames():Text(", ").." from "..LeaderEscort:GetCoordinate():ToString(self.EscortUnit))
-LeaderEscort:MessageTypeToGroup(Report:Text(),MESSAGE.Type.Information,self.PlayerUnit)
-self:SetFlightModeFormation(EscortGroup)
-self:FormationTrail()
-self:_InitFlightMenus()
-self:_InitEscortMenus(EscortGroup)
-self:_InitEscortRoute(EscortGroup)
-function EscortGroup:OnEventDeadOrCrash(EventData)
-self:F({"EventDead",EventData})
-self.EscortMenu:Remove()
-end
-EscortGroup:HandleEvent(EVENTS.Dead,EscortGroup.OnEventDeadOrCrash)
-EscortGroup:HandleEvent(EVENTS.Crash,EscortGroup.OnEventDeadOrCrash)
-end,EscortGroup
-)
-end
-function AI_ESCORT_REQUEST:onafterStart(EscortGroupSet)
-self:F()
-if not self.MenuRequestEscort then
-self.MainMenu=MENU_GROUP:New(self.PlayerGroup,self.EscortName)
-self.MenuRequestEscort=MENU_GROUP_COMMAND:New(self.LeaderGroup,"Request new escort ",self.MainMenu,
-function()
-self:SpawnEscort()
-end
-)
-end
-self:GetParent(self).onafterStart(self,EscortGroupSet)
-self:HandleEvent(EVENTS.Dead,self.OnEventDeadOrCrash)
-self:HandleEvent(EVENTS.Crash,self.OnEventDeadOrCrash)
-end
-function AI_ESCORT_REQUEST:onafterStop(EscortGroupSet)
-self:F()
-EscortGroupSet:ForEachGroup(
-function(EscortGroup)
-EscortGroup:WayPointInitialize()
-EscortGroup:OptionROTVertical()
-EscortGroup:OptionROEOpenFire()
-end
-)
-self.Detection:Stop()
-self.MainMenu:Remove()
-end
-function AI_ESCORT_REQUEST:SetEscortSpawnMission()
-self.SpawnMode=self.__Enum.Mode.Mission
-end
-AI_ESCORT_DISPATCHER={
-ClassName="AI_ESCORT_DISPATCHER",
-}
-AI_ESCORT_DISPATCHER.AI_Escorts={}
-function AI_ESCORT_DISPATCHER:New(CarrierSet,EscortSpawn,EscortAirbase,EscortName,EscortBriefing)
-local self=BASE:Inherit(self,FSM:New())
-self.CarrierSet=CarrierSet
-self.EscortSpawn=EscortSpawn
-self.EscortAirbase=EscortAirbase
-self.EscortName=EscortName
-self.EscortBriefing=EscortBriefing
-self:SetStartState("Idle")
-self:AddTransition("Monitoring","Monitor","Monitoring")
-self:AddTransition("Idle","Start","Monitoring")
-self:AddTransition("Monitoring","Stop","Idle")
-function self.CarrierSet.OnAfterRemoved(CarrierSet,From,Event,To,CarrierName,Carrier)
-self:F({Carrier=Carrier:GetName()})
-end
-return self
-end
-function AI_ESCORT_DISPATCHER:onafterStart(From,Event,To)
-self:HandleEvent(EVENTS.Birth)
-self:HandleEvent(EVENTS.PlayerLeaveUnit,self.OnEventExit)
-self:HandleEvent(EVENTS.Crash,self.OnEventExit)
-self:HandleEvent(EVENTS.Dead,self.OnEventExit)
-end
-function AI_ESCORT_DISPATCHER:OnEventExit(EventData)
-local PlayerGroupName=EventData.IniGroupName
-local PlayerGroup=EventData.IniGroup
-local PlayerUnit=EventData.IniUnit
-self:I({EscortAirbase=self.EscortAirbase})
-self:I({PlayerGroupName=PlayerGroupName})
-self:I({PlayerGroup=PlayerGroup})
-self:I({FirstGroup=self.CarrierSet:GetFirst()})
-self:I({FindGroup=self.CarrierSet:FindGroup(PlayerGroupName)})
-if self.CarrierSet:FindGroup(PlayerGroupName)then
-if self.AI_Escorts[PlayerGroupName]then
-self.AI_Escorts[PlayerGroupName]:Stop()
-self.AI_Escorts[PlayerGroupName]=nil
-end
-end
-end
-function AI_ESCORT_DISPATCHER:OnEventBirth(EventData)
-local PlayerGroupName=EventData.IniGroupName
-local PlayerGroup=EventData.IniGroup
-local PlayerUnit=EventData.IniUnit
-self:I({EscortAirbase=self.EscortAirbase})
-self:I({PlayerGroupName=PlayerGroupName})
-self:I({PlayerGroup=PlayerGroup})
-self:I({FirstGroup=self.CarrierSet:GetFirst()})
-self:I({FindGroup=self.CarrierSet:FindGroup(PlayerGroupName)})
-if self.CarrierSet:FindGroup(PlayerGroupName)then
-if not self.AI_Escorts[PlayerGroupName]then
-local LeaderUnit=PlayerUnit
-local EscortGroup=self.EscortSpawn:SpawnAtAirbase(self.EscortAirbase,SPAWN.Takeoff.Hot)
-self:I({EscortGroup=EscortGroup})
-self:ScheduleOnce(1,
-function(EscortGroup)
-local EscortSet=SET_GROUP:New()
-EscortSet:AddGroup(EscortGroup)
-self.AI_Escorts[PlayerGroupName]=AI_ESCORT:New(LeaderUnit,EscortSet,self.EscortName,self.EscortBriefing)
-self.AI_Escorts[PlayerGroupName]:FormationTrail(0,100,0)
-if EscortGroup:IsHelicopter()then
-self.AI_Escorts[PlayerGroupName]:MenusHelicopters()
-else
-self.AI_Escorts[PlayerGroupName]:MenusAirplanes()
-end
-self.AI_Escorts[PlayerGroupName]:__Start(0.1)
-end,EscortGroup
-)
-end
-end
-end
-AI_ESCORT_DISPATCHER_REQUEST={
-ClassName="AI_ESCORT_DISPATCHER_REQUEST",
-}
-AI_ESCORT_DISPATCHER_REQUEST.AI_Escorts={}
-function AI_ESCORT_DISPATCHER_REQUEST:New(CarrierSet,EscortSpawn,EscortAirbase,EscortName,EscortBriefing)
-local self=BASE:Inherit(self,FSM:New())
-self.CarrierSet=CarrierSet
-self.EscortSpawn=EscortSpawn
-self.EscortAirbase=EscortAirbase
-self.EscortName=EscortName
-self.EscortBriefing=EscortBriefing
-self:SetStartState("Idle")
-self:AddTransition("Monitoring","Monitor","Monitoring")
-self:AddTransition("Idle","Start","Monitoring")
-self:AddTransition("Monitoring","Stop","Idle")
-function self.CarrierSet.OnAfterRemoved(CarrierSet,From,Event,To,CarrierName,Carrier)
-self:F({Carrier=Carrier:GetName()})
-end
-return self
-end
-function AI_ESCORT_DISPATCHER_REQUEST:onafterStart(From,Event,To)
-self:HandleEvent(EVENTS.Birth)
-self:HandleEvent(EVENTS.PlayerLeaveUnit,self.OnEventExit)
-self:HandleEvent(EVENTS.Crash,self.OnEventExit)
-self:HandleEvent(EVENTS.Dead,self.OnEventExit)
-end
-function AI_ESCORT_DISPATCHER_REQUEST:OnEventExit(EventData)
-local PlayerGroupName=EventData.IniGroupName
-local PlayerGroup=EventData.IniGroup
-local PlayerUnit=EventData.IniUnit
-if self.CarrierSet:FindGroup(PlayerGroupName)then
-if self.AI_Escorts[PlayerGroupName]then
-self.AI_Escorts[PlayerGroupName]:Stop()
-self.AI_Escorts[PlayerGroupName]=nil
-end
-end
-end
-function AI_ESCORT_DISPATCHER_REQUEST:OnEventBirth(EventData)
-local PlayerGroupName=EventData.IniGroupName
-local PlayerGroup=EventData.IniGroup
-local PlayerUnit=EventData.IniUnit
-if self.CarrierSet:FindGroup(PlayerGroupName)then
-if not self.AI_Escorts[PlayerGroupName]then
-local LeaderUnit=PlayerUnit
-self:ScheduleOnce(0.1,
-function()
-self.AI_Escorts[PlayerGroupName]=AI_ESCORT_REQUEST:New(LeaderUnit,self.EscortSpawn,self.EscortAirbase,self.EscortName,self.EscortBriefing)
-self.AI_Escorts[PlayerGroupName]:FormationTrail(0,100,0)
-if PlayerGroup:IsHelicopter()then
-self.AI_Escorts[PlayerGroupName]:MenusHelicopters()
-else
-self.AI_Escorts[PlayerGroupName]:MenusAirplanes()
-end
-self.AI_Escorts[PlayerGroupName]:__Start(0.1)
-end
-)
-end
-end
-end
-AI_CARGO={
-ClassName="AI_CARGO",
-Coordinate=nil,
-Carrier_Cargo={},
-}
-function AI_CARGO:New(Carrier,CargoSet)
-local self=BASE:Inherit(self,FSM_CONTROLLABLE:New(Carrier))
-self.CargoSet=CargoSet
-self.CargoCarrier=Carrier
-self:SetStartState("Unloaded")
-self:AddTransition("Unloaded","Pickup","Unloaded")
-self:AddTransition("*","Load","*")
-self:AddTransition("*","Reload","*")
-self:AddTransition("*","Board","*")
-self:AddTransition("*","Loaded","Loaded")
-self:AddTransition("Loaded","PickedUp","Loaded")
-self:AddTransition("Loaded","Deploy","*")
-self:AddTransition("*","Unload","*")
-self:AddTransition("*","Unboard","*")
-self:AddTransition("*","Unloaded","Unloaded")
-self:AddTransition("Unloaded","Deployed","Unloaded")
-for _,CarrierUnit in pairs(Carrier:GetUnits())do
-local CarrierUnit=CarrierUnit
-CarrierUnit:SetCargoBayWeightLimit()
-end
-self.Transporting=false
-self.Relocating=false
-return self
-end
-function AI_CARGO:IsTransporting()
-return self.Transporting==true
-end
-function AI_CARGO:IsRelocating()
-return self.Relocating==true
-end
-function AI_CARGO:onafterPickup(APC,From,Event,To,Coordinate,Speed,Height,PickupZone)
-self.Transporting=false
-self.Relocating=true
-end
-function AI_CARGO:onafterDeploy(APC,From,Event,To,Coordinate,Speed,Height,DeployZone)
-self.Relocating=false
-self.Transporting=true
-end
-function AI_CARGO:onbeforeLoad(Carrier,From,Event,To,PickupZone)
-self:F({Carrier,From,Event,To})
-local Boarding=false
-local LoadInterval=2
-local LoadDelay=1
-local Carrier_List={}
-local Carrier_Weight={}
-if Carrier and Carrier:IsAlive()then
-self.Carrier_Cargo={}
-for _,CarrierUnit in pairs(Carrier:GetUnits())do
-local CarrierUnit=CarrierUnit
-local CargoBayFreeWeight=CarrierUnit:GetCargoBayFreeWeight()
-self:F({CargoBayFreeWeight=CargoBayFreeWeight})
-Carrier_List[#Carrier_List+1]=CarrierUnit
-Carrier_Weight[CarrierUnit]=CargoBayFreeWeight
-end
-local Carrier_Count=#Carrier_List
-local Carrier_Index=1
-local Loaded=false
-for _,Cargo in UTILS.spairs(self.CargoSet:GetSet(),function(t,a,b)return t[a]:GetWeight()>t[b]:GetWeight()end)do
-local Cargo=Cargo
-self:F({IsUnLoaded=Cargo:IsUnLoaded(),IsDeployed=Cargo:IsDeployed(),Cargo:GetName(),Carrier:GetName()})
-for Carrier_Loop=1,#Carrier_List do
-local CarrierUnit=Carrier_List[Carrier_Index]
-Carrier_Index=Carrier_Index+1
-if Carrier_Index>Carrier_Count then
-Carrier_Index=1
-end
-if Cargo:IsUnLoaded()and not Cargo:IsDeployed()then
-if Cargo:IsInLoadRadius(CarrierUnit:GetCoordinate())then
-self:F({"In radius",CarrierUnit:GetName()})
-local CargoWeight=Cargo:GetWeight()
-local CarrierSpace=Carrier_Weight[CarrierUnit]
-if CarrierSpace>CargoWeight then
-Carrier:RouteStop()
-Cargo:__Board(-LoadDelay,CarrierUnit)
-self:__Board(LoadDelay,Cargo,CarrierUnit,PickupZone)
-LoadDelay=LoadDelay+Cargo:GetCount()*LoadInterval
-self.Carrier_Cargo[Cargo]=CarrierUnit
-Boarding=true
-Carrier_Weight[CarrierUnit]=Carrier_Weight[CarrierUnit]-CargoWeight
-Loaded=true
-break
-else
-self:T(string.format("WARNING: Cargo too heavy for carrier %s. Cargo=%.1f > %.1f free space",tostring(CarrierUnit:GetName()),CargoWeight,CarrierSpace))
-end
-end
-end
-end
-end
-if not Loaded==true then
-self.Relocating=false
-end
-end
-return Boarding
-end
-function AI_CARGO:onbeforeReload(Carrier,From,Event,To)
-self:F({Carrier,From,Event,To})
-local Boarding=false
-local LoadInterval=2
-local LoadDelay=1
-local Carrier_List={}
-local Carrier_Weight={}
-if Carrier and Carrier:IsAlive()then
-for _,CarrierUnit in pairs(Carrier:GetUnits())do
-local CarrierUnit=CarrierUnit
-Carrier_List[#Carrier_List+1]=CarrierUnit
-end
-local Carrier_Count=#Carrier_List
-local Carrier_Index=1
-local Loaded=false
-for Cargo,CarrierUnit in pairs(self.Carrier_Cargo)do
-local Cargo=Cargo
-self:F({IsUnLoaded=Cargo:IsUnLoaded(),IsDeployed=Cargo:IsDeployed(),Cargo:GetName(),Carrier:GetName()})
-for Carrier_Loop=1,#Carrier_List do
-local CarrierUnit=Carrier_List[Carrier_Index]
-Carrier_Index=Carrier_Index+1
-if Carrier_Index>Carrier_Count then
-Carrier_Index=1
-end
-if Cargo:IsUnLoaded()and not Cargo:IsDeployed()then
-Carrier:RouteStop()
-Cargo:__Board(-LoadDelay,CarrierUnit)
-self:__Board(LoadDelay,Cargo,CarrierUnit)
-LoadDelay=LoadDelay+Cargo:GetCount()*LoadInterval
-self.Carrier_Cargo[Cargo]=CarrierUnit
-Boarding=true
-Loaded=true
-end
-end
-end
-if not Loaded==true then
-self.Relocating=false
-end
-end
-return Boarding
-end
-function AI_CARGO:onafterBoard(Carrier,From,Event,To,Cargo,CarrierUnit,PickupZone)
-self:F({Carrier,From,Event,To,Cargo,CarrierUnit:GetName()})
-if Carrier and Carrier:IsAlive()then
-self:F({IsLoaded=Cargo:IsLoaded(),Cargo:GetName(),Carrier:GetName()})
-if not Cargo:IsLoaded()and not Cargo:IsDestroyed()then
-self:__Board(-10,Cargo,CarrierUnit,PickupZone)
-return
-end
-end
-self:__Loaded(0.1,Cargo,CarrierUnit,PickupZone)
-end
-function AI_CARGO:onafterLoaded(Carrier,From,Event,To,Cargo,PickupZone)
-self:F({Carrier,From,Event,To})
-local Loaded=true
-if Carrier and Carrier:IsAlive()then
-for Cargo,CarrierUnit in pairs(self.Carrier_Cargo)do
-local Cargo=Cargo
-self:F({IsLoaded=Cargo:IsLoaded(),IsDestroyed=Cargo:IsDestroyed(),Cargo:GetName(),Carrier:GetName()})
-if not Cargo:IsLoaded()and not Cargo:IsDestroyed()then
-Loaded=false
-end
-end
-end
-if Loaded then
-self:__PickedUp(0.1,PickupZone)
-end
-end
-function AI_CARGO:onafterPickedUp(Carrier,From,Event,To,PickupZone)
-self:F({Carrier,From,Event,To})
-Carrier:RouteResume()
-local HasCargo=false
-if Carrier and Carrier:IsAlive()then
-for Cargo,CarrierUnit in pairs(self.Carrier_Cargo)do
-HasCargo=true
-break
-end
-end
-self.Relocating=false
-if HasCargo then
-self:F("Transporting")
-self.Transporting=true
-end
-end
-function AI_CARGO:onafterUnload(Carrier,From,Event,To,DeployZone,Defend)
-self:F({Carrier,From,Event,To,DeployZone,Defend=Defend})
-local UnboardInterval=5
-local UnboardDelay=5
-if Carrier and Carrier:IsAlive()then
-for _,CarrierUnit in pairs(Carrier:GetUnits())do
-local CarrierUnit=CarrierUnit
-Carrier:RouteStop()
-for _,Cargo in pairs(CarrierUnit:GetCargo())do
-self:F({Cargo=Cargo:GetName(),Isloaded=Cargo:IsLoaded()})
-if Cargo:IsLoaded()then
-Cargo:__UnBoard(UnboardDelay)
-UnboardDelay=UnboardDelay+Cargo:GetCount()*UnboardInterval
-self:__Unboard(UnboardDelay,Cargo,CarrierUnit,DeployZone,Defend)
-if not Defend==true then
-Cargo:SetDeployed(true)
-end
-end
-end
-end
-end
-end
-function AI_CARGO:onafterUnboard(Carrier,From,Event,To,Cargo,CarrierUnit,DeployZone,Defend)
-self:F({Carrier,From,Event,To,Cargo:GetName(),DeployZone=DeployZone,Defend=Defend})
-if Carrier and Carrier:IsAlive()then
-if not Cargo:IsUnLoaded()then
-self:__Unboard(10,Cargo,CarrierUnit,DeployZone,Defend)
-return
-end
-end
-self:Unloaded(Cargo,CarrierUnit,DeployZone,Defend)
-end
-function AI_CARGO:onafterUnloaded(Carrier,From,Event,To,Cargo,CarrierUnit,DeployZone,Defend)
-self:F({Carrier,From,Event,To,Cargo:GetName(),DeployZone=DeployZone,Defend=Defend})
-local AllUnloaded=true
-if Carrier and Carrier:IsAlive()then
-for _,CarrierUnit in pairs(Carrier:GetUnits())do
-local CarrierUnit=CarrierUnit
-local IsEmpty=CarrierUnit:IsCargoEmpty()
-self:I({IsEmpty=IsEmpty})
-if not IsEmpty then
-AllUnloaded=false
-break
-end
-end
-if AllUnloaded==true then
-if DeployZone==true then
-self.Carrier_Cargo={}
-end
-self.CargoCarrier=Carrier
-end
-end
-if AllUnloaded==true then
-self:__Deployed(5,DeployZone,Defend)
-end
-end
-function AI_CARGO:onafterDeployed(Carrier,From,Event,To,DeployZone,Defend)
-self:F({Carrier,From,Event,To,DeployZone=DeployZone,Defend=Defend})
-if not Defend==true then
-self.Transporting=false
-else
-self:F("Defending")
-end
-end
-AI_CARGO_APC={
-ClassName="AI_CARGO_APC",
-Coordinate=nil,
-}
-function AI_CARGO_APC:New(APC,CargoSet,CombatRadius)
-local self=BASE:Inherit(self,AI_CARGO:New(APC,CargoSet))
-self:AddTransition("*","Monitor","*")
-self:AddTransition("*","Follow","Following")
-self:AddTransition("*","Guard","Unloaded")
-self:AddTransition("*","Home","*")
-self:AddTransition("*","Reload","Boarding")
-self:AddTransition("*","Deployed","*")
-self:AddTransition("*","PickedUp","*")
-self:AddTransition("*","Destroyed","Destroyed")
-self:SetCombatRadius(CombatRadius)
-self:SetCarrier(APC)
-return self
-end
-function AI_CARGO_APC:SetCarrier(CargoCarrier)
-self.CargoCarrier=CargoCarrier
-self.CargoCarrier:SetState(self.CargoCarrier,"AI_CARGO_APC",self)
-CargoCarrier:HandleEvent(EVENTS.Dead)
-function CargoCarrier:OnEventDead(EventData)
-self:F({"dead"})
-local AICargoTroops=self:GetState(self,"AI_CARGO_APC")
-self:F({AICargoTroops=AICargoTroops})
-if AICargoTroops then
-self:F({})
-if not AICargoTroops:Is("Loaded")then
-AICargoTroops:Destroyed()
-end
-end
-end
-self.Zone=ZONE_UNIT:New(self.CargoCarrier:GetName().."-Zone",self.CargoCarrier,self.CombatRadius)
-self.Coalition=self.CargoCarrier:GetCoalition()
-self:SetControllable(CargoCarrier)
-self:Guard()
-return self
-end
-function AI_CARGO_APC:SetOffRoad(Offroad,Formation)
-self:SetPickupOffRoad(Offroad,Formation)
-self:SetDeployOffRoad(Offroad,Formation)
-return self
-end
-function AI_CARGO_APC:SetPickupOffRoad(Offroad,Formation)
-self.pickupOffroad=Offroad
-self.pickupFormation=Formation or ENUMS.Formation.Vehicle.OffRoad
-return self
-end
-function AI_CARGO_APC:SetDeployOffRoad(Offroad,Formation)
-self.deployOffroad=Offroad
-self.deployFormation=Formation or ENUMS.Formation.Vehicle.OffRoad
-return self
-end
-function AI_CARGO_APC:FindCarrier(Coordinate,Radius)
-local CoordinateZone=ZONE_RADIUS:New("Zone",Coordinate:GetVec2(),Radius)
-CoordinateZone:Scan({Object.Category.UNIT})
-for _,DCSUnit in pairs(CoordinateZone:GetScannedUnits())do
-local NearUnit=UNIT:Find(DCSUnit)
-self:F({NearUnit=NearUnit})
-if not NearUnit:GetState(NearUnit,"AI_CARGO_APC")then
-local Attributes=NearUnit:GetDesc()
-self:F({Desc=Attributes})
-if NearUnit:HasAttribute("Trucks")then
-return NearUnit:GetGroup()
-end
-end
-end
-return nil
-end
-function AI_CARGO_APC:SetCombatRadius(CombatRadius)
-self.CombatRadius=CombatRadius or 0
-if self.CombatRadius>0 then
-self:__Monitor(-5)
-end
-return self
-end
-function AI_CARGO_APC:FollowToCarrier(Me,APCUnit,CargoGroup)
-local InfantryGroup=CargoGroup:GetGroup()
-self:F({self=self:GetClassNameAndID(),InfantryGroup=InfantryGroup:GetName()})
-if APCUnit:IsAlive()then
-if InfantryGroup:IsPartlyInZone(ZONE_UNIT:New("Radius",APCUnit,25))then
-Me:Guard()
-else
-self:F({InfantryGroup=InfantryGroup:GetName()})
-if InfantryGroup:IsAlive()then
-self:F({InfantryGroup=InfantryGroup:GetName()})
-local Waypoints={}
-local FromCoord=InfantryGroup:GetCoordinate()
-local FromGround=FromCoord:WaypointGround(10,"Diamond")
-self:F({FromGround=FromGround})
-table.insert(Waypoints,FromGround)
-local ToCoord=APCUnit:GetCoordinate():GetRandomCoordinateInRadius(10,5)
-local ToGround=ToCoord:WaypointGround(10,"Diamond")
-self:F({ToGround=ToGround})
-table.insert(Waypoints,ToGround)
-local TaskRoute=InfantryGroup:TaskFunction("AI_CARGO_APC.FollowToCarrier",Me,APCUnit,CargoGroup)
-self:F({Waypoints=Waypoints})
-local Waypoint=Waypoints[#Waypoints]
-InfantryGroup:SetTaskWaypoint(Waypoint,TaskRoute)
-InfantryGroup:Route(Waypoints,1)
-end
-end
-end
-end
-function AI_CARGO_APC:onafterMonitor(APC,From,Event,To)
-self:F({APC,From,Event,To,IsTransporting=self:IsTransporting()})
-if self.CombatRadius>0 then
-if APC and APC:IsAlive()then
-if self.CarrierCoordinate then
-if self:IsTransporting()==true then
-local Coordinate=APC:GetCoordinate()
-if self:Is("Unloaded")or self:Is("Loaded")then
-self.Zone:Scan({Object.Category.UNIT})
-if self.Zone:IsAllInZoneOfCoalition(self.Coalition)then
-if self:Is("Unloaded")then
-self:Reload()
-end
-else
-if self:Is("Loaded")then
-self:__Unload(1,nil,true)
-else
-if self:Is("Unloaded")then
-end
-self:F("I am here"..self:GetCurrentState())
-if self:Is("Following")then
-for Cargo,APCUnit in pairs(self.Carrier_Cargo)do
-local Cargo=Cargo
-local APCUnit=APCUnit
-if Cargo:IsAlive()then
-if not Cargo:IsNear(APCUnit,40)then
-APCUnit:RouteStop()
-self.CarrierStopped=true
-else
-if self.CarrierStopped then
-if Cargo:IsNear(APCUnit,25)then
-APCUnit:RouteResume()
-self.CarrierStopped=nil
-end
-end
-end
-end
-end
-end
-end
-end
-end
-end
-end
-self.CarrierCoordinate=APC:GetCoordinate()
-end
-self:__Monitor(-5)
-end
-end
-function AI_CARGO_APC:onafterFollow(APC,From,Event,To)
-self:F({APC,From,Event,To})
-self:F("Follow")
-if APC and APC:IsAlive()then
-for Cargo,APCUnit in pairs(self.Carrier_Cargo)do
-local Cargo=Cargo
-if Cargo:IsUnLoaded()then
-self:FollowToCarrier(self,APCUnit,Cargo)
-APCUnit:RouteResume()
-end
-end
-end
-end
-function AI_CARGO_APC._Pickup(APC,self,Coordinate,Speed,PickupZone)
-APC:F({"AI_CARGO_APC._Pickup:",APC:GetName()})
-if APC:IsAlive()then
-self:Load(PickupZone)
-end
-end
-function AI_CARGO_APC._Deploy(APC,self,Coordinate,DeployZone)
-APC:F({"AI_CARGO_APC._Deploy:",APC})
-if APC:IsAlive()then
-self:Unload(DeployZone)
-end
-end
-function AI_CARGO_APC:onafterPickup(APC,From,Event,To,Coordinate,Speed,Height,PickupZone)
-if APC and APC:IsAlive()then
-if Coordinate then
-self.RoutePickup=true
-local _speed=Speed or APC:GetSpeedMax()*0.5
-local Waypoints={}
-if self.pickupOffroad then
-Waypoints[1]=APC:GetCoordinate():WaypointGround(Speed,self.pickupFormation)
-Waypoints[2]=Coordinate:WaypointGround(_speed,self.pickupFormation,DCSTasks)
-else
-Waypoints=APC:TaskGroundOnRoad(Coordinate,_speed,ENUMS.Formation.Vehicle.OffRoad,true)
-end
-local TaskFunction=APC:TaskFunction("AI_CARGO_APC._Pickup",self,Coordinate,Speed,PickupZone)
-local Waypoint=Waypoints[#Waypoints]
-APC:SetTaskWaypoint(Waypoint,TaskFunction)
-APC:Route(Waypoints,1)
-else
-AI_CARGO_APC._Pickup(APC,self,Coordinate,Speed,PickupZone)
-end
-self:GetParent(self,AI_CARGO_APC).onafterPickup(self,APC,From,Event,To,Coordinate,Speed,Height,PickupZone)
-end
-end
-function AI_CARGO_APC:onafterDeploy(APC,From,Event,To,Coordinate,Speed,Height,DeployZone)
-if APC and APC:IsAlive()then
-self.RouteDeploy=true
-local speedmax=APC:GetSpeedMax()
-local _speed=Speed or speedmax*0.5
-_speed=math.min(_speed,speedmax)
-local Waypoints={}
-if self.deployOffroad then
-Waypoints[1]=APC:GetCoordinate():WaypointGround(Speed,self.deployFormation)
-Waypoints[2]=Coordinate:WaypointGround(_speed,self.deployFormation,DCSTasks)
-else
-Waypoints=APC:TaskGroundOnRoad(Coordinate,_speed,ENUMS.Formation.Vehicle.OffRoad,true)
-end
-local TaskFunction=APC:TaskFunction("AI_CARGO_APC._Deploy",self,Coordinate,DeployZone)
-local Waypoint=Waypoints[#Waypoints]
-APC:SetTaskWaypoint(Waypoint,TaskFunction)
-APC:Route(Waypoints,1)
-self:GetParent(self,AI_CARGO_APC).onafterDeploy(self,APC,From,Event,To,Coordinate,Speed,Height,DeployZone)
-end
-end
-function AI_CARGO_APC:onafterUnloaded(Carrier,From,Event,To,Cargo,CarrierUnit,DeployZone,Defend)
-self:F({Carrier,From,Event,To,DeployZone=DeployZone,Defend=Defend})
-self:GetParent(self,AI_CARGO_APC).onafterUnloaded(self,Carrier,From,Event,To,Cargo,CarrierUnit,DeployZone,Defend)
-if Defend==true then
-self.Zone:Scan({Object.Category.UNIT})
-if not self.Zone:IsAllInZoneOfCoalition(self.Coalition)then
-local AttackUnits=self.Zone:GetScannedUnits()
-local Move={}
-local CargoGroup=Cargo.CargoObject
-Move[#Move+1]=CargoGroup:GetCoordinate():WaypointGround(70,"Custom")
-for UnitId,AttackUnit in pairs(AttackUnits)do
-local MooseUnit=UNIT:Find(AttackUnit)
-if MooseUnit:GetCoalition()~=CargoGroup:GetCoalition()then
-Move[#Move+1]=MooseUnit:GetCoordinate():WaypointGround(70,"Line abreast")
-self:F({MooseUnit=MooseUnit:GetName(),CargoGroup=CargoGroup:GetName()})
-end
-end
-CargoGroup:RoutePush(Move,0.1)
-end
-end
-end
-function AI_CARGO_APC:onafterDeployed(APC,From,Event,To,DeployZone,Defend)
-self:F({APC,From,Event,To,DeployZone=DeployZone,Defend=Defend})
-self:__Guard(0.1)
-self:GetParent(self,AI_CARGO_APC).onafterDeployed(self,APC,From,Event,To,DeployZone,Defend)
-end
-function AI_CARGO_APC:onafterHome(APC,From,Event,To,Coordinate,Speed,Height,HomeZone)
-if APC and APC:IsAlive()~=nil then
-self.RouteHome=true
-Speed=Speed or APC:GetSpeedMax()*0.5
-local Waypoints=APC:TaskGroundOnRoad(Coordinate,Speed,"Line abreast",true)
-self:F({Waypoints=Waypoints})
-local Waypoint=Waypoints[#Waypoints]
-APC:Route(Waypoints,1)
-end
-end
-AI_CARGO_HELICOPTER={
-ClassName="AI_CARGO_HELICOPTER",
-Coordinate=nil,
-}
-AI_CARGO_QUEUE={}
-function AI_CARGO_HELICOPTER:New(Helicopter,CargoSet)
-local self=BASE:Inherit(self,AI_CARGO:New(Helicopter,CargoSet))
-self.Zone=ZONE_GROUP:New(Helicopter:GetName(),Helicopter,300)
-self:SetStartState("Unloaded")
-self:AddTransition("Unloaded","Pickup","Unloaded")
-self:AddTransition("*","Landed","*")
-self:AddTransition("*","Load","*")
-self:AddTransition("*","Loaded","Loaded")
-self:AddTransition("Loaded","PickedUp","Loaded")
-self:AddTransition("Loaded","Deploy","*")
-self:AddTransition("*","Queue","*")
-self:AddTransition("*","Orbit","*")
-self:AddTransition("*","Destroyed","*")
-self:AddTransition("*","Unload","*")
-self:AddTransition("*","Unloaded","Unloaded")
-self:AddTransition("Unloaded","Deployed","Unloaded")
-self:AddTransition("*","Home","*")
-Helicopter:HandleEvent(EVENTS.Crash,
-function(Helicopter,EventData)
-AI_CARGO_QUEUE[Helicopter]=nil
-end
-)
-Helicopter:HandleEvent(EVENTS.Land,
-function(Helicopter,EventData)
-self:ScheduleOnce(60,
-function(Helicopter)
-AI_CARGO_QUEUE[Helicopter]=nil
-end,Helicopter
-)
-end
-)
-self:SetCarrier(Helicopter)
-self.landingspeed=15
-self.landingheight=5.5
-return self
-end
-function AI_CARGO_HELICOPTER:SetCarrier(Helicopter)
-local AICargo=self
-self.Helicopter=Helicopter
-self.Helicopter:SetState(self.Helicopter,"AI_CARGO_HELICOPTER",self)
-self.RoutePickup=false
-self.RouteDeploy=false
-Helicopter:HandleEvent(EVENTS.Dead)
-Helicopter:HandleEvent(EVENTS.Hit)
-Helicopter:HandleEvent(EVENTS.Land)
-function Helicopter:OnEventDead(EventData)
-local AICargoTroops=self:GetState(self,"AI_CARGO_HELICOPTER")
-self:F({AICargoTroops=AICargoTroops})
-if AICargoTroops then
-self:F({})
-if not AICargoTroops:Is("Loaded")then
-AICargoTroops:Destroyed()
-end
-end
-end
-function Helicopter:OnEventLand(EventData)
-AICargo:Landed()
-end
-self.Coalition=self.Helicopter:GetCoalition()
-self:SetControllable(Helicopter)
-return self
-end
-function AI_CARGO_HELICOPTER:SetLandingSpeedAndHeight(speed,height)
-local _speed=speed or 15
-local _height=height or 5.5
-self.landingheight=_height
-self.landingspeed=_speed
-return self
-end
-function AI_CARGO_HELICOPTER:onafterLanded(Helicopter,From,Event,To)
-self:F({From,Event,To})
-Helicopter:F({Name=Helicopter:GetName()})
-if Helicopter and Helicopter:IsAlive()then
-self:T({Helicopter:GetName(),Height=Helicopter:GetHeight(true),Velocity=Helicopter:GetVelocityKMH()})
-if self.RoutePickup==true then
-if Helicopter:GetHeight(true)<=self.landingheight then
-self:Load(self.PickupZone)
-self.RoutePickup=false
-end
-end
-if self.RouteDeploy==true then
-if Helicopter:GetHeight(true)<=self.landingheight then
-self:Unload(self.DeployZone)
-self.RouteDeploy=false
-end
-end
-end
-end
-function AI_CARGO_HELICOPTER:onafterQueue(Helicopter,From,Event,To,Coordinate,Speed,DeployZone)
-self:F({From,Event,To,Coordinate,Speed,DeployZone})
-local HelicopterInZone=false
-if Helicopter and Helicopter:IsAlive()==true then
-local Distance=Coordinate:DistanceFromPointVec2(Helicopter:GetCoordinate())
-if Distance>2000 then
-self:__Queue(-10,Coordinate,Speed,DeployZone)
-else
-local ZoneFree=true
-for Helicopter,ZoneQueue in pairs(AI_CARGO_QUEUE)do
-local ZoneQueue=ZoneQueue
-if ZoneQueue:IsCoordinateInZone(Coordinate)then
-ZoneFree=false
-end
-end
-self:F({ZoneFree=ZoneFree})
-if ZoneFree==true then
-local ZoneQueue=ZONE_RADIUS:New(Helicopter:GetName(),Coordinate:GetVec2(),100)
-AI_CARGO_QUEUE[Helicopter]=ZoneQueue
-local Route={}
-local CoordinateTo=Coordinate
-local landheight=CoordinateTo:GetLandHeight()
-CoordinateTo.y=landheight+50
-local WaypointTo=CoordinateTo:WaypointAir(
-"RADIO",
-POINT_VEC3.RoutePointType.TurningPoint,
-POINT_VEC3.RoutePointAction.TurningPoint,
-50,
-true
-)
-Route[#Route+1]=WaypointTo
-local Tasks={}
-Tasks[#Tasks+1]=Helicopter:TaskLandAtVec2(CoordinateTo:GetVec2())
-Route[#Route].task=Helicopter:TaskCombo(Tasks)
-Route[#Route+1]=WaypointTo
-Helicopter:Route(Route,0)
-self.DeployZone=DeployZone
-else
-self:__Queue(-10,Coordinate,Speed,DeployZone)
-end
-end
-else
-AI_CARGO_QUEUE[Helicopter]=nil
-end
-end
-function AI_CARGO_HELICOPTER:onafterOrbit(Helicopter,From,Event,To,Coordinate)
-self:F({From,Event,To,Coordinate})
-if Helicopter and Helicopter:IsAlive()then
-local Route={}
-local CoordinateTo=Coordinate
-local landheight=CoordinateTo:GetLandHeight()
-CoordinateTo.y=landheight+50
-local WaypointTo=CoordinateTo:WaypointAir("RADIO",POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,50,true)
-Route[#Route+1]=WaypointTo
-local Tasks={}
-Tasks[#Tasks+1]=Helicopter:TaskOrbitCircle(math.random(30,80),150,CoordinateTo:GetRandomCoordinateInRadius(800,500))
-Route[#Route].task=Helicopter:TaskCombo(Tasks)
-Route[#Route+1]=WaypointTo
-Helicopter:Route(Route,0)
-end
-end
-function AI_CARGO_HELICOPTER:onafterDeployed(Helicopter,From,Event,To,DeployZone)
-self:F({From,Event,To,DeployZone=DeployZone})
-self:Orbit(Helicopter:GetCoordinate(),50)
-self:ScheduleOnce(30,
-function(Helicopter)
-AI_CARGO_QUEUE[Helicopter]=nil
-end,Helicopter
-)
-self:GetParent(self,AI_CARGO_HELICOPTER).onafterDeployed(self,Helicopter,From,Event,To,DeployZone)
-end
-function AI_CARGO_HELICOPTER:onafterPickup(Helicopter,From,Event,To,Coordinate,Speed,Height,PickupZone)
-self:F({Coordinate,Speed,Height,PickupZone})
-if Helicopter and Helicopter:IsAlive()~=nil then
-Helicopter:Activate()
-self.RoutePickup=true
-Coordinate.y=Height
-local _speed=Speed or Helicopter:GetSpeedMax()*0.5
-local Route={}
-local CoordinateFrom=Helicopter:GetCoordinate()
-local WaypointFrom=CoordinateFrom:WaypointAir("RADIO",POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,_speed,true)
-local CoordinateTo=Coordinate
-local landheight=CoordinateTo:GetLandHeight()
-CoordinateTo.y=landheight+50
-local WaypointTo=CoordinateTo:WaypointAir("RADIO",POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,_speed,true)
-Route[#Route+1]=WaypointFrom
-Route[#Route+1]=WaypointTo
-Helicopter:WayPointInitialize(Route)
-local Tasks={}
-Tasks[#Tasks+1]=Helicopter:TaskLandAtVec2(CoordinateTo:GetVec2())
-Route[#Route].task=Helicopter:TaskCombo(Tasks)
-Route[#Route+1]=WaypointTo
-Helicopter:Route(Route,1)
-self.PickupZone=PickupZone
-self:GetParent(self,AI_CARGO_HELICOPTER).onafterPickup(self,Helicopter,From,Event,To,Coordinate,Speed,Height,PickupZone)
-end
-end
-function AI_CARGO_HELICOPTER:_Deploy(AICargoHelicopter,Coordinate,DeployZone)
-AICargoHelicopter:__Queue(-10,Coordinate,100,DeployZone)
-end
-function AI_CARGO_HELICOPTER:onafterDeploy(Helicopter,From,Event,To,Coordinate,Speed,Height,DeployZone)
-self:F({From,Event,To,Coordinate,Speed,Height,DeployZone})
-if Helicopter and Helicopter:IsAlive()~=nil then
-self.RouteDeploy=true
-local Route={}
-Coordinate.y=Height
-local _speed=Speed or Helicopter:GetSpeedMax()*0.5
-local CoordinateFrom=Helicopter:GetCoordinate()
-local WaypointFrom=CoordinateFrom:WaypointAir("RADIO",POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,_speed,true)
-Route[#Route+1]=WaypointFrom
-Route[#Route+1]=WaypointFrom
-local CoordinateTo=Coordinate
-local landheight=CoordinateTo:GetLandHeight()
-CoordinateTo.y=landheight+50
-local WaypointTo=CoordinateTo:WaypointAir("RADIO",POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,_speed,true)
-Route[#Route+1]=WaypointTo
-Route[#Route+1]=WaypointTo
-Helicopter:WayPointInitialize(Route)
-local Tasks={}
-Tasks[#Tasks+1]=Helicopter:TaskFunction("AI_CARGO_HELICOPTER._Deploy",self,Coordinate,DeployZone)
-Tasks[#Tasks+1]=Helicopter:TaskOrbitCircle(math.random(30,100),_speed,CoordinateTo:GetRandomCoordinateInRadius(800,500))
-Route[#Route].task=Helicopter:TaskCombo(Tasks)
-Route[#Route+1]=WaypointTo
-Helicopter:Route(Route,0)
-self:GetParent(self,AI_CARGO_HELICOPTER).onafterDeploy(self,Helicopter,From,Event,To,Coordinate,Speed,Height,DeployZone)
-end
-end
-function AI_CARGO_HELICOPTER:onafterHome(Helicopter,From,Event,To,Coordinate,Speed,Height,HomeZone)
-self:F({From,Event,To,Coordinate,Speed,Height})
-if Helicopter and Helicopter:IsAlive()~=nil then
-self.RouteHome=true
-local Route={}
-Height=Height or 50
-Speed=Speed or Helicopter:GetSpeedMax()*0.5
-local CoordinateFrom=Helicopter:GetCoordinate()
-local WaypointFrom=CoordinateFrom:WaypointAir("RADIO",POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,Speed,true)
-Route[#Route+1]=WaypointFrom
-local CoordinateTo=Coordinate
-local landheight=CoordinateTo:GetLandHeight()
-CoordinateTo.y=landheight+Height
-local WaypointTo=CoordinateTo:WaypointAir("RADIO",POINT_VEC3.RoutePointType.TurningPoint,POINT_VEC3.RoutePointAction.TurningPoint,Speed,true)
-Route[#Route+1]=WaypointTo
-Helicopter:WayPointInitialize(Route)
-local Tasks={}
-Tasks[#Tasks+1]=Helicopter:TaskLandAtVec2(CoordinateTo:GetVec2())
-Route[#Route].task=Helicopter:TaskCombo(Tasks)
-Route[#Route+1]=WaypointTo
-Helicopter:Route(Route,0)
-end
-end
-AI_CARGO_AIRPLANE={
-ClassName="AI_CARGO_AIRPLANE",
-Coordinate=nil,
-}
-function AI_CARGO_AIRPLANE:New(Airplane,CargoSet)
-local self=BASE:Inherit(self,AI_CARGO:New(Airplane,CargoSet))
-self:AddTransition("*","Landed","*")
-self:AddTransition("*","Home","*")
-self:AddTransition("*","Destroyed","Destroyed")
-self:SetCarrier(Airplane)
-return self
-end
-function AI_CARGO_AIRPLANE:SetCarrier(Airplane)
-local AICargo=self
-self.Airplane=Airplane
-self.Airplane:SetState(self.Airplane,"AI_CARGO_AIRPLANE",self)
-self.RoutePickup=false
-self.RouteDeploy=false
-Airplane:HandleEvent(EVENTS.Dead)
-Airplane:HandleEvent(EVENTS.Hit)
-Airplane:HandleEvent(EVENTS.EngineShutdown)
-function Airplane:OnEventDead(EventData)
-local AICargoTroops=self:GetState(self,"AI_CARGO_AIRPLANE")
-self:F({AICargoTroops=AICargoTroops})
-if AICargoTroops then
-self:F({})
-if not AICargoTroops:Is("Loaded")then
-AICargoTroops:Destroyed()
-end
-end
-end
-function Airplane:OnEventHit(EventData)
-local AICargoTroops=self:GetState(self,"AI_CARGO_AIRPLANE")
-if AICargoTroops then
-self:F({OnHitLoaded=AICargoTroops:Is("Loaded")})
-if AICargoTroops:Is("Loaded")or AICargoTroops:Is("Boarding")then
-AICargoTroops:Unload()
-end
-end
-end
-function Airplane:OnEventEngineShutdown(EventData)
-AICargo.Relocating=false
-AICargo:Landed(self.Airplane)
-end
-self.Coalition=self.Airplane:GetCoalition()
-self:SetControllable(Airplane)
-return self
-end
-function AI_CARGO_AIRPLANE:FindCarrier(Coordinate,Radius)
-local CoordinateZone=ZONE_RADIUS:New("Zone",Coordinate:GetVec2(),Radius)
-CoordinateZone:Scan({Object.Category.UNIT})
-for _,DCSUnit in pairs(CoordinateZone:GetScannedUnits())do
-local NearUnit=UNIT:Find(DCSUnit)
-self:F({NearUnit=NearUnit})
-if not NearUnit:GetState(NearUnit,"AI_CARGO_AIRPLANE")then
-local Attributes=NearUnit:GetDesc()
-self:F({Desc=Attributes})
-if NearUnit:HasAttribute("Trucks")then
-self:SetCarrier(NearUnit)
-break
-end
-end
-end
-end
-function AI_CARGO_AIRPLANE:onafterLanded(Airplane,From,Event,To)
-self:F({Airplane,From,Event,To})
-if Airplane and Airplane:IsAlive()~=nil then
-if self.RoutePickup==true then
-self:Load(self.PickupZone)
-end
-if self.RouteDeploy==true then
-self:Unload()
-self.RouteDeploy=false
-end
-end
-end
-function AI_CARGO_AIRPLANE:onafterPickup(Airplane,From,Event,To,Coordinate,Speed,Height,PickupZone)
-if Airplane and Airplane:IsAlive()then
-local airbasepickup=Coordinate:GetClosestAirbase()
-self.PickupZone=PickupZone or ZONE_AIRBASE:New(airbasepickup:GetName())
-local ClosestAirbase,DistToAirbase=Airplane:GetCoordinate():GetClosestAirbase()
-if Airplane:InAir()then
-self.Airbase=nil
-else
-self.Airbase=ClosestAirbase
-end
-local Airbase=self.PickupZone:GetAirbase()
-local Dist=Airbase:GetCoordinate():Get2DDistance(ClosestAirbase:GetCoordinate())
-if Airplane:InAir()or Dist>500 then
-self:Route(Airplane,Airbase,Speed,Height)
-self.Airbase=Airbase
-self.RoutePickup=true
-else
-self.RoutePickup=true
-self:Landed()
-end
-self:GetParent(self,AI_CARGO_AIRPLANE).onafterPickup(self,Airplane,From,Event,To,Coordinate,Speed,Height,self.PickupZone)
-end
-end
-function AI_CARGO_AIRPLANE:onafterDeploy(Airplane,From,Event,To,Coordinate,Speed,Height,DeployZone)
-if Airplane and Airplane:IsAlive()~=nil then
-local Airbase=Coordinate:GetClosestAirbase()
-if DeployZone then
-Airbase=DeployZone:GetAirbase()
-end
-if Airplane:IsAlive()==false then
-Airplane:SetCommand({id='Start',params={}})
-end
-self:Route(Airplane,Airbase,Speed,Height)
-self.RouteDeploy=true
-self.Airbase=Airbase
-self:GetParent(self,AI_CARGO_AIRPLANE).onafterDeploy(self,Airplane,From,Event,To,Coordinate,Speed,Height,DeployZone)
-end
-end
-function AI_CARGO_AIRPLANE:onafterUnload(Airplane,From,Event,To,DeployZone)
-local UnboardInterval=10
-local UnboardDelay=10
-if Airplane and Airplane:IsAlive()then
-for _,AirplaneUnit in pairs(Airplane:GetUnits())do
-local Cargos=AirplaneUnit:GetCargo()
-for CargoID,Cargo in pairs(Cargos)do
-local Angle=180
-local CargoCarrierHeading=Airplane:GetHeading()
-local CargoDeployHeading=((CargoCarrierHeading+Angle)>=360)and(CargoCarrierHeading+Angle-360)or(CargoCarrierHeading+Angle)
-self:T({CargoCarrierHeading,CargoDeployHeading})
-local CargoDeployCoordinate=Airplane:GetPointVec2():Translate(150,CargoDeployHeading)
-Cargo:__UnBoard(UnboardDelay,CargoDeployCoordinate)
-UnboardDelay=UnboardDelay+UnboardInterval
-Cargo:SetDeployed(true)
-self:__Unboard(UnboardDelay,Cargo,AirplaneUnit,DeployZone)
-end
-end
-end
-end
-function AI_CARGO_AIRPLANE:Route(Airplane,Airbase,Speed,Height,Uncontrolled)
-if Airplane and Airplane:IsAlive()then
-local Takeoff=SPAWN.Takeoff.Cold
-local Template=Airplane:GetTemplate()
-if Template==nil then
-return
-end
-local Points={}
-local AirbasePointVec2=Airbase:GetPointVec2()
-local ToWaypoint=AirbasePointVec2:WaypointAir(POINT_VEC3.RoutePointAltType.BARO,"Land","Landing",Speed or Airplane:GetSpeedMax()*0.8,true,Airbase)
-if self.Airbase then
-Template.route.points[2]=ToWaypoint
-Airplane:RespawnAtCurrentAirbase(Template,Takeoff,Uncontrolled)
-else
-local GroupPoint=Airplane:GetVec2()
-local FromWaypoint={}
-FromWaypoint.x=GroupPoint.x
-FromWaypoint.y=GroupPoint.y
-FromWaypoint.type="Turning Point"
-FromWaypoint.action="Turning Point"
-FromWaypoint.speed=Airplane:GetSpeedMax()*0.8
-Points[1]=FromWaypoint
-Points[2]=ToWaypoint
-local PointVec3=Airplane:GetPointVec3()
-Template.x=PointVec3.x
-Template.y=PointVec3.z
-Template.route.points=Points
-local GroupSpawned=Airplane:Respawn(Template)
-end
-end
-end
-function AI_CARGO_AIRPLANE:onafterHome(Airplane,From,Event,To,Coordinate,Speed,Height,HomeZone)
-if Airplane and Airplane:IsAlive()then
-self.RouteHome=true
-local HomeBase=HomeZone:GetAirbase()
-self.Airbase=HomeBase
-self:Route(Airplane,HomeBase,Speed,Height)
-end
-end
-AI_CARGO_SHIP={
-ClassName="AI_CARGO_SHIP",
-Coordinate=nil
-}
-function AI_CARGO_SHIP:New(Ship,CargoSet,CombatRadius,ShippingLane)
-local self=BASE:Inherit(self,AI_CARGO:New(Ship,CargoSet))
-self:AddTransition("*","Monitor","*")
-self:AddTransition("*","Destroyed","Destroyed")
-self:AddTransition("*","Home","*")
-self:SetCombatRadius(0)
-self:SetShippingLane(ShippingLane)
-self:SetCarrier(Ship)
-return self
-end
-function AI_CARGO_SHIP:SetCarrier(CargoCarrier)
-self.CargoCarrier=CargoCarrier
-self.CargoCarrier:SetState(self.CargoCarrier,"AI_CARGO_SHIP",self)
-CargoCarrier:HandleEvent(EVENTS.Dead)
-function CargoCarrier:OnEventDead(EventData)
-self:F({"dead"})
-local AICargoTroops=self:GetState(self,"AI_CARGO_SHIP")
-self:F({AICargoTroops=AICargoTroops})
-if AICargoTroops then
-self:F({})
-if not AICargoTroops:Is("Loaded")then
-AICargoTroops:Destroyed()
-end
-end
-end
-self.Zone=ZONE_UNIT:New(self.CargoCarrier:GetName().."-Zone",self.CargoCarrier,self.CombatRadius)
-self.Coalition=self.CargoCarrier:GetCoalition()
-self:SetControllable(CargoCarrier)
-return self
-end
-function AI_CARGO_SHIP:FindCarrier(Coordinate,Radius)
-local CoordinateZone=ZONE_RADIUS:New("Zone",Coordinate:GetVec2(),Radius)
-CoordinateZone:Scan({Object.Category.UNIT})
-for _,DCSUnit in pairs(CoordinateZone:GetScannedUnits())do
-local NearUnit=UNIT:Find(DCSUnit)
-self:F({NearUnit=NearUnit})
-if not NearUnit:GetState(NearUnit,"AI_CARGO_SHIP")then
-local Attributes=NearUnit:GetDesc()
-self:F({Desc=Attributes})
-if NearUnit:HasAttributes("Trucks")then
-return NearUnit:GetGroup()
-end
-end
-end
-return nil
-end
-function AI_CARGO_SHIP:SetShippingLane(ShippingLane)
-self.ShippingLane=ShippingLane
-return self
-end
-function AI_CARGO_SHIP:SetCombatRadius(CombatRadius)
-self.CombatRadius=CombatRadius or 0
-return self
-end
-function AI_CARGO_SHIP:FollowToCarrier(Me,ShipUnit,CargoGroup)
-local InfantryGroup=CargoGroup:GetGroup()
-self:F({self=self:GetClassNameAndID(),InfantryGroup=InfantryGroup:GetName()})
-if ShipUnit:IsAlive()then
-if InfantryGroup:IsPartlyInZone(ZONE_UNIT:New("Radius",ShipUnit,1000))then
-Me:Guard()
-else
-self:F({InfantryGroup=InfantryGroup:GetName()})
-if InfantryGroup:IsAlive()then
-self:F({InfantryGroup=InfantryGroup:GetName()})
-local Waypoints={}
-local FromCoord=InfantryGroup:GetCoordinate()
-local FromGround=FromCoord:WaypointGround(10,"Diamond")
-self:F({FromGround=FromGround})
-table.insert(Waypoints,FromGround)
-local ToCoord=ShipUnit:GetCoordinate():GetRandomCoordinateInRadius(10,5)
-local ToGround=ToCoord:WaypointGround(10,"Diamond")
-self:F({ToGround=ToGround})
-table.insert(Waypoints,ToGround)
-local TaskRoute=InfantryGroup:TaskFunction("AI_CARGO_SHIP.FollowToCarrier",Me,ShipUnit,CargoGroup)
-self:F({Waypoints=Waypoints})
-local Waypoint=Waypoints[#Waypoints]
-InfantryGroup:SetTaskWaypoint(Waypoint,TaskRoute)
-InfantryGroup:Route(Waypoints,1)
-end
-end
-end
-end
-function AI_CARGO_SHIP:onafterMonitor(Ship,From,Event,To)
-self:F({Ship,From,Event,To,IsTransporting=self:IsTransporting()})
-if self.CombatRadius>0 then
-if Ship and Ship:IsAlive()then
-if self.CarrierCoordinate then
-if self:IsTransporting()==true then
-local Coordinate=Ship:GetCoordinate()
-if self:Is("Unloaded")or self:Is("Loaded")then
-self.Zone:Scan({Object.Category.UNIT})
-if self.Zone:IsAllInZoneOfCoalition(self.Coalition)then
-if self:Is("Unloaded")then
-self:Reload()
-end
-else
-if self:Is("Loaded")then
-self:__Unload(1,nil,true)
-else
-if self:Is("Unloaded")then
-end
-self:F("I am here"..self:GetCurrentState())
-if self:Is("Following")then
-for Cargo,ShipUnit in pairs(self.Carrier_Cargo)do
-local Cargo=Cargo
-local ShipUnit=ShipUnit
-if Cargo:IsAlive()then
-if not Cargo:IsNear(ShipUnit,40)then
-ShipUnit:RouteStop()
-self.CarrierStopped=true
-else
-if self.CarrierStopped then
-if Cargo:IsNear(ShipUnit,25)then
-ShipUnit:RouteResume()
-self.CarrierStopped=nil
-end
-end
-end
-end
-end
-end
-end
-end
-end
-end
-end
-self.CarrierCoordinate=Ship:GetCoordinate()
-end
-self:__Monitor(-5)
-end
-end
-function AI_CARGO_SHIP._Pickup(Ship,self,Coordinate,Speed,PickupZone)
-Ship:F({"AI_CARGO_Ship._Pickup:",Ship:GetName()})
-if Ship:IsAlive()then
-self:Load(PickupZone)
-end
-end
-function AI_CARGO_SHIP._Deploy(Ship,self,Coordinate,DeployZone)
-Ship:F({"AI_CARGO_Ship._Deploy:",Ship})
-if Ship:IsAlive()then
-self:Unload(DeployZone)
-end
-end
-function AI_CARGO_SHIP:onafterPickup(Ship,From,Event,To,Coordinate,Speed,Height,PickupZone)
-if Ship and Ship:IsAlive()then
-AI_CARGO_SHIP._Pickup(Ship,self,Coordinate,Speed,PickupZone)
-self:GetParent(self,AI_CARGO_SHIP).onafterPickup(self,Ship,From,Event,To,Coordinate,Speed,Height,PickupZone)
-end
-end
-function AI_CARGO_SHIP:onafterDeploy(Ship,From,Event,To,Coordinate,Speed,Height,DeployZone)
-if Ship and Ship:IsAlive()then
-Speed=Speed or Ship:GetSpeedMax()*0.8
-local lane=self.ShippingLane
-if lane then
-local Waypoints={}
-for i=1,#lane do
-local coord=lane[i]
-local Waypoint=coord:WaypointGround(_speed)
-table.insert(Waypoints,Waypoint)
-end
-local TaskFunction=Ship:TaskFunction("AI_CARGO_SHIP._Deploy",self,Coordinate,DeployZone)
-local Waypoint=Waypoints[#Waypoints]
-Ship:SetTaskWaypoint(Waypoint,TaskFunction)
-Ship:Route(Waypoints,1)
-self:GetParent(self,AI_CARGO_SHIP).onafterDeploy(self,Ship,From,Event,To,Coordinate,Speed,Height,DeployZone)
-else
-self:E(self.lid.."ERROR: No shipping lane defined for Naval Transport!")
-end
-end
-end
-function AI_CARGO_SHIP:onafterUnload(Ship,From,Event,To,DeployZone,Defend)
-self:F({Ship,From,Event,To,DeployZone,Defend=Defend})
-local UnboardInterval=5
-local UnboardDelay=5
-if Ship and Ship:IsAlive()then
-for _,ShipUnit in pairs(Ship:GetUnits())do
-local ShipUnit=ShipUnit
-Ship:RouteStop()
-for _,Cargo in pairs(ShipUnit:GetCargo())do
-self:F({Cargo=Cargo:GetName(),Isloaded=Cargo:IsLoaded()})
-if Cargo:IsLoaded()then
-local unboardCoord=DeployZone:GetRandomPointVec2()
-Cargo:__UnBoard(UnboardDelay,unboardCoord,1000)
-UnboardDelay=UnboardDelay+Cargo:GetCount()*UnboardInterval
-self:__Unboard(UnboardDelay,Cargo,ShipUnit,DeployZone,Defend)
-if not Defend==true then
-Cargo:SetDeployed(true)
-end
-end
-end
-end
-end
-end
-function AI_CARGO_SHIP:onafterHome(Ship,From,Event,To,Coordinate,Speed,Height,HomeZone)
-if Ship and Ship:IsAlive()then
-self.RouteHome=true
-Speed=Speed or Ship:GetSpeedMax()*0.8
-local lane=self.ShippingLane
-if lane then
-local Waypoints={}
-for i=#lane,1,-1 do
-local coord=lane[i]
-local Waypoint=coord:WaypointGround(_speed)
-table.insert(Waypoints,Waypoint)
-end
-local Waypoint=Waypoints[#Waypoints]
-Ship:Route(Waypoints,1)
-else
-self:E(self.lid.."ERROR: No shipping lane defined for Naval Transport!")
-end
-end
-end
-AI_CARGO_DISPATCHER={
-ClassName="AI_CARGO_DISPATCHER",
-AI_Cargo={},
-PickupCargo={}
-}
-AI_CARGO_DISPATCHER.AI_Cargo={}
-AI_CARGO_DISPATCHER.PickupCargo={}
-function AI_CARGO_DISPATCHER:New(CarrierSet,CargoSet,PickupZoneSet,DeployZoneSet)
-local self=BASE:Inherit(self,FSM:New())
-self.SetCarrier=CarrierSet
-self.SetCargo=CargoSet
-self.PickupZoneSet=PickupZoneSet
-self.DeployZoneSet=DeployZoneSet
-self:SetStartState("Idle")
-self:AddTransition("Monitoring","Monitor","Monitoring")
-self:AddTransition("Idle","Start","Monitoring")
-self:AddTransition("Monitoring","Stop","Idle")
-self:AddTransition("Monitoring","Pickup","Monitoring")
-self:AddTransition("Monitoring","Load","Monitoring")
-self:AddTransition("Monitoring","Loading","Monitoring")
-self:AddTransition("Monitoring","Loaded","Monitoring")
-self:AddTransition("Monitoring","PickedUp","Monitoring")
-self:AddTransition("Monitoring","Transport","Monitoring")
-self:AddTransition("Monitoring","Deploy","Monitoring")
-self:AddTransition("Monitoring","Unload","Monitoring")
-self:AddTransition("Monitoring","Unloading","Monitoring")
-self:AddTransition("Monitoring","Unloaded","Monitoring")
-self:AddTransition("Monitoring","Deployed","Monitoring")
-self:AddTransition("Monitoring","Home","Monitoring")
-self:SetMonitorTimeInterval(30)
-self:SetDeployRadius(500,200)
-self.PickupCargo={}
-self.CarrierHome={}
-function self.SetCarrier.OnAfterRemoved(SetCarrier,From,Event,To,CarrierName,Carrier)
-self:F({Carrier=Carrier:GetName()})
-self.PickupCargo[Carrier]=nil
-self.CarrierHome[Carrier]=nil
-end
-return self
-end
-function AI_CARGO_DISPATCHER:SetMonitorTimeInterval(MonitorTimeInterval)
-self.MonitorTimeInterval=MonitorTimeInterval
-return self
-end
-function AI_CARGO_DISPATCHER:SetHomeZone(HomeZone)
-self.HomeZone=HomeZone
-return self
-end
-function AI_CARGO_DISPATCHER:SetPickupRadius(OuterRadius,InnerRadius)
-OuterRadius=OuterRadius or 0
-InnerRadius=InnerRadius or OuterRadius
-self.PickupOuterRadius=OuterRadius
-self.PickupInnerRadius=InnerRadius
-return self
-end
-function AI_CARGO_DISPATCHER:SetPickupSpeed(MaxSpeed,MinSpeed)
-MaxSpeed=MaxSpeed or 999
-MinSpeed=MinSpeed or MaxSpeed
-self.PickupMinSpeed=MinSpeed
-self.PickupMaxSpeed=MaxSpeed
-return self
-end
-function AI_CARGO_DISPATCHER:SetDeployRadius(OuterRadius,InnerRadius)
-OuterRadius=OuterRadius or 0
-InnerRadius=InnerRadius or OuterRadius
-self.DeployOuterRadius=OuterRadius
-self.DeployInnerRadius=InnerRadius
-return self
-end
-function AI_CARGO_DISPATCHER:SetDeploySpeed(MaxSpeed,MinSpeed)
-MaxSpeed=MaxSpeed or 999
-MinSpeed=MinSpeed or MaxSpeed
-self.DeployMinSpeed=MinSpeed
-self.DeployMaxSpeed=MaxSpeed
-return self
-end
-function AI_CARGO_DISPATCHER:SetPickupHeight(MaxHeight,MinHeight)
-MaxHeight=MaxHeight or 200
-MinHeight=MinHeight or MaxHeight
-self.PickupMinHeight=MinHeight
-self.PickupMaxHeight=MaxHeight
-return self
-end
-function AI_CARGO_DISPATCHER:SetDeployHeight(MaxHeight,MinHeight)
-MaxHeight=MaxHeight or 200
-MinHeight=MinHeight or MaxHeight
-self.DeployMinHeight=MinHeight
-self.DeployMaxHeight=MaxHeight
-return self
-end
-function AI_CARGO_DISPATCHER:onafterMonitor()
-self:F("Carriers")
-self.SetCarrier:Flush()
-for CarrierGroupName,Carrier in pairs(self.SetCarrier:GetSet())do
-local Carrier=Carrier
-if Carrier:IsAlive()~=nil then
-local AI_Cargo=self.AI_Cargo[Carrier]
-if not AI_Cargo then
-self.AI_Cargo[Carrier]=self:AICargo(Carrier,self.SetCargo,self.CombatRadius)
-AI_Cargo=self.AI_Cargo[Carrier]
-function AI_Cargo.OnAfterPickup(AI_Cargo,CarrierGroup,From,Event,To,Coordinate,Speed,Height,PickupZone)
-self:Pickup(CarrierGroup,Coordinate,Speed,Height,PickupZone)
-end
-function AI_Cargo.OnAfterLoad(AI_Cargo,CarrierGroup,From,Event,To,PickupZone)
-self:Load(CarrierGroup,PickupZone)
-end
-function AI_Cargo.OnAfterBoard(AI_Cargo,CarrierGroup,From,Event,To,Cargo,CarrierUnit,PickupZone)
-self:Loading(CarrierGroup,Cargo,CarrierUnit,PickupZone)
-end
-function AI_Cargo.OnAfterLoaded(AI_Cargo,CarrierGroup,From,Event,To,Cargo,CarrierUnit,PickupZone)
-self:Loaded(CarrierGroup,Cargo,CarrierUnit,PickupZone)
-end
-function AI_Cargo.OnAfterPickedUp(AI_Cargo,CarrierGroup,From,Event,To,PickupZone)
-self:PickedUp(CarrierGroup,PickupZone)
-self:Transport(CarrierGroup)
-end
-function AI_Cargo.OnAfterDeploy(AI_Cargo,CarrierGroup,From,Event,To,Coordinate,Speed,Height,DeployZone)
-self:Deploy(CarrierGroup,Coordinate,Speed,Height,DeployZone)
-end
-function AI_Cargo.OnAfterUnload(AI_Cargo,Carrier,From,Event,To,Cargo,CarrierUnit,DeployZone)
-self:Unloading(Carrier,Cargo,CarrierUnit,DeployZone)
-end
-function AI_Cargo.OnAfterUnboard(AI_Cargo,CarrierGroup,From,Event,To,Cargo,CarrierUnit,DeployZone)
-self:Unloading(CarrierGroup,Cargo,CarrierUnit,DeployZone)
-end
-function AI_Cargo.OnAfterUnloaded(AI_Cargo,Carrier,From,Event,To,Cargo,CarrierUnit,DeployZone)
-self:Unloaded(Carrier,Cargo,CarrierUnit,DeployZone)
-end
-function AI_Cargo.OnAfterDeployed(AI_Cargo,Carrier,From,Event,To,DeployZone)
-self:Deployed(Carrier,DeployZone)
-end
-function AI_Cargo.OnAfterHome(AI_Cargo,Carrier,From,Event,To,Coordinate,Speed,Height,HomeZone)
-self:Home(Carrier,Coordinate,Speed,Height,HomeZone)
-end
-end
-self:T({Carrier=CarrierGroupName,IsRelocating=AI_Cargo:IsRelocating(),IsTransporting=AI_Cargo:IsTransporting()})
-if AI_Cargo:IsRelocating()==false and AI_Cargo:IsTransporting()==false then
-local PickupCargo=nil
-local PickupZone=nil
-self.SetCargo:Flush()
-for CargoName,Cargo in UTILS.spairs(self.SetCargo:GetSet(),function(t,a,b)return t[a]:GetWeight()=Cargo:GetWeight()then
-self.PickupCargo[Carrier]=CargoCoordinate
-PickupCargo=Cargo
-break
-else
-local text=string.format("WARNING: Cargo %s is too heavy to be loaded into transport. Cargo weight %.1f > %.1f load capacity of carrier %s.",
-tostring(Cargo:GetName()),Cargo:GetWeight(),LargestLoadCapacity,tostring(Carrier:GetName()))
-self:I(text)
-end
-end
-end
-end
-end
-if PickupCargo then
-self.CarrierHome[Carrier]=nil
-local PickupCoordinate=PickupCargo:GetCoordinate():GetRandomCoordinateInRadius(self.PickupOuterRadius,self.PickupInnerRadius)
-AI_Cargo:Pickup(PickupCoordinate,math.random(self.PickupMinSpeed,self.PickupMaxSpeed),math.random(self.PickupMinHeight,self.PickupMaxHeight),PickupZone)
-break
-else
-if self.HomeZone then
-if not self.CarrierHome[Carrier]then
-self.CarrierHome[Carrier]=true
-AI_Cargo:Home(self.HomeZone:GetRandomPointVec2(),math.random(self.PickupMinSpeed,self.PickupMaxSpeed),math.random(self.PickupMinHeight,self.PickupMaxHeight),self.HomeZone)
-end
-end
-end
-end
-end
-end
-self:__Monitor(self.MonitorTimeInterval)
-end
-function AI_CARGO_DISPATCHER:onafterStart(From,Event,To)
-self:__Monitor(-1)
-end
-function AI_CARGO_DISPATCHER:onafterTransport(From,Event,To,Carrier,Cargo)
-if self.DeployZoneSet then
-if self.AI_Cargo[Carrier]:IsTransporting()==true then
-local DeployZone=self.DeployZoneSet:GetRandomZone()
-local DeployCoordinate=DeployZone:GetCoordinate():GetRandomCoordinateInRadius(self.DeployOuterRadius,self.DeployInnerRadius)
-self.AI_Cargo[Carrier]:__Deploy(0.1,DeployCoordinate,math.random(self.DeployMinSpeed,self.DeployMaxSpeed),math.random(self.DeployMinHeight,self.DeployMaxHeight),DeployZone)
-end
-end
-self:F({Carrier=Carrier:GetName(),PickupCargo=self.PickupCargo})
-self.PickupCargo[Carrier]=nil
-end
-AI_CARGO_DISPATCHER_APC={
-ClassName="AI_CARGO_DISPATCHER_APC",
-}
-function AI_CARGO_DISPATCHER_APC:New(APCSet,CargoSet,PickupZoneSet,DeployZoneSet,CombatRadius)
-local self=BASE:Inherit(self,AI_CARGO_DISPATCHER:New(APCSet,CargoSet,PickupZoneSet,DeployZoneSet))
-self:SetDeploySpeed(120,70)
-self:SetPickupSpeed(120,70)
-self:SetPickupRadius(0,0)
-self:SetDeployRadius(0,0)
-self:SetPickupHeight()
-self:SetDeployHeight()
-self:SetCombatRadius(CombatRadius)
-return self
-end
-function AI_CARGO_DISPATCHER_APC:AICargo(APC,CargoSet)
-local aicargoapc=AI_CARGO_APC:New(APC,CargoSet,self.CombatRadius)
-aicargoapc:SetDeployOffRoad(self.deployOffroad,self.deployFormation)
-aicargoapc:SetPickupOffRoad(self.pickupOffroad,self.pickupFormation)
-return aicargoapc
-end
-function AI_CARGO_DISPATCHER_APC:SetCombatRadius(CombatRadius)
-self.CombatRadius=CombatRadius or 0
-return self
-end
-function AI_CARGO_DISPATCHER_APC:SetOffRoad(Offroad,Formation)
-self:SetPickupOffRoad(Offroad,Formation)
-self:SetDeployOffRoad(Offroad,Formation)
-return self
-end
-function AI_CARGO_DISPATCHER_APC:SetPickupOffRoad(Offroad,Formation)
-self.pickupOffroad=Offroad
-self.pickupFormation=Formation or ENUMS.Formation.Vehicle.OffRoad
-return self
-end
-function AI_CARGO_DISPATCHER_APC:SetDeployOffRoad(Offroad,Formation)
-self.deployOffroad=Offroad
-self.deployFormation=Formation or ENUMS.Formation.Vehicle.OffRoad
-return self
-end
-AI_CARGO_DISPATCHER_HELICOPTER={
-ClassName="AI_CARGO_DISPATCHER_HELICOPTER",
-}
-function AI_CARGO_DISPATCHER_HELICOPTER:New(HelicopterSet,CargoSet,PickupZoneSet,DeployZoneSet)
-local self=BASE:Inherit(self,AI_CARGO_DISPATCHER:New(HelicopterSet,CargoSet,PickupZoneSet,DeployZoneSet))
-self:SetPickupSpeed(350,150)
-self:SetDeploySpeed(350,150)
-self:SetPickupRadius(40,12)
-self:SetDeployRadius(40,12)
-self:SetPickupHeight(500,200)
-self:SetDeployHeight(500,200)
-return self
-end
-function AI_CARGO_DISPATCHER_HELICOPTER:AICargo(Helicopter,CargoSet)
-local dispatcher=AI_CARGO_HELICOPTER:New(Helicopter,CargoSet)
-dispatcher:SetLandingSpeedAndHeight(27,6)
-return dispatcher
-end
-AI_CARGO_DISPATCHER_AIRPLANE={
-ClassName="AI_CARGO_DISPATCHER_AIRPLANE",
-}
-function AI_CARGO_DISPATCHER_AIRPLANE:New(AirplaneSet,CargoSet,PickupZoneSet,DeployZoneSet)
-local self=BASE:Inherit(self,AI_CARGO_DISPATCHER:New(AirplaneSet,CargoSet,PickupZoneSet,DeployZoneSet))
-self:SetPickupSpeed(1200,600)
-self:SetDeploySpeed(1200,600)
-self:SetPickupRadius(0,0)
-self:SetDeployRadius(0,0)
-self:SetPickupHeight(8000,6000)
-self:SetDeployHeight(8000,6000)
-self:SetMonitorTimeInterval(600)
-return self
-end
-function AI_CARGO_DISPATCHER_AIRPLANE:AICargo(Airplane,CargoSet)
-return AI_CARGO_AIRPLANE:New(Airplane,CargoSet)
-end
-AI_CARGO_DISPATCHER_SHIP={
-ClassName="AI_CARGO_DISPATCHER_SHIP"
-}
-function AI_CARGO_DISPATCHER_SHIP:New(ShipSet,CargoSet,PickupZoneSet,DeployZoneSet,ShippingLane)
-local self=BASE:Inherit(self,AI_CARGO_DISPATCHER:New(ShipSet,CargoSet,PickupZoneSet,DeployZoneSet))
-self:SetPickupSpeed(60,10)
-self:SetDeploySpeed(60,10)
-self:SetPickupRadius(500,6000)
-self:SetDeployRadius(500,6000)
-self:SetPickupHeight(0,0)
-self:SetDeployHeight(0,0)
-self:SetShippingLane(ShippingLane)
-self:SetMonitorTimeInterval(600)
-return self
-end
-function AI_CARGO_DISPATCHER_SHIP:SetShippingLane(ShippingLane)
-self.ShippingLane=ShippingLane
-return self
-end
-function AI_CARGO_DISPATCHER_SHIP:AICargo(Ship,CargoSet)
-return AI_CARGO_SHIP:New(Ship,CargoSet,0,self.ShippingLane)
-end
-do
-ACT_ASSIGN={
-ClassName="ACT_ASSIGN",
-}
-function ACT_ASSIGN:New()
-local self=BASE:Inherit(self,FSM_PROCESS:New("ACT_ASSIGN"))
-self:AddTransition("UnAssigned","Start","Waiting")
-self:AddTransition("Waiting","Assign","Assigned")
-self:AddTransition("Waiting","Reject","Rejected")
-self:AddTransition("*","Fail","Failed")
-self:AddEndState("Assigned")
-self:AddEndState("Rejected")
-self:AddEndState("Failed")
-self:SetStartState("UnAssigned")
-return self
-end
-end
-do
-ACT_ASSIGN_ACCEPT={
-ClassName="ACT_ASSIGN_ACCEPT",
-}
-function ACT_ASSIGN_ACCEPT:New(TaskBriefing)
-local self=BASE:Inherit(self,ACT_ASSIGN:New())
-self.TaskBriefing=TaskBriefing
-return self
-end
-function ACT_ASSIGN_ACCEPT:Init(FsmAssign)
-self.TaskBriefing=FsmAssign.TaskBriefing
-end
-function ACT_ASSIGN_ACCEPT:onafterStart(ProcessUnit,Task,From,Event,To)
-self:__Assign(1)
-end
-function ACT_ASSIGN_ACCEPT:onenterAssigned(ProcessUnit,Task,From,Event,To,TaskGroup)
-self.Task:Assign(ProcessUnit,ProcessUnit:GetPlayerName())
-end
-end
-do
-ACT_ASSIGN_MENU_ACCEPT={
-ClassName="ACT_ASSIGN_MENU_ACCEPT",
-}
-function ACT_ASSIGN_MENU_ACCEPT:New(TaskBriefing)
-local self=BASE:Inherit(self,ACT_ASSIGN:New())
-self.TaskBriefing=TaskBriefing
-return self
-end
-function ACT_ASSIGN_MENU_ACCEPT:Init(TaskBriefing)
-self.TaskBriefing=TaskBriefing
-return self
-end
-function ACT_ASSIGN_MENU_ACCEPT:onafterStart(ProcessUnit,Task,From,Event,To)
-self:GetCommandCenter():MessageToGroup("Task "..self.Task:GetName().." has been assigned to you and your group!\nRead the briefing and use the Radio Menu (F10) / Task ... CONFIRMATION menu to accept or reject the task.\nYou have 2 minutes to accept, or the task assignment will be cancelled!",ProcessUnit:GetGroup(),120)
-local TaskGroup=ProcessUnit:GetGroup()
-self.Menu=MENU_GROUP:New(TaskGroup,"Task "..self.Task:GetName().." CONFIRMATION")
-self.MenuAcceptTask=MENU_GROUP_COMMAND:New(TaskGroup,"Accept task "..self.Task:GetName(),self.Menu,self.MenuAssign,self,TaskGroup)
-self.MenuRejectTask=MENU_GROUP_COMMAND:New(TaskGroup,"Reject task "..self.Task:GetName(),self.Menu,self.MenuReject,self,TaskGroup)
-self:__Reject(120,TaskGroup)
-end
-function ACT_ASSIGN_MENU_ACCEPT:MenuAssign(TaskGroup)
-self:__Assign(-1,TaskGroup)
-end
-function ACT_ASSIGN_MENU_ACCEPT:MenuReject(TaskGroup)
-self:__Reject(-1,TaskGroup)
-end
-function ACT_ASSIGN_MENU_ACCEPT:onafterAssign(ProcessUnit,Task,From,Event,To,TaskGroup)
-self.Menu:Remove()
-end
-function ACT_ASSIGN_MENU_ACCEPT:onafterReject(ProcessUnit,Task,From,Event,To,TaskGroup)
-self:F({TaskGroup=TaskGroup})
-self.Menu:Remove()
-self.Task:RejectGroup(TaskGroup)
-end
-function ACT_ASSIGN_MENU_ACCEPT:onenterAssigned(ProcessUnit,Task,From,Event,To,TaskGroup)
-self.Task:Assign(ProcessUnit,ProcessUnit:GetPlayerName())
-end
-end
-do
-ACT_ROUTE={
-ClassName="ACT_ROUTE",
-}
-function ACT_ROUTE:New()
-local self=BASE:Inherit(self,FSM_PROCESS:New("ACT_ROUTE"))
-self:AddTransition("*","Reset","None")
-self:AddTransition("None","Start","Routing")
-self:AddTransition("*","Report","*")
-self:AddTransition("Routing","Route","Routing")
-self:AddTransition("Routing","Pause","Pausing")
-self:AddTransition("Routing","Arrive","Arrived")
-self:AddTransition("*","Cancel","Cancelled")
-self:AddTransition("Arrived","Success","Success")
-self:AddTransition("*","Fail","Failed")
-self:AddTransition("","","")
-self:AddTransition("","","")
-self:AddEndState("Arrived")
-self:AddEndState("Failed")
-self:AddEndState("Cancelled")
-self:SetStartState("None")
-self:SetRouteMode("C")
-return self
-end
-function ACT_ROUTE:SetMenuCancel(MenuGroup,MenuText,ParentMenu,MenuTime,MenuTag)
-self.CancelMenuGroupCommand=MENU_GROUP_COMMAND:New(
-MenuGroup,
-MenuText,
-ParentMenu,
-self.MenuCancel,
-self
-):SetTime(MenuTime):SetTag(MenuTag)
-ParentMenu:SetTime(MenuTime)
-ParentMenu:Remove(MenuTime,MenuTag)
-return self
-end
-function ACT_ROUTE:SetRouteMode(RouteMode)
-self.RouteMode=RouteMode
-return self
-end
-function ACT_ROUTE:GetRouteText(Controllable)
-local RouteText=""
-local Coordinate=nil
-if self.Coordinate then
-Coordinate=self.Coordinate
-end
-if self.Zone then
-Coordinate=self.Zone:GetPointVec3(self.Altitude)
-Coordinate:SetHeading(self.Heading)
-end
-local Task=self:GetTask()
-local CC=self:GetTask():GetMission():GetCommandCenter()
-if CC then
-if CC:IsModeWWII()then
-local ShortestDistance=0
-local ShortestReferencePoint=nil
-local ShortestReferenceName=""
-self:F({CC.ReferencePoints})
-for ZoneName,Zone in pairs(CC.ReferencePoints)do
-self:F({ZoneName=ZoneName})
-local Zone=Zone
-local ZoneCoord=Zone:GetCoordinate()
-local ZoneDistance=ZoneCoord:Get2DDistance(Coordinate)
-self:F({ShortestDistance,ShortestReferenceName})
-if ShortestDistance==0 or ZoneDistance=self.DisplayInterval then
-self:T({HasArrived=HasArrived})
-if not HasArrived then
-self:Report()
-end
-self.DisplayCount=1
-else
-self.DisplayCount=self.DisplayCount+1
-end
-if HasArrived then
-self:__Arrive(1)
-else
-self:__Route(1)
-end
-return HasArrived
-end
-return false
-end
-end
-do
-ACT_ROUTE_POINT={
-ClassName="ACT_ROUTE_POINT",
-}
-function ACT_ROUTE_POINT:New(Coordinate,Range)
-local self=BASE:Inherit(self,ACT_ROUTE:New())
-self.Coordinate=Coordinate
-self.Range=Range or 0
-self.DisplayInterval=30
-self.DisplayCount=30
-self.DisplayMessage=true
-self.DisplayTime=10
-return self
-end
-function ACT_ROUTE_POINT:Init(FsmRoute)
-self.Coordinate=FsmRoute.Coordinate
-self.Range=FsmRoute.Range or 0
-self.DisplayInterval=30
-self.DisplayCount=30
-self.DisplayMessage=true
-self.DisplayTime=10
-self:SetStartState("None")
-end
-function ACT_ROUTE_POINT:SetCoordinate(Coordinate)
-self:F2({Coordinate})
-self.Coordinate=Coordinate
-end
-function ACT_ROUTE_POINT:GetCoordinate()
-self:F2({self.Coordinate})
-return self.Coordinate
-end
-function ACT_ROUTE_POINT:SetRange(Range)
-self:F2({Range})
-self.Range=Range or 10000
-end
-function ACT_ROUTE_POINT:GetRange()
-self:F2({self.Range})
-return self.Range
-end
-function ACT_ROUTE_POINT:onfuncHasArrived(ProcessUnit)
-if ProcessUnit:IsAlive()then
-local Distance=self.Coordinate:Get2DDistance(ProcessUnit:GetCoordinate())
-if Distance<=self.Range then
-local RouteText="Task \""..self:GetTask():GetName().."\", you have arrived."
-self:GetCommandCenter():MessageTypeToGroup(RouteText,ProcessUnit:GetGroup(),MESSAGE.Type.Information)
-return true
-end
-end
-return false
-end
-function ACT_ROUTE_POINT:onafterReport(ProcessUnit,From,Event,To)
-local RouteText="Task \""..self:GetTask():GetName().."\", "..self:GetRouteText(ProcessUnit)
-self:GetCommandCenter():MessageTypeToGroup(RouteText,ProcessUnit:GetGroup(),MESSAGE.Type.Update)
-end
-end
-do
-ACT_ROUTE_ZONE={
-ClassName="ACT_ROUTE_ZONE",
-}
-function ACT_ROUTE_ZONE:New(Zone)
-local self=BASE:Inherit(self,ACT_ROUTE:New())
-self.Zone=Zone
-self.DisplayInterval=30
-self.DisplayCount=30
-self.DisplayMessage=true
-self.DisplayTime=10
-return self
-end
-function ACT_ROUTE_ZONE:Init(FsmRoute)
-self.Zone=FsmRoute.Zone
-self.DisplayInterval=30
-self.DisplayCount=30
-self.DisplayMessage=true
-self.DisplayTime=10
-end
-function ACT_ROUTE_ZONE:SetZone(Zone,Altitude,Heading)
-self.Zone=Zone
-self.Altitude=Altitude
-self.Heading=Heading
-end
-function ACT_ROUTE_ZONE:GetZone()
-return self.Zone
-end
-function ACT_ROUTE_ZONE:onfuncHasArrived(ProcessUnit)
-if ProcessUnit:IsInZone(self.Zone)then
-local RouteText="Task \""..self:GetTask():GetName().."\", you have arrived within the zone."
-self:GetCommandCenter():MessageTypeToGroup(RouteText,ProcessUnit:GetGroup(),MESSAGE.Type.Information)
-end
-return ProcessUnit:IsInZone(self.Zone)
-end
-function ACT_ROUTE_ZONE:onafterReport(ProcessUnit,From,Event,To)
-self:F({ProcessUnit=ProcessUnit})
-local RouteText="Task \""..self:GetTask():GetName().."\", "..self:GetRouteText(ProcessUnit)
-self:GetCommandCenter():MessageTypeToGroup(RouteText,ProcessUnit:GetGroup(),MESSAGE.Type.Update)
-end
-end
-do
-ACT_ACCOUNT={
-ClassName="ACT_ACCOUNT",
-TargetSetUnit=nil,
-}
-function ACT_ACCOUNT:New()
-local self=BASE:Inherit(self,FSM_PROCESS:New())
-self:AddTransition("Assigned","Start","Waiting")
-self:AddTransition("*","Wait","Waiting")
-self:AddTransition("*","Report","Report")
-self:AddTransition("*","Event","Account")
-self:AddTransition("Account","Player","AccountForPlayer")
-self:AddTransition("Account","Other","AccountForOther")
-self:AddTransition({"Account","AccountForPlayer","AccountForOther"},"More","Wait")
-self:AddTransition({"Account","AccountForPlayer","AccountForOther"},"NoMore","Accounted")
-self:AddTransition("*","Fail","Failed")
-self:AddEndState("Failed")
-self:SetStartState("Assigned")
-return self
-end
-function ACT_ACCOUNT:onafterStart(ProcessUnit,From,Event,To)
-self:HandleEvent(EVENTS.Dead,self.onfuncEventDead)
-self:HandleEvent(EVENTS.Crash,self.onfuncEventCrash)
-self:HandleEvent(EVENTS.Hit)
-self:__Wait(1)
-end
-function ACT_ACCOUNT:onenterWaiting(ProcessUnit,From,Event,To)
-if self.DisplayCount>=self.DisplayInterval then
-self:Report()
-self.DisplayCount=1
-else
-self.DisplayCount=self.DisplayCount+1
-end
-return true
-end
-function ACT_ACCOUNT:onafterEvent(ProcessUnit,From,Event,To,Event)
-self:__NoMore(1)
-end
-end
-do
-ACT_ACCOUNT_DEADS={
-ClassName="ACT_ACCOUNT_DEADS",
-}
-function ACT_ACCOUNT_DEADS:New()
-local self=BASE:Inherit(self,ACT_ACCOUNT:New())
-self.DisplayInterval=30
-self.DisplayCount=30
-self.DisplayMessage=true
-self.DisplayTime=10
-self.DisplayCategory="HQ"
-return self
-end
-function ACT_ACCOUNT_DEADS:Init(FsmAccount)
-self.Task=self:GetTask()
-self.TaskName=self.Task:GetName()
-end
-function ACT_ACCOUNT_DEADS:onenterReport(ProcessUnit,Task,From,Event,To)
-local MessageText="Your group with assigned "..self.TaskName.." task has "..Task.TargetSetUnit:GetUnitTypesText().." targets left to be destroyed."
-self:GetCommandCenter():MessageTypeToGroup(MessageText,ProcessUnit:GetGroup(),MESSAGE.Type.Information)
-end
-function ACT_ACCOUNT_DEADS:onafterEvent(ProcessUnit,Task,From,Event,To,EventData)
-self:T({ProcessUnit:GetName(),Task:GetName(),From,Event,To,EventData})
-if Task.TargetSetUnit:FindUnit(EventData.IniUnitName)then
-local PlayerName=ProcessUnit:GetPlayerName()
-local PlayerHit=self.PlayerHits and self.PlayerHits[EventData.IniUnitName]
-if PlayerHit==PlayerName then
-self:Player(EventData)
-else
-self:Other(EventData)
-end
-end
-end
-function ACT_ACCOUNT_DEADS:onenterAccountForPlayer(ProcessUnit,Task,From,Event,To,EventData)
-self:T({ProcessUnit:GetName(),Task:GetName(),From,Event,To,EventData})
-local TaskGroup=ProcessUnit:GetGroup()
-Task.TargetSetUnit:Remove(EventData.IniUnitName)
-local MessageText="You have destroyed a target.\nYour group assigned with task "..self.TaskName.." has\n"..Task.TargetSetUnit:Count().." targets ( "..Task.TargetSetUnit:GetUnitTypesText().." ) left to be destroyed."
-self:GetCommandCenter():MessageTypeToGroup(MessageText,ProcessUnit:GetGroup(),MESSAGE.Type.Information)
-local PlayerName=ProcessUnit:GetPlayerName()
-Task:AddProgress(PlayerName,"Destroyed "..EventData.IniTypeName,timer.getTime(),1)
-if Task.TargetSetUnit:Count()>0 then
-self:__More(1)
-else
-self:__NoMore(1)
-end
-end
-function ACT_ACCOUNT_DEADS:onenterAccountForOther(ProcessUnit,Task,From,Event,To,EventData)
-self:T({ProcessUnit:GetName(),Task:GetName(),From,Event,To,EventData})
-local TaskGroup=ProcessUnit:GetGroup()
-Task.TargetSetUnit:Remove(EventData.IniUnitName)
-local MessageText="One of the task targets has been destroyed.\nYour group assigned with task "..self.TaskName.." has\n"..Task.TargetSetUnit:Count().." targets ( "..Task.TargetSetUnit:GetUnitTypesText().." ) left to be destroyed."
-self:GetCommandCenter():MessageTypeToGroup(MessageText,ProcessUnit:GetGroup(),MESSAGE.Type.Information)
-if Task.TargetSetUnit:Count()>0 then
-self:__More(1)
-else
-self:__NoMore(1)
-end
-end
-function ACT_ACCOUNT_DEADS:OnEventHit(EventData)
-self:T({"EventDead",EventData})
-if EventData.IniPlayerName and EventData.TgtDCSUnitName then
-self.PlayerHits=self.PlayerHits or{}
-self.PlayerHits[EventData.TgtDCSUnitName]=EventData.IniPlayerName
-end
-end
-function ACT_ACCOUNT_DEADS:onfuncEventDead(EventData)
-self:T({"EventDead",EventData})
-if EventData.IniDCSUnit then
-self:Event(EventData)
-end
-end
-function ACT_ACCOUNT_DEADS:onfuncEventCrash(EventData)
-self:T({"EventDead",EventData})
-if EventData.IniDCSUnit then
-self:Event(EventData)
-end
-end
-end
-do
-ACT_ASSIST={
-ClassName="ACT_ASSIST",
-}
-function ACT_ASSIST:New()
-local self=BASE:Inherit(self,FSM_PROCESS:New("ACT_ASSIST"))
-self:AddTransition("None","Start","AwaitSmoke")
-self:AddTransition("AwaitSmoke","Next","Smoking")
-self:AddTransition("Smoking","Next","AwaitSmoke")
-self:AddTransition("*","Stop","Success")
-self:AddTransition("*","Fail","Failed")
-self:AddEndState("Failed")
-self:AddEndState("Success")
-self:SetStartState("None")
-return self
-end
-function ACT_ASSIST:onafterStart(ProcessUnit,From,Event,To)
-local ProcessGroup=ProcessUnit:GetGroup()
-local MissionMenu=self:GetMission():GetMenu(ProcessGroup)
-local function MenuSmoke(MenuParam)
-local self=MenuParam.self
-local SmokeColor=MenuParam.SmokeColor
-self.SmokeColor=SmokeColor
-self:__Next(1)
-end
-self.Menu=MENU_GROUP:New(ProcessGroup,"Target acquisition",MissionMenu)
-self.MenuSmokeBlue=MENU_GROUP_COMMAND:New(ProcessGroup,"Drop blue smoke on targets",self.Menu,MenuSmoke,{self=self,SmokeColor=SMOKECOLOR.Blue})
-self.MenuSmokeGreen=MENU_GROUP_COMMAND:New(ProcessGroup,"Drop green smoke on targets",self.Menu,MenuSmoke,{self=self,SmokeColor=SMOKECOLOR.Green})
-self.MenuSmokeOrange=MENU_GROUP_COMMAND:New(ProcessGroup,"Drop Orange smoke on targets",self.Menu,MenuSmoke,{self=self,SmokeColor=SMOKECOLOR.Orange})
-self.MenuSmokeRed=MENU_GROUP_COMMAND:New(ProcessGroup,"Drop Red smoke on targets",self.Menu,MenuSmoke,{self=self,SmokeColor=SMOKECOLOR.Red})
-self.MenuSmokeWhite=MENU_GROUP_COMMAND:New(ProcessGroup,"Drop White smoke on targets",self.Menu,MenuSmoke,{self=self,SmokeColor=SMOKECOLOR.White})
-end
-function ACT_ASSIST:onafterStop(ProcessUnit,From,Event,To)
-self.Menu:Remove()
-end
-end
-do
-ACT_ASSIST_SMOKE_TARGETS_ZONE={
-ClassName="ACT_ASSIST_SMOKE_TARGETS_ZONE",
-}
-function ACT_ASSIST_SMOKE_TARGETS_ZONE:New(TargetSetUnit,TargetZone)
-local self=BASE:Inherit(self,ACT_ASSIST:New())
-self.TargetSetUnit=TargetSetUnit
-self.TargetZone=TargetZone
-return self
-end
-function ACT_ASSIST_SMOKE_TARGETS_ZONE:Init(FsmSmoke)
-self.TargetSetUnit=FsmSmoke.TargetSetUnit
-self.TargetZone=FsmSmoke.TargetZone
-end
-function ACT_ASSIST_SMOKE_TARGETS_ZONE:Init(TargetSetUnit,TargetZone)
-self.TargetSetUnit=TargetSetUnit
-self.TargetZone=TargetZone
-return self
-end
-function ACT_ASSIST_SMOKE_TARGETS_ZONE:onenterSmoking(ProcessUnit,From,Event,To)
-self.TargetSetUnit:ForEachUnit(
-function(SmokeUnit)
-if math.random(1,(100*self.TargetSetUnit:Count())/4)<=100 then
-SCHEDULER:New(self,
-function()
-if SmokeUnit:IsAlive()then
-SmokeUnit:Smoke(self.SmokeColor,150)
-end
-end,{},math.random(10,60)
-)
-end
-end
-)
-end
-end
-RADIO={
-ClassName="RADIO",
-FileName="",
-Frequency=0,
-Modulation=radio.modulation.AM,
-Subtitle="",
-SubtitleDuration=0,
-Power=100,
-Loop=false,
-alias=nil,
-}
-function RADIO:New(Positionable)
-local self=BASE:Inherit(self,BASE:New())
-self:F(Positionable)
-if Positionable:GetPointVec2()then
-self.Positionable=Positionable
-return self
-end
-self:E({error="The passed positionable is invalid, no RADIO created!",positionable=Positionable})
-return nil
-end
-function RADIO:SetAlias(alias)
-self.alias=tostring(alias)
-return self
-end
-function RADIO:GetAlias()
-return tostring(self.alias)
-end
-function RADIO:SetFileName(FileName)
-self:F2(FileName)
-if type(FileName)=="string"then
-if FileName:find(".ogg")or FileName:find(".wav")then
-if not FileName:find("l10n/DEFAULT/")then
-FileName="l10n/DEFAULT/"..FileName
-end
-self.FileName=FileName
-return self
-end
-end
-self:E({"File name invalid. Maybe something wrong with the extension?",FileName})
-return self
-end
-function RADIO:SetFrequency(Frequency)
-self:F2(Frequency)
-if type(Frequency)=="number"then
-self.Frequency=Frequency*1000000
-if self.Positionable.ClassName=="UNIT"or self.Positionable.ClassName=="GROUP"then
-local commandSetFrequency={
-id="SetFrequency",
-params={
-frequency=self.Frequency,
-modulation=self.Modulation,
-}
-}
-self:T2(commandSetFrequency)
-self.Positionable:SetCommand(commandSetFrequency)
-end
-return self
-end
-self:E({"Frequency is not a number. Frequency unchanged.",Frequency})
-return self
-end
-function RADIO:SetModulation(Modulation)
-self:F2(Modulation)
-if type(Modulation)=="number"then
-if Modulation==radio.modulation.AM or Modulation==radio.modulation.FM then
-self.Modulation=Modulation
-return self
-end
-end
-self:E({"Modulation is invalid. Use DCS's enum radio.modulation. Modulation unchanged.",self.Modulation})
-return self
-end
-function RADIO:SetPower(Power)
-self:F2(Power)
-if type(Power)=="number"then
-self.Power=math.floor(math.abs(Power))
-else
-self:E({"Power is invalid. Power unchanged.",self.Power})
-end
-return self
-end
-function RADIO:SetLoop(Loop)
-self:F2(Loop)
-if type(Loop)=="boolean"then
-self.Loop=Loop
-return self
-end
-self:E({"Loop is invalid. Loop unchanged.",self.Loop})
-return self
-end
-function RADIO:SetSubtitle(Subtitle,SubtitleDuration)
-self:F2({Subtitle,SubtitleDuration})
-if type(Subtitle)=="string"then
-self.Subtitle=Subtitle
-else
-self.Subtitle=""
-self:E({"Subtitle is invalid. Subtitle reset.",self.Subtitle})
-end
-if type(SubtitleDuration)=="number"then
-self.SubtitleDuration=SubtitleDuration
-else
-self.SubtitleDuration=0
-self:E({"SubtitleDuration is invalid. SubtitleDuration reset.",self.SubtitleDuration})
-end
-return self
-end
-function RADIO:NewGenericTransmission(FileName,Frequency,Modulation,Power,Loop)
-self:F({FileName,Frequency,Modulation,Power})
-self:SetFileName(FileName)
-if Frequency then self:SetFrequency(Frequency)end
-if Modulation then self:SetModulation(Modulation)end
-if Power then self:SetPower(Power)end
-if Loop then self:SetLoop(Loop)end
-return self
-end
-function RADIO:NewUnitTransmission(FileName,Subtitle,SubtitleDuration,Frequency,Modulation,Loop)
-self:F({FileName,Subtitle,SubtitleDuration,Frequency,Modulation,Loop})
-self:SetFileName(FileName)
-if Modulation then
-self:SetModulation(Modulation)
-end
-if Frequency then
-self:SetFrequency(Frequency)
-end
-if Subtitle then
-self:SetSubtitle(Subtitle,SubtitleDuration or 0)
-end
-if Loop then
-self:SetLoop(Loop)
-end
-return self
-end
-function RADIO:Broadcast(viatrigger)
-self:F({viatrigger=viatrigger})
-if(self.Positionable.ClassName=="UNIT"or self.Positionable.ClassName=="GROUP")and(not viatrigger)then
-self:T("Broadcasting from a UNIT or a GROUP")
-local commandTransmitMessage={
-id="TransmitMessage",
-params={
-file=self.FileName,
-duration=self.SubtitleDuration,
-subtitle=self.Subtitle,
-loop=self.Loop,
-}}
-self:T3(commandTransmitMessage)
-self.Positionable:SetCommand(commandTransmitMessage)
-else
-self:T("Broadcasting from a POSITIONABLE")
-trigger.action.radioTransmission(self.FileName,self.Positionable:GetPositionVec3(),self.Modulation,self.Loop,self.Frequency,self.Power,tostring(self.ID))
-end
-return self
-end
-function RADIO:StopBroadcast()
-self:F()
-if self.Positionable.ClassName=="UNIT"or self.Positionable.ClassName=="GROUP"then
-local commandStopTransmission={id="StopTransmission",params={}}
-self.Positionable:SetCommand(commandStopTransmission)
-else
-trigger.action.stopRadioTransmission(tostring(self.ID))
-end
-return self
-end
-RADIOQUEUE={
-ClassName="RADIOQUEUE",
-Debugmode=nil,
-lid=nil,
-frequency=nil,
-modulation=nil,
-scheduler=nil,
-RQid=nil,
-queue={},
-alias=nil,
-dt=nil,
-delay=nil,
-Tlast=nil,
-sendercoord=nil,
-sendername=nil,
-senderinit=nil,
-power=nil,
-numbers={},
-checking=nil,
-schedonce=false,
-}
-function RADIOQUEUE:New(frequency,modulation,alias)
-local self=BASE:Inherit(self,BASE:New())
-self.alias=alias or"My Radio"
-self.lid=string.format("RADIOQUEUE %s | ",self.alias)
-if frequency==nil then
-self:E(self.lid.."ERROR: No frequency specified as first parameter!")
-return nil
-end
-self.frequency=frequency*1000000
-self.modulation=modulation or radio.modulation.AM
-self:SetRadioPower()
-self.scheduler=SCHEDULER:New()
-self.scheduler:NoTrace()
-return self
-end
-function RADIOQUEUE:Start(delay,dt)
-self.delay=delay or 1
-self.dt=dt or 0.01
-self:I(self.lid..string.format("Starting RADIOQUEUE %s on Frequency %.2f MHz [modulation=%d] in %.1f seconds (dt=%.3f sec)",self.alias,self.frequency/1000000,self.modulation,self.delay,self.dt))
-if self.schedonce then
-self:_CheckRadioQueueDelayed(self.delay)
-else
-self.RQid=self.scheduler:Schedule(nil,RADIOQUEUE._CheckRadioQueue,{self},self.delay,self.dt)
-end
-return self
-end
-function RADIOQUEUE:Stop()
-self:I(self.lid.."Stopping RADIOQUEUE.")
-self.scheduler:Stop(self.RQid)
-self.queue={}
-return self
-end
-function RADIOQUEUE:SetSenderCoordinate(coordinate)
-self.sendercoord=coordinate
-return self
-end
-function RADIOQUEUE:SetSenderUnitName(name)
-self.sendername=name
-return self
-end
-function RADIOQUEUE:SetRadioPower(power)
-self.power=power or 100
-return self
-end
-function RADIOQUEUE:SetSRS(PathToSRS,Port)
-self.msrs=MSRS:New(PathToSRS,self.frequency/1000000,self.modulation)
-self.msrs:SetPort(Port)
-return self
-end
-function RADIOQUEUE:SetDigit(digit,filename,duration,path,subtitle,subduration)
-local transmission={}
-transmission.filename=filename
-transmission.duration=duration
-transmission.path=path or"l10n/DEFAULT/"
-transmission.subtitle=nil
-transmission.subduration=nil
-if type(digit)=="number"then
-digit=tostring(digit)
-end
-self.numbers[digit]=transmission
-return self
-end
-function RADIOQUEUE:AddTransmission(transmission)
-self:F({transmission=transmission})
-transmission.isplaying=false
-transmission.Tstarted=nil
-table.insert(self.queue,transmission)
-if self.schedonce and not self.checking then
-self:_CheckRadioQueueDelayed()
-end
-return self
-end
-function RADIOQUEUE:NewTransmission(filename,duration,path,tstart,interval,subtitle,subduration)
-if not filename then
-self:E(self.lid.."ERROR: No filename specified.")
-return nil
-end
-if type(filename)~="string"then
-self:E(self.lid.."ERROR: Filename specified is NOT a string.")
-return nil
-end
-if not duration then
-self:E(self.lid.."ERROR: No duration of transmission specified.")
-return nil
-end
-if type(duration)~="number"then
-self:E(self.lid.."ERROR: Duration specified is NOT a number.")
-return nil
-end
-local transmission={}
-transmission.filename=filename
-transmission.duration=duration
-transmission.path=path or"l10n/DEFAULT/"
-transmission.Tplay=tstart or timer.getAbsTime()
-transmission.subtitle=subtitle
-transmission.interval=interval or 0
-if transmission.subtitle then
-transmission.subduration=subduration or 5
-else
-transmission.subduration=nil
-end
-self:AddTransmission(transmission)
-return transmission
-end
-function RADIOQUEUE:AddSoundFile(soundfile,tstart,interval)
-local transmission=self:NewTransmission(soundfile:GetFileName(),soundfile.duration,soundfile:GetPath(),tstart,interval,soundfile.subtitle,soundfile.subduration)
-transmission.soundfile=soundfile
-return self
-end
-function RADIOQUEUE:AddSoundText(soundtext,tstart,interval)
-local transmission=self:NewTransmission("SoundText.ogg",soundtext.duration,nil,tstart,interval,soundtext.subtitle,soundtext.subduration)
-transmission.soundtext=soundtext
-return self
-end
-function RADIOQUEUE:Number2Transmission(number,delay,interval)
-local numbers=UTILS.GetCharacters(number)
-local wait=0
-for i=1,#numbers do
-local n=numbers[i]
-local transmission=UTILS.DeepCopy(self.numbers[n])
-transmission.Tplay=timer.getAbsTime()+(delay or 0)
-if interval and i==1 then
-transmission.interval=interval
-end
-self:AddTransmission(transmission)
-wait=wait+transmission.duration
-end
-return wait
-end
-function RADIOQUEUE:Broadcast(transmission)
-if((transmission.soundfile and transmission.soundfile.useSRS)or transmission.soundtext)and self.msrs then
-self:_BroadcastSRS(transmission)
-return
-end
-local sender=self:_GetRadioSender()
-local filename=string.format("%s%s",transmission.path,transmission.filename)
-if sender then
-self:T(self.lid..string.format("Broadcasting from aircraft %s",sender:GetName()))
-if not self.senderinit then
-local commandFrequency={
-id="SetFrequency",
-params={
-frequency=self.frequency,
-modulation=self.modulation,
-}}
-sender:SetCommand(commandFrequency)
-self.senderinit=true
-end
-local subtitle=nil
-local duration=nil
-if transmission.subtitle and transmission.subduration and transmission.subduration>0 then
-subtitle=transmission.subtitle
-duration=transmission.subduration
-end
-local commandTransmit={
-id="TransmitMessage",
-params={
-file=filename,
-duration=duration,
-subtitle=subtitle,
-loop=false,
-}}
-sender:SetCommand(commandTransmit)
-if self.Debugmode then
-local text=string.format("file=%s, freq=%.2f MHz, duration=%.2f sec, subtitle=%s",filename,self.frequency/1000000,transmission.duration,transmission.subtitle or"")
-MESSAGE:New(text,2,"RADIOQUEUE "..self.alias):ToAll()
-end
-else
-self:T(self.lid..string.format("Broadcasting via trigger.action.radioTransmission()."))
-local vec3=nil
-if self.sendername then
-vec3=self:_GetRadioSenderCoord()
-end
-if self.sendercoord and not vec3 then
-vec3=self.sendercoord:GetVec3()
-end
-if vec3 then
-self:T("Sending")
-self:T({filename=filename,vec3=vec3,modulation=self.modulation,frequency=self.frequency,power=self.power})
-trigger.action.radioTransmission(filename,vec3,self.modulation,false,self.frequency,self.power)
-if self.Debugmode then
-local text=string.format("file=%s, freq=%.2f MHz, duration=%.2f sec, subtitle=%s",filename,self.frequency/1000000,transmission.duration,transmission.subtitle or"")
-MESSAGE:New(string.format(text,filename,transmission.duration,transmission.subtitle or""),5,"RADIOQUEUE "..self.alias):ToAll()
-end
-end
-end
-end
-function RADIOQUEUE:_BroadcastSRS(transmission)
-if transmission.soundfile and transmission.soundfile.useSRS then
-self.msrs:PlaySoundFile(transmission.soundfile)
-elseif transmission.soundtext then
-self.msrs:PlaySoundText(transmission.soundtext)
-end
-end
-function RADIOQUEUE:_CheckRadioQueueDelayed(delay)
-self.checking=true
-self:ScheduleOnce(delay or self.dt,RADIOQUEUE._CheckRadioQueue,self)
-end
-function RADIOQUEUE:_CheckRadioQueue()
-if#self.queue==0 then
-self.checking=false
-return
-end
-local time=timer.getAbsTime()
-local playing=false
-local next=nil
-local remove=nil
-for i,_transmission in ipairs(self.queue)do
-local transmission=_transmission
-if time>=transmission.Tplay then
-if transmission.isplaying then
-if time>=transmission.Tstarted+transmission.duration then
-transmission.isplaying=false
-remove=i
-self.Tlast=time
-else
-playing=true
-end
-else
-local Tlast=self.Tlast
-if transmission.interval==nil then
-if next==nil then
-next=transmission
-end
-else
-if Tlast==nil or time-Tlast>=transmission.interval then
-next=transmission
-else
-end
-end
-if next or Tlast then
-break
-end
-end
-else
-end
-end
-if next~=nil and not playing then
-self:Broadcast(next)
-next.isplaying=true
-next.Tstarted=time
-end
-if remove then
-table.remove(self.queue,remove)
-end
-if self.schedonce then
-self:_CheckRadioQueueDelayed()
-end
-end
-function RADIOQUEUE:_GetRadioSender()
-local sender=nil
-if self.sendername then
-sender=UNIT:FindByName(self.sendername)
-if sender and sender:IsAlive()and(sender:IsAir()or sender:IsGround())then
-return sender
-end
-end
-return nil
-end
-function RADIOQUEUE:_GetRadioSenderCoord()
-local vec3=nil
-if self.sendername then
-local sender=UNIT:FindByName(self.sendername)
-if sender and sender:IsAlive()then
-return sender:GetVec3()
-end
-local sender=STATIC:FindByName(self.sendername,false)
-if sender then
-return sender:GetVec3()
-end
-end
-return nil
-end
-RADIOSPEECH={
-ClassName="RADIOSPEECH",
-Vocabulary={
-EN={},
-DE={},
-RU={},
-}
-}
-RADIOSPEECH.Vocabulary.EN={
-["1"]={"1",0.25},
-["2"]={"2",0.25},
-["3"]={"3",0.30},
-["4"]={"4",0.35},
-["5"]={"5",0.35},
-["6"]={"6",0.42},
-["7"]={"7",0.38},
-["8"]={"8",0.20},
-["9"]={"9",0.32},
-["10"]={"10",0.35},
-["11"]={"11",0.40},
-["12"]={"12",0.42},
-["13"]={"13",0.38},
-["14"]={"14",0.42},
-["15"]={"15",0.42},
-["16"]={"16",0.52},
-["17"]={"17",0.59},
-["18"]={"18",0.40},
-["19"]={"19",0.47},
-["20"]={"20",0.38},
-["30"]={"30",0.29},
-["40"]={"40",0.35},
-["50"]={"50",0.32},
-["60"]={"60",0.44},
-["70"]={"70",0.48},
-["80"]={"80",0.26},
-["90"]={"90",0.36},
-["100"]={"100",0.55},
-["200"]={"200",0.55},
-["300"]={"300",0.61},
-["400"]={"400",0.60},
-["500"]={"500",0.61},
-["600"]={"600",0.65},
-["700"]={"700",0.70},
-["800"]={"800",0.54},
-["900"]={"900",0.60},
-["1000"]={"1000",0.60},
-["2000"]={"2000",0.61},
-["3000"]={"3000",0.64},
-["4000"]={"4000",0.62},
-["5000"]={"5000",0.69},
-["6000"]={"6000",0.69},
-["7000"]={"7000",0.75},
-["8000"]={"8000",0.59},
-["9000"]={"9000",0.65},
-["chevy"]={"chevy",0.35},
-["colt"]={"colt",0.35},
-["springfield"]={"springfield",0.65},
-["dodge"]={"dodge",0.35},
-["enfield"]={"enfield",0.5},
-["ford"]={"ford",0.32},
-["pontiac"]={"pontiac",0.55},
-["uzi"]={"uzi",0.28},
-["degrees"]={"degrees",0.5},
-["kilometers"]={"kilometers",0.65},
-["km"]={"kilometers",0.65},
-["miles"]={"miles",0.45},
-["meters"]={"meters",0.41},
-["mi"]={"miles",0.45},
-["feet"]={"feet",0.29},
-["br"]={"br",1.1},
-["bra"]={"bra",0.3},
-["returning to base"]={"returning_to_base",0.85},
-["on route to ground target"]={"on_route_to_ground_target",1.05},
-["intercepting bogeys"]={"intercepting_bogeys",1.00},
-["engaging ground target"]={"engaging_ground_target",1.20},
-["engaging bogeys"]={"engaging_bogeys",0.81},
-["wheels up"]={"wheels_up",0.42},
-["landing at base"]={"landing at base",0.8},
-["patrolling"]={"patrolling",0.55},
-["for"]={"for",0.31},
-["and"]={"and",0.31},
-["at"]={"at",0.3},
-["dot"]={"dot",0.26},
-["defender"]={"defender",0.45},
-}
-RADIOSPEECH.Vocabulary.RU={
-["1"]={"1",0.34},
-["2"]={"2",0.30},
-["3"]={"3",0.23},
-["4"]={"4",0.51},
-["5"]={"5",0.31},
-["6"]={"6",0.44},
-["7"]={"7",0.25},
-["8"]={"8",0.43},
-["9"]={"9",0.45},
-["10"]={"10",0.53},
-["11"]={"11",0.66},
-["12"]={"12",0.70},
-["13"]={"13",0.66},
-["14"]={"14",0.80},
-["15"]={"15",0.65},
-["16"]={"16",0.75},
-["17"]={"17",0.74},
-["18"]={"18",0.85},
-["19"]={"19",0.80},
-["20"]={"20",0.58},
-["30"]={"30",0.51},
-["40"]={"40",0.51},
-["50"]={"50",0.67},
-["60"]={"60",0.76},
-["70"]={"70",0.68},
-["80"]={"80",0.84},
-["90"]={"90",0.71},
-["100"]={"100",0.35},
-["200"]={"200",0.59},
-["300"]={"300",0.53},
-["400"]={"400",0.70},
-["500"]={"500",0.50},
-["600"]={"600",0.58},
-["700"]={"700",0.64},
-["800"]={"800",0.77},
-["900"]={"900",0.75},
-["1000"]={"1000",0.87},
-["2000"]={"2000",0.83},
-["3000"]={"3000",0.84},
-["4000"]={"4000",1.00},
-["5000"]={"5000",0.77},
-["6000"]={"6000",0.90},
-["7000"]={"7000",0.77},
-["8000"]={"8000",0.92},
-["9000"]={"9000",0.87},
-["градусы"]={"degrees",0.5},
-["километры"]={"kilometers",0.65},
-["km"]={"kilometers",0.65},
-["мили"]={"miles",0.45},
-["mi"]={"miles",0.45},
-["метров"]={"meters",0.41},
-["m"]={"meters",0.41},
-["ноги"]={"feet",0.37},
-["br"]={"br",1.1},
-["bra"]={"bra",0.3},
-["возвращение на базу"]={"returning_to_base",1.40},
-["на пути к наземной цели"]={"on_route_to_ground_target",1.45},
-["перехват боги"]={"intercepting_bogeys",1.22},
-["поражение наземной цели"]={"engaging_ground_target",1.53},
-["привлечение болотных птиц"]={"engaging_bogeys",1.68},
-["колёса вверх..."]={"wheels_up",0.92},
-["посадка на базу"]={"landing at base",1.04},
-["патрулирование"]={"patrolling",0.96},
-["для"]={"for",0.27},
-["и"]={"and",0.17},
-["на сайте"]={"at",0.19},
-["точка"]={"dot",0.51},
-["защитник"]={"defender",0.45},
-}
-function RADIOSPEECH:New(frequency,modulation)
-local self=BASE:Inherit(self,RADIOQUEUE:New(frequency,modulation))
-self.Language="EN"
-self:BuildTree()
-return self
-end
-function RADIOSPEECH:SetLanguage(Langauge)
-self.Language=Langauge
-end
-function RADIOSPEECH:AddSentenceToSpeech(RemainingSentence,Speech,Sentence,Data)
-self:I({RemainingSentence,Speech,Sentence,Data})
-local Token,RemainingSentence=RemainingSentence:match("^ *([^ ]+)(.*)")
-self:I({Token=Token,RemainingSentence=RemainingSentence})
-if Token then
-if not Speech[Token]then
-Speech[Token]={}
-if RemainingSentence and RemainingSentence~=""then
-Speech[Token].Next={}
-self:AddSentenceToSpeech(RemainingSentence,Speech[Token].Next,Sentence,Data)
-else
-Speech[Token].Sentence=Sentence
-Speech[Token].Data=Data
-end
-end
-end
-end
-function RADIOSPEECH:BuildTree()
-self.Speech={}
-for Language,Sentences in pairs(self.Vocabulary)do
-self:I({Language=Language,Sentences=Sentences})
-self.Speech[Language]={}
-for Sentence,Data in pairs(Sentences)do
-self:I({Sentence=Sentence,Data=Data})
-self:AddSentenceToSpeech(Sentence,self.Speech[Language],Sentence,Data)
-end
-end
-self:I({Speech=self.Speech})
-return self
-end
-function RADIOSPEECH:SpeakWords(Sentence,Speech,Language)
-local OriginalSentence=Sentence
-local Word,RemainderSentence=Sentence:match("^[., ]*([^ .,]+)(.*)")
-self:I({Word=Word,Speech=Speech[Word],RemainderSentence=RemainderSentence})
-if Word then
-if Word~=""and tonumber(Word)==nil then
-Word=Word:lower()
-if Speech[Word]then
-if Speech[Word].Next==nil then
-self:I({Sentence=Speech[Word].Sentence,Data=Speech[Word].Data})
-self:NewTransmission(Speech[Word].Data[1]..".wav",Speech[Word].Data[2],Language.."/")
-else
-if RemainderSentence and RemainderSentence~=""then
-return self:SpeakWords(RemainderSentence,Speech[Word].Next,Language)
-end
-end
-end
-return RemainderSentence
-end
-return OriginalSentence
-else
-return""
-end
-end
-function RADIOSPEECH:SpeakDigits(Sentence,Speech,Langauge)
-local OriginalSentence=Sentence
-local Digits,RemainderSentence=Sentence:match("^[., ]*([^ .,]+)(.*)")
-self:I({Digits=Digits,Speech=Speech[Digits],RemainderSentence=RemainderSentence})
-if Digits then
-if Digits~=""and tonumber(Digits)~=nil then
-local Number=tonumber(Digits)
-local Multiple=nil
-while Number>=0 do
-if Number>1000 then
-Multiple=math.floor(Number/1000)*1000
-elseif Number>100 then
-Multiple=math.floor(Number/100)*100
-elseif Number>20 then
-Multiple=math.floor(Number/10)*10
-elseif Number>=0 then
-Multiple=Number
-end
-Sentence=tostring(Multiple)
-if Speech[Sentence]then
-self:I({Speech=Speech[Sentence].Sentence,Data=Speech[Sentence].Data})
-self:NewTransmission(Speech[Sentence].Data[1]..".wav",Speech[Sentence].Data[2],Langauge.."/")
-end
-Number=Number-Multiple
-Number=(Number==0)and-1 or Number
-end
-return RemainderSentence
-end
-return OriginalSentence
-else
-return""
-end
-end
-function RADIOSPEECH:Speak(Sentence,Language)
-self:I({Sentence,Language})
-local Language=Language or"EN"
-self:I({Language=Language})
-local Speech=self.Speech[Language]
-self:I({Speech=Speech,Language=Language})
-self:NewTransmission("_In.wav",0.52,Language.."/")
-repeat
-Sentence=self:SpeakWords(Sentence,Speech,Language)
-self:I({Sentence=Sentence})
-Sentence=self:SpeakDigits(Sentence,Speech,Language)
-self:I({Sentence=Sentence})
-until not Sentence or Sentence==""
-self:NewTransmission("_Out.wav",0.28,Language.."/")
-end
-do
-SOUNDBASE={
-ClassName="SOUNDBASE",
-}
-function SOUNDBASE:New()
-local self=BASE:Inherit(self,BASE:New())
-return self
-end
-function SOUNDBASE:GetSpeechTime(length,speed,isGoogle)
-local maxRateRatio=3
-speed=speed or 1.0
-isGoogle=isGoogle or false
-local speedFactor=1.0
-if isGoogle then
-speedFactor=speed
-else
-if speed~=0 then
-speedFactor=math.abs(speed)*(maxRateRatio-1)/10+1
-end
-if speed<0 then
-speedFactor=1/speedFactor
-end
-end
-local wpm=math.ceil(100*speedFactor)
-local cps=math.floor((wpm*5)/60)
-if type(length)=="string"then
-length=string.len(length)
-end
-return math.ceil(length/cps)
-end
-end
-do
-SOUNDFILE={
-ClassName="SOUNDFILE",
-filename=nil,
-path="l10n/DEFAULT/",
-duration=3,
-subtitle=nil,
-subduration=0,
-useSRS=false,
-}
-function SOUNDFILE:New(FileName,Path,Duration)
-local self=BASE:Inherit(self,BASE:New())
-self:SetFileName(FileName)
-self:SetPath(Path)
-self:SetDuration(Duration)
-self:T(string.format("New SOUNDFILE: file name=%s, path=%s",self.filename,self.path))
-return self
-end
-function SOUNDFILE:SetPath(Path)
-self.path=Path or"l10n/DEFAULT/"
-if not Path and self.useSRS then
-self.path=os.getenv('TMP').."\\DCS\\Mission\\l10n\\DEFAULT"
-end
-local nmax=1000;local n=1
-while(self.path:sub(-1)=="/"or self.path:sub(-1)==[[\]])and n<=nmax do
-self.path=self.path:sub(1,#self.path-1)
-n=n+1
-end
-self.path=self.path.."/"
-return self
-end
-function SOUNDFILE:GetPath()
-local path=self.path or"l10n/DEFAULT/"
-return path
-end
-function SOUNDFILE:SetFileName(FileName)
-self.filename=FileName or"Hello World.mp3"
-return self
-end
-function SOUNDFILE:GetFileName()
-return self.filename
-end
-function SOUNDFILE:SetDuration(Duration)
-self.duration=Duration or 3
-return self
-end
-function SOUNDFILE:GetDuration()
-return self.duration or 3
-end
-function SOUNDFILE:GetName()
-local path=self:GetPath()
-local filename=self:GetFileName()
-local name=string.format("%s%s",path,filename)
-return name
-end
-function SOUNDFILE:SetPlayWithSRS(Switch)
-if Switch==true or Switch==nil then
-self.useSRS=true
-else
-self.useSRS=false
-end
-return self
-end
-end
-do
-SOUNDTEXT={
-ClassName="SOUNDTEXT",
-}
-function SOUNDTEXT:New(Text,Duration)
-local self=BASE:Inherit(self,BASE:New())
-self:SetText(Text)
-self:SetDuration(Duration or STTS.getSpeechTime(Text))
-self:T(string.format("New SOUNDTEXT: text=%s, duration=%.1f sec",self.text,self.duration))
-return self
-end
-function SOUNDTEXT:SetText(Text)
-self.text=Text or"Hello World!"
-return self
-end
-function SOUNDTEXT:SetDuration(Duration)
-self.duration=Duration or 3
-return self
-end
-function SOUNDTEXT:SetGender(Gender)
-self.gender=Gender or"female"
-return self
-end
-function SOUNDTEXT:SetCulture(Culture)
-self.culture=Culture or"en-GB"
-return self
-end
-function SOUNDTEXT:SetVoice(VoiceName)
-self.voice=VoiceName
-return self
-end
-end
-MSRS={
-ClassName="MSRS",
-lid=nil,
-port=5002,
-name="MSRS",
-frequencies={},
-modulations={},
-coalition=0,
-gender="female",
-culture=nil,
-voice=nil,
-volume=1,
-speed=1,
-coordinate=nil,
-Label="ROBOT",
-AltBackend=nil,
-ConfigFileName="Moose_MSRS.lua",
-ConfigFilePath="Config\\",
-ConfigLoaded=false,
-ttsprovider="Microsoft",
-}
-MSRS.version="0.1.3"
-MSRS.Voices={
-Microsoft={
-["Hedda"]="Microsoft Hedda Desktop",
-["Hazel"]="Microsoft Hazel Desktop",
-["David"]="Microsoft David Desktop",
-["Zira"]="Microsoft Zira Desktop",
-["Hortense"]="Microsoft Hortense Desktop",
-},
-Google={
-Standard={
-["en_AU_Standard_A"]='en-AU-Standard-A',
-["en_AU_Standard_B"]='en-AU-Standard-B',
-["en_AU_Standard_C"]='en-AU-Standard-C',
-["en_AU_Standard_D"]='en-AU-Standard-D',
-["en_IN_Standard_A"]='en-IN-Standard-A',
-["en_IN_Standard_B"]='en-IN-Standard-B',
-["en_IN_Standard_C"]='en-IN-Standard-C',
-["en_IN_Standard_D"]='en-IN-Standard-D',
-["en_GB_Standard_A"]='en-GB-Standard-A',
-["en_GB_Standard_B"]='en-GB-Standard-B',
-["en_GB_Standard_C"]='en-GB-Standard-C',
-["en_GB_Standard_D"]='en-GB-Standard-D',
-["en_GB_Standard_F"]='en-GB-Standard-F',
-["en_US_Standard_A"]='en-US-Standard-A',
-["en_US_Standard_B"]='en-US-Standard-B',
-["en_US_Standard_C"]='en-US-Standard-C',
-["en_US_Standard_D"]='en-US-Standard-D',
-["en_US_Standard_E"]='en-US-Standard-E',
-["en_US_Standard_F"]='en-US-Standard-F',
-["en_US_Standard_G"]='en-US-Standard-G',
-["en_US_Standard_H"]='en-US-Standard-H',
-["en_US_Standard_I"]='en-US-Standard-I',
-["en_US_Standard_J"]='en-US-Standard-J',
-["fr_FR_Standard_A"]="fr-FR-Standard-A",
-["fr_FR_Standard_B"]="fr-FR-Standard-B",
-["fr_FR_Standard_C"]="fr-FR-Standard-C",
-["fr_FR_Standard_D"]="fr-FR-Standard-D",
-["fr_FR_Standard_E"]="fr-FR-Standard-E",
-["de_DE_Standard_A"]="de-DE-Standard-A",
-["de_DE_Standard_B"]="de-DE-Standard-B",
-["de_DE_Standard_C"]="de-DE-Standard-C",
-["de_DE_Standard_D"]="de-DE-Standard-D",
-["de_DE_Standard_E"]="de-DE-Standard-E",
-["de_DE_Standard_F"]="de-DE-Standard-F",
-["es_ES_Standard_A"]="es-ES-Standard-A",
-["es_ES_Standard_B"]="es-ES-Standard-B",
-["es_ES_Standard_C"]="es-ES-Standard-C",
-["es_ES_Standard_D"]="es-ES-Standard-D",
-["it_IT_Standard_A"]="it-IT-Standard-A",
-["it_IT_Standard_B"]="it-IT-Standard-B",
-["it_IT_Standard_C"]="it-IT-Standard-C",
-["it_IT_Standard_D"]="it-IT-Standard-D",
-},
-Wavenet={
-["en_AU_Wavenet_A"]='en-AU-Wavenet-A',
-["en_AU_Wavenet_B"]='en-AU-Wavenet-B',
-["en_AU_Wavenet_C"]='en-AU-Wavenet-C',
-["en_AU_Wavenet_D"]='en-AU-Wavenet-D',
-["en_IN_Wavenet_A"]='en-IN-Wavenet-A',
-["en_IN_Wavenet_B"]='en-IN-Wavenet-B',
-["en_IN_Wavenet_C"]='en-IN-Wavenet-C',
-["en_IN_Wavenet_D"]='en-IN-Wavenet-D',
-["en_GB_Wavenet_A"]='en-GB-Wavenet-A',
-["en_GB_Wavenet_B"]='en-GB-Wavenet-B',
-["en_GB_Wavenet_C"]='en-GB-Wavenet-C',
-["en_GB_Wavenet_D"]='en-GB-Wavenet-D',
-["en_GB_Wavenet_F"]='en-GB-Wavenet-F',
-["en_US_Wavenet_A"]='en-US-Wavenet-A',
-["en_US_Wavenet_B"]='en-US-Wavenet-B',
-["en_US_Wavenet_C"]='en-US-Wavenet-C',
-["en_US_Wavenet_D"]='en-US-Wavenet-D',
-["en_US_Wavenet_E"]='en-US-Wavenet-E',
-["en_US_Wavenet_F"]='en-US-Wavenet-F',
-["en_US_Wavenet_G"]='en-US-Wavenet-G',
-["en_US_Wavenet_H"]='en-US-Wavenet-H',
-["en_US_Wavenet_I"]='en-US-Wavenet-I',
-["en_US_Wavenet_J"]='en-US-Wavenet-J',
-["fr_FR_Wavenet_A"]="fr-FR-Wavenet-A",
-["fr_FR_Wavenet_B"]="fr-FR-Wavenet-B",
-["fr_FR_Wavenet_C"]="fr-FR-Wavenet-C",
-["fr_FR_Wavenet_D"]="fr-FR-Wavenet-D",
-["fr_FR_Wavenet_E"]="fr-FR-Wavenet-E",
-["de_DE_Wavenet_A"]="de-DE-Wavenet-A",
-["de_DE_Wavenet_B"]="de-DE-Wavenet-B",
-["de_DE_Wavenet_C"]="de-DE-Wavenet-C",
-["de_DE_Wavenet_D"]="de-DE-Wavenet-D",
-["de_DE_Wavenet_E"]="de-DE-Wavenet-E",
-["de_DE_Wavenet_F"]="de-DE-Wavenet-F",
-["es_ES_Wavenet_B"]="es-ES-Wavenet-B",
-["es_ES_Wavenet_C"]="es-ES-Wavenet-C",
-["es_ES_Wavenet_D"]="es-ES-Wavenet-D",
-["it_IT_Wavenet_A"]="it-IT-Wavenet-A",
-["it_IT_Wavenet_B"]="it-IT-Wavenet-B",
-["it_IT_Wavenet_C"]="it-IT-Wavenet-C",
-["it_IT_Wavenet_D"]="it-IT-Wavenet-D",
-},
-},
-}
-MSRS.GRPCOptions={}
-MSRS.GRPCOptions.gcloud={}
-MSRS.GRPCOptions.win={}
-MSRS.GRPCOptions.azure={}
-MSRS.GRPCOptions.aws={}
-MSRS.GRPCOptions.win.defaultVoice="Hedda"
-MSRS.GRPCOptions.win.voice="Hedda"
-MSRS.GRPCOptions.DefaultProvider="win"
-function MSRS:New(PathToSRS,Frequency,Modulation,Volume,AltBackend)
-Frequency=Frequency or 143
-Modulation=Modulation or radio.modulation.AM
-local self=BASE:Inherit(self,BASE:New())
-if type(AltBackend)=="table"or type(self.AltBackend)=="table"then
-local Backend=UTILS.DeepCopy(AltBackend)or UTILS.DeepCopy(self.AltBackend)
-Backend.Vars=Backend.Vars or{}
-Backend.Vars.PathToSRS=PathToSRS
-Backend.Vars.Frequency=UTILS.DeepCopy(Frequency)
-Backend.Vars.Modulation=UTILS.DeepCopy(Modulation)
-Backend.Vars.Volume=Volume
-Backend.Functions=Backend.Functions or{}
-return self:_NewAltBackend(Backend)
-end
-if not self.ConfigLoaded then
-self:SetPath(PathToSRS)
-self:SetPort()
-self:SetFrequencies(Frequency)
-self:SetModulations(Modulation)
-self:SetGender()
-self:SetCoalition()
-self:SetLabel()
-self:SetVolume(Volume)
-else
-if PathToSRS then
-self:SetPath(PathToSRS)
-end
-if Frequency then
-self:SetFrequencies(Frequency)
-self:SetModulations(Modulation)
-end
-if Volume then
-self:SetVolume(Volume)
-end
-end
-self.lid=string.format("%s-%s | ",self.name,self.version)
-if not io or not os then
-self:E(self.lid.."***** ERROR - io or os NOT desanitized! MSRS will not work!")
-end
-return self
-end
-function MSRS:SetPath(Path)
-if Path==nil and not self.path then
-self:E("ERROR: No path to SRS directory specified!")
-return nil
-end
-if Path then
-self.path=Path
-local n=1;local nmax=1000
-while(self.path:sub(-1)=="/"or self.path:sub(-1)==[[\]])and n<=nmax do
-self.path=self.path:sub(1,#self.path-1)
-n=n+1
-end
-self:T(string.format("SRS path=%s",self:GetPath()))
-end
-return self
-end
-function MSRS:GetPath()
-return self.path
-end
-function MSRS:SetVolume(Volume)
-local volume=Volume or 1
-if volume>1 then volume=1 elseif volume<0 then volume=0 end
-self.volume=volume
-return self
-end
-function MSRS:GetVolume()
-return self.volume
-end
-function MSRS:SetLabel(Label)
-self.Label=Label or"ROBOT"
-return self
-end
-function MSRS:GetLabel()
-return self.Label
-end
-function MSRS:SetPort(Port)
-self.port=Port or 5002
-return self
-end
-function MSRS:GetPort()
-return self.port
-end
-function MSRS:SetCoalition(Coalition)
-self.coalition=Coalition or 0
-return self
-end
-function MSRS:GetCoalition()
-return self.coalition
-end
-function MSRS:SetFrequencies(Frequencies)
-if type(Frequencies)~="table"then
-Frequencies={Frequencies}
-end
-self.frequencies=Frequencies
-return self
-end
-function MSRS:AddFrequencies(Frequencies)
-if type(Frequencies)~="table"then
-Frequencies={Frequencies}
-end
-for _,_freq in pairs(Frequencies)do
-table.insert(self.frequencies,_freq)
-end
-return self
-end
-function MSRS:GetFrequencies()
-return self.frequencies
-end
-function MSRS:SetModulations(Modulations)
-if type(Modulations)~="table"then
-Modulations={Modulations}
-end
-self.modulations=Modulations
-return self
-end
-function MSRS:AddModulations(Modulations)
-if type(Modulations)~="table"then
-Modulations={Modulations}
-end
-for _,_mod in pairs(Modulations)do
-table.insert(self.modulations,_mod)
-end
-return self
-end
-function MSRS:GetModulations()
-return self.modulations
-end
-function MSRS:SetGender(Gender)
-Gender=Gender or"female"
-self.gender=Gender:lower()
-self:T("Setting gender to "..tostring(self.gender))
-return self
-end
-function MSRS:SetCulture(Culture)
-self.culture=Culture
-return self
-end
-function MSRS:SetVoice(Voice)
-self.voice=Voice
-return self
-end
-function MSRS:SetDefaultVoice(Voice)
-self.defaultVoice=Voice
-local provider=self.provider or self.GRPCOptions.DefaultProvider or MSRS.GRPCOptions.DefaultProvider or"win"
-self.GRPCOptions[provider].defaultVoice=Voice
-return self
-end
-function MSRS:SetCoordinate(Coordinate)
-self.coordinate=Coordinate
-return self
-end
-function MSRS:SetGoogle(PathToCredentials)
-if PathToCredentials then
-self.google=PathToCredentials
-self.APIKey=PathToCredentials
-self.provider="gcloud"
-self.GRPCOptions.DefaultProvider="gcloud"
-self.GRPCOptions.gcloud.key=PathToCredentials
-self.ttsprovider="Google"
-end
-return self
-end
-function MSRS:SetGoogleAPIKey(APIKey)
-if APIKey then
-self.APIKey=APIKey
-self.provider="gcloud"
-self.GRPCOptions.DefaultProvider="gcloud"
-self.GRPCOptions.gcloud.key=APIKey
-end
-return self
-end
-function MSRS:SetTTSProviderGoogle()
-self.ttsprovider="Google"
-return self
-end
-function MSRS:SetTTSProviderMicrosoft()
-self.ttsprovider="Microsoft"
-return self
-end
-function MSRS:Help()
-local path=self:GetPath()or STTS.DIRECTORY
-local exe=STTS.EXECUTABLE or"DCS-SR-ExternalAudio.exe"
-local filename=os.getenv('TMP').."\\MSRS-help-"..STTS.uuid()..".txt"
-local command=string.format("%s/%s --help > %s",path,exe,filename)
-os.execute(command)
-local f=assert(io.open(filename,"rb"))
-local data=f:read("*all")
-f:close()
-env.info("SRS STTS help output:")
-env.info("======================================================================")
-env.info(data)
-env.info("======================================================================")
-return self
-end
-function MSRS.SetDefaultBackend(Backend)
-if type(Backend)=="table"then
-MSRS.AltBackend=UTILS.DeepCopy(Backend)
-else
-return false
-end
-return true
-end
-function MSRS.ResetDefaultBackend()
-MSRS.AltBackend=nil
-return true
-end
-function MSRS.SetDefaultBackendGRPC()
-return MSRS.SetDefaultBackend(MSRS_BACKEND_DCSGRPC)
-end
-function MSRS:PlaySoundFile(Soundfile,Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,MSRS.PlaySoundFile,self,Soundfile,0)
-else
-local soundfile=Soundfile:GetName()
-local command=self:_GetCommand()
-command=command..' --file="'..tostring(soundfile)..'"'
-self:_ExecCommand(command)
-end
-return self
-end
-function MSRS:PlaySoundText(SoundText,Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,MSRS.PlaySoundText,self,SoundText,0)
-else
-local command=self:_GetCommand(nil,nil,nil,SoundText.gender,SoundText.voice,SoundText.culture,SoundText.volume,SoundText.speed)
-command=command..string.format(" --text=\"%s\"",tostring(SoundText.text))
-self:_ExecCommand(command)
-end
-return self
-end
-function MSRS:PlayText(Text,Delay,Coordinate)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,MSRS.PlayText,self,Text,nil,Coordinate)
-else
-local command=self:_GetCommand(nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,Coordinate)
-command=command..string.format(" --text=\"%s\"",tostring(Text))
-self:_ExecCommand(command)
-end
-return self
-end
-function MSRS:PlayTextExt(Text,Delay,Frequencies,Modulations,Gender,Culture,Voice,Volume,Label,Coordinate)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,MSRS.PlayTextExt,self,Text,0,Frequencies,Modulations,Gender,Culture,Voice,Volume,Label,Coordinate)
-else
-if Frequencies and type(Frequencies)~="table"then
-Frequencies={Frequencies}
-end
-if Modulations and type(Modulations)~="table"then
-Modulations={Modulations}
-end
-local command=self:_GetCommand(Frequencies,Modulations,nil,Gender,Voice,Culture,Volume,nil,nil,Label,Coordinate)
-command=command..string.format(" --text=\"%s\"",tostring(Text))
-self:_ExecCommand(command)
-end
-return self
-end
-function MSRS:PlayTextFile(TextFile,Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,MSRS.PlayTextFile,self,TextFile,0)
-else
-local exists=UTILS.FileExists(TextFile)
-if not exists then
-self:E("ERROR: MSRS Text file does not exist! File="..tostring(TextFile))
-return self
-end
-local command=self:_GetCommand()
-command=command..string.format(" --textFile=\"%s\"",tostring(TextFile))
-self:T(string.format("MSRS TextFile command=%s",command))
-local l=string.len(command)
-self:_ExecCommand(command)
-end
-return self
-end
-function MSRS:_NewAltBackend(Backend)
-BASE:T('Entering MSRS:_NewAltBackend()')
-for funcName,funcDef in pairs(Backend.Functions)do
-if type(funcDef)=='function'then
-BASE:T('MSRS (re-)defining function MSRS:'..funcName)
-self[funcName]=funcDef
-end
-end
-for varName,varVal in pairs(Backend.Vars)do
-BASE:T('MSRS setting self.'..varName)
-self[varName]=UTILS.DeepCopy(varVal)
-end
-if self._MSRSbackendInit and type(self._MSRSbackendInit)=='function'then
-return self:_MSRSbackendInit()
-end
-return self
-end
-function MSRS:_ExecCommand(command)
-local filename=os.getenv('TMP').."\\MSRS-"..STTS.uuid()..".bat"
-local script=io.open(filename,"w+")
-script:write(command.." && exit")
-script:close()
-command=string.format('start /b "" "%s"',filename)
-local res=nil
-if true then
-local filenvbs=os.getenv('TMP').."\\MSRS-"..STTS.uuid()..".vbs"
-local script=io.open(filenvbs,"w+")
-script:write(string.format('Dim WinScriptHost\n'))
-script:write(string.format('Set WinScriptHost = CreateObject("WScript.Shell")\n'))
-script:write(string.format('WinScriptHost.Run Chr(34) & "%s" & Chr(34), 0\n',filename))
-script:write(string.format('Set WinScriptHost = Nothing'))
-script:close()
-local runvbs=string.format('cscript.exe //Nologo //B "%s"',filenvbs)
-self:T("MSRS execute command="..command)
-self:T("MSRS execute VBS command="..runvbs)
-res=os.execute(runvbs)
-timer.scheduleFunction(os.remove,filename,timer.getTime()+1)
-timer.scheduleFunction(os.remove,filenvbs,timer.getTime()+1)
-elseif false then
-local filenvbs=os.getenv('TMP').."\\MSRS-"..STTS.uuid()..".vbs"
-local script=io.open(filenvbs,"w+")
-script:write(string.format('Set oShell = CreateObject ("Wscript.Shell")\n'))
-script:write(string.format('Dim strArgs\n'))
-script:write(string.format('strArgs = "cmd /c %s"\n',filename))
-script:write(string.format('oShell.Run strArgs, 0, false'))
-script:close()
-local runvbs=string.format('cscript.exe //Nologo //B "%s"',filenvbs)
-res=os.execute(runvbs)
-else
-self:T("MSRS execute command="..command)
-res=os.execute(command)
-timer.scheduleFunction(os.remove,filename,timer.getTime()+1)
-end
-return res
-end
-function MSRS:_GetLatLongAlt(Coordinate)
-local lat,lon,alt=coord.LOtoLL(Coordinate)
-return lat,lon,math.floor(alt)
-end
-function MSRS:_GetCommand(freqs,modus,coal,gender,voice,culture,volume,speed,port,label,coordinate)
-local path=self:GetPath()or STTS.DIRECTORY
-local exe=STTS.EXECUTABLE or"DCS-SR-ExternalAudio.exe"
-freqs=table.concat(freqs or self.frequencies,",")
-modus=table.concat(modus or self.modulations,",")
-coal=coal or self.coalition
-gender=gender or self.gender
-voice=voice or self.voice
-culture=culture or self.culture
-volume=volume or self.volume
-speed=speed or self.speed
-port=port or self.port
-label=label or self.Label
-coordinate=coordinate or self.coordinate
-modus=modus:gsub("0","AM")
-modus=modus:gsub("1","FM")
-local command=string.format('"%s\\%s" -f "%s" -m "%s" -c %s -p %s -n "%s" -v "%.1f"',path,exe,freqs,modus,coal,port,label,volume)
-if voice then
-command=command..string.format(" --voice=\"%s\"",tostring(voice))
-else
-if gender and gender~="female"then
-command=command..string.format(" -g %s",tostring(gender))
-end
-if culture and culture~="en-GB"then
-command=command..string.format(" -l %s",tostring(culture))
-end
-end
-if coordinate then
-local lat,lon,alt=self:_GetLatLongAlt(coordinate)
-command=command..string.format(" -L %.4f -O %.4f -A %d",lat,lon,alt)
-end
-if self.google and self.ttsprovider=="Google"then
-command=command..string.format(' --ssml -G "%s"',self.google)
-end
-self:T("MSRS command="..command)
-return command
-end
-function MSRS:LoadConfigFile(Path,Filename)
-if lfs==nil then
-env.info("*****Note - lfs and os need to be desanitized for MSRS to work!")
-return false
-end
-local path=Path or lfs.writedir()..MSRS.ConfigFilePath
-local file=Filename or MSRS.ConfigFileName or"Moose_MSRS.lua"
-local pathandfile=path..file
-local filexsists=UTILS.FileExists(pathandfile)
-if filexsists and not MSRS.ConfigLoaded then
-assert(loadfile(path..file))()
-if MSRS_Config then
-if self then
-self.path=MSRS_Config.Path or"C:\\Program Files\\DCS-SimpleRadio-Standalone"
-self.port=MSRS_Config.Port or 5002
-self.frequencies=MSRS_Config.Frequency or{127,243}
-self.modulations=MSRS_Config.Modulation or{0,0}
-self.coalition=MSRS_Config.Coalition or 0
-if MSRS_Config.Coordinate then
-self.coordinate=COORDINATE:New(MSRS_Config.Coordinate[1],MSRS_Config.Coordinate[2],MSRS_Config.Coordinate[3])
-end
-self.culture=MSRS_Config.Culture or"en-GB"
-self.gender=MSRS_Config.Gender or"male"
-self.google=MSRS_Config.Google
-if MSRS_Config.Provider then
-self.ttsprovider=MSRS_Config.Provider
-end
-self.Label=MSRS_Config.Label or"MSRS"
-self.voice=MSRS_Config.Voice
-if MSRS_Config.GRPC then
-self.provider=MSRS_Config.GRPC.DefaultProvider
-if MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider]then
-self.APIKey=MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].key
-self.defaultVoice=MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].defaultVoice
-self.region=MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].secret
-self.secret=MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].region
-end
-end
-self.ConfigLoaded=true
-else
-MSRS.path=MSRS_Config.Path or"C:\\Program Files\\DCS-SimpleRadio-Standalone"
-MSRS.port=MSRS_Config.Port or 5002
-MSRS.frequencies=MSRS_Config.Frequency or{127,243}
-MSRS.modulations=MSRS_Config.Modulation or{0,0}
-MSRS.coalition=MSRS_Config.Coalition or 0
-if MSRS_Config.Coordinate then
-MSRS.coordinate=COORDINATE:New(MSRS_Config.Coordinate[1],MSRS_Config.Coordinate[2],MSRS_Config.Coordinate[3])
-end
-MSRS.culture=MSRS_Config.Culture or"en-GB"
-MSRS.gender=MSRS_Config.Gender or"male"
-MSRS.google=MSRS_Config.Google
-if MSRS_Config.Provider then
-MSRS.ttsprovider=MSRS_Config.Provider
-end
-MSRS.Label=MSRS_Config.Label or"MSRS"
-MSRS.voice=MSRS_Config.Voice
-if MSRS_Config.GRPC then
-MSRS.provider=MSRS_Config.GRPC.DefaultProvider
-if MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider]then
-MSRS.APIKey=MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].key
-MSRS.defaultVoice=MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].defaultVoice
-MSRS.region=MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].secret
-MSRS.secret=MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].region
-end
-end
-MSRS.ConfigLoaded=true
-end
-end
-env.info("MSRS - Successfully loaded default configuration from disk!",false)
-end
-if not filexsists then
-env.info("MSRS - Cannot find default configuration file!",false)
-return false
-end
-return true
-end
-MSRS_BACKEND_DCSGRPC={}
-MSRS_BACKEND_DCSGRPC.version=0.1
-MSRS_BACKEND_DCSGRPC.Functions={}
-MSRS_BACKEND_DCSGRPC.Vars={provider='win'}
-MSRS_BACKEND_DCSGRPC.Functions._MSRSbackendInit=function(self)
-BASE:I('Loaded MSRS DCS-gRPC alternate backend version '..self.AltBackend.version or'unspecified')
-return self
-end
-MSRS_BACKEND_DCSGRPC.Functions.SetPath=function(self)
-return self
-end
-MSRS_BACKEND_DCSGRPC.Functions.GetPath=function(self)
-return''
-end
-MSRS_BACKEND_DCSGRPC.Functions.SetVolume=function(self)
-BASE:I('NOTE: MSRS:SetVolume() not used with DCS-gRPC backend.')
-return self
-end
-MSRS_BACKEND_DCSGRPC.Functions.GetVolume=function(self)
-BASE:I('NOTE: MSRS:GetVolume() not used with DCS-gRPC backend.')
-return 1
-end
-MSRS_BACKEND_DCSGRPC.Functions.SetGender=function(self,Gender)
-if Gender then
-self.gender=Gender:lower()
-end
-self:T("Setting gender to "..tostring(self.gender))
-return self
-end
-MSRS_BACKEND_DCSGRPC.Functions.SetGoogle=function(self)
-self.provider='gcloud'
-return self
-end
-MSRS_BACKEND_DCSGRPC.Functions.SetAWS=function(self)
-self.provider='aws'
-return self
-end
-MSRS_BACKEND_DCSGRPC.Functions.SetAzure=function(self)
-self.provider='azure'
-return self
-end
-MSRS_BACKEND_DCSGRPC.Functions.SetWin=function(self)
-self.provider='win'
-return self
-end
-MSRS_BACKEND_DCSGRPC.Functions.Help=function(self)
-env.info('For DCS-gRPC help, please see: https://github.com/DCS-gRPC/rust-server')
-return self
-end
-MSRS_BACKEND_DCSGRPC.Functions.PlaySoundFile=function(self)
-BASE:E("ERROR: MSRS:PlaySoundFile() is not supported by the DCS-gRPC backend.")
-return self
-end
-MSRS_BACKEND_DCSGRPC.Functions.PlaySoundText=function(self,SoundText,Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,self.PlaySoundText,self,SoundText,0)
-else
-self:_DCSgRPCtts(tostring(SoundText.text))
-end
-return self
-end
-MSRS_BACKEND_DCSGRPC.Functions.PlayText=function(self,Text,Delay)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,self.PlayText,self,Text,0)
-else
-self:_DCSgRPCtts(tostring(Text))
-end
-return self
-end
-MSRS_BACKEND_DCSGRPC.Functions.PlayTextExt=function(self,Text,Delay,Frequencies,Modulations,Gender,Culture,Voice,Volume,Label)
-if Delay and Delay>0 then
-self:ScheduleOnce(Delay,self.PlayTextExt,self,Text,0,Frequencies,Modulations,Gender,Culture,Voice,Volume,Label)
-else
-self:_DCSgRPCtts(tostring(Text),nil,Frequencies,Voice,Label)
-end
-return self
-end
-MSRS_BACKEND_DCSGRPC.Functions.PlayTextFile=function(self,TextFile,Delay)
-BASE:E("ERROR: MSRS:PlayTextFile() is not supported by the DCS-gRPC backend.")
-return self
-end
-MSRS_BACKEND_DCSGRPC.Functions._DCSgRPCtts=function(self,Text,Plaintext,Frequencies,Voice,Label)
-BASE:T("MSRS_BACKEND_DCSGRPC:_DCSgRPCtts()")
-BASE:T({Text,Plaintext,Frequencies,Voice,Label})
-local options=self.ProviderOptions or MSRS.ProviderOptions or{}
-local ssml=Text or''
-local XmitFrequencies=Frequencies or self.Frequency
-if type(XmitFrequencies)~="table"then
-XmitFrequencies={XmitFrequencies}
-end
-options.plaintext=Plaintext
-options.srsClientName=Label or self.Label
-options.position={}
-if self.coordinate then
-options.position.lat,options.position.lon,options.position.alt=self:_GetLatLongAlt(self.coordinate)
-end
-options.position.lat=options.position.lat or 0.0
-options.position.lon=options.position.lon or 0.0
-options.position.alt=options.position.alt or 0.0
-if UTILS.GetCoalitionName(self.coalition)=='Blue'then
-options.coalition='blue'
-elseif UTILS.GetCoalitionName(self.coalition)=='Red'then
-options.coalition='red'
-end
-local provider=self.provider or self.GRPCOptions.DefaultProvider or MSRS.GRPCOptions.DefaultProvider
-options.provider={}
-options.provider[provider]={}
-if self.APIKey then
-options.provider[provider].key=self.APIKey
-end
-if self.defaultVoice then
-options.provider[provider].defaultVoice=self.defaultVoice
-end
-if self.voice then
-options.provider[provider].voice=Voice or self.voice or self.defaultVoice
-elseif ssml then
-local preTag,genderProp,langProp,postTag='','','',''
-if self.gender then
-genderProp=' gender=\"'..self.gender..'\"'
-end
-if self.culture then
-langProp=' language=\"'..self.culture..'\"'
-end
-if self.culture or self.gender then
-preTag=''
-postTag=''
-ssml=preTag..Text..postTag
-end
-end
-for _,_freq in ipairs(XmitFrequencies)do
-local freq=_freq*1000000
-BASE:T("GRPC.tts")
-BASE:T(ssml)
-BASE:T(freq)
-BASE:T({options})
-GRPC.tts(ssml,freq,options)
-end
-end
-MSRSQUEUE={
-ClassName="MSRSQUEUE",
-Debugmode=nil,
-lid=nil,
-queue={},
-alias=nil,
-dt=nil,
-Tlast=nil,
-checking=nil,
-}
-function MSRSQUEUE:New(alias)
-local self=BASE:Inherit(self,BASE:New())
-self.alias=alias or"My Radio"
-self.dt=1.0
-self.lid=string.format("MSRSQUEUE %s | ",self.alias)
-return self
-end
-function MSRSQUEUE:Clear()
-self:I(self.lid.."Clearing MSRSQUEUE")
-self.queue={}
-return self
-end
-function MSRSQUEUE:AddTransmission(transmission)
-transmission.isplaying=false
-transmission.Tstarted=nil
-table.insert(self.queue,transmission)
-if not self.checking then
-self:_CheckRadioQueue()
-end
-return self
-end
-function MSRSQUEUE:SetTransmitOnlyWithPlayers(Switch)
-self.TransmitOnlyWithPlayers=Switch
-if Switch==false or Switch==nil then
-if self.PlayerSet then
-self.PlayerSet:FilterStop()
-end
-self.PlayerSet=nil
-else
-self.PlayerSet=SET_CLIENT:New():FilterStart()
-end
-return self
-end
-function MSRSQUEUE:NewTransmission(text,duration,msrs,tstart,interval,subgroups,subtitle,subduration,frequency,modulation,gender,culture,voice,volume,label,coordinate)
-if self.TransmitOnlyWithPlayers then
-if self.PlayerSet and self.PlayerSet:CountAlive()==0 then
-return self
-end
-end
-if not text then
-self:E(self.lid.."ERROR: No text specified.")
-return nil
-end
-if type(text)~="string"then
-self:E(self.lid.."ERROR: Text specified is NOT a string.")
-return nil
-end
-local transmission={}
-transmission.text=text
-transmission.duration=duration or STTS.getSpeechTime(text)
-transmission.msrs=msrs
-transmission.Tplay=tstart or timer.getAbsTime()
-transmission.subtitle=subtitle
-transmission.interval=interval or 0
-transmission.frequency=frequency
-transmission.modulation=modulation
-transmission.subgroups=subgroups
-if transmission.subtitle then
-transmission.subduration=subduration or transmission.duration
-else
-transmission.subduration=0
-end
-transmission.gender=gender
-transmission.culture=culture
-transmission.voice=voice
-transmission.volume=volume
-transmission.label=label
-transmission.coordinate=coordinate
-self:AddTransmission(transmission)
-return transmission
-end
-function MSRSQUEUE:Broadcast(transmission)
-if transmission.frequency then
-transmission.msrs:PlayTextExt(transmission.text,nil,transmission.frequency,transmission.modulation,transmission.gender,transmission.culture,transmission.voice,transmission.volume,transmission.label,transmission.coordinate)
-else
-transmission.msrs:PlayText(transmission.text,nil,transmission.coordinate)
-end
-local function texttogroup(gid)
-trigger.action.outTextForGroup(gid,transmission.subtitle,transmission.subduration,true)
-end
-if transmission.subgroups and#transmission.subgroups>0 then
-for _,_group in pairs(transmission.subgroups)do
-local group=_group
-if group and group:IsAlive()then
-local gid=group:GetID()
-self:ScheduleOnce(4,texttogroup,gid)
-end
-end
-end
-end
-function MSRSQUEUE:CalcTransmisstionDuration()
-local Tnow=timer.getAbsTime()
-local T=0
-for _,_transmission in pairs(self.queue)do
-local transmission=_transmission
-if transmission.isplaying then
-local dt=Tnow-transmission.Tstarted
-T=T+transmission.duration-dt
-else
-T=T+transmission.duration
-end
-end
-return T
-end
-function MSRSQUEUE:_CheckRadioQueue(delay)
-local N=#self.queue
-self:T2(self.lid..string.format("Check radio queue %s: delay=%.3f sec, N=%d, checking=%s",self.alias,delay or 0,N,tostring(self.checking)))
-if delay and delay>0 then
-self:ScheduleOnce(delay,MSRSQUEUE._CheckRadioQueue,self)
-self.checking=true
-else
-if N==0 then
-self:T(self.lid..string.format("Check radio queue %s empty ==> disable checking",self.alias))
-self.checking=false
-return
-end
-local time=timer.getAbsTime()
-self.checking=true
-local dt=self.dt
-local playing=false
-local next=nil
-local remove=nil
-for i,_transmission in ipairs(self.queue)do
-local transmission=_transmission
-if time>=transmission.Tplay then
-if transmission.isplaying then
-if time>=transmission.Tstarted+transmission.duration then
-transmission.isplaying=false
-remove=i
-self.Tlast=time
-else
-playing=true
-dt=transmission.duration-(time-transmission.Tstarted)
-end
-else
-local Tlast=self.Tlast
-if transmission.interval==nil then
-if next==nil then
-next=transmission
-end
-else
-if Tlast==nil or time-Tlast>=transmission.interval then
-next=transmission
-else
-end
-end
-if next or Tlast then
-break
-end
-end
-else
-end
-end
-if next~=nil and not playing then
-self:T(self.lid..string.format("Broadcasting text=\"%s\" at T=%.3f",next.text,time))
-self:Broadcast(next)
-next.isplaying=true
-next.Tstarted=time
-dt=next.duration
-end
-if remove then
-table.remove(self.queue,remove)
-N=N-1
-if#self.queue==0 then
-self:T(self.lid..string.format("Check radio queue %s empty ==> disable checking",self.alias))
-self.checking=false
-return
-end
-end
-self:_CheckRadioQueue(dt)
-end
-end
-MSRS.LoadConfigFile()
-do
-USERSOUND={
-ClassName="USERSOUND",
-}
-function USERSOUND:New(UserSoundFileName)
-local self=BASE:Inherit(self,BASE:New())
-self.UserSoundFileName=UserSoundFileName
-return self
-end
-function USERSOUND:SetFileName(UserSoundFileName)
-self.UserSoundFileName=UserSoundFileName
-return self
-end
-function USERSOUND:ToAll()
-trigger.action.outSound(self.UserSoundFileName)
-return self
-end
-function USERSOUND:ToCoalition(Coalition)
-trigger.action.outSoundForCoalition(Coalition,self.UserSoundFileName)
-return self
-end
-function USERSOUND:ToCountry(Country)
-trigger.action.outSoundForCountry(Country,self.UserSoundFileName)
-return self
-end
-function USERSOUND:ToGroup(Group,Delay)
-Delay=Delay or 0
-if Delay>0 then
-SCHEDULER:New(nil,USERSOUND.ToGroup,{self,Group},Delay)
-else
-trigger.action.outSoundForGroup(Group:GetID(),self.UserSoundFileName)
-end
-return self
-end
-function USERSOUND:ToUnit(Unit,Delay)
-Delay=Delay or 0
-if Delay>0 then
-SCHEDULER:New(nil,USERSOUND.ToUnit,{self,Unit},Delay)
-else
-trigger.action.outSoundForUnit(Unit:GetID(),self.UserSoundFileName)
-end
-return self
-end
-function USERSOUND:ToClient(Client,Delay)
-Delay=Delay or 0
-if Delay>0 then
-SCHEDULER:New(nil,USERSOUND.ToClient,{self,Client},Delay)
-else
-trigger.action.outSoundForUnit(Client:GetID(),self.UserSoundFileName)
-end
-return self
-end
-end
-COMMANDCENTER={
-ClassName="COMMANDCENTER",
-CommandCenterName="",
-CommandCenterCoalition=nil,
-CommandCenterPositionable=nil,
-Name="",
-ReferencePoints={},
-ReferenceNames={},
-CommunicationMode="80",
-}
-COMMANDCENTER.AutoAssignMethods={
-["Random"]=1,
-["Distance"]=2,
-["Priority"]=3,
-}
-function COMMANDCENTER:New(CommandCenterPositionable,CommandCenterName)
-local self=BASE:Inherit(self,BASE:New())
-self.CommandCenterPositionable=CommandCenterPositionable
-self.CommandCenterName=CommandCenterName or CommandCenterPositionable:GetName()
-self.CommandCenterCoalition=CommandCenterPositionable:GetCoalition()
-self.Missions={}
-self:SetAutoAssignTasks(false)
-self:SetAutoAcceptTasks(true)
-self:SetAutoAssignMethod(COMMANDCENTER.AutoAssignMethods.Distance)
-self:SetFlashStatus(false)
-self:SetMessageDuration(10)
-self:HandleEvent(EVENTS.Birth,
-function(self,EventData)
-if EventData.IniObjectCategory==1 then
-local EventGroup=GROUP:Find(EventData.IniDCSGroup)
-if EventGroup and EventGroup:IsAlive()and self:HasGroup(EventGroup)then
-local CommandCenterMenu=MENU_GROUP:New(EventGroup,self:GetText())
-local MenuReporting=MENU_GROUP:New(EventGroup,"Missions Reports",CommandCenterMenu)
-local MenuMissionsSummary=MENU_GROUP_COMMAND:New(EventGroup,"Missions Status Report",MenuReporting,self.ReportSummary,self,EventGroup)
-local MenuMissionsDetails=MENU_GROUP_COMMAND:New(EventGroup,"Missions Players Report",MenuReporting,self.ReportMissionsPlayers,self,EventGroup)
-local PlayerUnit=EventData.IniUnit
-for MissionID,Mission in pairs(self:GetMissions())do
-local Mission=Mission
-local PlayerGroup=EventData.IniGroup
-Mission:JoinUnit(PlayerUnit,PlayerGroup)
-end
-self:SetMenu()
-end
-end
-end
-)
-self:HandleEvent(EVENTS.MissionEnd,
-function(self,EventData)
-local PlayerUnit=EventData.IniUnit
-for MissionID,Mission in pairs(self:GetMissions())do
-local Mission=Mission
-Mission:Stop()
-end
-end
-)
-self:HandleEvent(EVENTS.PlayerLeaveUnit,
-function(self,EventData)
-local PlayerUnit=EventData.IniUnit
-for MissionID,Mission in pairs(self:GetMissions())do
-local Mission=Mission
-if Mission:IsENGAGED()then
-Mission:AbortUnit(PlayerUnit)
-end
-end
-end
-)
-self:HandleEvent(EVENTS.Crash,
-function(self,EventData)
-local PlayerUnit=EventData.IniUnit
-for MissionID,Mission in pairs(self:GetMissions())do
-local Mission=Mission
-if Mission:IsENGAGED()then
-Mission:CrashUnit(PlayerUnit)
-end
-end
-end
-)
-self:SetMenu()
-_SETTINGS:SetSystemMenu(CommandCenterPositionable)
-self:SetCommandMenu()
-return self
-end
-function COMMANDCENTER:GetName()
-return self.CommandCenterName
-end
-function COMMANDCENTER:GetText()
-return"Command Center ["..self.CommandCenterName.."]"
-end
-function COMMANDCENTER:GetShortText()
-return"CC ["..self.CommandCenterName.."]"
-end
-function COMMANDCENTER:GetCoalition()
-return self.CommandCenterCoalition
-end
-function COMMANDCENTER:GetPositionable()
-return self.CommandCenterPositionable
-end
-function COMMANDCENTER:GetMissions()
-return self.Missions or{}
-end
-function COMMANDCENTER:AddMission(Mission)
-self.Missions[Mission]=Mission
-return Mission
-end
-function COMMANDCENTER:RemoveMission(Mission)
-self.Missions[Mission]=nil
-return Mission
-end
-function COMMANDCENTER:SetReferenceZones(ReferenceZonePrefix)
-local MatchPattern="(.*)#(.*)"
-self:F({MatchPattern=MatchPattern})
-for ReferenceZoneName in pairs(_DATABASE.ZONENAMES)do
-local ZoneName,ReferenceName=string.match(ReferenceZoneName,MatchPattern)
-self:F({ZoneName=ZoneName,ReferenceName=ReferenceName})
-if ZoneName and ReferenceName and ZoneName==ReferenceZonePrefix then
-self.ReferencePoints[ReferenceZoneName]=ZONE:New(ReferenceZoneName)
-self.ReferenceNames[ReferenceZoneName]=ReferenceName
-end
-end
-return self
-end
-function COMMANDCENTER:SetModeWWII()
-self.CommunicationMode="WWII"
-return self
-end
-function COMMANDCENTER:IsModeWWII()
-return self.CommunicationMode=="WWII"
-end
-function COMMANDCENTER:SetMenu()
-self:F2()
-local MenuTime=timer.getTime()
-for MissionID,Mission in pairs(self:GetMissions()or{})do
-local Mission=Mission
-Mission:SetMenu(MenuTime)
-end
-for MissionID,Mission in pairs(self:GetMissions()or{})do
-Mission=Mission
-Mission:RemoveMenu(MenuTime)
-end
-end
-function COMMANDCENTER:GetMenu(TaskGroup)
-local MenuTime=timer.getTime()
-self.CommandCenterMenus=self.CommandCenterMenus or{}
-local CommandCenterMenu
-local CommandCenterText=self:GetText()
-CommandCenterMenu=MENU_GROUP:New(TaskGroup,CommandCenterText):SetTime(MenuTime)
-self.CommandCenterMenus[TaskGroup]=CommandCenterMenu
-if self.AutoAssignTasks==false then
-local AssignTaskMenu=MENU_GROUP_COMMAND:New(TaskGroup,"Assign Task",CommandCenterMenu,self.AssignTask,self,TaskGroup):SetTime(MenuTime):SetTag("AutoTask")
-end
-CommandCenterMenu:Remove(MenuTime,"AutoTask")
-return self.CommandCenterMenus[TaskGroup]
-end
-function COMMANDCENTER:AssignTask(TaskGroup)
-local Tasks={}
-local AssignPriority=99999999
-local AutoAssignMethod=self.AutoAssignMethod
-for MissionID,Mission in pairs(self:GetMissions())do
-local Mission=Mission
-local MissionTasks=Mission:GetGroupTasks(TaskGroup)
-for MissionTaskName,MissionTask in pairs(MissionTasks or{})do
-local MissionTask=MissionTask
-if MissionTask:IsStatePlanned()or MissionTask:IsStateReplanned()or MissionTask:IsStateAssigned()then
-local TaskPriority=MissionTask:GetAutoAssignPriority(self.AutoAssignMethod,self,TaskGroup)
-if TaskPriority Adding TASK ",MissionName=self:GetName(),TaskName=TaskName})
-self.Tasks[TaskName]=Task
-self:GetCommandCenter():SetMenu()
-return Task
-end
-function MISSION:RemoveTask(Task)
-local TaskName=Task:GetTaskName()
-self:T({"<== Removing TASK ",MissionName=self:GetName(),TaskName=TaskName})
-self:F(TaskName)
-self.Tasks[TaskName]=self.Tasks[TaskName]or{n=0}
-self.Tasks[TaskName]=nil
-Task=nil
-collectgarbage()
-self:GetCommandCenter():SetMenu()
-return nil
-end
-function MISSION:IsCOMPLETED()
-return self:Is("COMPLETED")
-end
-function MISSION:IsIDLE()
-return self:Is("IDLE")
-end
-function MISSION:IsENGAGED()
-return self:Is("ENGAGED")
-end
-function MISSION:IsFAILED()
-return self:Is("FAILED")
-end
-function MISSION:IsHOLD()
-return self:Is("HOLD")
-end
-function MISSION:HasGroup(TaskGroup)
-local Has=false
-for TaskID,Task in pairs(self:GetTasks())do
-local Task=Task
-if Task:HasGroup(TaskGroup)then
-Has=true
-break
-end
-end
-return Has
-end
-function MISSION:GetTasksRemaining()
-local TasksRemaining=0
-for TaskID,Task in pairs(self:GetTasks())do
-local Task=Task
-if Task:IsStateSuccess()or Task:IsStateFailed()then
-else
-TasksRemaining=TasksRemaining+1
-end
-end
-return TasksRemaining
-end
-function MISSION:GetTaskTypes()
-local TaskTypeList={}
-local TasksRemaining=0
-for TaskID,Task in pairs(self:GetTasks())do
-local Task=Task
-local TaskType=Task:GetType()
-TaskTypeList[TaskType]=TaskType
-end
-return TaskTypeList
-end
-function MISSION:AddPlayerName(PlayerName)
-self.PlayerNames=self.PlayerNames or{}
-self.PlayerNames[PlayerName]=PlayerName
-return self
-end
-function MISSION:GetPlayerNames()
-return self.PlayerNames
-end
-function MISSION:ReportBriefing()
-local Report=REPORT:New()
-local Name=self:GetText()
-local Status="<"..self:GetState()..">"
-Report:Add(string.format('%s - %s - Mission Briefing Report',Name,Status))
-Report:Add(self.MissionBriefing)
-return Report:Text()
-end
-function MISSION:ReportPlayersPerTask(ReportGroup)
-local Report=REPORT:New()
-local Name=self:GetText()
-local Status="<"..self:GetState()..">"
-Report:Add(string.format('%s - %s - Players per Task Report',Name,Status))
-local PlayerList={}
-for TaskID,Task in pairs(self:GetTasks())do
-local Task=Task
-local PlayerNames=Task:GetPlayerNames()
-for PlayerName,PlayerGroup in pairs(PlayerNames)do
-PlayerList[PlayerName]=Task:GetName()
-end
-end
-for PlayerName,TaskName in pairs(PlayerList)do
-Report:Add(string.format(' - Player (%s): Task "%s"',PlayerName,TaskName))
-end
-return Report:Text()
-end
-function MISSION:ReportPlayersProgress(ReportGroup)
-local Report=REPORT:New()
-local Name=self:GetText()
-local Status="<"..self:GetState()..">"
-Report:Add(string.format('%s - %s - Players per Task Progress Report',Name,Status))
-local PlayerList={}
-for TaskID,Task in pairs(self:GetTasks())do
-local Task=Task
-local TaskName=Task:GetName()
-local Goal=Task:GetGoal()
-PlayerList[TaskName]=PlayerList[TaskName]or{}
-if Goal then
-local TotalContributions=Goal:GetTotalContributions()
-local PlayerContributions=Goal:GetPlayerContributions()
-self:F({TotalContributions=TotalContributions,PlayerContributions=PlayerContributions})
-for PlayerName,PlayerContribution in pairs(PlayerContributions)do
-PlayerList[TaskName][PlayerName]=string.format('Player (%s): Task "%s": %d%%',PlayerName,TaskName,PlayerContributions[PlayerName]*100/TotalContributions)
-end
-else
-PlayerList[TaskName]["_"]=string.format('Player (---): Task "%s": %d%%',TaskName,0)
-end
-end
-for TaskName,TaskData in pairs(PlayerList)do
-for PlayerName,TaskText in pairs(TaskData)do
-Report:Add(string.format(' - %s',TaskText))
-end
-end
-return Report:Text()
-end
-function MISSION:MarkTargetLocations(ReportGroup)
-local Report=REPORT:New()
-local Name=self:GetText()
-local Status="<"..self:GetState()..">"
-Report:Add(string.format('%s - %s - All Tasks are marked on the map. Select a Task from the Mission Menu and Join the Task!!!',Name,Status))
-for TaskID,Task in UTILS.spairs(self:GetTasks(),function(t,a,b)return t[a]:ReportOrder(ReportGroup)"
-Report:Add(string.format('%s - %s - Task Overview Report',Name,Status))
-for TaskID,Task in UTILS.spairs(self:GetTasks(),function(t,a,b)return t[a]:ReportOrder(ReportGroup)"
-Report:Add(string.format('%s - %s - %s Tasks Report',Name,Status,TaskStatus))
-local Tasks=0
-for TaskID,Task in UTILS.spairs(self:GetTasks(),function(t,a,b)return t[a]:ReportOrder(ReportGroup)=8 then
-break
-end
-end
-return Report:Text()
-end
-function MISSION:ReportDetails(ReportGroup)
-local Report=REPORT:New()
-local Name=self:GetText()
-local Status="<"..self:GetState()..">"
-Report:Add(string.format('%s - %s - Task Detailed Report',Name,Status))
-local TasksRemaining=0
-for TaskID,Task in pairs(self:GetTasks())do
-local Task=Task
-Report:Add(string.rep("-",140))
-Report:Add(Task:ReportDetails(ReportGroup))
-end
-return Report:Text()
-end
-function MISSION:GetTasks()
-return self.Tasks or{}
-end
-function MISSION:GetGroupTasks(TaskGroup)
-local Tasks={}
-for TaskID,Task in pairs(self:GetTasks())do
-local Task=Task
-if Task:HasGroup(TaskGroup)then
-Tasks[#Tasks+1]=Task
-end
-end
-return Tasks
-end
-function MISSION:MenuReportBriefing(ReportGroup)
-local Report=self:ReportBriefing()
-self:GetCommandCenter():MessageTypeToGroup(Report,ReportGroup,MESSAGE.Type.Briefing)
-end
-function MISSION:MenuMarkTargetLocations(ReportGroup)
-local Report=self:MarkTargetLocations(ReportGroup)
-self:GetCommandCenter():MessageTypeToGroup(Report,ReportGroup,MESSAGE.Type.Overview)
-end
-function MISSION:MenuReportTasksSummary(ReportGroup)
-local Report=self:ReportSummary(ReportGroup)
-self:GetCommandCenter():MessageTypeToGroup(Report,ReportGroup,MESSAGE.Type.Overview)
-end
-function MISSION:MenuReportTasksPerStatus(ReportGroup,TaskStatus)
-local Report=self:ReportOverview(ReportGroup,TaskStatus)
-self:GetCommandCenter():MessageTypeToGroup(Report,ReportGroup,MESSAGE.Type.Overview)
-end
-function MISSION:MenuReportPlayersPerTask(ReportGroup)
-local Report=self:ReportPlayersPerTask()
-self:GetCommandCenter():MessageTypeToGroup(Report,ReportGroup,MESSAGE.Type.Overview)
-end
-function MISSION:MenuReportPlayersProgress(ReportGroup)
-local Report=self:ReportPlayersProgress()
-self:GetCommandCenter():MessageTypeToGroup(Report,ReportGroup,MESSAGE.Type.Overview)
-end
-TASK={
-ClassName="TASK",
-TaskScheduler=nil,
-ProcessClasses={},
-Processes={},
-Players=nil,
-Scores={},
-Menu={},
-SetGroup=nil,
-FsmTemplate=nil,
-Mission=nil,
-CommandCenter=nil,
-TimeOut=0,
-AssignedGroups={},
-}
-function TASK:New(Mission,SetGroupAssign,TaskName,TaskType,TaskBriefing)
-local self=BASE:Inherit(self,FSM_TASK:New(TaskName))
-self:SetStartState("Planned")
-self:AddTransition("Planned","Assign","Assigned")
-self:AddTransition("Assigned","AssignUnit","Assigned")
-self:AddTransition("Assigned","Success","Success")
-self:AddTransition("Assigned","Hold","Hold")
-self:AddTransition("Assigned","Fail","Failed")
-self:AddTransition({"Planned","Assigned"},"Abort","Aborted")
-self:AddTransition("Assigned","Cancel","Cancelled")
-self:AddTransition("Assigned","Goal","*")
-self.Fsm={}
-local Fsm=self:GetUnitProcess()
-Fsm:SetStartState("Planned")
-Fsm:AddProcess("Planned","Accept",ACT_ASSIGN_ACCEPT:New(self.TaskBriefing),{Assigned="Assigned",Rejected="Reject"})
-Fsm:AddTransition("Assigned","Assigned","*")
-self:AddTransition("*","PlayerCrashed","*")
-self:AddTransition("*","PlayerAborted","*")
-self:AddTransition("*","PlayerRejected","*")
-self:AddTransition("*","PlayerDead","*")
-self:AddTransition({"Failed","Aborted","Cancelled"},"Replan","Planned")
-self:AddTransition("*","TimeOut","Cancelled")
-self:F("New TASK "..TaskName)
-self.Processes={}
-self.Mission=Mission
-self.CommandCenter=Mission:GetCommandCenter()
-self.SetGroup=SetGroupAssign
-self:SetType(TaskType)
-self:SetName(TaskName)
-self:SetID(Mission:GetNextTaskID(self))
-self:SetBriefing(TaskBriefing)
-self.TaskInfo=TASKINFO:New(self)
-self.TaskProgress={}
-return self
-end
-function TASK:GetUnitProcess(TaskUnit)
-if TaskUnit then
-return self:GetStateMachine(TaskUnit)
-else
-self.FsmTemplate=self.FsmTemplate or FSM_PROCESS:New()
-return self.FsmTemplate
-end
-end
-function TASK:SetUnitProcess(FsmTemplate)
-self.FsmTemplate=FsmTemplate
-end
-function TASK:JoinUnit(PlayerUnit,PlayerGroup)
-self:F({PlayerUnit=PlayerUnit,PlayerGroup=PlayerGroup})
-local PlayerUnitAdded=false
-local PlayerGroups=self:GetGroups()
-if PlayerGroups:IsIncludeObject(PlayerGroup)then
-if self:IsStatePlanned()or self:IsStateReplanned()then
-end
-if self:IsStateAssigned()then
-local IsGroupAssigned=self:IsGroupAssigned(PlayerGroup)
-self:F({IsGroupAssigned=IsGroupAssigned})
-if IsGroupAssigned then
-self:AssignToUnit(PlayerUnit)
-self:MessageToGroups(PlayerUnit:GetPlayerName().." joined Task "..self:GetName())
-end
-end
-end
-return PlayerUnitAdded
-end
-function TASK:RejectGroup(PlayerGroup)
-local PlayerGroups=self:GetGroups()
-if PlayerGroups:IsIncludeObject(PlayerGroup)then
-if self:IsStatePlanned()then
-local IsGroupAssigned=self:IsGroupAssigned(PlayerGroup)
-if IsGroupAssigned then
-local PlayerName=PlayerGroup:GetUnit(1):GetPlayerName()
-self:GetMission():GetCommandCenter():MessageToGroup("Task "..self:GetName().." has been rejected! We will select another task.",PlayerGroup)
-self:UnAssignFromGroup(PlayerGroup)
-self:PlayerRejected(PlayerGroup:GetUnit(1))
-end
-end
-end
-return self
-end
-function TASK:AbortGroup(PlayerGroup)
-local PlayerGroups=self:GetGroups()
-if PlayerGroups:IsIncludeObject(PlayerGroup)then
-if self:IsStateAssigned()then
-local IsGroupAssigned=self:IsGroupAssigned(PlayerGroup)
-if IsGroupAssigned then
-local PlayerName=PlayerGroup:GetUnit(1):GetPlayerName()
-self:UnAssignFromGroup(PlayerGroup)
-PlayerGroups:Flush(self)
-local IsRemaining=false
-for GroupName,AssignedGroup in pairs(PlayerGroups:GetSet()or{})do
-if self:IsGroupAssigned(AssignedGroup)==true then
-IsRemaining=true
-self:F({Task=self:GetName(),IsRemaining=IsRemaining})
-break
-end
-end
-self:F({Task=self:GetName(),IsRemaining=IsRemaining})
-if IsRemaining==false then
-self:Abort()
-end
-self:PlayerAborted(PlayerGroup:GetUnit(1))
-end
-end
-end
-return self
-end
-function TASK:CrashGroup(PlayerGroup)
-self:F({PlayerGroup=PlayerGroup})
-local PlayerGroups=self:GetGroups()
-if PlayerGroups:IsIncludeObject(PlayerGroup)then
-if self:IsStateAssigned()then
-local IsGroupAssigned=self:IsGroupAssigned(PlayerGroup)
-self:F({IsGroupAssigned=IsGroupAssigned})
-if IsGroupAssigned then
-local PlayerName=PlayerGroup:GetUnit(1):GetPlayerName()
-self:MessageToGroups(PlayerName.." crashed! ")
-self:UnAssignFromGroup(PlayerGroup)
-PlayerGroups:Flush(self)
-local IsRemaining=false
-for GroupName,AssignedGroup in pairs(PlayerGroups:GetSet()or{})do
-if self:IsGroupAssigned(AssignedGroup)==true then
-IsRemaining=true
-self:F({Task=self:GetName(),IsRemaining=IsRemaining})
-break
-end
-end
-self:F({Task=self:GetName(),IsRemaining=IsRemaining})
-if IsRemaining==false then
-self:Abort()
-end
-self:PlayerCrashed(PlayerGroup:GetUnit(1))
-end
-end
-end
-return self
-end
-function TASK:GetMission()
-return self.Mission
-end
-function TASK:GetGroups()
-return self.SetGroup
-end
-function TASK:AddGroups(GroupSet)
-GroupSet=GroupSet or SET_GROUP:New()
-self.SetGroup:ForEachGroup(
-function(GroupItem)
-GroupSet:Add(GroupItem:GetName(),GroupItem)
-end
-)
-return GroupSet
-end
-do
-function TASK:IsGroupAssigned(TaskGroup)
-local TaskGroupName=TaskGroup:GetName()
-if self.AssignedGroups[TaskGroupName]then
-return true
-end
-return false
-end
-function TASK:SetGroupAssigned(TaskGroup)
-local TaskName=self:GetName()
-local TaskGroupName=TaskGroup:GetName()
-self.AssignedGroups[TaskGroupName]=TaskGroup
-self:F(string.format("Task %s is assigned to %s",TaskName,TaskGroupName))
-self:GetMission():SetGroupAssigned(TaskGroup)
-local SetAssignedGroups=self:GetGroups()
-return self
-end
-function TASK:ClearGroupAssignment(TaskGroup)
-local TaskName=self:GetName()
-local TaskGroupName=TaskGroup:GetName()
-self.AssignedGroups[TaskGroupName]=nil
-self:GetMission():ClearGroupAssignment(TaskGroup)
-local SetAssignedGroups=self:GetGroups()
-SetAssignedGroups:ForEachGroup(
-function(AssignedGroup)
-if self:IsGroupAssigned(AssignedGroup)then
-else
-end
-end
-)
-return self
-end
-end
-do
-function TASK:SetAssignMethod(AcceptClass)
-local ProcessTemplate=self:GetUnitProcess()
-ProcessTemplate:SetProcess("Planned","Accept",AcceptClass)
-end
-function TASK:AssignToGroup(TaskGroup)
-self:F(TaskGroup:GetName())
-local TaskGroupName=TaskGroup:GetName()
-local Mission=self:GetMission()
-local CommandCenter=Mission:GetCommandCenter()
-self:SetGroupAssigned(TaskGroup)
-local TaskUnits=TaskGroup:GetUnits()
-for UnitID,UnitData in pairs(TaskUnits)do
-local TaskUnit=UnitData
-local PlayerName=TaskUnit:GetPlayerName()
-self:F(PlayerName)
-if PlayerName~=nil and PlayerName~=""then
-self:AssignToUnit(TaskUnit)
-CommandCenter:MessageToGroup(
-string.format('Task "%s": Briefing for player (%s):\n%s',
-self:GetName(),
-PlayerName,
-self:GetBriefing()
-),TaskGroup
-)
-end
-end
-CommandCenter:SetMenu()
-self:MenuFlashTaskStatus(TaskGroup,self:GetMission():GetCommandCenter().FlashStatus)
-return self
-end
-function TASK:UnAssignFromGroup(TaskGroup)
-self:F2({TaskGroup=TaskGroup:GetName()})
-self:ClearGroupAssignment(TaskGroup)
-local TaskUnits=TaskGroup:GetUnits()
-for UnitID,UnitData in pairs(TaskUnits)do
-local TaskUnit=UnitData
-local PlayerName=TaskUnit:GetPlayerName()
-if PlayerName~=nil and PlayerName~=""then
-self:UnAssignFromUnit(TaskUnit)
-end
-end
-local Mission=self:GetMission()
-local CommandCenter=Mission:GetCommandCenter()
-CommandCenter:SetMenu()
-self:MenuFlashTaskStatus(TaskGroup,false)
-end
-end
-function TASK:HasGroup(FindGroup)
-local SetAttackGroup=self:GetGroups()
-return SetAttackGroup:FindGroup(FindGroup:GetName())
-end
-function TASK:AssignToUnit(TaskUnit)
-self:F(TaskUnit:GetName())
-local FsmTemplate=self:GetUnitProcess()
-local FsmUnit=self:SetStateMachine(TaskUnit,FsmTemplate:Copy(TaskUnit,self))
-FsmUnit:SetStartState("Planned")
-FsmUnit:Accept()
-return self
-end
-function TASK:UnAssignFromUnit(TaskUnit)
-self:F(TaskUnit:GetName())
-self:RemoveStateMachine(TaskUnit)
-self:RemoveTaskControlMenu(TaskUnit)
-return self
-end
-function TASK:SetTimeOut(Timer)
-self:F(Timer)
-self.TimeOut=Timer
-self:__TimeOut(self.TimeOut)
-return self
-end
-function TASK:MessageToGroups(Message)
-self:F({Message=Message})
-local Mission=self:GetMission()
-local CC=Mission:GetCommandCenter()
-for TaskGroupName,TaskGroup in pairs(self.SetGroup:GetSet())do
-TaskGroup=TaskGroup
-if TaskGroup:IsAlive()==true then
-CC:MessageToGroup(Message,TaskGroup,TaskGroup:GetName())
-end
-end
-end
-function TASK:SendBriefingToAssignedGroups()
-self:F2()
-for TaskGroupName,TaskGroup in pairs(self.SetGroup:GetSet())do
-if TaskGroup:IsAlive()then
-if self:IsGroupAssigned(TaskGroup)then
-TaskGroup:Message(self.TaskBriefing,60)
-end
-end
-end
-end
-function TASK:UnAssignFromGroups()
-self:F2()
-for TaskGroupName,TaskGroup in pairs(self.SetGroup:GetSet())do
-if TaskGroup:IsAlive()==true then
-if self:IsGroupAssigned(TaskGroup)then
-self:UnAssignFromGroup(TaskGroup)
-end
-end
-end
-end
-function TASK:HasAliveUnits()
-self:F()
-for TaskGroupID,TaskGroup in pairs(self.SetGroup:GetSet())do
-if TaskGroup:IsAlive()==true then
-if self:IsStateAssigned()then
-if self:IsGroupAssigned(TaskGroup)then
-for TaskUnitID,TaskUnit in pairs(TaskGroup:GetUnits())do
-if TaskUnit:IsAlive()then
-self:T({HasAliveUnits=true})
-return true
-end
-end
-end
-end
-end
-end
-self:T({HasAliveUnits=false})
-return false
-end
-function TASK:SetMenu(MenuTime)
-self:F({self:GetName(),MenuTime})
-for TaskGroupID,TaskGroupData in pairs(self.SetGroup:GetSet())do
-local TaskGroup=TaskGroupData
-if TaskGroup:IsAlive()==true and TaskGroup:GetPlayerNames()then
-local Mission=self:GetMission()
-local MissionMenu=Mission:GetMenu(TaskGroup)
-if MissionMenu then
-self:SetMenuForGroup(TaskGroup,MenuTime)
-end
-end
-end
-end
-function TASK:SetMenuForGroup(TaskGroup,MenuTime)
-if self:IsStatePlanned()or self:IsStateAssigned()then
-self:SetPlannedMenuForGroup(TaskGroup,MenuTime)
-if self:IsGroupAssigned(TaskGroup)then
-self:SetAssignedMenuForGroup(TaskGroup,MenuTime)
-end
-end
-end
-function TASK:SetPlannedMenuForGroup(TaskGroup,MenuTime)
-self:F(TaskGroup:GetName())
-local Mission=self:GetMission()
-local MissionName=Mission:GetName()
-local MissionMenu=Mission:GetMenu(TaskGroup)
-local TaskType=self:GetType()
-local TaskPlayerCount=self:GetPlayerCount()
-local TaskPlayerString=string.format(" (%dp)",TaskPlayerCount)
-local TaskText=string.format("%s",self:GetName())
-local TaskName=string.format("%s",self:GetName())
-self.MenuPlanned=self.MenuPlanned or{}
-self.MenuPlanned[TaskGroup]=MENU_GROUP_DELAYED:New(TaskGroup,"Join Planned Task",MissionMenu,Mission.MenuReportTasksPerStatus,Mission,TaskGroup,"Planned"):SetTime(MenuTime):SetTag("Tasking")
-local TaskTypeMenu=MENU_GROUP_DELAYED:New(TaskGroup,TaskType,self.MenuPlanned[TaskGroup]):SetTime(MenuTime):SetTag("Tasking")
-local TaskTypeMenu=MENU_GROUP_DELAYED:New(TaskGroup,TaskText,TaskTypeMenu):SetTime(MenuTime):SetTag("Tasking")
-if not Mission:IsGroupAssigned(TaskGroup)then
-local JoinTaskMenu=MENU_GROUP_COMMAND_DELAYED:New(TaskGroup,string.format("Join Task"),TaskTypeMenu,self.MenuAssignToGroup,self,TaskGroup):SetTime(MenuTime):SetTag("Tasking")
-local MarkTaskMenu=MENU_GROUP_COMMAND_DELAYED:New(TaskGroup,string.format("Mark Task Location on Map"),TaskTypeMenu,self.MenuMarkToGroup,self,TaskGroup):SetTime(MenuTime):SetTag("Tasking")
-end
-local ReportTaskMenu=MENU_GROUP_COMMAND_DELAYED:New(TaskGroup,string.format("Report Task Details"),TaskTypeMenu,self.MenuTaskStatus,self,TaskGroup):SetTime(MenuTime):SetTag("Tasking")
-return self
-end
-function TASK:SetAssignedMenuForGroup(TaskGroup,MenuTime)
-self:F({TaskGroup:GetName(),MenuTime})
-local TaskType=self:GetType()
-local TaskPlayerCount=self:GetPlayerCount()
-local TaskPlayerString=string.format(" (%dp)",TaskPlayerCount)
-local TaskText=string.format("%s%s",self:GetName(),TaskPlayerString)
-local TaskName=string.format("%s",self:GetName())
-for UnitName,TaskUnit in pairs(TaskGroup:GetPlayerUnits())do
-local TaskUnit=TaskUnit
-if TaskUnit then
-local MenuControl=self:GetTaskControlMenu(TaskUnit)
-local TaskControl=MENU_GROUP:New(TaskGroup,"Control Task",MenuControl):SetTime(MenuTime):SetTag("Tasking")
-if self:IsStateAssigned()then
-local TaskMenu=MENU_GROUP_COMMAND:New(TaskGroup,string.format("Abort Task"),TaskControl,self.MenuTaskAbort,self,TaskGroup):SetTime(MenuTime):SetTag("Tasking")
-end
-local MarkMenu=MENU_GROUP_COMMAND:New(TaskGroup,string.format("Mark Task Location on Map"),TaskControl,self.MenuMarkToGroup,self,TaskGroup):SetTime(MenuTime):SetTag("Tasking")
-local TaskTypeMenu=MENU_GROUP_COMMAND:New(TaskGroup,string.format("Report Task Details"),TaskControl,self.MenuTaskStatus,self,TaskGroup):SetTime(MenuTime):SetTag("Tasking")
-if not self.FlashTaskStatus then
-local TaskFlashStatusMenu=MENU_GROUP_COMMAND:New(TaskGroup,string.format("Flash Task Details"),TaskControl,self.MenuFlashTaskStatus,self,TaskGroup,true):SetTime(MenuTime):SetTag("Tasking")
-else
-local TaskFlashStatusMenu=MENU_GROUP_COMMAND:New(TaskGroup,string.format("Stop Flash Task Details"),TaskControl,self.MenuFlashTaskStatus,self,TaskGroup,nil):SetTime(MenuTime):SetTag("Tasking")
-end
-end
-end
-return self
-end
-function TASK:RemoveMenu(MenuTime)
-self:F({self:GetName(),MenuTime})
-for TaskGroupID,TaskGroup in pairs(self.SetGroup:GetSet())do
-if TaskGroup:IsAlive()==true then
-local TaskGroup=TaskGroup
-if TaskGroup:IsAlive()==true and TaskGroup:GetPlayerNames()then
-self:RefreshMenus(TaskGroup,MenuTime)
-end
-end
-end
-end
-function TASK:RefreshMenus(TaskGroup,MenuTime)
-self:F({TaskGroup:GetName(),MenuTime})
-local Mission=self:GetMission()
-local MissionName=Mission:GetName()
-local MissionMenu=Mission:GetMenu(TaskGroup)
-local TaskName=self:GetName()
-self.MenuPlanned=self.MenuPlanned or{}
-local PlannedMenu=self.MenuPlanned[TaskGroup]
-self.MenuAssigned=self.MenuAssigned or{}
-local AssignedMenu=self.MenuAssigned[TaskGroup]
-if PlannedMenu then
-self.MenuPlanned[TaskGroup]=PlannedMenu:Remove(MenuTime,"Tasking")
-PlannedMenu:Set()
-end
-if AssignedMenu then
-self.MenuAssigned[TaskGroup]=AssignedMenu:Remove(MenuTime,"Tasking")
-AssignedMenu:Set()
-end
-end
-function TASK:RemoveAssignedMenuForGroup(TaskGroup)
-self:F()
-local Mission=self:GetMission()
-local MissionName=Mission:GetName()
-local MissionMenu=Mission:GetMenu(TaskGroup)
-if MissionMenu then
-MissionMenu:RemoveSubMenus()
-end
-end
-function TASK:MenuAssignToGroup(TaskGroup)
-self:F("Join Task menu selected")
-self:AssignToGroup(TaskGroup)
-end
-function TASK:MenuMarkToGroup(TaskGroup)
-self:F()
-self:UpdateTaskInfo(self.DetectedItem)
-local TargetCoordinates=self.TaskInfo:GetData("Coordinates")
-if TargetCoordinates then
-for TargetCoordinateID,TargetCoordinate in pairs(TargetCoordinates)do
-local Report=REPORT:New():SetIndent(0)
-self.TaskInfo:Report(Report,"M",TaskGroup,self)
-local MarkText=Report:Text(", ")
-self:F({Coordinate=TargetCoordinate,MarkText=MarkText})
-TargetCoordinate:MarkToGroup(MarkText,TaskGroup)
-end
-else
-local TargetCoordinate=self.TaskInfo:GetData("Coordinate")
-if TargetCoordinate then
-local Report=REPORT:New():SetIndent(0)
-self.TaskInfo:Report(Report,"M",TaskGroup,self)
-local MarkText=Report:Text(", ")
-self:F({Coordinate=TargetCoordinate,MarkText=MarkText})
-TargetCoordinate:MarkToGroup(MarkText,TaskGroup)
-end
-end
-end
-function TASK:MenuTaskStatus(TaskGroup)
-if TaskGroup:IsAlive()then
-local ReportText=self:ReportDetails(TaskGroup)
-self:T(ReportText)
-self:GetMission():GetCommandCenter():MessageTypeToGroup(ReportText,TaskGroup,MESSAGE.Type.Detailed)
-end
-end
-function TASK:MenuFlashTaskStatus(TaskGroup,Flash)
-self.FlashTaskStatus=Flash
-if self.FlashTaskStatus then
-self.FlashTaskScheduler,self.FlashTaskScheduleID=SCHEDULER:New(self,self.MenuTaskStatus,{TaskGroup},0,60)
-else
-if self.FlashTaskScheduler then
-self.FlashTaskScheduler:Stop(self.FlashTaskScheduleID)
-self.FlashTaskScheduler=nil
-self.FlashTaskScheduleID=nil
-end
-end
-end
-function TASK:MenuTaskAbort(TaskGroup)
-self:AbortGroup(TaskGroup)
-end
-function TASK:GetTaskName()
-return self.TaskName
-end
-function TASK:GetTaskBriefing()
-return self.TaskBriefing
-end
-function TASK:GetProcessTemplate(ProcessName)
-local ProcessTemplate=self.ProcessClasses[ProcessName]
-return ProcessTemplate
-end
-function TASK:FailProcesses(TaskUnitName)
-for ProcessID,ProcessData in pairs(self.Processes[TaskUnitName])do
-local Process=ProcessData
-Process.Fsm:Fail()
-end
-end
-function TASK:SetStateMachine(TaskUnit,Fsm)
-self:F2({TaskUnit,self.Fsm[TaskUnit]~=nil,Fsm:GetClassNameAndID()})
-self.Fsm[TaskUnit]=Fsm
-return Fsm
-end
-function TASK:GetStateMachine(TaskUnit)
-self:F2({TaskUnit,self.Fsm[TaskUnit]~=nil})
-return self.Fsm[TaskUnit]
-end
-function TASK:RemoveStateMachine(TaskUnit)
-self:F({TaskUnit=TaskUnit:GetName(),HasFsm=(self.Fsm[TaskUnit]~=nil)})
-if self.Fsm[TaskUnit]then
-self.Fsm[TaskUnit]:Remove()
-self.Fsm[TaskUnit]=nil
-end
-collectgarbage()
-self:F("Garbage Collected, Processes should be finalized now ...")
-end
-function TASK:HasStateMachine(TaskUnit)
-self:F({TaskUnit,self.Fsm[TaskUnit]~=nil})
-return(self.Fsm[TaskUnit]~=nil)
-end
-function TASK:GetScoring()
-return self.Mission:GetScoring()
-end
-function TASK:GetTaskIndex()
-local TaskType=self:GetType()
-local TaskName=self:GetName()
-return TaskType.."."..TaskName
-end
-function TASK:SetName(TaskName)
-self.TaskName=TaskName
-end
-function TASK:GetName()
-return self.TaskName
-end
-function TASK:SetType(TaskType)
-self.TaskType=TaskType
-end
-function TASK:GetType()
-return self.TaskType
-end
-function TASK:SetID(TaskID)
-self.TaskID=TaskID
-end
-function TASK:GetID()
-return self.TaskID
-end
-function TASK:StateSuccess()
-self:SetState(self,"State","Success")
-return self
-end
-function TASK:IsStateSuccess()
-return self:Is("Success")
-end
-function TASK:StateFailed()
-self:SetState(self,"State","Failed")
-return self
-end
-function TASK:IsStateFailed()
-return self:Is("Failed")
-end
-function TASK:StatePlanned()
-self:SetState(self,"State","Planned")
-return self
-end
-function TASK:IsStatePlanned()
-return self:Is("Planned")
-end
-function TASK:StateAborted()
-self:SetState(self,"State","Aborted")
-return self
-end
-function TASK:IsStateAborted()
-return self:Is("Aborted")
-end
-function TASK:StateCancelled()
-self:SetState(self,"State","Cancelled")
-return self
-end
-function TASK:IsStateCancelled()
-return self:Is("Cancelled")
-end
-function TASK:StateAssigned()
-self:SetState(self,"State","Assigned")
-return self
-end
-function TASK:IsStateAssigned()
-return self:Is("Assigned")
-end
-function TASK:StateHold()
-self:SetState(self,"State","Hold")
-return self
-end
-function TASK:IsStateHold()
-return self:Is("Hold")
-end
-function TASK:StateReplanned()
-self:SetState(self,"State","Replanned")
-return self
-end
-function TASK:IsStateReplanned()
-return self:Is("Replanned")
-end
-function TASK:GetStateString()
-return self:GetState(self,"State")
-end
-function TASK:SetBriefing(TaskBriefing)
-self:F(TaskBriefing)
-self.TaskBriefing=TaskBriefing
-return self
-end
-function TASK:GetBriefing()
-return self.TaskBriefing
-end
-function TASK:onenterAssigned(From,Event,To,PlayerUnit,PlayerName)
-if From~="Assigned"then
-local PlayerNames=self:GetPlayerNames()
-local PlayerText=REPORT:New()
-for PlayerName,TaskName in pairs(PlayerNames)do
-PlayerText:Add(PlayerName)
-end
-self:GetMission():GetCommandCenter():MessageToCoalition("Task "..self:GetName().." is assigned to players "..PlayerText:Text(",")..". Good Luck!")
-self:SetGoalTotal()
-if self.Dispatcher then
-self:F("Firing Assign event ")
-self.Dispatcher:Assign(self,PlayerUnit,PlayerName)
-end
-self:GetMission():__Start(1)
-self:__Goal(-10,PlayerUnit,PlayerName)
-self:SetMenu()
-self:F({"--> Task Assigned",TaskName=self:GetName(),Mission=self:GetMission():GetName()})
-self:F({"--> Task Player Names",PlayerNames=PlayerNames})
-end
-end
-function TASK:onenterSuccess(From,Event,To)
-self:F({"<-> Task Replanned",TaskName=self:GetName(),Mission=self:GetMission():GetName()})
-self:F({"<-> Task Player Names",PlayerNames=self:GetPlayerNames()})
-self:GetMission():GetCommandCenter():MessageToCoalition("Task "..self:GetName().." is successful! Good job!")
-self:UnAssignFromGroups()
-self:GetMission():__MissionGoals(1)
-end
-function TASK:onenterAborted(From,Event,To)
-self:F({"<-- Task Aborted",TaskName=self:GetName(),Mission=self:GetMission():GetName()})
-self:F({"<-- Task Player Names",PlayerNames=self:GetPlayerNames()})
-if From~="Aborted"then
-self:GetMission():GetCommandCenter():MessageToCoalition("Task "..self:GetName().." has been aborted! Task may be replanned.")
-self:__Replan(5)
-self:SetMenu()
-end
-end
-function TASK:onenterCancelled(From,Event,To)
-self:F({"<-- Task Cancelled",TaskName=self:GetName(),Mission=self:GetMission():GetName()})
-self:F({"<-- Player Names",PlayerNames=self:GetPlayerNames()})
-if From~="Cancelled"then
-self:GetMission():GetCommandCenter():MessageToCoalition("Task "..self:GetName().." has been cancelled! The tactical situation has changed.")
-self:UnAssignFromGroups()
-self:SetMenu()
-end
-end
-function TASK:onafterReplan(From,Event,To)
-self:F({"Task Replanned",TaskName=self:GetName(),Mission=self:GetMission():GetName()})
-self:F({"Task Player Names",PlayerNames=self:GetPlayerNames()})
-self:GetMission():GetCommandCenter():MessageToCoalition("Replanning Task "..self:GetName()..".")
-self:SetMenu()
-end
-function TASK:onenterFailed(From,Event,To)
-self:F({"Task Failed",TaskName=self:GetName(),Mission=self:GetMission():GetName()})
-self:F({"Task Player Names",PlayerNames=self:GetPlayerNames()})
-self:GetMission():GetCommandCenter():MessageToCoalition("Task "..self:GetName().." has failed!")
-self:UnAssignFromGroups()
-end
-function TASK:onstatechange(From,Event,To)
-if self:IsTrace()then
-end
-if self.Scores[To]then
-local Scoring=self:GetScoring()
-if Scoring then
-self:F({self.Scores[To].ScoreText,self.Scores[To].Score})
-Scoring:_AddMissionScore(self.Mission,self.Scores[To].ScoreText,self.Scores[To].Score)
-end
-end
-end
-function TASK:onenterPlanned(From,Event,To)
-if not self.TimeOut==0 then
-self.__TimeOut(self.TimeOut)
-end
-end
-function TASK:onbeforeTimeOut(From,Event,To)
-if From=="Planned"then
-self:RemoveMenu()
-return true
-end
-return false
-end
-do
-function TASK:SetGoal(Goal)
-self.Goal=Goal
-end
-function TASK:GetGoal()
-return self.Goal
-end
-function TASK:SetDispatcher(Dispatcher)
-self.Dispatcher=Dispatcher
-end
-function TASK:SetDetection(Detection,DetectedItem)
-self:F({DetectedItem,Detection})
-self.Detection=Detection
-self.DetectedItem=DetectedItem
-end
-end
-do
-function TASK:ReportSummary(ReportGroup)
-self:UpdateTaskInfo(self.DetectedItem)
-local Report=REPORT:New()
-Report:Add("Task "..self:GetName())
-Report:Add("State: <"..self:GetState()..">")
-self.TaskInfo:Report(Report,"S",ReportGroup,self)
-return Report:Text(', ')
-end
-function TASK:ReportOverview(ReportGroup)
-self:UpdateTaskInfo(self.DetectedItem)
-local TaskName=self:GetName()
-local Report=REPORT:New()
-self.TaskInfo:Report(Report,"O",ReportGroup,self)
-return Report:Text()
-end
-function TASK:GetPlayerCount()
-local PlayerCount=0
-for TaskGroupID,PlayerGroup in pairs(self:GetGroups():GetSet())do
-local PlayerGroup=PlayerGroup
-if PlayerGroup:IsAlive()==true then
-if self:IsGroupAssigned(PlayerGroup)then
-local PlayerNames=PlayerGroup:GetPlayerNames()
-PlayerCount=PlayerCount+((PlayerNames)and#PlayerNames or 0)
-end
-end
-end
-return PlayerCount
-end
-function TASK:GetPlayerNames()
-local PlayerNameMap={}
-for TaskGroupID,PlayerGroup in pairs(self:GetGroups():GetSet())do
-local PlayerGroup=PlayerGroup
-if PlayerGroup:IsAlive()==true then
-if self:IsGroupAssigned(PlayerGroup)then
-local PlayerNames=PlayerGroup:GetPlayerNames()
-for PlayerNameID,PlayerName in pairs(PlayerNames or{})do
-PlayerNameMap[PlayerName]=PlayerGroup
-end
-end
-end
-end
-return PlayerNameMap
-end
-function TASK:ReportDetails(ReportGroup)
-self:UpdateTaskInfo(self.DetectedItem)
-local Report=REPORT:New():SetIndent(3)
-local Name=self:GetName()
-local Status="<"..self:GetState()..">"
-Report:Add("Task "..Name.." - "..Status.." - Detailed Report")
-local PlayerNames=self:GetPlayerNames()
-local PlayerReport=REPORT:New()
-for PlayerName,PlayerGroup in pairs(PlayerNames)do
-PlayerReport:Add("Players group "..PlayerGroup:GetCallsign()..": "..PlayerName)
-end
-local Players=PlayerReport:Text()
-if Players~=""then
-Report:AddIndent("Players assigned:","-")
-Report:AddIndent(Players)
-end
-self.TaskInfo:Report(Report,"D",ReportGroup,self)
-return Report:Text()
-end
-end
-do
-function TASK:AddProgress(PlayerName,ProgressText,ProgressTime,ProgressPoints)
-self.TaskProgress=self.TaskProgress or{}
-self.TaskProgress[ProgressTime]=self.TaskProgress[ProgressTime]or{}
-self.TaskProgress[ProgressTime].PlayerName=PlayerName
-self.TaskProgress[ProgressTime].ProgressText=ProgressText
-self.TaskProgress[ProgressTime].ProgressPoints=ProgressPoints
-self:GetMission():AddPlayerName(PlayerName)
-return self
-end
-function TASK:GetPlayerProgress(PlayerName)
-local ProgressPlayer=0
-for ProgressTime,ProgressData in pairs(self.TaskProgress)do
-if PlayerName==ProgressData.PlayerName then
-ProgressPlayer=ProgressPlayer+ProgressData.ProgressPoints
-end
-end
-return ProgressPlayer
-end
-function TASK:SetScoreOnProgress(PlayerName,Score,TaskUnit)
-self:F({PlayerName,Score,TaskUnit})
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-ProcessUnit:AddScoreProcess("Engaging","Account","AccountPlayer","Player "..PlayerName.." has achieved progress.",Score)
-return self
-end
-function TASK:SetScoreOnSuccess(PlayerName,Score,TaskUnit)
-self:F({PlayerName,Score,TaskUnit})
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-ProcessUnit:AddScore("Success","The task is a success!",Score)
-return self
-end
-function TASK:SetScoreOnFail(PlayerName,Penalty,TaskUnit)
-self:F({PlayerName,Penalty,TaskUnit})
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-ProcessUnit:AddScore("Failed","The task is a failure!",Penalty)
-return self
-end
-end
-do
-function TASK:InitTaskControlMenu(TaskUnit)
-self.TaskControlMenuTime=timer.getTime()
-return self.TaskControlMenuTime
-end
-function TASK:GetTaskControlMenu(TaskUnit,TaskName)
-TaskName=TaskName or""
-local TaskGroup=TaskUnit:GetGroup()
-local TaskPlayerCount=TaskGroup:GetPlayerCount()
-if TaskPlayerCount<=1 then
-self.TaskControlMenu=MENU_GROUP:New(TaskUnit:GetGroup(),"Task "..self:GetName().." control"):SetTime(self.TaskControlMenuTime)
-else
-self.TaskControlMenu=MENU_GROUP:New(TaskUnit:GetGroup(),"Task "..self:GetName().." control for "..TaskUnit:GetPlayerName()):SetTime(self.TaskControlMenuTime)
-end
-return self.TaskControlMenu
-end
-function TASK:RemoveTaskControlMenu(TaskUnit)
-if self.TaskControlMenu then
-self.TaskControlMenu:Remove()
-self.TaskControlMenu=nil
-end
-end
-function TASK:RefreshTaskControlMenu(TaskUnit,MenuTime,MenuTag)
-if self.TaskControlMenu then
-self.TaskControlMenu:Remove(MenuTime,MenuTag)
-end
-end
-end
-TASKINFO={
-ClassName="TASKINFO",
-}
-TASKINFO.Detail=""
-function TASKINFO:New(Task)
-local self=BASE:Inherit(self,BASE:New())
-self.Task=Task
-self.VolatileInfo=SET_BASE:New()
-self.PersistentInfo=SET_BASE:New()
-self.Info=self.VolatileInfo
-return self
-end
-function TASKINFO:AddInfo(Key,Data,Order,Detail,Keep,ShowKey,Type)
-self.VolatileInfo:Add(Key,{Data=Data,Order=Order,Detail=Detail,ShowKey=ShowKey,Type=Type})
-if Keep==true then
-self.PersistentInfo:Add(Key,{Data=Data,Order=Order,Detail=Detail,ShowKey=ShowKey,Type=Type})
-end
-return self
-end
-function TASKINFO:GetInfo(Key)
-local Object=self:Get(Key)
-return Object.Data,Object.Order,Object.Detail
-end
-function TASKINFO:GetData(Key)
-local Object=self.Info:Get(Key)
-return Object and Object.Data
-end
-function TASKINFO:AddText(Key,Text,Order,Detail,Keep)
-self:AddInfo(Key,Text,Order,Detail,Keep)
-return self
-end
-function TASKINFO:AddTaskName(Order,Detail,Keep)
-self:AddInfo("TaskName",self.Task:GetName(),Order,Detail,Keep)
-return self
-end
-function TASKINFO:AddCoordinate(Coordinate,Order,Detail,Keep,ShowKey,Name)
-self:AddInfo(Name or"Coordinate",Coordinate,Order,Detail,Keep,ShowKey,"Coordinate")
-return self
-end
-function TASKINFO:GetCoordinate(Name)
-return self:GetData(Name or"Coordinate")
-end
-function TASKINFO:AddCoordinates(Coordinates,Order,Detail,Keep)
-self:AddInfo("Coordinates",Coordinates,Order,Detail,Keep)
-return self
-end
-function TASKINFO:AddThreat(ThreatText,ThreatLevel,Order,Detail,Keep)
-self:AddInfo("Threat"," ["..string.rep("■",ThreatLevel)..string.rep("□",10-ThreatLevel).."]:"..ThreatText,Order,Detail,Keep)
-return self
-end
-function TASKINFO:GetThreat()
-self:GetInfo("Threat")
-return self
-end
-function TASKINFO:AddTargetCount(TargetCount,Order,Detail,Keep)
-self:AddInfo("Counting",string.format("%d",TargetCount),Order,Detail,Keep)
-return self
-end
-function TASKINFO:AddTargets(TargetCount,TargetTypes,Order,Detail,Keep)
-self:AddInfo("Targets",string.format("%d of %s",TargetCount,TargetTypes),Order,Detail,Keep)
-return self
-end
-function TASKINFO:GetTargets()
-self:GetInfo("Targets")
-return self
-end
-function TASKINFO:AddQFEAtCoordinate(Coordinate,Order,Detail,Keep)
-self:AddInfo("QFE",Coordinate,Order,Detail,Keep)
-return self
-end
-function TASKINFO:AddTemperatureAtCoordinate(Coordinate,Order,Detail,Keep)
-self:AddInfo("Temperature",Coordinate,Order,Detail,Keep)
-return self
-end
-function TASKINFO:AddWindAtCoordinate(Coordinate,Order,Detail,Keep)
-self:AddInfo("Wind",Coordinate,Order,Detail,Keep)
-return self
-end
-function TASKINFO:AddCargo(Cargo,Order,Detail,Keep)
-self:AddInfo("Cargo",Cargo,Order,Detail,Keep)
-return self
-end
-function TASKINFO:AddCargoSet(SetCargo,Order,Detail,Keep)
-local CargoReport=REPORT:New()
-CargoReport:Add("")
-SetCargo:ForEachCargo(
-function(Cargo)
-CargoReport:Add(string.format(' - %s (%s) %s - status %s ',Cargo:GetName(),Cargo:GetType(),Cargo:GetTransportationMethod(),Cargo:GetCurrentState()))
-end
-)
-self:AddInfo("Cargo",CargoReport:Text(),Order,Detail,Keep)
-return self
-end
-function TASKINFO:Report(Report,Detail,ReportGroup,Task)
-local Line=0
-local LineReport=REPORT:New()
-if not self.Task:IsStatePlanned()and not self.Task:IsStateAssigned()then
-self.Info=self.PersistentInfo
-end
-for Key,Data in UTILS.spairs(self.Info.Set,function(t,a,b)return t[a].Order0 then
-local TargetSetUnit=SET_UNIT:New()
-TargetSetUnit:SetDatabase(DetectedSet)
-TargetSetUnit:FilterHasSEAD()
-TargetSetUnit:FilterOnce()
-return TargetSetUnit
-end
-return nil
-end
-function TASK_A2G_DISPATCHER:EvaluateCAS(DetectedItem)
-self:F({DetectedItem.ItemID})
-local DetectedSet=DetectedItem.Set
-local DetectedZone=DetectedItem.Zone
-local GroundUnitCount=DetectedSet:HasGroundUnits()
-local FriendliesNearBy=self.Detection:IsFriendliesNearBy(DetectedItem,Unit.Category.GROUND_UNIT)
-local RadarCount=DetectedSet:HasSEAD()
-if RadarCount==0 and GroundUnitCount>0 and FriendliesNearBy==true then
-local TargetSetUnit=SET_UNIT:New()
-TargetSetUnit:SetDatabase(DetectedSet)
-TargetSetUnit:FilterOnce()
-return TargetSetUnit
-end
-return nil
-end
-function TASK_A2G_DISPATCHER:EvaluateBAI(DetectedItem,FriendlyCoalition)
-self:F({DetectedItem.ItemID})
-local DetectedSet=DetectedItem.Set
-local DetectedZone=DetectedItem.Zone
-local GroundUnitCount=DetectedSet:HasGroundUnits()
-local FriendliesNearBy=self.Detection:IsFriendliesNearBy(DetectedItem,Unit.Category.GROUND_UNIT)
-local RadarCount=DetectedSet:HasSEAD()
-if RadarCount==0 and GroundUnitCount>0 and FriendliesNearBy==false then
-local TargetSetUnit=SET_UNIT:New()
-TargetSetUnit:SetDatabase(DetectedSet)
-TargetSetUnit:FilterOnce()
-return TargetSetUnit
-end
-return nil
-end
-function TASK_A2G_DISPATCHER:RemoveTask(TaskIndex)
-self.Mission:RemoveTask(self.Tasks[TaskIndex])
-self.Tasks[TaskIndex]=nil
-end
-function TASK_A2G_DISPATCHER:EvaluateRemoveTask(Mission,Task,TaskIndex,DetectedItemChanged)
-if Task then
-if(Task:IsStatePlanned()and DetectedItemChanged==true)or Task:IsStateCancelled()then
-self:RemoveTask(TaskIndex)
-end
-end
-return Task
-end
-function TASK_A2G_DISPATCHER:ProcessDetected(Detection)
-self:F()
-local AreaMsg={}
-local TaskMsg={}
-local ChangeMsg={}
-local Mission=self.Mission
-if Mission:IsIDLE()or Mission:IsENGAGED()then
-local TaskReport=REPORT:New()
-for TaskIndex,TaskData in pairs(self.Tasks)do
-local Task=TaskData
-if Task:IsStatePlanned()then
-local DetectedItem=Detection:GetDetectedItemByIndex(TaskIndex)
-if not DetectedItem then
-local TaskText=Task:GetName()
-for TaskGroupID,TaskGroup in pairs(self.SetGroup:GetSet())do
-if self.FlashNewTask then
-Mission:GetCommandCenter():MessageToGroup(string.format("Obsolete A2G task %s for %s removed.",TaskText,Mission:GetShortText()),TaskGroup)
-end
-end
-Task=self:RemoveTask(TaskIndex)
-end
-end
-end
-for DetectedItemID,DetectedItem in pairs(Detection:GetDetectedItems())do
-local DetectedItem=DetectedItem
-local DetectedSet=DetectedItem.Set
-local DetectedZone=DetectedItem.Zone
-local DetectedItemID=DetectedItem.ID
-local TaskIndex=DetectedItem.Index
-local DetectedItemChanged=DetectedItem.Changed
-self:F({DetectedItemChanged=DetectedItemChanged,DetectedItemID=DetectedItemID,TaskIndex=TaskIndex})
-local Task=self.Tasks[TaskIndex]
-if Task then
-if Task:IsStateAssigned()then
-if DetectedItemChanged==true then
-local TargetsReport=REPORT:New()
-local TargetSetUnit=self:EvaluateSEAD(DetectedItem)
-if TargetSetUnit then
-if Task:IsInstanceOf(TASK_A2G_SEAD)then
-Task:SetTargetSetUnit(TargetSetUnit)
-Task:SetDetection(Detection,DetectedItem)
-Task:UpdateTaskInfo(DetectedItem)
-TargetsReport:Add(Detection:GetChangeText(DetectedItem))
-else
-Task:Cancel()
-end
-else
-local TargetSetUnit=self:EvaluateCAS(DetectedItem)
-if TargetSetUnit then
-if Task:IsInstanceOf(TASK_A2G_CAS)then
-Task:SetTargetSetUnit(TargetSetUnit)
-Task:SetDetection(Detection,DetectedItem)
-Task:UpdateTaskInfo(DetectedItem)
-TargetsReport:Add(Detection:GetChangeText(DetectedItem))
-else
-Task:Cancel()
-Task=self:RemoveTask(TaskIndex)
-end
-else
-local TargetSetUnit=self:EvaluateBAI(DetectedItem)
-if TargetSetUnit then
-if Task:IsInstanceOf(TASK_A2G_BAI)then
-Task:SetTargetSetUnit(TargetSetUnit)
-Task:SetDetection(Detection,DetectedItem)
-Task:UpdateTaskInfo(DetectedItem)
-TargetsReport:Add(Detection:GetChangeText(DetectedItem))
-else
-Task:Cancel()
-Task=self:RemoveTask(TaskIndex)
-end
-end
-end
-end
-for TaskGroupID,TaskGroup in pairs(self.SetGroup:GetSet())do
-local TargetsText=TargetsReport:Text(", ")
-if(Mission:IsGroupAssigned(TaskGroup))and TargetsText~=""and self.FlashNewTask then
-Mission:GetCommandCenter():MessageToGroup(string.format("Task %s has change of targets:\n %s",Task:GetName(),TargetsText),TaskGroup)
-end
-end
-end
-end
-end
-if Task then
-if Task:IsStatePlanned()then
-if DetectedItemChanged==true then
-if Task:IsInstanceOf(TASK_A2G_SEAD)then
-local TargetSetUnit=self:EvaluateSEAD(DetectedItem)
-if TargetSetUnit then
-Task:SetTargetSetUnit(TargetSetUnit)
-Task:SetDetection(Detection,DetectedItem)
-Task:UpdateTaskInfo(DetectedItem)
-else
-Task:Cancel()
-Task=self:RemoveTask(TaskIndex)
-end
-else
-if Task:IsInstanceOf(TASK_A2G_CAS)then
-local TargetSetUnit=self:EvaluateCAS(DetectedItem)
-if TargetSetUnit then
-Task:SetTargetSetUnit(TargetSetUnit)
-Task:SetDetection(Detection,DetectedItem)
-Task:UpdateTaskInfo(DetectedItem)
-else
-Task:Cancel()
-Task=self:RemoveTask(TaskIndex)
-end
-else
-if Task:IsInstanceOf(TASK_A2G_BAI)then
-local TargetSetUnit=self:EvaluateBAI(DetectedItem)
-if TargetSetUnit then
-Task:SetTargetSetUnit(TargetSetUnit)
-Task:SetDetection(Detection,DetectedItem)
-Task:UpdateTaskInfo(DetectedItem)
-else
-Task:Cancel()
-Task=self:RemoveTask(TaskIndex)
-end
-else
-Task:Cancel()
-Task=self:RemoveTask(TaskIndex)
-end
-end
-end
-end
-end
-end
-if not Task then
-local TargetSetUnit=self:EvaluateSEAD(DetectedItem)
-if TargetSetUnit then
-Task=TASK_A2G_SEAD:New(Mission,self.SetGroup,string.format("SEAD.%03d",DetectedItemID),TargetSetUnit)
-DetectedItem.DesignateMenuName=string.format("SEAD.%03d",DetectedItemID)
-Task:SetDetection(Detection,DetectedItem)
-end
-if not Task then
-local TargetSetUnit=self:EvaluateCAS(DetectedItem)
-if TargetSetUnit then
-Task=TASK_A2G_CAS:New(Mission,self.SetGroup,string.format("CAS.%03d",DetectedItemID),TargetSetUnit)
-DetectedItem.DesignateMenuName=string.format("CAS.%03d",DetectedItemID)
-Task:SetDetection(Detection,DetectedItem)
-end
-if not Task then
-local TargetSetUnit=self:EvaluateBAI(DetectedItem,self.Mission:GetCommandCenter():GetPositionable():GetCoalition())
-if TargetSetUnit then
-Task=TASK_A2G_BAI:New(Mission,self.SetGroup,string.format("BAI.%03d",DetectedItemID),TargetSetUnit)
-DetectedItem.DesignateMenuName=string.format("BAI.%03d",DetectedItemID)
-Task:SetDetection(Detection,DetectedItem)
-end
-end
-end
-if Task then
-self.Tasks[TaskIndex]=Task
-Task:SetTargetZone(DetectedZone)
-Task:SetDispatcher(self)
-Task:UpdateTaskInfo(DetectedItem)
-Mission:AddTask(Task)
-function Task.OnEnterSuccess(Task,From,Event,To)
-self:Success(Task)
-end
-function Task.OnEnterCancelled(Task,From,Event,To)
-self:Cancelled(Task)
-end
-function Task.OnEnterFailed(Task,From,Event,To)
-self:Failed(Task)
-end
-function Task.OnEnterAborted(Task,From,Event,To)
-self:Aborted(Task)
-end
-TaskReport:Add(Task:GetName())
-else
-self:F("This should not happen")
-end
-end
-Detection:AcceptChanges(DetectedItem)
-end
-Mission:GetCommandCenter():SetMenu()
-local TaskText=TaskReport:Text(", ")
-for TaskGroupID,TaskGroup in pairs(self.SetGroup:GetSet())do
-if(not Mission:IsGroupAssigned(TaskGroup))and TaskText~=""and self.FlashNewTask then
-Mission:GetCommandCenter():MessageToGroup(string.format("%s has tasks %s. Subscribe to a task using the radio menu.",Mission:GetShortText(),TaskText),TaskGroup)
-end
-end
-end
-return true
-end
-end
-do
-TASK_A2G={
-ClassName="TASK_A2G"
-}
-function TASK_A2G:New(Mission,SetGroup,TaskName,TargetSetUnit,TaskType,TaskBriefing)
-local self=BASE:Inherit(self,TASK:New(Mission,SetGroup,TaskName,TaskType,TaskBriefing))
-self:F()
-self.TargetSetUnit=TargetSetUnit
-self.TaskType=TaskType
-local Fsm=self:GetUnitProcess()
-Fsm:AddTransition("Assigned","RouteToRendezVous","RoutingToRendezVous")
-Fsm:AddProcess("RoutingToRendezVous","RouteToRendezVousPoint",ACT_ROUTE_POINT:New(),{Arrived="ArriveAtRendezVous"})
-Fsm:AddProcess("RoutingToRendezVous","RouteToRendezVousZone",ACT_ROUTE_ZONE:New(),{Arrived="ArriveAtRendezVous"})
-Fsm:AddTransition({"Arrived","RoutingToRendezVous"},"ArriveAtRendezVous","ArrivedAtRendezVous")
-Fsm:AddTransition({"ArrivedAtRendezVous","HoldingAtRendezVous"},"Engage","Engaging")
-Fsm:AddTransition({"ArrivedAtRendezVous","HoldingAtRendezVous"},"HoldAtRendezVous","HoldingAtRendezVous")
-Fsm:AddProcess("Engaging","Account",ACT_ACCOUNT_DEADS:New(),{})
-Fsm:AddTransition("Engaging","RouteToTarget","Engaging")
-Fsm:AddProcess("Engaging","RouteToTargetZone",ACT_ROUTE_ZONE:New(),{})
-Fsm:AddProcess("Engaging","RouteToTargetPoint",ACT_ROUTE_POINT:New(),{})
-Fsm:AddTransition("Engaging","RouteToTargets","Engaging")
-Fsm:AddTransition("Rejected","Reject","Aborted")
-Fsm:AddTransition("Failed","Fail","Failed")
-function Fsm:onafterAssigned(TaskUnit,Task)
-self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()})
-self:RouteToRendezVous()
-end
-function Fsm:onafterRouteToRendezVous(TaskUnit,Task)
-self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()})
-if Task:GetRendezVousZone(TaskUnit)then
-self:__RouteToRendezVousZone(0.1)
-else
-if Task:GetRendezVousCoordinate(TaskUnit)then
-self:__RouteToRendezVousPoint(0.1)
-else
-self:__ArriveAtRendezVous(0.1)
-end
-end
-end
-function Fsm:OnAfterArriveAtRendezVous(TaskUnit,Task)
-self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()})
-self:__Engage(0.1)
-end
-function Fsm:onafterEngage(TaskUnit,Task)
-self:F({self})
-self:__Account(0.1)
-self:__RouteToTarget(0.1)
-self:__RouteToTargets(-10)
-end
-function Fsm:onafterRouteToTarget(TaskUnit,Task)
-self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()})
-if Task:GetTargetZone(TaskUnit)then
-self:__RouteToTargetZone(0.1)
-else
-local TargetUnit=Task.TargetSetUnit:GetFirst()
-if TargetUnit then
-local Coordinate=TargetUnit:GetPointVec3()
-self:T({TargetCoordinate=Coordinate,Coordinate:GetX(),Coordinate:GetY(),Coordinate:GetZ()})
-Task:SetTargetCoordinate(Coordinate,TaskUnit)
-end
-self:__RouteToTargetPoint(0.1)
-end
-end
-function Fsm:onafterRouteToTargets(TaskUnit,Task)
-self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()})
-local TargetUnit=Task.TargetSetUnit:GetFirst()
-if TargetUnit then
-Task:SetTargetCoordinate(TargetUnit:GetCoordinate(),TaskUnit)
-end
-self:__RouteToTargets(-10)
-end
-return self
-end
-function TASK_A2G:SetTargetSetUnit(TargetSetUnit)
-self.TargetSetUnit=TargetSetUnit
-end
-function TASK_A2G:GetPlannedMenuText()
-return self:GetStateString().." - "..self:GetTaskName().." ( "..self.TargetSetUnit:GetUnitTypesText().." )"
-end
-function TASK_A2G:SetRendezVousCoordinate(RendezVousCoordinate,RendezVousRange,TaskUnit)
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-local ActRouteRendezVous=ProcessUnit:GetProcess("RoutingToRendezVous","RouteToRendezVousPoint")
-ActRouteRendezVous:SetCoordinate(RendezVousCoordinate)
-ActRouteRendezVous:SetRange(RendezVousRange)
-end
-function TASK_A2G:GetRendezVousCoordinate(TaskUnit)
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-local ActRouteRendezVous=ProcessUnit:GetProcess("RoutingToRendezVous","RouteToRendezVousPoint")
-return ActRouteRendezVous:GetCoordinate(),ActRouteRendezVous:GetRange()
-end
-function TASK_A2G:SetRendezVousZone(RendezVousZone,TaskUnit)
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-local ActRouteRendezVous=ProcessUnit:GetProcess("RoutingToRendezVous","RouteToRendezVousZone")
-ActRouteRendezVous:SetZone(RendezVousZone)
-end
-function TASK_A2G:GetRendezVousZone(TaskUnit)
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-local ActRouteRendezVous=ProcessUnit:GetProcess("RoutingToRendezVous","RouteToRendezVousZone")
-return ActRouteRendezVous:GetZone()
-end
-function TASK_A2G:SetTargetCoordinate(TargetCoordinate,TaskUnit)
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-local ActRouteTarget=ProcessUnit:GetProcess("Engaging","RouteToTargetPoint")
-ActRouteTarget:SetCoordinate(TargetCoordinate)
-end
-function TASK_A2G:GetTargetCoordinate(TaskUnit)
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-local ActRouteTarget=ProcessUnit:GetProcess("Engaging","RouteToTargetPoint")
-return ActRouteTarget:GetCoordinate()
-end
-function TASK_A2G:SetTargetZone(TargetZone,TaskUnit)
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-local ActRouteTarget=ProcessUnit:GetProcess("Engaging","RouteToTargetZone")
-ActRouteTarget:SetZone(TargetZone)
-end
-function TASK_A2G:GetTargetZone(TaskUnit)
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-local ActRouteTarget=ProcessUnit:GetProcess("Engaging","RouteToTargetZone")
-return ActRouteTarget:GetZone()
-end
-function TASK_A2G:SetGoalTotal()
-self.GoalTotal=self.TargetSetUnit:Count()
-end
-function TASK_A2G:GetGoalTotal()
-return self.GoalTotal
-end
-function TASK_A2G:ReportOrder(ReportGroup)
-self:UpdateTaskInfo(self.DetectedItem)
-local Coordinate=self.TaskInfo:GetData("Coordinate")
-local Distance=ReportGroup:GetCoordinate():Get2DDistance(Coordinate)
-return Distance
-end
-function TASK_A2G:onafterGoal(TaskUnit,From,Event,To)
-local TargetSetUnit=self.TargetSetUnit
-if TargetSetUnit:Count()==0 then
-self:Success()
-end
-self:__Goal(-10)
-end
-function TASK_A2G:UpdateTaskInfo(DetectedItem)
-if self:IsStatePlanned()or self:IsStateAssigned()then
-local TargetCoordinate=DetectedItem and self.Detection:GetDetectedItemCoordinate(DetectedItem)or self.TargetSetUnit:GetFirst():GetCoordinate()
-self.TaskInfo:AddTaskName(0,"MSOD")
-self.TaskInfo:AddCoordinate(TargetCoordinate,1,"SOD")
-local ThreatLevel,ThreatText
-if DetectedItem then
-ThreatLevel,ThreatText=self.Detection:GetDetectedItemThreatLevel(DetectedItem)
-else
-ThreatLevel,ThreatText=self.TargetSetUnit:CalculateThreatLevelA2G()
-end
-self.TaskInfo:AddThreat(ThreatText,ThreatLevel,10,"MOD",true)
-if self.Detection then
-local DetectedItemsCount=self.TargetSetUnit:Count()
-local ReportTypes=REPORT:New()
-local TargetTypes={}
-for TargetUnitName,TargetUnit in pairs(self.TargetSetUnit:GetSet())do
-local TargetType=self.Detection:GetDetectedUnitTypeName(TargetUnit)
-if not TargetTypes[TargetType]then
-TargetTypes[TargetType]=TargetType
-ReportTypes:Add(TargetType)
-end
-end
-self.TaskInfo:AddTargetCount(DetectedItemsCount,11,"O",true)
-self.TaskInfo:AddTargets(DetectedItemsCount,ReportTypes:Text(", "),20,"D",true)
-else
-local DetectedItemsCount=self.TargetSetUnit:Count()
-local DetectedItemsTypes=self.TargetSetUnit:GetTypeNames()
-self.TaskInfo:AddTargetCount(DetectedItemsCount,11,"O",true)
-self.TaskInfo:AddTargets(DetectedItemsCount,DetectedItemsTypes,20,"D",true)
-end
-self.TaskInfo:AddQFEAtCoordinate(TargetCoordinate,30,"MOD")
-self.TaskInfo:AddTemperatureAtCoordinate(TargetCoordinate,31,"MD")
-self.TaskInfo:AddWindAtCoordinate(TargetCoordinate,32,"MD")
-end
-end
-function TASK_A2G:GetAutoAssignPriority(AutoAssignMethod,CommandCenter,TaskGroup)
-if AutoAssignMethod==COMMANDCENTER.AutoAssignMethods.Random then
-return math.random(1,9)
-elseif AutoAssignMethod==COMMANDCENTER.AutoAssignMethods.Distance then
-local Coordinate=self.TaskInfo:GetData("Coordinate")
-local Distance=Coordinate:Get2DDistance(CommandCenter:GetPositionable():GetCoordinate())
-self:F({Distance=Distance})
-return math.floor(Distance)
-elseif AutoAssignMethod==COMMANDCENTER.AutoAssignMethods.Priority then
-return 1
-end
-return 0
-end
-end
-do
-TASK_A2G_SEAD={
-ClassName="TASK_A2G_SEAD"
-}
-function TASK_A2G_SEAD:New(Mission,SetGroup,TaskName,TargetSetUnit,TaskBriefing)
-local self=BASE:Inherit(self,TASK_A2G:New(Mission,SetGroup,TaskName,TargetSetUnit,"SEAD",TaskBriefing))
-self:F()
-Mission:AddTask(self)
-self:SetBriefing(TaskBriefing or"Execute a Suppression of Enemy Air Defenses.")
-return self
-end
-function TASK_A2G_SEAD:SetScoreOnProgress(PlayerName,Score,TaskUnit)
-self:F({PlayerName,Score,TaskUnit})
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-ProcessUnit:AddScoreProcess("Engaging","Account","AccountForPlayer","Player "..PlayerName.." has SEADed a target.",Score)
-return self
-end
-function TASK_A2G_SEAD:SetScoreOnSuccess(PlayerName,Score,TaskUnit)
-self:F({PlayerName,Score,TaskUnit})
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-ProcessUnit:AddScore("Success","All radar emitting targets have been successfully SEADed!",Score)
-return self
-end
-function TASK_A2G_SEAD:SetScoreOnFail(PlayerName,Penalty,TaskUnit)
-self:F({PlayerName,Penalty,TaskUnit})
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-ProcessUnit:AddScore("Failed","The SEADing has failed!",Penalty)
-return self
-end
-end
-do
-TASK_A2G_BAI={ClassName="TASK_A2G_BAI"}
-function TASK_A2G_BAI:New(Mission,SetGroup,TaskName,TargetSetUnit,TaskBriefing)
-local self=BASE:Inherit(self,TASK_A2G:New(Mission,SetGroup,TaskName,TargetSetUnit,"BAI",TaskBriefing))
-self:F()
-Mission:AddTask(self)
-self:SetBriefing(TaskBriefing or"Execute a Battlefield Air Interdiction of a group of enemy targets.")
-return self
-end
-function TASK_A2G_BAI:SetScoreOnProgress(PlayerName,Score,TaskUnit)
-self:F({PlayerName,Score,TaskUnit})
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-ProcessUnit:AddScoreProcess("Engaging","Account","AccountForPlayer","Player "..PlayerName.." has destroyed a target in Battlefield Air Interdiction (BAI).",Score)
-return self
-end
-function TASK_A2G_BAI:SetScoreOnSuccess(PlayerName,Score,TaskUnit)
-self:F({PlayerName,Score,TaskUnit})
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-ProcessUnit:AddScore("Success","All targets have been successfully destroyed! The Battlefield Air Interdiction (BAI) is a success!",Score)
-return self
-end
-function TASK_A2G_BAI:SetScoreOnFail(PlayerName,Penalty,TaskUnit)
-self:F({PlayerName,Penalty,TaskUnit})
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-ProcessUnit:AddScore("Failed","The Battlefield Air Interdiction (BAI) has failed!",Penalty)
-return self
-end
-end
-do
-TASK_A2G_CAS={ClassName="TASK_A2G_CAS"}
-function TASK_A2G_CAS:New(Mission,SetGroup,TaskName,TargetSetUnit,TaskBriefing)
-local self=BASE:Inherit(self,TASK_A2G:New(Mission,SetGroup,TaskName,TargetSetUnit,"CAS",TaskBriefing))
-self:F()
-Mission:AddTask(self)
-self:SetBriefing(TaskBriefing or("Execute a Close Air Support for a group of enemy targets. ".."Beware of friendlies at the vicinity! "))
-return self
-end
-function TASK_A2G_CAS:SetScoreOnProgress(PlayerName,Score,TaskUnit)
-self:F({PlayerName,Score,TaskUnit})
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-ProcessUnit:AddScoreProcess("Engaging","Account","AccountForPlayer","Player "..PlayerName.." has destroyed a target in Close Air Support (CAS).",Score)
-return self
-end
-function TASK_A2G_CAS:SetScoreOnSuccess(PlayerName,Score,TaskUnit)
-self:F({PlayerName,Score,TaskUnit})
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-ProcessUnit:AddScore("Success","All targets have been successfully destroyed! The Close Air Support (CAS) was a success!",Score)
-return self
-end
-function TASK_A2G_CAS:SetScoreOnFail(PlayerName,Penalty,TaskUnit)
-self:F({PlayerName,Penalty,TaskUnit})
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-ProcessUnit:AddScore("Failed","The Close Air Support (CAS) has failed!",Penalty)
-return self
-end
-end
-do
-TASK_A2A_DISPATCHER={
-ClassName="TASK_A2A_DISPATCHER",
-Mission=nil,
-Detection=nil,
-Tasks={},
-SweepZones={},
-}
-function TASK_A2A_DISPATCHER:New(Mission,SetGroup,Detection)
-local self=BASE:Inherit(self,DETECTION_MANAGER:New(SetGroup,Detection))
-self.Detection=Detection
-self.Mission=Mission
-self.FlashNewTask=false
-self.Detection:FilterCategories(Unit.Category.AIRPLANE,Unit.Category.HELICOPTER)
-self.Detection:InitDetectRadar(true)
-self.Detection:SetRefreshTimeInterval(30)
-self:AddTransition("Started","Assign","Started")
-self:__Start(5)
-return self
-end
-function TASK_A2A_DISPATCHER:SetEngageRadius(EngageRadius)
-self.Detection:SetFriendliesRange(EngageRadius or 100000)
-return self
-end
-function TASK_A2A_DISPATCHER:SetSendMessages(onoff)
-self.FlashNewTask=onoff
-end
-function TASK_A2A_DISPATCHER:EvaluateINTERCEPT(DetectedItem)
-self:F({DetectedItem.ItemID})
-local DetectedSet=DetectedItem.Set
-local DetectedZone=DetectedItem.Zone
-if DetectedItem.IsDetected==true then
-local TargetSetUnit=SET_UNIT:New()
-TargetSetUnit:SetDatabase(DetectedSet)
-TargetSetUnit:FilterOnce()
-return TargetSetUnit
-end
-return nil
-end
-function TASK_A2A_DISPATCHER:EvaluateSWEEP(DetectedItem)
-self:F({DetectedItem.ItemID})
-local DetectedSet=DetectedItem.Set
-local DetectedZone=DetectedItem.Zone
-if DetectedItem.IsDetected==false then
-local TargetSetUnit=SET_UNIT:New()
-TargetSetUnit:SetDatabase(DetectedSet)
-TargetSetUnit:FilterOnce()
-return TargetSetUnit
-end
-return nil
-end
-function TASK_A2A_DISPATCHER:EvaluateENGAGE(DetectedItem)
-self:F({DetectedItem.ItemID})
-local DetectedSet=DetectedItem.Set
-local DetectedZone=DetectedItem.Zone
-local PlayersCount,PlayersReport=self:GetPlayerFriendliesNearBy(DetectedItem)
-if PlayersCount>0 and DetectedItem.IsDetected==true then
-local TargetSetUnit=SET_UNIT:New()
-TargetSetUnit:SetDatabase(DetectedSet)
-TargetSetUnit:FilterOnce()
-return TargetSetUnit
-end
-return nil
-end
-function TASK_A2A_DISPATCHER:EvaluateRemoveTask(Mission,Task,Detection,DetectedItem,DetectedItemIndex,DetectedItemChanged)
-if Task then
-if Task:IsStatePlanned()then
-local TaskName=Task:GetName()
-local TaskType=TaskName:match("(%u+)%.%d+")
-self:T2({TaskType=TaskType})
-local Remove=false
-local IsPlayers=Detection:IsPlayersNearBy(DetectedItem)
-if TaskType=="ENGAGE"then
-if IsPlayers==false then
-Remove=true
-end
-end
-if TaskType=="INTERCEPT"then
-if IsPlayers==true then
-Remove=true
-end
-if DetectedItem.IsDetected==false then
-Remove=true
-end
-end
-if TaskType=="SWEEP"then
-if DetectedItem.IsDetected==true then
-Remove=true
-end
-end
-local DetectedSet=DetectedItem.Set
-if DetectedSet:Count()==0 then
-Remove=true
-end
-if DetectedItemChanged==true or Remove then
-Task=self:RemoveTask(DetectedItemIndex)
-end
-end
-end
-return Task
-end
-function TASK_A2A_DISPATCHER:GetFriendliesNearBy(DetectedItem)
-local DetectedSet=DetectedItem.Set
-local FriendlyUnitsNearBy=self.Detection:GetFriendliesNearBy(DetectedItem,Unit.Category.AIRPLANE)
-local FriendlyTypes={}
-local FriendliesCount=0
-if FriendlyUnitsNearBy then
-local DetectedTreatLevel=DetectedSet:CalculateThreatLevelA2G()
-for FriendlyUnitName,FriendlyUnitData in pairs(FriendlyUnitsNearBy)do
-local FriendlyUnit=FriendlyUnitData
-if FriendlyUnit:IsAirPlane()then
-local FriendlyUnitThreatLevel=FriendlyUnit:GetThreatLevel()
-FriendliesCount=FriendliesCount+1
-local FriendlyType=FriendlyUnit:GetTypeName()
-FriendlyTypes[FriendlyType]=FriendlyTypes[FriendlyType]and(FriendlyTypes[FriendlyType]+1)or 1
-if DetectedTreatLevel0 then
-for FriendlyType,FriendlyTypeCount in pairs(FriendlyTypes)do
-FriendlyTypesReport:Add(string.format("%d of %s",FriendlyTypeCount,FriendlyType))
-end
-else
-FriendlyTypesReport:Add("-")
-end
-return FriendliesCount,FriendlyTypesReport
-end
-function TASK_A2A_DISPATCHER:GetPlayerFriendliesNearBy(DetectedItem)
-local DetectedSet=DetectedItem.Set
-local PlayersNearBy=self.Detection:GetPlayersNearBy(DetectedItem)
-local PlayerTypes={}
-local PlayersCount=0
-if PlayersNearBy then
-local DetectedTreatLevel=DetectedSet:CalculateThreatLevelA2G()
-for PlayerUnitName,PlayerUnitData in pairs(PlayersNearBy)do
-local PlayerUnit=PlayerUnitData
-local PlayerName=PlayerUnit:GetPlayerName()
-if PlayerUnit:IsAirPlane()and PlayerName~=nil then
-local FriendlyUnitThreatLevel=PlayerUnit:GetThreatLevel()
-PlayersCount=PlayersCount+1
-local PlayerType=PlayerUnit:GetTypeName()
-PlayerTypes[PlayerName]=PlayerType
-if DetectedTreatLevel0 then
-for PlayerName,PlayerType in pairs(PlayerTypes)do
-PlayerTypesReport:Add(string.format('"%s" in %s',PlayerName,PlayerType))
-end
-else
-PlayerTypesReport:Add("-")
-end
-return PlayersCount,PlayerTypesReport
-end
-function TASK_A2A_DISPATCHER:RemoveTask(TaskIndex)
-self.Mission:RemoveTask(self.Tasks[TaskIndex])
-self.Tasks[TaskIndex]=nil
-end
-function TASK_A2A_DISPATCHER:ProcessDetected(Detection)
-self:F()
-local AreaMsg={}
-local TaskMsg={}
-local ChangeMsg={}
-local Mission=self.Mission
-if Mission:IsIDLE()or Mission:IsENGAGED()then
-local TaskReport=REPORT:New()
-for TaskIndex,TaskData in pairs(self.Tasks)do
-local Task=TaskData
-if Task:IsStatePlanned()then
-local DetectedItem=Detection:GetDetectedItemByIndex(TaskIndex)
-if not DetectedItem then
-local TaskText=Task:GetName()
-for TaskGroupID,TaskGroup in pairs(self.SetGroup:GetSet())do
-Mission:GetCommandCenter():MessageToGroup(string.format("Obsolete A2A task %s for %s removed.",TaskText,Mission:GetShortText()),TaskGroup)
-end
-Task=self:RemoveTask(TaskIndex)
-end
-end
-end
-for DetectedItemID,DetectedItem in pairs(Detection:GetDetectedItems())do
-local DetectedItem=DetectedItem
-local DetectedSet=DetectedItem.Set
-local DetectedCount=DetectedSet:Count()
-local DetectedZone=DetectedItem.Zone
-local DetectedID=DetectedItem.ID
-local TaskIndex=DetectedItem.Index
-local DetectedItemChanged=DetectedItem.Changed
-local Task=self.Tasks[TaskIndex]
-Task=self:EvaluateRemoveTask(Mission,Task,Detection,DetectedItem,TaskIndex,DetectedItemChanged)
-if not Task and DetectedCount>0 then
-local TargetSetUnit=self:EvaluateENGAGE(DetectedItem)
-if TargetSetUnit then
-Task=TASK_A2A_ENGAGE:New(Mission,self.SetGroup,string.format("ENGAGE.%03d",DetectedID),TargetSetUnit)
-Task:SetDetection(Detection,DetectedItem)
-Task:UpdateTaskInfo(DetectedItem)
-else
-local TargetSetUnit=self:EvaluateINTERCEPT(DetectedItem)
-if TargetSetUnit then
-Task=TASK_A2A_INTERCEPT:New(Mission,self.SetGroup,string.format("INTERCEPT.%03d",DetectedID),TargetSetUnit)
-Task:SetDetection(Detection,DetectedItem)
-Task:UpdateTaskInfo(DetectedItem)
-else
-local TargetSetUnit=self:EvaluateSWEEP(DetectedItem)
-if TargetSetUnit then
-Task=TASK_A2A_SWEEP:New(Mission,self.SetGroup,string.format("SWEEP.%03d",DetectedID),TargetSetUnit)
-Task:SetDetection(Detection,DetectedItem)
-Task:UpdateTaskInfo(DetectedItem)
-end
-end
-end
-if Task then
-self.Tasks[TaskIndex]=Task
-Task:SetTargetZone(DetectedZone,DetectedItem.Coordinate.y,DetectedItem.Coordinate.Heading)
-Task:SetDispatcher(self)
-Mission:AddTask(Task)
-function Task.OnEnterSuccess(Task,From,Event,To)
-self:Success(Task)
-end
-function Task.OnEnterCancelled(Task,From,Event,To)
-self:Cancelled(Task)
-end
-function Task.OnEnterFailed(Task,From,Event,To)
-self:Failed(Task)
-end
-function Task.OnEnterAborted(Task,From,Event,To)
-self:Aborted(Task)
-end
-TaskReport:Add(Task:GetName())
-else
-self:F("This should not happen")
-end
-end
-if Task then
-local FriendliesCount,FriendliesReport=self:GetFriendliesNearBy(DetectedItem,Unit.Category.AIRPLANE)
-Task.TaskInfo:AddText("Friendlies",string.format("%d ( %s )",FriendliesCount,FriendliesReport:Text(",")),40,"MOD")
-local PlayersCount,PlayersReport=self:GetPlayerFriendliesNearBy(DetectedItem)
-Task.TaskInfo:AddText("Players",string.format("%d ( %s )",PlayersCount,PlayersReport:Text(",")),40,"MOD")
-end
-Detection:AcceptChanges(DetectedItem)
-end
-Mission:GetCommandCenter():SetMenu()
-local TaskText=TaskReport:Text(", ")
-for TaskGroupID,TaskGroup in pairs(self.SetGroup:GetSet())do
-if(not Mission:IsGroupAssigned(TaskGroup))and TaskText~=""and(self.FlashNewTask)then
-Mission:GetCommandCenter():MessageToGroup(string.format("%s has tasks %s. Subscribe to a task using the radio menu.",Mission:GetShortText(),TaskText),TaskGroup)
-end
-end
-end
-return true
-end
-end
-do
-TASK_A2A={
-ClassName="TASK_A2A"
-}
-function TASK_A2A:New(Mission,SetAttack,TaskName,TargetSetUnit,TaskType,TaskBriefing)
-local self=BASE:Inherit(self,TASK:New(Mission,SetAttack,TaskName,TaskType,TaskBriefing))
-self:F()
-self.TargetSetUnit=TargetSetUnit
-self.TaskType=TaskType
-local Fsm=self:GetUnitProcess()
-Fsm:AddTransition("Assigned","RouteToRendezVous","RoutingToRendezVous")
-Fsm:AddProcess("RoutingToRendezVous","RouteToRendezVousPoint",ACT_ROUTE_POINT:New(),{Arrived="ArriveAtRendezVous"})
-Fsm:AddProcess("RoutingToRendezVous","RouteToRendezVousZone",ACT_ROUTE_ZONE:New(),{Arrived="ArriveAtRendezVous"})
-Fsm:AddTransition({"Arrived","RoutingToRendezVous"},"ArriveAtRendezVous","ArrivedAtRendezVous")
-Fsm:AddTransition({"ArrivedAtRendezVous","HoldingAtRendezVous"},"Engage","Engaging")
-Fsm:AddTransition({"ArrivedAtRendezVous","HoldingAtRendezVous"},"HoldAtRendezVous","HoldingAtRendezVous")
-Fsm:AddProcess("Engaging","Account",ACT_ACCOUNT_DEADS:New(),{})
-Fsm:AddTransition("Engaging","RouteToTarget","Engaging")
-Fsm:AddProcess("Engaging","RouteToTargetZone",ACT_ROUTE_ZONE:New(),{})
-Fsm:AddProcess("Engaging","RouteToTargetPoint",ACT_ROUTE_POINT:New(),{})
-Fsm:AddTransition("Engaging","RouteToTargets","Engaging")
-Fsm:AddTransition("Rejected","Reject","Aborted")
-Fsm:AddTransition("Failed","Fail","Failed")
-function Fsm:OnLeaveAssigned(TaskUnit,Task)
-self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()})
-self:SelectAction()
-end
-function Fsm:onafterRouteToRendezVous(TaskUnit,Task)
-self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()})
-if Task:GetRendezVousZone(TaskUnit)then
-self:__RouteToRendezVousZone(0.1)
-else
-if Task:GetRendezVousCoordinate(TaskUnit)then
-self:__RouteToRendezVousPoint(0.1)
-else
-self:__ArriveAtRendezVous(0.1)
-end
-end
-end
-function Fsm:OnAfterArriveAtRendezVous(TaskUnit,Task)
-self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()})
-self:__Engage(0.1)
-end
-function Fsm:onafterEngage(TaskUnit,Task)
-self:F({self})
-self:__Account(0.1)
-self:__RouteToTarget(0.1)
-self:__RouteToTargets(-10)
-end
-function Fsm:onafterRouteToTarget(TaskUnit,Task)
-self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()})
-if Task:GetTargetZone(TaskUnit)then
-self:__RouteToTargetZone(0.1)
-else
-local TargetUnit=Task.TargetSetUnit:GetFirst()
-if TargetUnit then
-local Coordinate=TargetUnit:GetPointVec3()
-self:T({TargetCoordinate=Coordinate,Coordinate:GetX(),Coordinate:GetAlt(),Coordinate:GetZ()})
-Task:SetTargetCoordinate(Coordinate,TaskUnit)
-end
-self:__RouteToTargetPoint(0.1)
-end
-end
-function Fsm:onafterRouteToTargets(TaskUnit,Task)
-self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()})
-local TargetUnit=Task.TargetSetUnit:GetFirst()
-if TargetUnit then
-Task:SetTargetCoordinate(TargetUnit:GetCoordinate(),TaskUnit)
-end
-self:__RouteToTargets(-10)
-end
-return self
-end
-function TASK_A2A:SetTargetSetUnit(TargetSetUnit)
-self.TargetSetUnit=TargetSetUnit
-end
-function TASK_A2A:GetPlannedMenuText()
-return self:GetStateString().." - "..self:GetTaskName().." ( "..self.TargetSetUnit:GetUnitTypesText().." )"
-end
-function TASK_A2A:SetRendezVousCoordinate(RendezVousCoordinate,RendezVousRange,TaskUnit)
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-local ActRouteRendezVous=ProcessUnit:GetProcess("RoutingToRendezVous","RouteToRendezVousPoint")
-ActRouteRendezVous:SetCoordinate(RendezVousCoordinate)
-ActRouteRendezVous:SetRange(RendezVousRange)
-end
-function TASK_A2A:GetRendezVousCoordinate(TaskUnit)
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-local ActRouteRendezVous=ProcessUnit:GetProcess("RoutingToRendezVous","RouteToRendezVousPoint")
-return ActRouteRendezVous:GetCoordinate(),ActRouteRendezVous:GetRange()
-end
-function TASK_A2A:SetRendezVousZone(RendezVousZone,TaskUnit)
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-local ActRouteRendezVous=ProcessUnit:GetProcess("RoutingToRendezVous","RouteToRendezVousZone")
-ActRouteRendezVous:SetZone(RendezVousZone)
-end
-function TASK_A2A:GetRendezVousZone(TaskUnit)
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-local ActRouteRendezVous=ProcessUnit:GetProcess("RoutingToRendezVous","RouteToRendezVousZone")
-return ActRouteRendezVous:GetZone()
-end
-function TASK_A2A:SetTargetCoordinate(TargetCoordinate,TaskUnit)
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-local ActRouteTarget=ProcessUnit:GetProcess("Engaging","RouteToTargetPoint")
-ActRouteTarget:SetCoordinate(TargetCoordinate)
-end
-function TASK_A2A:GetTargetCoordinate(TaskUnit)
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-local ActRouteTarget=ProcessUnit:GetProcess("Engaging","RouteToTargetPoint")
-return ActRouteTarget:GetCoordinate()
-end
-function TASK_A2A:SetTargetZone(TargetZone,Altitude,Heading,TaskUnit)
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-local ActRouteTarget=ProcessUnit:GetProcess("Engaging","RouteToTargetZone")
-ActRouteTarget:SetZone(TargetZone,Altitude,Heading)
-end
-function TASK_A2A:GetTargetZone(TaskUnit)
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-local ActRouteTarget=ProcessUnit:GetProcess("Engaging","RouteToTargetZone")
-return ActRouteTarget:GetZone()
-end
-function TASK_A2A:SetGoalTotal()
-self.GoalTotal=self.TargetSetUnit:Count()
-end
-function TASK_A2A:GetGoalTotal()
-return self.GoalTotal
-end
-function TASK_A2A:ReportOrder(ReportGroup)
-self:UpdateTaskInfo(self.DetectedItem)
-local Coordinate=self.TaskInfo:GetData("Coordinate")
-local Distance=ReportGroup:GetCoordinate():Get2DDistance(Coordinate)
-return Distance
-end
-function TASK_A2A:onafterGoal(TaskUnit,From,Event,To)
-local TargetSetUnit=self.TargetSetUnit
-if TargetSetUnit:Count()==0 then
-self:Success()
-end
-self:__Goal(-10)
-end
-function TASK_A2A:UpdateTaskInfo(DetectedItem)
-if self:IsStatePlanned()or self:IsStateAssigned()then
-local TargetCoordinate=DetectedItem and self.Detection:GetDetectedItemCoordinate(DetectedItem)or self.TargetSetUnit:GetFirst():GetCoordinate()
-self.TaskInfo:AddTaskName(0,"MSOD")
-self.TaskInfo:AddCoordinate(TargetCoordinate,1,"SOD")
-local ThreatLevel,ThreatText
-if DetectedItem then
-ThreatLevel,ThreatText=self.Detection:GetDetectedItemThreatLevel(DetectedItem)
-else
-ThreatLevel,ThreatText=self.TargetSetUnit:CalculateThreatLevelA2G()
-end
-self.TaskInfo:AddThreat(ThreatText,ThreatLevel,10,"MOD",true)
-if self.Detection then
-local DetectedItemsCount=self.TargetSetUnit:Count()
-local ReportTypes=REPORT:New()
-local TargetTypes={}
-for TargetUnitName,TargetUnit in pairs(self.TargetSetUnit:GetSet())do
-local TargetType=self.Detection:GetDetectedUnitTypeName(TargetUnit)
-if not TargetTypes[TargetType]then
-TargetTypes[TargetType]=TargetType
-ReportTypes:Add(TargetType)
-end
-end
-self.TaskInfo:AddTargetCount(DetectedItemsCount,11,"O",true)
-self.TaskInfo:AddTargets(DetectedItemsCount,ReportTypes:Text(", "),20,"D",true)
-else
-local DetectedItemsCount=self.TargetSetUnit:Count()
-local DetectedItemsTypes=self.TargetSetUnit:GetTypeNames()
-self.TaskInfo:AddTargetCount(DetectedItemsCount,11,"O",true)
-self.TaskInfo:AddTargets(DetectedItemsCount,DetectedItemsTypes,20,"D",true)
-end
-end
-end
-function TASK_A2A:GetAutoAssignPriority(AutoAssignMethod,CommandCenter,TaskGroup)
-if AutoAssignMethod==COMMANDCENTER.AutoAssignMethods.Random then
-return math.random(1,9)
-elseif AutoAssignMethod==COMMANDCENTER.AutoAssignMethods.Distance then
-local Coordinate=self.TaskInfo:GetData("Coordinate")
-local Distance=Coordinate:Get2DDistance(CommandCenter:GetPositionable():GetCoordinate())
-return math.floor(Distance)
-elseif AutoAssignMethod==COMMANDCENTER.AutoAssignMethods.Priority then
-return 1
-end
-return 0
-end
-end
-do
-TASK_A2A_INTERCEPT={
-ClassName="TASK_A2A_INTERCEPT"
-}
-function TASK_A2A_INTERCEPT:New(Mission,SetGroup,TaskName,TargetSetUnit,TaskBriefing)
-local self=BASE:Inherit(self,TASK_A2A:New(Mission,SetGroup,TaskName,TargetSetUnit,"INTERCEPT",TaskBriefing))
-self:F()
-Mission:AddTask(self)
-self:SetBriefing(TaskBriefing or"Intercept incoming intruders.\n")
-return self
-end
-function TASK_A2A_INTERCEPT:SetScoreOnProgress(PlayerName,Score,TaskUnit)
-self:F({PlayerName,Score,TaskUnit})
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-ProcessUnit:AddScoreProcess("Engaging","Account","AccountForPlayer","Player "..PlayerName.." has intercepted a target.",Score)
-return self
-end
-function TASK_A2A_INTERCEPT:SetScoreOnSuccess(PlayerName,Score,TaskUnit)
-self:F({PlayerName,Score,TaskUnit})
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-ProcessUnit:AddScore("Success","All targets have been successfully intercepted!",Score)
-return self
-end
-function TASK_A2A_INTERCEPT:SetScoreOnFail(PlayerName,Penalty,TaskUnit)
-self:F({PlayerName,Penalty,TaskUnit})
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-ProcessUnit:AddScore("Failed","The intercept has failed!",Penalty)
-return self
-end
-end
-do
-TASK_A2A_SWEEP={
-ClassName="TASK_A2A_SWEEP"
-}
-function TASK_A2A_SWEEP:New(Mission,SetGroup,TaskName,TargetSetUnit,TaskBriefing)
-local self=BASE:Inherit(self,TASK_A2A:New(Mission,SetGroup,TaskName,TargetSetUnit,"SWEEP",TaskBriefing))
-self:F()
-Mission:AddTask(self)
-self:SetBriefing(TaskBriefing or"Perform a fighter sweep. Incoming intruders were detected and could be hiding at the location.\n")
-return self
-end
-function TASK_A2A_SWEEP:onafterGoal(TaskUnit,From,Event,To)
-local TargetSetUnit=self.TargetSetUnit
-if TargetSetUnit:Count()==0 then
-self:Success()
-end
-self:__Goal(-10)
-end
-function TASK_A2A_SWEEP:SetScoreOnProgress(PlayerName,Score,TaskUnit)
-self:F({PlayerName,Score,TaskUnit})
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-ProcessUnit:AddScoreProcess("Engaging","Account","AccountForPlayer","Player "..PlayerName.." has sweeped a target.",Score)
-return self
-end
-function TASK_A2A_SWEEP:SetScoreOnSuccess(PlayerName,Score,TaskUnit)
-self:F({PlayerName,Score,TaskUnit})
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-ProcessUnit:AddScore("Success","All targets have been successfully sweeped!",Score)
-return self
-end
-function TASK_A2A_SWEEP:SetScoreOnFail(PlayerName,Penalty,TaskUnit)
-self:F({PlayerName,Penalty,TaskUnit})
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-ProcessUnit:AddScore("Failed","The sweep has failed!",Penalty)
-return self
-end
-end
-do
-TASK_A2A_ENGAGE={
-ClassName="TASK_A2A_ENGAGE"
-}
-function TASK_A2A_ENGAGE:New(Mission,SetGroup,TaskName,TargetSetUnit,TaskBriefing)
-local self=BASE:Inherit(self,TASK_A2A:New(Mission,SetGroup,TaskName,TargetSetUnit,"ENGAGE",TaskBriefing))
-self:F()
-Mission:AddTask(self)
-self:SetBriefing(TaskBriefing or"Bogeys are nearby! Players close by are ordered to ENGAGE the intruders!\n")
-return self
-end
-function TASK_A2A_ENGAGE:SetScoreOnProgress(PlayerName,Score,TaskUnit)
-self:F({PlayerName,Score,TaskUnit})
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-ProcessUnit:AddScoreProcess("Engaging","Account","AccountForPlayer","Player "..PlayerName.." has engaged and destroyed a target.",Score)
-return self
-end
-function TASK_A2A_ENGAGE:SetScoreOnSuccess(PlayerName,Score,TaskUnit)
-self:F({PlayerName,Score,TaskUnit})
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-ProcessUnit:AddScore("Success","All targets have been successfully engaged!",Score)
-return self
-end
-function TASK_A2A_ENGAGE:SetScoreOnFail(PlayerName,Penalty,TaskUnit)
-self:F({PlayerName,Penalty,TaskUnit})
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-ProcessUnit:AddScore("Failed","The target engagement has failed!",Penalty)
-return self
-end
-end
-do
-TASK_CARGO={
-ClassName="TASK_CARGO",
-}
-function TASK_CARGO:New(Mission,SetGroup,TaskName,SetCargo,TaskType,TaskBriefing)
-local self=BASE:Inherit(self,TASK:New(Mission,SetGroup,TaskName,TaskType,TaskBriefing))
-self:F({Mission,SetGroup,TaskName,SetCargo,TaskType})
-self.SetCargo=SetCargo
-self.TaskType=TaskType
-self.SmokeColor=SMOKECOLOR.Red
-self.CargoItemCount={}
-self.CargoLimit=10
-self.DeployZones={}
-self:AddTransition("*","CargoDeployed","*")
-self:AddTransition("*","CargoPickedUp","*")
-local Fsm=self:GetUnitProcess()
-Fsm:AddTransition({"Planned","Assigned","Cancelled","WaitingForCommand","ArrivedAtPickup","ArrivedAtDeploy","Boarded","UnBoarded","Loaded","UnLoaded","Landed","Boarding"},"SelectAction","*")
-Fsm:AddTransition("*","RouteToPickup","RoutingToPickup")
-Fsm:AddProcess("RoutingToPickup","RouteToPickupPoint",ACT_ROUTE_POINT:New(),{Arrived="ArriveAtPickup",Cancelled="CancelRouteToPickup"})
-Fsm:AddTransition("Arrived","ArriveAtPickup","ArrivedAtPickup")
-Fsm:AddTransition("Cancelled","CancelRouteToPickup","Cancelled")
-Fsm:AddTransition("*","RouteToDeploy","RoutingToDeploy")
-Fsm:AddProcess("RoutingToDeploy","RouteToDeployZone",ACT_ROUTE_ZONE:New(),{Arrived="ArriveAtDeploy",Cancelled="CancelRouteToDeploy"})
-Fsm:AddTransition("Arrived","ArriveAtDeploy","ArrivedAtDeploy")
-Fsm:AddTransition("Cancelled","CancelRouteToDeploy","Cancelled")
-Fsm:AddTransition({"ArrivedAtPickup","ArrivedAtDeploy","Landing"},"Land","Landing")
-Fsm:AddTransition("Landing","Landed","Landed")
-Fsm:AddTransition("*","PrepareBoarding","AwaitBoarding")
-Fsm:AddTransition("AwaitBoarding","Board","Boarding")
-Fsm:AddTransition("Boarding","Boarded","Boarded")
-Fsm:AddTransition("*","Load","Loaded")
-Fsm:AddTransition("*","PrepareUnBoarding","AwaitUnBoarding")
-Fsm:AddTransition("AwaitUnBoarding","UnBoard","UnBoarding")
-Fsm:AddTransition("UnBoarding","UnBoarded","UnBoarded")
-Fsm:AddTransition("*","Unload","Unloaded")
-Fsm:AddTransition("*","Planned","Planned")
-Fsm:AddTransition("Deployed","Success","Success")
-Fsm:AddTransition("Rejected","Reject","Aborted")
-Fsm:AddTransition("Failed","Fail","Failed")
-function Fsm:OnAfterAssigned(TaskUnit,Task)
-self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()})
-self:SelectAction()
-end
-function Fsm:onafterSelectAction(TaskUnit,Task)
-local TaskUnitName=TaskUnit:GetName()
-local MenuTime=Task:InitTaskControlMenu(TaskUnit)
-local MenuControl=Task:GetTaskControlMenu(TaskUnit)
-Task.SetCargo:ForEachCargo(
-function(Cargo)
-if Cargo:IsAlive()then
-local TaskGroup=TaskUnit:GetGroup()
-if Cargo:IsUnLoaded()then
-local CargoBayFreeWeight=TaskUnit:GetCargoBayFreeWeight()
-local CargoWeight=Cargo:GetWeight()
-self:F({CargoBayFreeWeight=CargoBayFreeWeight})
-if CargoBayFreeWeight>CargoWeight then
-if Cargo:IsInReportRadius(TaskUnit:GetPointVec2())then
-local NotInDeployZones=true
-for DeployZoneName,DeployZone in pairs(Task.DeployZones)do
-if Cargo:IsInZone(DeployZone)then
-NotInDeployZones=false
-end
-end
-if NotInDeployZones then
-if not TaskUnit:InAir()then
-if Cargo:CanBoard()==true then
-if Cargo:IsInLoadRadius(TaskUnit:GetPointVec2())then
-Cargo:Report("Ready for boarding.","board",TaskUnit:GetGroup())
-local BoardMenu=MENU_GROUP:New(TaskGroup,"Board cargo",MenuControl):SetTime(MenuTime):SetTag("Cargo")
-MENU_GROUP_COMMAND:New(TaskUnit:GetGroup(),Cargo.Name,BoardMenu,self.MenuBoardCargo,self,Cargo):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent()
-else
-Cargo:Report("Board at "..Cargo:GetCoordinate():ToString(TaskUnit:GetGroup().."."),"reporting",TaskUnit:GetGroup())
-end
-else
-if Cargo:CanLoad()==true then
-if Cargo:IsInLoadRadius(TaskUnit:GetPointVec2())then
-Cargo:Report("Ready for loading.","load",TaskUnit:GetGroup())
-local LoadMenu=MENU_GROUP:New(TaskGroup,"Load cargo",MenuControl):SetTime(MenuTime):SetTag("Cargo")
-MENU_GROUP_COMMAND:New(TaskUnit:GetGroup(),Cargo.Name,LoadMenu,self.MenuLoadCargo,self,Cargo):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent()
-else
-Cargo:Report("Load at "..Cargo:GetCoordinate():ToString(TaskUnit:GetGroup()).." within "..Cargo.NearRadius..".","reporting",TaskUnit:GetGroup())
-end
-else
-if Cargo:CanSlingload()==true then
-if Cargo:IsInLoadRadius(TaskUnit:GetPointVec2())then
-Cargo:Report("Ready for sling loading.","slingload",TaskUnit:GetGroup())
-local SlingloadMenu=MENU_GROUP:New(TaskGroup,"Slingload cargo",MenuControl):SetTime(MenuTime):SetTag("Cargo")
-MENU_GROUP_COMMAND:New(TaskUnit:GetGroup(),Cargo.Name,SlingloadMenu,self.MenuLoadCargo,self,Cargo):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent()
-else
-Cargo:Report("Slingload at "..Cargo:GetCoordinate():ToString(TaskUnit:GetGroup())..".","reporting",TaskUnit:GetGroup())
-end
-end
-end
-end
-else
-Cargo:ReportResetAll(TaskUnit:GetGroup())
-end
-end
-else
-if not Cargo:IsDeployed()==true then
-local RouteToPickupMenu=MENU_GROUP:New(TaskGroup,"Route to pickup cargo",MenuControl):SetTime(MenuTime):SetTag("Cargo")
-Cargo:ReportResetAll(TaskUnit:GetGroup())
-if Cargo:CanBoard()==true then
-if not Cargo:IsInLoadRadius(TaskUnit:GetPointVec2())then
-local BoardMenu=MENU_GROUP:New(TaskGroup,"Board cargo",RouteToPickupMenu):SetTime(MenuTime):SetTag("Cargo")
-MENU_GROUP_COMMAND:New(TaskUnit:GetGroup(),Cargo.Name,BoardMenu,self.MenuRouteToPickup,self,Cargo):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent()
-end
-else
-if Cargo:CanLoad()==true then
-if not Cargo:IsInLoadRadius(TaskUnit:GetPointVec2())then
-local LoadMenu=MENU_GROUP:New(TaskGroup,"Load cargo",RouteToPickupMenu):SetTime(MenuTime):SetTag("Cargo")
-MENU_GROUP_COMMAND:New(TaskUnit:GetGroup(),Cargo.Name,LoadMenu,self.MenuRouteToPickup,self,Cargo):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent()
-end
-else
-if Cargo:CanSlingload()==true then
-if not Cargo:IsInLoadRadius(TaskUnit:GetPointVec2())then
-local SlingloadMenu=MENU_GROUP:New(TaskGroup,"Slingload cargo",RouteToPickupMenu):SetTime(MenuTime):SetTag("Cargo")
-MENU_GROUP_COMMAND:New(TaskUnit:GetGroup(),Cargo.Name,SlingloadMenu,self.MenuRouteToPickup,self,Cargo):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent()
-end
-end
-end
-end
-end
-end
-end
-for DeployZoneName,DeployZone in pairs(Task.DeployZones)do
-if Cargo:IsInZone(DeployZone)then
-Task:I({CargoIsDeployed=Task.CargoDeployed and"true"or"false"})
-if Cargo:IsDeployed()==false then
-Cargo:SetDeployed(true)
-Task:I({CargoIsAlive=Cargo:IsAlive()and"true"or"false"})
-if Cargo:IsAlive()then
-Task:CargoDeployed(TaskUnit,Cargo,DeployZone)
-end
-end
-end
-end
-end
-if Cargo:IsLoaded()==true and Cargo:IsLoadedInCarrier(TaskUnit)==true then
-if not TaskUnit:InAir()then
-if Cargo:CanUnboard()==true then
-local UnboardMenu=MENU_GROUP:New(TaskGroup,"Unboard cargo",MenuControl):SetTime(MenuTime):SetTag("Cargo")
-MENU_GROUP_COMMAND:New(TaskUnit:GetGroup(),Cargo.Name,UnboardMenu,self.MenuUnboardCargo,self,Cargo):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent()
-else
-if Cargo:CanUnload()==true then
-local UnloadMenu=MENU_GROUP:New(TaskGroup,"Unload cargo",MenuControl):SetTime(MenuTime):SetTag("Cargo")
-MENU_GROUP_COMMAND:New(TaskUnit:GetGroup(),Cargo.Name,UnloadMenu,self.MenuUnloadCargo,self,Cargo):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent()
-end
-end
-end
-end
-for DeployZoneName,DeployZone in pairs(Task.DeployZones)do
-if not Cargo:IsInZone(DeployZone)then
-local RouteToDeployMenu=MENU_GROUP:New(TaskGroup,"Route to deploy cargo",MenuControl):SetTime(MenuTime):SetTag("Cargo")
-MENU_GROUP_COMMAND:New(TaskUnit:GetGroup(),"Zone "..DeployZoneName,RouteToDeployMenu,self.MenuRouteToDeploy,self,DeployZone):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent()
-end
-end
-end
-end
-)
-Task:RefreshTaskControlMenu(TaskUnit,MenuTime,"Cargo")
-self:__SelectAction(-1)
-end
-function Fsm:OnLeaveWaitingForCommand(TaskUnit,Task)
-self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()})
-end
-function Fsm:MenuBoardCargo(Cargo)
-self:__PrepareBoarding(1.0,Cargo)
-end
-function Fsm:MenuLoadCargo(Cargo)
-self:__Load(1.0,Cargo)
-end
-function Fsm:MenuUnboardCargo(Cargo,DeployZone)
-self:__PrepareUnBoarding(1.0,Cargo,DeployZone)
-end
-function Fsm:MenuUnloadCargo(Cargo,DeployZone)
-self:__Unload(1.0,Cargo,DeployZone)
-end
-function Fsm:MenuRouteToPickup(Cargo)
-self:__RouteToPickup(1.0,Cargo)
-end
-function Fsm:MenuRouteToDeploy(DeployZone)
-self:__RouteToDeploy(1.0,DeployZone)
-end
-function Fsm:onafterRouteToPickup(TaskUnit,Task,From,Event,To,Cargo)
-self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()})
-if Cargo:IsAlive()then
-self.Cargo=Cargo
-Task:SetCargoPickup(self.Cargo,TaskUnit)
-self:__RouteToPickupPoint(-0.1)
-end
-end
-function Fsm:onafterArriveAtPickup(TaskUnit,Task)
-self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()})
-if self.Cargo:IsAlive()then
-if TaskUnit:IsAir()then
-Task:GetMission():GetCommandCenter():MessageToGroup("Land",TaskUnit:GetGroup())
-self:__Land(-0.1,"Pickup")
-else
-self:__SelectAction(-0.1)
-end
-end
-end
-function Fsm:onafterCancelRouteToPickup(TaskUnit,Task)
-self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()})
-Task:GetMission():GetCommandCenter():MessageToGroup("Cancelled routing to Cargo "..self.Cargo:GetName(),TaskUnit:GetGroup())
-self:__SelectAction(-0.1)
-end
-function Fsm:onafterRouteToDeploy(TaskUnit,Task,From,Event,To,DeployZone)
-self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()})
-self:F(DeployZone)
-self.DeployZone=DeployZone
-Task:SetDeployZone(self.DeployZone,TaskUnit)
-self:__RouteToDeployZone(-0.1)
-end
-function Fsm:onafterArriveAtDeploy(TaskUnit,Task)
-self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()})
-if TaskUnit:IsAir()then
-Task:GetMission():GetCommandCenter():MessageToGroup("Land",TaskUnit:GetGroup())
-self:__Land(-0.1,"Deploy")
-else
-self:__SelectAction(-0.1)
-end
-end
-function Fsm:onafterCancelRouteToDeploy(TaskUnit,Task)
-self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()})
-Task:GetMission():GetCommandCenter():MessageToGroup("Cancelled routing to deploy zone "..self.DeployZone:GetName(),TaskUnit:GetGroup())
-self:__SelectAction(-0.1)
-end
-function Fsm:onafterLand(TaskUnit,Task,From,Event,To,Action)
-self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()})
-if Action=="Pickup"then
-if self.Cargo:IsAlive()then
-if self.Cargo:IsInReportRadius(TaskUnit:GetPointVec2())then
-if TaskUnit:InAir()then
-self:__Land(-10,Action)
-else
-Task:GetMission():GetCommandCenter():MessageToGroup("Landed at pickup location...",TaskUnit:GetGroup())
-self:__Landed(-0.1,Action)
-end
-else
-self:__RouteToPickup(-0.1,self.Cargo)
-end
-end
-else
-if TaskUnit:IsAlive()then
-if TaskUnit:IsInZone(self.DeployZone)then
-if TaskUnit:InAir()then
-self:__Land(-10,Action)
-else
-Task:GetMission():GetCommandCenter():MessageToGroup("Landed at deploy zone "..self.DeployZone:GetName(),TaskUnit:GetGroup())
-self:__Landed(-0.1,Action)
-end
-else
-self:__RouteToDeploy(-0.1,self.Cargo)
-end
-end
-end
-end
-function Fsm:onafterLanded(TaskUnit,Task,From,Event,To,Action)
-self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()})
-if Action=="Pickup"then
-if self.Cargo:IsAlive()then
-if self.Cargo:IsInReportRadius(TaskUnit:GetPointVec2())then
-if TaskUnit:InAir()then
-self:__Land(-0.1,Action)
-else
-self:__SelectAction(-0.1)
-end
-else
-self:__RouteToPickup(-0.1,self.Cargo)
-end
-end
-else
-if TaskUnit:IsAlive()then
-if TaskUnit:IsInZone(self.DeployZone)then
-if TaskUnit:InAir()then
-self:__Land(-10,Action)
-else
-self:__SelectAction(-0.1)
-end
-else
-self:__RouteToDeploy(-0.1,self.Cargo)
-end
-end
-end
-end
-function Fsm:onafterPrepareBoarding(TaskUnit,Task,From,Event,To,Cargo)
-self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()})
-if Cargo and Cargo:IsAlive()then
-self:__Board(-0.1,Cargo)
-end
-end
-function Fsm:onafterBoard(TaskUnit,Task,From,Event,To,Cargo)
-self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID()})
-function Cargo:OnEnterLoaded(From,Event,To,TaskUnit,TaskProcess)
-self:F({From,Event,To,TaskUnit,TaskProcess})
-TaskProcess:__Boarded(0.1,self)
-end
-if Cargo:IsAlive()then
-if Cargo:IsInLoadRadius(TaskUnit:GetPointVec2())then
-if TaskUnit:InAir()then
-else
-Cargo:MessageToGroup("Boarding ...",TaskUnit:GetGroup())
-if not Cargo:IsBoarding()then
-Cargo:Board(TaskUnit,nil,self)
-end
-end
-else
-end
-end
-end
-function Fsm:onafterBoarded(TaskUnit,Task,From,Event,To,Cargo)
-local TaskUnitName=TaskUnit:GetName()
-self:F({TaskUnit=TaskUnitName,Task=Task and Task:GetClassNameAndID()})
-Cargo:MessageToGroup("Boarded cargo "..Cargo:GetName(),TaskUnit:GetGroup())
-self:__Load(-0.1,Cargo)
-end
-function Fsm:onafterLoad(TaskUnit,Task,From,Event,To,Cargo)
-local TaskUnitName=TaskUnit:GetName()
-self:F({TaskUnit=TaskUnitName,Task=Task and Task:GetClassNameAndID()})
-if not Cargo:IsLoaded()then
-Cargo:Load(TaskUnit)
-end
-Cargo:MessageToGroup("Loaded cargo "..Cargo:GetName(),TaskUnit:GetGroup())
-TaskUnit:AddCargo(Cargo)
-Task:CargoPickedUp(TaskUnit,Cargo)
-self:SelectAction(-1)
-end
-function Fsm:onafterPrepareUnBoarding(TaskUnit,Task,From,Event,To,Cargo)
-self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID(),From,Event,To,Cargo})
-self.Cargo=Cargo
-self.DeployZone=nil
-if Cargo:IsAlive()then
-for DeployZoneName,DeployZone in pairs(Task.DeployZones)do
-if Cargo:IsInZone(DeployZone)then
-self.DeployZone=DeployZone
-break
-end
-end
-self:__UnBoard(-0.1,Cargo,self.DeployZone)
-end
-end
-function Fsm:onafterUnBoard(TaskUnit,Task,From,Event,To,Cargo,DeployZone)
-self:F({TaskUnit=TaskUnit,Task=Task and Task:GetClassNameAndID(),From,Event,To,Cargo,DeployZone})
-function self.Cargo:OnEnterUnLoaded(From,Event,To,DeployZone,TaskProcess)
-self:F({From,Event,To,DeployZone,TaskProcess})
-TaskProcess:__UnBoarded(-0.1)
-end
-if self.Cargo:IsAlive()then
-self.Cargo:MessageToGroup("UnBoarding ...",TaskUnit:GetGroup())
-if DeployZone then
-self.Cargo:UnBoard(DeployZone:GetCoordinate():GetRandomCoordinateInRadius(25,10),400,self)
-else
-self.Cargo:UnBoard(TaskUnit:GetCoordinate():GetRandomCoordinateInRadius(25,10),400,self)
-end
-end
-end
-function Fsm:onafterUnBoarded(TaskUnit,Task)
-local TaskUnitName=TaskUnit:GetName()
-self:F({TaskUnit=TaskUnitName,Task=Task and Task:GetClassNameAndID()})
-self.Cargo:MessageToGroup("UnBoarded cargo "..self.Cargo:GetName(),TaskUnit:GetGroup())
-self:Unload(self.Cargo)
-end
-function Fsm:onafterUnload(TaskUnit,Task,From,Event,To,Cargo,DeployZone)
-local TaskUnitName=TaskUnit:GetName()
-self:F({TaskUnit=TaskUnitName,Task=Task and Task:GetClassNameAndID()})
-if not Cargo:IsUnLoaded()then
-if DeployZone then
-Cargo:UnLoad(DeployZone:GetCoordinate():GetRandomCoordinateInRadius(25,10),400,self)
-else
-Cargo:UnLoad(TaskUnit:GetCoordinate():GetRandomCoordinateInRadius(25,10),400,self)
-end
-end
-TaskUnit:RemoveCargo(Cargo)
-Cargo:MessageToGroup("Unloaded cargo "..Cargo:GetName(),TaskUnit:GetGroup())
-self:Planned()
-self:__SelectAction(1)
-end
-return self
-end
-function TASK_CARGO:SetCargoLimit(CargoLimit)
-self.CargoLimit=CargoLimit
-return self
-end
-function TASK_CARGO:SetSmokeColor(SmokeColor)
-if SmokeColor==nil then
-self.SmokeColor=SMOKECOLOR.Red
-elseif type(SmokeColor)=="number"then
-self:F2(SmokeColor)
-if SmokeColor>0 and SmokeColor<=5 then
-self.SmokeColor=SMOKECOLOR.SmokeColor
-end
-end
-end
-function TASK_CARGO:GetSmokeColor()
-return self.SmokeColor
-end
-function TASK_CARGO:GetPlannedMenuText()
-return self:GetStateString().." - "..self:GetTaskName().." ( "..self.TargetSetUnit:GetUnitTypesText().." )"
-end
-function TASK_CARGO:GetCargoSet()
-return self.SetCargo
-end
-function TASK_CARGO:GetDeployZones()
-return self.DeployZones
-end
-function TASK_CARGO:SetCargoPickup(Cargo,TaskUnit)
-self:F({Cargo,TaskUnit})
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-local MenuTime=self:InitTaskControlMenu(TaskUnit)
-local MenuControl=self:GetTaskControlMenu(TaskUnit)
-local ActRouteCargo=ProcessUnit:GetProcess("RoutingToPickup","RouteToPickupPoint")
-ActRouteCargo:Reset()
-ActRouteCargo:SetCoordinate(Cargo:GetCoordinate())
-ActRouteCargo:SetRange(Cargo:GetLoadRadius())
-ActRouteCargo:SetMenuCancel(TaskUnit:GetGroup(),"Cancel Routing to Cargo "..Cargo:GetName(),MenuControl,MenuTime,"Cargo")
-ActRouteCargo:Start()
-return self
-end
-function TASK_CARGO:SetDeployZone(DeployZone,TaskUnit)
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-local MenuTime=self:InitTaskControlMenu(TaskUnit)
-local MenuControl=self:GetTaskControlMenu(TaskUnit)
-local ActRouteDeployZone=ProcessUnit:GetProcess("RoutingToDeploy","RouteToDeployZone")
-ActRouteDeployZone:Reset()
-ActRouteDeployZone:SetZone(DeployZone)
-ActRouteDeployZone:SetMenuCancel(TaskUnit:GetGroup(),"Cancel Routing to Deploy Zone"..DeployZone:GetName(),MenuControl,MenuTime,"Cargo")
-ActRouteDeployZone:Start()
-return self
-end
-function TASK_CARGO:AddDeployZone(DeployZone,TaskUnit)
-self.DeployZones[DeployZone:GetName()]=DeployZone
-return self
-end
-function TASK_CARGO:RemoveDeployZone(DeployZone,TaskUnit)
-self.DeployZones[DeployZone:GetName()]=nil
-return self
-end
-function TASK_CARGO:SetDeployZones(DeployZones,TaskUnit)
-for DeployZoneID,DeployZone in pairs(DeployZones or{})do
-self.DeployZones[DeployZone:GetName()]=DeployZone
-end
-return self
-end
-function TASK_CARGO:GetTargetZone(TaskUnit)
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-local ActRouteTarget=ProcessUnit:GetProcess("Engaging","RouteToTargetZone")
-return ActRouteTarget:GetZone()
-end
-function TASK_CARGO:SetScoreOnProgress(Text,Score,TaskUnit)
-self:F({Text,Score,TaskUnit})
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-ProcessUnit:AddScoreProcess("Engaging","Account","Account",Text,Score)
-return self
-end
-function TASK_CARGO:SetScoreOnSuccess(Text,Score,TaskUnit)
-self:F({Text,Score,TaskUnit})
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-ProcessUnit:AddScore("Success",Text,Score)
-return self
-end
-function TASK_CARGO:SetScoreOnFail(Text,Penalty,TaskUnit)
-self:F({Text,Score,TaskUnit})
-local ProcessUnit=self:GetUnitProcess(TaskUnit)
-ProcessUnit:AddScore("Failed",Text,Penalty)
-return self
-end
-function TASK_CARGO:SetGoalTotal()
-self.GoalTotal=self.SetCargo:Count()
-end
-function TASK_CARGO:GetGoalTotal()
-return self.GoalTotal
-end
-function TASK_CARGO:UpdateTaskInfo()
-if self:IsStatePlanned()or self:IsStateAssigned()then
-self.TaskInfo:AddTaskName(0,"MSOD")
-self.TaskInfo:AddCargoSet(self.SetCargo,10,"SOD",true)
-local Coordinates={}
-for CargoName,Cargo in pairs(self.SetCargo:GetSet())do
-local Cargo=Cargo
-if not Cargo:IsLoaded()then
-Coordinates[#Coordinates+1]=Cargo:GetCoordinate()
-end
-end
-self.TaskInfo:AddCoordinates(Coordinates,1,"M")
-end
-end
-function TASK_CARGO:ReportOrder(ReportGroup)
-return 0
-end
-function TASK_CARGO:GetAutoAssignPriority(AutoAssignMethod,TaskGroup)
-if AutoAssignMethod==COMMANDCENTER.AutoAssignMethods.Random then
-return math.random(1,9)
-elseif AutoAssignMethod==COMMANDCENTER.AutoAssignMethods.Distance then
-return 0
-elseif AutoAssignMethod==COMMANDCENTER.AutoAssignMethods.Priority then
-return 1
-end
-return 0
-end
-end
-do
-TASK_CARGO_TRANSPORT={
-ClassName="TASK_CARGO_TRANSPORT",
-}
-function TASK_CARGO_TRANSPORT:New(Mission,SetGroup,TaskName,SetCargo,TaskBriefing)
-local self=BASE:Inherit(self,TASK_CARGO:New(Mission,SetGroup,TaskName,SetCargo,"Transport",TaskBriefing))
-self:F()
-Mission:AddTask(self)
-local Fsm=self:GetUnitProcess()
-local CargoReport=REPORT:New("Transport Cargo. The following cargo needs to be transported including initial positions:")
-SetCargo:ForEachCargo(
-function(Cargo)
-local CargoType=Cargo:GetType()
-local CargoName=Cargo:GetName()
-local CargoCoordinate=Cargo:GetCoordinate()
-CargoReport:Add(string.format('- "%s" (%s) at %s',CargoName,CargoType,CargoCoordinate:ToStringMGRS()))
-end
-)
-self:SetBriefing(
-TaskBriefing or
-CargoReport:Text()
-)
-return self
-end
-function TASK_CARGO_TRANSPORT:ReportOrder(ReportGroup)
-return 0
-end
-function TASK_CARGO_TRANSPORT:IsAllCargoTransported()
-local CargoSet=self:GetCargoSet()
-local Set=CargoSet:GetSet()
-local DeployZones=self:GetDeployZones()
-local CargoDeployed=true
-for CargoID,CargoData in pairs(Set)do
-local Cargo=CargoData
-self:F({Cargo=Cargo:GetName(),CargoDeployed=Cargo:IsDeployed()})
-if Cargo:IsDeployed()then
-else
-CargoDeployed=false
-end
-end
-self:F({CargoDeployed=CargoDeployed})
-return CargoDeployed
-end
-function TASK_CARGO_TRANSPORT:onafterGoal(TaskUnit,From,Event,To)
-local CargoSet=self.CargoSet
-if self:IsAllCargoTransported()then
-self:Success()
-end
-self:__Goal(-10)
-end
-end
-do
-TASK_CARGO_CSAR={
-ClassName="TASK_CARGO_CSAR",
-}
-function TASK_CARGO_CSAR:New(Mission,SetGroup,TaskName,SetCargo,TaskBriefing)
-local self=BASE:Inherit(self,TASK_CARGO:New(Mission,SetGroup,TaskName,SetCargo,"CSAR",TaskBriefing))
-self:F()
-Mission:AddTask(self)
-self:AddTransition("*","CargoPickedUp","*")
-self:AddTransition("*","CargoDeployed","*")
-self:F({CargoDeployed=self.CargoDeployed~=nil and"true"or"false"})
-local Fsm=self:GetUnitProcess()
-local CargoReport=REPORT:New("Rescue a downed pilot from the following position:")
-SetCargo:ForEachCargo(
-function(Cargo)
-local CargoType=Cargo:GetType()
-local CargoName=Cargo:GetName()
-local CargoCoordinate=Cargo:GetCoordinate()
-CargoReport:Add(string.format('- "%s" (%s) at %s',CargoName,CargoType,CargoCoordinate:ToStringMGRS()))
-end
-)
-self:SetBriefing(
-TaskBriefing or
-CargoReport:Text()
-)
-return self
-end
-function TASK_CARGO_CSAR:ReportOrder(ReportGroup)
-return 0
-end
-function TASK_CARGO_CSAR:IsAllCargoTransported()
-local CargoSet=self:GetCargoSet()
-local Set=CargoSet:GetSet()
-local DeployZones=self:GetDeployZones()
-local CargoDeployed=true
-for CargoID,CargoData in pairs(Set)do
-local Cargo=CargoData
-self:F({Cargo=Cargo:GetName(),CargoDeployed=Cargo:IsDeployed()})
-if Cargo:IsDeployed()then
-else
-CargoDeployed=false
-end
-end
-self:F({CargoDeployed=CargoDeployed})
-return CargoDeployed
-end
-function TASK_CARGO_CSAR:onafterGoal(TaskUnit,From,Event,To)
-local CargoSet=self.CargoSet
-if self:IsAllCargoTransported()then
-self:Success()
-end
-self:__Goal(-10)
-end
-end
-do
-TASK_CARGO_DISPATCHER={
-ClassName="TASK_CARGO_DISPATCHER",
-Mission=nil,
-Tasks={},
-CSAR={},
-CSARSpawned=0,
-Transport={},
-TransportCount=0,
-}
-function TASK_CARGO_DISPATCHER:New(Mission,SetGroup)
-local self=BASE:Inherit(self,TASK_MANAGER:New(SetGroup))
-self.Mission=Mission
-self:AddTransition("Started","Assign","Started")
-self:AddTransition("Started","CargoPickedUp","Started")
-self:AddTransition("Started","CargoDeployed","Started")
-self:SetCSARRadius()
-self:__StartTasks(5)
-self.MaxCSAR=nil
-self.CountCSAR=0
-self:HandleEvent(EVENTS.Ejection)
-return self
-end
-function TASK_CARGO_DISPATCHER:SetCSARZones(SetZonesCSAR)
-self.SetZonesCSAR=SetZonesCSAR
-end
-function TASK_CARGO_DISPATCHER:SetMaxCSAR(MaxCSAR)
-self.MaxCSAR=MaxCSAR
-end
-function TASK_CARGO_DISPATCHER:OnEventEjection(EventData)
-self:F({EventData=EventData})
-if self.CSARTasks==true then
-local CSARCoordinate=EventData.IniUnit:GetCoordinate()
-local CSARCoalition=EventData.IniUnit:GetCoalition()
-local CSARCountry=EventData.IniUnit:GetCountry()
-local CSARHeading=EventData.IniUnit:GetHeading()
-if CSARCoalition==self.Mission:GetCommandCenter():GetCoalition()then
-if not self.SetZonesCSAR or(self.SetZonesCSAR and self.SetZonesCSAR:IsCoordinateInZone(CSARCoordinate))then
-if not self.MaxCSAR or(self.MaxCSAR and self.CountCSAR/Scripts/MissionScripting.lua and comment out the lines with sanitizeModule(''). Use at your own risk!)")
-end
-BASE.ServerName="Unknown"
-if lfs and loadfile then
-local serverfile=lfs.writedir()..'Config/serverSettings.lua'
-if UTILS.FileExists(serverfile)then
-loadfile(serverfile)()
-if cfg and cfg.name then
-BASE.ServerName=cfg.name
-end
-end
-BASE.ServerName=BASE.ServerName or"Unknown"
-BASE:I("Server Name: "..tostring(BASE.ServerName))
-end
-BASE:TraceOnOff(false)
-env.info('*** MOOSE INCLUDE END *** ')