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 UnitAmount0 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 *** ')