From 5511c05bcacebb602a0aba557883cfd74bd9ecaf Mon Sep 17 00:00:00 2001 From: kaltokri Date: Mon, 5 Feb 2024 18:43:36 +0100 Subject: [PATCH] Fixed wrong file names of lua files --- .../AIC-HEL-000 - Helicopter.lua | 38 - .../AIC-HEL-010 - Blue Helicopter.miz | Bin 7989213 -> 748844 bytes .../Moose_Test.lua | 122667 --------------- 3 files changed, 122705 deletions(-) delete mode 100644 AI/AI_Cargo_Helicopter/AIC-HEL-010 - Blue Helicopter/AIC-HEL-000 - Helicopter.lua delete mode 100644 AI/AI_Cargo_Helicopter/AIC-HEL-010 - Blue Helicopter/Moose_Test.lua diff --git a/AI/AI_Cargo_Helicopter/AIC-HEL-010 - Blue Helicopter/AIC-HEL-000 - Helicopter.lua b/AI/AI_Cargo_Helicopter/AIC-HEL-010 - Blue Helicopter/AIC-HEL-000 - Helicopter.lua deleted file mode 100644 index eeff770c0e..0000000000 --- a/AI/AI_Cargo_Helicopter/AIC-HEL-010 - Blue Helicopter/AIC-HEL-000 - Helicopter.lua +++ /dev/null @@ -1,38 +0,0 @@ ---- --- Name: AIC-HEL-000 - Helicopter --- Author: FlightControl --- Date Created: 13 Apr 2018 --- Date Checked: 01 Jan 2021 --- Updated Moose, needs fix #1417 to work --- -BASE:TraceClass("AI_CARGO") -BASE:TraceClass("AI_CARGO_HELICOPTER") -BASE:TraceOn() - -WorkerCargoSet = SET_CARGO:New():FilterTypes( "Workers" ):FilterStart() - - -for i = 1, 5 do - local WorkerGroup = GROUP:FindByName( string.format( "Infantry %03d", i ) ) - local WorkersCargo = CARGO_GROUP:New( WorkerGroup, "Workers", string.format( "Infantry %d", i ), 750, 35 ) -end - -local Helicopter = GROUP:FindByName( "Helicopter" ) - -CargoHelicopter = AI_CARGO_HELICOPTER:New( Helicopter, WorkerCargoSet ) - - -PickupZone = ZONE:New( "PickupZone" ) -DeployZones = { ZONE:New( "DeployZone Alpha" ), ZONE:New( "DeployZone Beta" ), ZONE:New( "DeployZone Gamma" ) } - -CargoHelicopter:Pickup( PickupZone:GetRandomCoordinate( 400, 100 ) ) - -function CargoHelicopter:OnAfterLoaded( Helicopter, From, Event, To, Cargo ) - CargoHelicopter:__Deploy(5,DeployZones[math.random( 1, #DeployZones ) ]:GetRandomCoordinate( 500, 100 ), math.random( 50, 250 ) ) -end - - -function CargoHelicopter:OnAfterUnloaded( Helicopter, From, Event, To, Cargo ) - CargoHelicopter:__Pickup( 5,PickupZone:GetRandomCoordinate( 500, 200 ), math.random( 50, 250 ) ) -end - diff --git a/AI/AI_Cargo_Helicopter/AIC-HEL-010 - Blue Helicopter/AIC-HEL-010 - Blue Helicopter.miz b/AI/AI_Cargo_Helicopter/AIC-HEL-010 - Blue Helicopter/AIC-HEL-010 - Blue Helicopter.miz index 8c9505f2c2d9481d17776ac55c799a97bee3471f..64b3f968be7853ef72a1b2edec69389655c44e0a 100644 GIT binary patch literal 748844 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 *5T-oc8XQ=*$u)wBy?s8=~%IEZKqsH!LmRB}h}G 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ɠHj4fghkKN#x+YTy7^s@XK|G&p~q{c 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%0Dh4!zsKP5qFF}SK3dT(uS#vk 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 z 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ə(8xG+hfe*D^XhN^Yv>LW8XD6bI-8lG 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
4lYbk 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

0YAnAjFS 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

LZ*a5Cx`oGD0#m<`O97kuU3deDr?KrX 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 Ge)@kT$UNTw literal 7989213 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

p%M8gbZ+$5 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∋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

A4W+xgEE4-z;h84PJE6pB-k>cv$>;Xl_8Z#( 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

@VG9}`cnFQ^jozdWh>hNd-gW!Uh`VL#CUV8U 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

|(w1tREDQL=!ZW{fONEiwUnNS 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�MlmqtHprYuz1}qD6G1 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%0AG=0D>&h_g)j<_Pj9BLRF7~At#XknYOK&KPtov% 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≯epg;mbHS+xPGkP-<1F&H_E8Cfl_Q#*VcN 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%0DFktB=#%iE>A&;?>LZ-YWsRQCdXl*EUM~`QV|4{5 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

cl;T-EP!DT3#toycmduG9KGQM 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⋘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⪙h zO6o{vbscy62#9MP;8r=!c`1uFsp;fy$F63z_(E+IZTiK3yZnbV63oMmtw)j^;l|@iCri1*N&Va8rwiZ^F>9v?)3>aK+eSD5MJJ>y^A+qgL z{&eNvfM9yD^N|(^c*-Z7neonjngdKV=egsV0#W$2=pqPQ3t@5N(8vd5DP!e^SZ~o& zfyq1}L%og;5`)6|a;KHB9H*9>v`P#um+X}13osmAy%+``Aov{6hbK8XnOdBD2>N)k zHAJ6F@xL+r`#3R|AaxOWR?!G{#T6tkgLniM!`sy(&Si8TM;sPk-f`!Vf^l4ObFmw) z!)boy)DRcqA+9TiWzm7(TfGf;9$mYwY#61F_v}H(R>YQypLu7*IB@H5xfVZp!AKaxgNOemPKGv6#l0j5veUcv3Kq#&B|!JyMGsmJb#uPCY*+K?%adBeJ9B~aK4ovJP;oaR)UJF#HdVtt>YhcYKjim6A|@3l3Kl zNWGGEBzJ)H7Bw#7iGCOV4?uP_UKm+xV=-japR^2L z&AmUmi{fhV7eVzvV_72@RqF;O&oO;rMQLg(Z=328wd!lZ!u_!f0SjBKbKAXy+(kNwtJ-{~E~A#*F)4TrK^((s|- z|6C`k0`_`qcW2|^u)o!-0>{{XiOLk3Yfd0$r=?dGaWytaXU1xNxO`zY(M$ zE}KB#Y7q9xHpE%fuVkjY*6JS#8F4vV$qtgOlza>m1 zEZlAGjd;rYWP4>}D>1Ew<_xP9efcMji=mOHpf>x1Oz$ks(Vw`sWwW%6+$9KsR^H?JQmeFJPKS1Lsf;{)*0*@ z6z6jQ8!f2)7mXjDGpID}IPlNZo)FQ-;EHz;mI|7M=&x;dLvMmvqhxOH2Y%*bY6S*0 ziJrvPn#%n^R>N`5Ooebi2$J8Ju3Zrk6c!4L6xX&?I&G0d9X z;A&v|7!m))`MwPoEMBBI9WVq81>71U=)){0CY=$D+~%a!O&Z4P4QCR41SW%odwd6E z{5D`f%DiP9H%F}-ku)5;0a?ej8?c7jsyd#xCz9DdEX!M#Ec@WH?WpOR?EpMgRyvll z8d}O>d8(K4+UbP>@dCGM6U0=PX#CyZ$xw*yK|gVc--6%pWdCP)B8#z=-pOVW(+E!5 zt<|OX(u{UA&|q2m;Gf!#e?xou+6UrsL*aJw@8=urx8{d8yq5k zT?x{WmZVIvBt1i6jgPD_Ky2eEXasOR;)|-`-N_9+T8I1hh5^0EWix?=oH3^~egNV* zz|ZMqbdBA`si@kze`p(gom=_fKyq59CFF$+&VI#Xc6)LHe7LL*AB!ZDD(DiYHATZr?W@#*lhUec8``BwSCMyik#T9V$s@xKfDa0K1a$_ukBd563d7tF^rr*9>}w(vYo;zg$nA*X|Glk@OYh4 zTGbO}+WdGHNL-cd6fZa^QB-tz!kLN49vnktGR-7~O{2a`XW_2srzhO^9#7c_?D7&8 z4-4V21afWZL}H;%^0h!slYCOoe%ICtgN$0?cWo``F7hCgcWX_YoqXqAO0IX!F%_Iy zs0OY#9n&5=0bA%$8bCGTLPe;eaGzDir+`XHt^->(V(*%V_bUD7UZwj~BqY?KArt29 zV)*k2V#*h9`uMQ`Ib&axYM|%FWHQCliX~xz`muLlWWA~+Z`{U^LIExfVeqYI=sR64 zq#zHoy7DWEo7xcwt-cE|-bWyr4*2A)BwtGOJOvj4aJZZ@WTyREX2P2B;L3iu0|4&% zTE?~=SNrU54UNO)6kC+)ihsH`g{&J?v-&EkxtRu6S6uuc@KPq?ga}Pj&zPFJ?{Kw> zG2lQZYP9;H&uW1I+rvy%wWbo1DO8nZ=30B##Nj`N zXRgsEmfQx`+{#dgWqqg$)cAHdxW+3o@^IpVfE&t65ncSZll>CCBjUq)i=ZPu z6AMtejDicI!)B_`>FI+5^y&ChiKE&rn|UzX+*Gg)-nV`L>sJv5H(29S(GP%4t^vmB zF!kjl!!`X*bR((4=1@IX7-epVE4~lv?V=c&mrNBpTYUVHah(4U-z-8N@xZAf)wW@Y=cQ1sU_nPttU`zUQY=+;^y74NCk%muvFSnmsji><~-{U;O= z*%r+$F;tUmC!Wib{Zqy|C@wnNlW@6*D;vy})6w}klvghFlW}K;qf;E)Z(k11#XpKW zFaF!&~5sWdngUQ*V6tad$zI)VUO+x zAT@Ut7k7ZbO&@ZYvm3mVTnIAo?K?GH<(mxoZp@0NI@yhB z3#q|2O?Iu8`!*}-9ZZw7BWVi82^i;6U}csh*TQo557knT;FpY4vU@QLU)M+kV2)DW z(gOnr@`Pi=A_V1D?NesxP9AL!emWs5^FoYlbJ0=`qmAG;(Ct1m^sT|J{gvs-+3TH7 zKt!{Xo0`1`P(dHLVL46XcGUspO;-Vi@QHJ!m-dlNpaq2G4q%d@)~tN{&b;hQW{_-p zB|{{;>SBBXRKYTZnfA@Dj4FoS1#B#)vx@<|GQ?FsWF{1iICOQo@WJm7gPB?P611gx z&t-qYoEH|=9t6>m8hT%Qr zc*4rYLBBz`<>?c+jA=**vU4rnbK2H||qB($gi9Ci7jPjwP!03!|L)#Y$7J*SNsf1pv}0df8#Uq!UBj3{Z+k(W&8Qy(}< z^qKA~sENAf`f(Q#$kNNI%IX)+O~t^HJV%i`b-}Z$LL1y@v>wyY_%&aonhn7&r+hkG zs3cA*?UXDyhbar4ej9o9w@}rR$@k$WbeAIalt&hPx#A%wqRK%(o2M<@FM^H`FY72@CuPjR z%C9fQk~S_`M-&lf<+X#Qi*76QmRoMG@|sKJ?@$-PWYujMEr&#@^xblM<;4qE#yzk5 zroXd$)al8=^{c@x?tl!&t+eVBFA87%rEgciNj}@f<=_nS?Xz}TF8rpSOrYoG=;}kn zR{1rhi}jQ){$6KO?-SM3dV#J!OKQE=Q}XW5pRGP22bkNi@?Rp)N~L*sa@&s}k7kTQr;NggOQ(@^T$1fj7Wo9IcOM1Qo}o%xw>SY1-2TbSzg?o){lpu zuZJi0WQiwQKf^?G)jld&K|>?<`Ns`nFt^ar5rwVow`0^dwj29|m>1T9tU+tX zhoh5^o1W(4N#g_XxXv|f?vbRCymlQjTuJV7fOSZvLP!rSCkl&@T9puXFci`HwS8`g;=q>ad6Pv+}U66 z8=Lr+yYo4^S-}J1Ciz0h84=A&h+l^%t69xZ#pcgQ2iul5#mHh(h5!;)NGgQxacnXt zXORZ4BV$(*L6LopTT@}7a%WS0sR&=`v-T|6t9&93FB?vpGQ1! z1(%}a;#^!=+Ue@hR)pH#p3vPj*W6AczfFb4p!<&Pc%yI$YoM?^63$zZG--Z)%J*Aa;u8ja zax4Ylc)I-bbgAi}6Gzi9rr&bEnRu8Ir9@ee0$*n$MlX0=QmT<-lQg9>5(P}|X`!^B zufGvm@d_OBWx$Kb?>or+1~|bgZ%J8fX^u_X;v5n(%X3K0h}WXglLdM$vp|Q+zC?d3 z_PMfk8gB%pL^KUe9t_f5R$Qt5trZ?3XBTZ9NcHa2X{*0e$U!*d_v>7tQ7|YL<5M*O zC)Cz}pg%rUUa)q;WlL@S>duGK{+c)$;C2t3w-v3hQ{e5JfKh&&BdEl3QV!?aRhfzj zzS5ZME@42T(`d2?_(W=lZHYCcBY_PK#q5#<)0kYf18PCKt6mZsNhNL($0RCAtOK$z zJR?06=arJ|D^hqj@#tv%mFfs=fI%O^t4T6PD>x>nykJ%*REv67H>*a7?yS>W$GWIv z^jO`DqQv}i16NHEJoyx>$_ReToyNL$*r!I;98ic7&+~E}PoImb>v&XCaIc2X)rD6>Lz(LmP2K5+wk-K`20+eFo00 zWsBmSz^g`H)Xuc|6z>`(F`(Q{0d($cNO~7o3A^7@;ZwAcfSY}*NDvQo4mIyS%1z6t zG4gt$PCf;Ih#-%&rs|Ht*7mdeK&7o`hj7OqiVy&80qBdk`?QzQ$H9B~*TDw!%% zH7pj?e|3vxGY2^Ya**)AVV zlr4)P4&AriR7@bSu3`IFRzt+SXr&ta(l>F)WtbCi(Fm0!wv*okuZyO30~X?&!E3EH z_%LfFMW4iGoGY#`Mpl$aY29xP*`&;|>gw78>Y#VBmjRK|27XE!5KWLpb$b*yE8igT zQ39izlHv(7MF8G6`P%#0f8&7w?fql@^NkVV+ttV6m+|O=HN9Rs zIOhX+*lUvrW7p#QzwDFJNPanCmDGc-HKvS1(_tvS^sO)x(*&#!*N5Ud2Dq%&1>urY zKNK}(Qo{?ab_dx~4&2KT(n<;{4!@&$!7>>@DIa7?)p41CQmSH!Ysu$ITN|)3ON$^N zN4`i{#IV@|4J7-UWae1EWGiS50ZLM!op3%SN9|%-1&XuFw~kBSVEs|7!d^JjZnkh( zlhZ1oiG127$ZhT6O+Lq0aEf*r`Ha1lE`=hn?ts?H+ev+mHlk|?N`RI@|M&V@g!Nr; zp}s)-Nzy`PXe*K+B2mg@EKfg59WR~9Vm*5hZ@yl(mAQJ5{85Ow39Ic;IaOVVBvKNf z!uJusaIEWD?gRebhKh&NZu^Coq2O?{N88J1TYplOOi~Hk>io(xuN)EWH7HCC(+@}fXsf*!{l?lB-=#|_- zQ91sv+IX(JYAmU(tStjV zwsFF(Z`m6zs9e?XFXKH3XAKfI)1xtq_**MdO_d{h3C=-9qn^&ZT7Gj?PXT$~0~}HH z76g<`??49!l;qW-lt`DPYpJ$DgsSKocI?tC?^IUfP#Cl@lylV|4z;z~;#!KAc_ zJDvFV{aPQs2(wED&G>)Wd)MwZZ=-GWyM6`BEN@fxEn1S3G}RMkEk%-Jt;UkKL^sV= z{c6!7WiggWm84=je$wCmUe_D|+$WOqp{MQD#^MbO2Ebr2GZ+j21ImJKSVX<=2QT4V zz#XSMGF6&>)s`7-DJ2CUm~e}!S46!O4Ye7cEiH%hE%}IZHjnsrU94to zV{}#jw`T$BSrG8HJOJKfu`mq`O*#z>B&evhD3!70XigaHE4s&MDf#U6-iC6I)y2Dr zN308x5{qYox5gR7Q2cvS0c>>9oL*kKxRW14Az+BzlxKI<_75gA*8qA) z+cl3(;@Z#QWZHUt%`4evy%(2*HeQ(?APyvy$gXvNTg`qsNH`pwj}w6APqr2S#`7`J z;@*r??A6T%Tkv$$$tE>F;<1~QR7uQYa^-6n$jo}EQ+pnw`K4`2c~oUY1oeI9Nvo zkCV&0@A`_ARF3@GwNGxKL!+lzQj3PXTil3teKf_Bq|?qu=ryi8z2fy_$s-#ElOTE; zpEI4}?T;6Oi5~3s!XbDOpaGQ7SW+rtQdyw8sRJ7GV+`O?Y{Qz>LLQn3=74Sr<=l2riAPK5f z>#L4g0gz`@X-vG0sL*Sj9yGCWkb63WFpfQ&+R00Jq58Sw?O%k>Pq7_P5K=L&dz!yN zIw|CL?$Ccl1`JvGAp0nK7LK`i!U}z30KZ*F9V;NA<4x4OfVnq~j-gm1h{&N_8n25V zvPTOSBi`YeCepsXihQ;EySSuOL`z}B*aU0M5{&f+GFI$vHH%SZ6%U6ZqS#qlZ+gKz zENVGvUX|twwnuU#_O-C4U>K=`*X=hfxqTk;==c?h4JV;~`2QH8B>e z4k`#&A2lkw+_=0f3_6BWdye7`c%tfN&C84y+%`)q_gb1`Z8y7xcAo>B>gff8!Ai+u zw;PF0)Z^Ckl*pi(&J3C(Rrbw%61=w-{}J5IBdB7rJxFNYXr@7uq>5KXM( zImJOssNLA=%vxgunq~#fZn`CwQZpLRe4jq_5(-oD~<4 zior}f4nxLq4^~!yApH&?vBj#YH|3&*7)rWbPV{ceEm2MCOJL>iVbJM;XfxqT?a8kb z+HiBEW8wFA1e-0Cudv$Cylq`{PWMNY%`M79x8+Tgi5;a{(uG@oh3jx}X-namYIPj| zQ$lCF;XlnyrvmwcZP?nMic=25)3Y%aOWY$Df(<*k6AL%>aNLH$2#7$P0N$pKXdmo7>mF_Y=u3MP*-+hAU*u?S zHv!*V1pfVx&-P@nwY|L;NfTZQzw^S&X=~^s4tmQKn1QK#FCr(O4uw?-z=u#dnVk^V z0*iZ40Aoeh8424tpLd2^Ux#71qe?@j|s&$6D#m|GRRiY z%S*3kf7(7e*gyC|`XU1bahzNYHN|3vo*3zHWBlS5+{6X@Ej&k{^t3f~YAfU?V~*i> zoX62@m3)DXX+$Qz)H4|w^2<_7OO94#E}Q8^w`({oZTTFoZDqAcbWXS=KWT9%GJI-Q z@_s^L(G5N26l#$}_4u;8iO3K!&+2V^#cjT~`*eFZ$Z|uv`9VmJAMZb9hhU_)Vmqnm z!q;YnnTgs8GM;5;ci!X6}g)@e`Y7~bv?7a4@QS|Wk>v|73J zq~e~3jrt2NY;u@Io8#1Dxm3M%g_q9oYHbF#+us#&VJXQyWS$Zg5NIp~5+MDKAWD=j z9){@@AEVK%5#&E!D_a+ipYky&xk(&1iRdd+oFTTkBmMXE$oKN-N5|9k?spEqq>Dt7 zU$6nTSygKlJQ?2^OU(svh=O8@&{TkxC)(KBsDJR9nzsCUGB_W;u_!W>&@*GazTyWS z@6swUZ3Q-J=6uY5kA;?zg6I6wKsXlwNzLg3ndy#r>aJThfK{=}3vMU0tg zEI+?f)_&Zjr5A>QR*FC;IMemJ{g#s^975H)qC+o`JS4I0IlYwdcpRZ5u~dNDFgp3s z;rQaUHCY4WA}0Oz{@|{Ca8hWMKC)w(2lCWb%+#m1Jab)h!{Fzi;nIG0UxG>X=WhJj zmq)Hb4jTkxrZ;(OkLG~3K5|N3Z{A5W|4>& zFkxr(gcEYDB7X&4)3N8sB!0mN8=8lef_A=$j925@vtOKt3iX}L-Y%B;%9XDzt4Z zPQ1eBZji(X*TTNRGxia-#%$92b#QsfTPn?O%11_YeDw0)s|GFsz~yGR*(}}z>=k9t zUS>*AaU(RIy+n|w2sxuG38d5FAEJu&cY6<4qs_h_PVf-0&?{lou*isG99S)OFw!Kh ziTs_ZW{w5P4hh=^elf<<;#@n&`r=e56uuSWh%dB1MhPhU1aqs3$s&_f@{pr14}J|h zrRJgHemZg@==ZL2L+9zJhSBgqpS-EInh;A;+`~dZ7%u4TPDfH#$CDO)r23J}&?@tY8(Tx}7M{x+pqM3azXXh# z>RB;L4pdNoXRACx#(zpD$r`OY{Rh}Bq)+(8VD@@|+Xs)!}P50&#~B`wtp(WWw7&l zGb!F|oOs@I8N1+!BM_INCqbktz})I-Y|(rCu)TiXxO0bnp*1$|Kn3kLIT@3bp@lcE zjGm2TMZ-ooZVFo~f4ne5$~-JSrxQJ5hi!mZPOR$@lPW2&@T5sZ&fVTH`PpVe-IQxg z-C=JC!N#2HJ|>IU_irO!EuUyT#Sh|}y5k6RaqWCEVq9Yd&#`tZm1z) zx&ogTOjFejeT2c6*|#w$TY#&a5Qr5)VWeQ47IJamO5ioJCRu6qy_3lUzR3` zUk@)Yd6{*j^TFXyg^MFA8wF)Y4riXcW+yevIx-~tOBOP6{-Q*B8|1KWYelHiW43&M zgh#e&mWyho1|ua^z3TajPj&U*U~2|G@C&H4Jk~8`nkP-?!FMw~>#GE=hZTtYnez4VW zy*7^IBn+j_Z-dF00~vUuIHHcYjx(0RZ2KInzJkDb??Y_^l~x(0>J7$pNz#fVN&PeP z=N#|})3|Iok&?PtuF9|jdL3<#&6?2|u25;9-nOZ~lS6#U^rJDHn`N-LKHJ7?WDfeU z3e=;)>nBl=c(#|R+y@${Zzj;Oa&CYa;Cjfr4LX0&T>bM`>obgrON23+AIl@jNqiK6M9Zm8J_s7$G(jG_YeB+cGu%5jTaF@*OSn*C)GB2pK#>!Cc>CeRH!8w(L|Ki$rE|Df<@x#=VewM(PXw_+3 zS%dI{3^$)oK31-dL=Jfj3>837I|9NyD1n4~)t*BaPFt=#bp-e{-Zcr>{g;TTAC z$?{}^Z2rmEgU_Wp@mV^ssBO37)Rp%7x~+(+^MF(5g`<$2IN7et`ILa&4HV0v}{mq5xcO6k##z71Xj?;%Wd*jr3_*;Q->M|C1Fq@eBB>;88 zFYNqOt>C&SBIKpnG#b3-LRfY#Xupc;<=Uf%1v2z195Jw&u_MZ7fg9Vi+2HCLeqlL} ziU#0Uenl55&$EiRwPG|%3OOB(7|5ohp#&Dfh9~1%*mlV3{tNBF0CC0O!|BT(tWYrx zNae6HDht~xDoPvXQV>djxYap6!j`HY$Z6oRA}mEf;a_{hOP>59t&1^S2@8}NIFPI^ zuAq`k5oP{u3hD=l3bZxzM+EUh#8;fv<9|kJvw}{r}mrHru{Lg)E!?}h$Pgl@<`h(Qi$yliK#8G8b%XA zY6nz`A0k19PX;bz{#ml9$oe*v#Sj?=?DM4^C#Zu!VGp?p$np-rmqmXo)#E z*J4aS6E!#2ZVU|e-prL$3ci`kVs>0DWiFlZQ3L;XtVXCcAVRSo87#vTr43ahl-VhB z2he4WE6SXoP^GHK)8$uE=FBvNU~hFAn?YXJpGs9$r|m60ueRa=hTF?G=d8Kx$8#3{ z{k+F@&j8lmVp&vTI}+$NJh$E5nc~dIneF|Hu0fi*iHHmacbwXBH5gRC7$t#oXKvQy z?Yz<0AU+B=&nL_xXL{_7F9Ox09@E*&W!&l?kN1VG=Q6&D=!=_KA6}1i3dpRzog*oa z>|d_j!DaKkNf3}+G9)9EdUsU+oZU<&aFWr#W6^Zcg%}I^kXPELbh2$V#2b!PS-L`- zg6{B;)7>HN z(81fLR(SBWk|CTq{@D`b{ggXBc$4v4D0lcl>qMfe^j+K7jnw&gqASlh^Nq*H*ue@D zWR43wj7ot=3jT1QYrJ@if&W88qcUgMmB8x0(Z$2_11|J)+kqiiIy51jqpLBHKjwE| z&U_B9BB*YTr3*-5-TdMpT@nk{5AeG$l#&IGF3?V@_eCb>7rgBkTwj~KgKsIaMf?5DlBL2O7^4^^y?DGd)NJCok@ zVku6n-;7Bx{mT`oyQ)(bQ#X0CB_kw~ZSauw>tM|z)~klE$TIL;PA?#DK<_@@QiqR{ zt}BF;-HaHLf@ltUC@Z-rR$i~^A~WD9ROj`0a@lVWs1@4Br+a_dJLsP79Bm&z);T52 z4wpx1C~YriF|ysg?cF>s4r$62DI@vs+$JUJFP4#Z-Od^#ivJ82n&aCmXm90@1)?R6 zQDQ6A@P}4iW4w@p%a|-ah##^{l>wh?s+U;uJ$OQJ{L7hQp~}%wIbD9wQsh9`qC6cw zZ|iA`!lqu#2mdP;9K|4!AfbVeyYcX?hv&2d`o25XvW z<8p95OIJwlruXf_V704pTB#J~QsJL%gNtZLcdZh~zYJE~tn^pJb*WP8uU9dUlN78Z zuRz=~f5}i!M1f!S-o_ZF(q(A3iCPla1Veu2X%v$lh?6rzRoN#^mA5!Kpvy>`Xjpl0 z%UD{A<4s^V`Kih@mnrGF@Csik)|JRHtQB{UR-7*d+ZN`ffWkJh>KPVthhe`l&{r^v zJ;Z4H&;gkB$2*VrcAx#YceHgdcr9C;0?W;1uCTIq{LEyCe$ST;dz|MR>sN!(O`LPb zH!q$bfvf-GATa$zYDj>dpERX{Y4f>-m~9;muEso*m@{ZAS%|IlI4L6DdbH`EI+u5M zj)hxeG$B$BL@M(@JAi@xsT;mxDOa^e}s2HVTRyZS?85+cbCoPJnOHX&VyW8Jy<7oQs z;Bqhm+6(3odakZ`QnYTIg;^KJLb zOi6r50>dQSZFk-lu!m1j~01=a3kSp*JDNu5}CPfpp(*A7PNHC?}6hd@|&6EBdFx z&Kbgx%-jGJDBxW79+a`WoIe$M&O(l1+Ir5#gm?G}koVyjdo>`3y-0&w&_-?EvibGQ z0zeVHCy%r=C={soxrY@5+G@ik9amF6MQ=M&CTXaA>y6`2^f0Y=1^yK*I(Ps|g!H^K zQ2tb{6q&M!nj18FJWoB5^RU3X10SKhD+E_+z#=BpymFL|h%O&C)~py4G9mrEV#*Q> zOJ`LkGUpXgHPVpl-Ruln)_6-dRQQ4r3g7akk*Xto>q)g*`?m+Y#lSvbeaixo|CCOS zivElE^le%m3HKwR=n`k?w!{^hHusz2{-wEy(w!*9NvR}m+l9NqK2ghPR%K<5>8 z^%Yukh4ZdcIo}ynTjqLAOg~fOI$dE3=9F8I{`FMhZCaFd9*x! zwcn`4(|(gCzO}^P(nRoUCk#^|l;q?ww*aZ6KsYUJ2NN^?M*K_9LX{98&s zg?xL3TrusFKJ0b3#RkhKIkcMHN#2MzEhJ_n))*}tX;}`*0XeofgimAm(q#sK8OgZ6 z%cw<;D_JVJ6wC5GyfK9;Wa$E@kXR86$*X~M5+wb&9FAUb<>0ZC`@1Q{E(Vvlf2P0R zPqNL>SzMppZ}iWs(&If#b@qmnK5XGZsV7B8RnxUD`o~cT7dTPz#`n@zscglif~lL3 z^Lm2ELnpK009XC3oHTzO^?rqOVI#xu=~?e`fRA;4$QzRW@oVmDIvWfo$lB!cMwM~5 zH+{7;zIrj{yo*V87)soRTc-rFl-9d2BeO6Ke~$|rnHi!ow@!~BipN)UApU7RTW|j< z>;e*>tLCJNM+j-=f~AGkNw*y+;>oIsU4ti-nE_ovY%mGu^g2!H8|!PPw3KWFI`^*R z-bCgL;K>)^CttGThqsd}eNH+(g@&b4&dvXJZ^N7zL>+a=YN~`9zkMn?}%Pslg*YWzTc736!*k>SRjPi>2d86O@m1 zI>{{bBwmOA+RRwF7ZN1r8!-J;( zXY+pR?Z(<${&H1sZ*AGU-@)A+(?LrckjWpugF2q|E<5t?{drehgqs!QwhoNZo#EQr zcQBvb_q6dO^ybxrn6BJLI zOwKQRFU3?WaM+#c7v%F?_)c8H?qloW&3zn&S424QWO}S`Nn0V2e2z~(OgBB zlTlxA!jQd6AaUD=&$?yzlg z3#zrYDMKKeYDAOnspx%4`y{#xYgpM@*+^=Mmm4#)<=SV)*i6 zCgWIu&o~F;Vh5ekVG*8NW>9*Q38JVl3b+ytMZAUnC z_%A2IqGAZR*ZpaT?1S(N}6M|8b1{#nH>G-ERB#-Dc}uArtYYVf)TSy3X+J z$;%rqXZ}2JO+Ul2UOu3$dXI67o>Jf}6IhZOfVWP!aUt9p+zvRk_QM2G0|#)mgzYm_ zek@yev^m1Z$rs@#mxN7i+LIx~+;{|yh#|SuMrgt6%TdQWZEbI~wW$PlB?tCyl@Dkw1o!5Vdpn^e&!^AFIr30c>>( zCj8k&t0@;|Z#7!&d#mt9i22~PD5dRC*1#X@({Yi!EU6+R(nZ8{crM>+`Yx)N#}gRd z>@7)&lQr&w)Q$-p147(w%p-Q54FTS2RE#QvaIbr3uUgpSRBoIC>#A9udPV(Yj- z?XPAHI-e05%xnmGZ<~sdsB6=!Ji`negoD@2MtZ!3SO5h6HB#yx$;Q#qiO+)$+KnU^ls$o9*!{>JQdE&&2|?ODf4GGuHQ-$8O;4n{9$7tAJD`^ET88&etn z9dRJ`UQDUAV%Bif#yr<)dNJT)qEz&4O;KRQwzL*~J?vLh`ddv&RQg*`n9?pZv864~ z1eH?J)Ll(nn_f`V#Z@4}8S-`-UdzNC?^qh)N5}qBm2Qf*8fK*CFls1eDGbjuq+2AQ zc1QqaScY^LX}LrZ(Hs}0kA z#wiASfe-nO8g|(Wu~1IO*Zl6ZH=#SBHHG~0?=QGU2OW&oUuhxd3Mu_-Y#n1l^D^|w za1^N0yS%=@TY7_8FT>x97e3$m8=Z^Z1DQIw+pi^HA#byD@gnB2aF}d;I1V?n{>J_O zX34h)3$@?(cUx^Ab&A~s+>Gdt0Ab}VyQ_H<&r;<%b#f<0M%Bmx7<7O1BPgV3{Dqpx z&APRod)!GXtbmmEIvMj6OX|5qWdj)-Z}SjVf1HexZ&Q#tcn1cJ)JbIHk{iPXV`XGY zU-T3qaOxe1HH+$XQu#ATAeSXYntWiB826HEkIwtwpx-4zx_vc-UVz#lpgzw_3HMty z4Honts{H71&jj#fjN8NuQ>_UIamD})Q;RQf0Sqfbag~YuWm*u#97?`ah!zRK0IYr= zl!w<2*G`VkhZ7qxEp>R!Jrd*}N%kXTLmOJWLmA2ILGOAzI-d-xk^CgtkC1Ih%Npr54QN0jv_O@1B|*aLTwN}A0O{j>8?YDTPu5rte4fT{QgZQiHe~Y0{tRFzjzCL1}(o+ zXlz-&c)Nxgw6Fx+=uUWl@Uo>AE%e`JtN%8`Vp7;jfWEbR;I+iVuh;L(FKLL0w3v{e zzP6=}XV|!V?n?E7oz$dnI~(5^`U`CgZk-F@((Q}Rnlz3XsC>1Y9-EAL+Ymo=WEzw zSpz-f$CvT}%rOU5#1ATPe3(lQ10?f%DWYj5+{1BH z+%#k`v*=&j4nY#4x93G)Joip1or+NoB#r-XB3-?6PL;2jVV6qgWWDHZ`e{$ zD7VX>R+uSsRFMMlHKc=b@vnok4nq16&-MoKe7s*1aY?`N$6NOu=>TZF4%R+DaN~rV z;rk3F&{I#cP*StV@UnVd++OU~-$rb-qO>-;&8He#2;LoUUtWq!&nYUf8yT9b7x`vQ zi;~O_rJh%NV(hNw^{ebx>ZkOzL&qx1fkOQgIXXxbtzVT=oFt2o6xiX_^KkI3JSCru z`@{3M;xzBthE_5&^-5E9v62VUr;h9rI{Nosfj9ceZRpro^XW5y(go#P3|d zkEml{hW$Fotfo$(#!gam*tL7g&>24L*x5NeJlfqq*zWGhhI44p$;NZ=SMHO1mP^pX zyRlg+-0N_Y0h3jDES562Q&(8FQ`()AG0P2uM=0vt@Zwy1cl<+eaUsN1zA2%w(tz=Z zh9^$xIrVbK>cBnM!Uzt+-4b`m6LI- zGdwboAmwpSq#IVgLon^xv%L&}rzZ2nq=S?Tvcs$dxIRej#8L`%%dOXX`G`|jZ1I&pk$o0Fy4hr z<`z`HL!q4ch>T9u_{p6J+L|wtOsgkNk+qN8pYvjH0ppb?PfyRM?&HDbINrt&@3fOx zE{kv6NH8F=N_ZAI8Wt@4)AI?PK{RWSQp(7vYj`%k2DU>uGcXuMnlkdHFdC1>6X+A! z>2A505sp}tm{;K3e+_M) zP3ivn#MISFJ|=x-X-D7RE&27s_|)gihmyWrS!Mi8EB6&_2RLjU+Abs77iWOF^odR z3|Ar@;SUiE$TT@>%LJTc${Muk`0*7>9B%J~--O)XUH6gA30yM8E=vd7tUtNqSQFWh)k0yk#ZuUm?ig(Wttd?9TP1LHi_G1^v*YNh z(Gj*ZR+j8sALkWEH`LU;I8e+`NnU72i&}7l)HV1iq8|pM@y*MN=%Wy2Ivy>*=yIL& zU1vizIXQw`31;77{PQ6bx(K)=GiHUB%c9C} z3B=4UJ{fk*>vE#QOjV}3tVlJJ`^Tgsy0Ih*UGb6tdAx$ zzpq-V^~IOESgQn$$mPTnY0Q~$WV|dvJ&XO0Kl-OsE5Snev;@R|049=&Jk2J;D(tF!vbOHon5;Is9ief&VRm zx8TGIcrbvB_Pq6ctQRL{y$w_$Ji_I7$7%SZ)qvZ4GT!Ke*~O()D8wVTOCex~c>GtV zryvo@_wl$3x77@;2DqbsboMr~8dnxMN1$*7x6jCtC?nsuXGq%!_vp@G7V-y$0+&9x zXC;+-s=k@wEU!1~`d3@T_lcJDGx*1U?W83_;KK3G`E+-pegD@2Qml(+gZ4ECNd&7O z<^TTY1%QsR``0+c;M3OeU)R3>t80wz&q?NQ$ozTj`=8YXTXtuXH8pi9Z2~Gr2l*0_ zkZ|)w<1h>}#1G{`*6=!S<<&c8d#eX1KYv%|K_-)={o7nfYQUln7nxBDsnPxGf}VGOo(oXo2UYPR;Y%?TB0tEm#qHL4#D9Yu)arSh zo3Zoqu{RrP-Qw1`ZqOGj1yUMZD2QZdSu9lsbL0rD++ubyKf=VbftIcFs*F>FVNeu9 z!c;LW<02T?TM#Ezn4A^GzI3Kf4E5SYOOBgv(0I4Y8L{$IZCt(guMnKUR$#Faj%ro4 ziVG{10v}dC!bGrW$OnfkQ$Og-Cd}gm-!Zhmb78Ni!_JxmV$3uML2b@?btoJMTYt&8 zxWNNkjGG9eDCQtJX%Y-~UCA8MLV{;*ndvKBQtyT2LpG$iX*nLf822W9tiqP4+kj62 zO>D&|m^=MD>zh6)BH~N~t)zvrB3|2Ay|b}y$q{ZH>GyR!)y~TIDFk7{Fp=JeFwM{z zY!>5OrNx65OG{QPE1xQC&FC2-L@J4>38pgF{CQ+Kt7P~bVMP^VkTZ}A2=Xe`d4QXj z87{7vz-}V1zsgRdlXPY$ zdtP-q0-{rOb|N47u))eY3fHR0fbd59Fd#}$%vNm*r!;|ho@HWJvgnm594I!=?k-8M zuBj$Jyd+9M);FJo>(vBOhD5BGPVmY*nAjxgRYJ7yzR2VZ{En^;W@suvzu#N zv&(n*5=mN(L>)3k6!u;vPIib0(Hj$^)Gt~dJ~NFmFeT9pV|w8P+;B+Cq>cHD7!7tq zrzBw>e7&CW?;ZSQJ4*bf(%yWlgw*pg92uK8DOI~FS;+xwPQTm$`ed^aB=)8H!`L1Q z?RMiQyPC;*mE-k+JdT$3Q5(cw>M!*3HeI|KC6i0IN4#H*VwrQ4#);1fMG*l?h#>KK z!jm#Cl`Exzr~}L=FZlmbD@P+D=0Lc8fLu$iHMp8MUiom}4wFea;SHGRx!2C2Xq z-fsLn#(W|taUsfa<)Cws@o+dt@G6H;jtyH~4Q;+({5w?2kf zgXy&Qa)28GDyPCDvDklvKEN$gRAxdDFRUHaFnNsgvNcUJln;t;r3G?!*H`c1oUGq$yyT>$vgC8WGH_fD!!fJ6 z5uq$H_%S7Ca!UcRM&0PHG*FkAavG0L{jHSsw>aHU)_`m=_N(dXgPO{thb|7FG3Cgj zPjCbl(&2%nCOuSJjaWOj1V#fqe<>89At4t|v-XBg!Rdnwj1TGY5Qku$8r5yG;mWK! z8{3j7BNJsoknzjgtzt~`YlPUB4qNP_HkW%HKD<8-F5!EGTbj4Tddmel=~^egWN{F$ zd=Uv&3Hz`t>b_h;)K!W*Hw$R$*sMz369sZdi~dwCdN|WqQu?;U0=-i9C6>Ojq>5KJ zYs326+foHoY5THOh@>%D7wz(Tsc8!^lN21RARltIGRNFjshfa{2qR)g98YStE3Hnx z(7!wk{7wkQ+Vl?V*tXzo0|Q}KYtWL54EzFslg;Pfy}&=Bx->#+ih0#i%(y8*%0zdK z%Srod>oPhpIcoV+tcrspoD02js#l}Renogp$>78t!_dP=xP^TO4$bKSCmLVzuylmt z$u_1gm@?kZj3#9(B#QjNksJ8sm_#XoBRcXMhei~P@Oa#01c^cAldW{mc(WGnDo2c8 zcPg8q8*O~x9!zNZi2~V&ZW{8*lh`168zg=<{uj5hhJlFb`lu;tb&nkBc|T?m|@pQdzclsy>L(rZWsUM=iZU1NBNljocD%n zO|pC0mcdN(*EQXSwjzgokR}I&#MPT>ar7dE!Dm8Tk)m^mYtHC^n8I89(L|#&T_Y`R ztGdu4J+G8+0f)jnz9su!EqIKU86DY%X{j3`NFMC0K!V_3_Ufzp6>Q{AVIt15z%hPP z^mK-O9xP|kH>}zH9bY~%VU<$Vbofu{xZD$&LC$qsD5E}XhIR%%;3X0DDEM|=B(4N< zW7#av?L_4#;OexsrppL z!FNZDGRusg77SjtkzF)fA`zZmy`OYaW~+RQcz^|R!FvyN*Oy6Ictztt<{X|-!sXJt z!!x^PnX}CXfzXaxe+V^PPSFpOdoV_;MR^V#RlzS9&jV1NBS~QXqm4CzTJf|qSgn03 zoc$?;vP(=?NTm$)c>aGHZkmSJ6o zh*!%^j7kDob7;!&$p@S}J=SiROwGYMZgb#XPfkJz& z`E6xyuTz^sbY5X-U|I-^27?*=XwY$K=K4lQt*VFbr#Vs zo#(GL{^N08^iic&i|Q+#eg6(RKw9l~8v=L=->?VsvWCWa(j`?E%H-Lws2+;TQ#W3< zRBPzU4DwhjR>5p`4cUBX*Eh9d%RbbXAA61uVm|cp_>%h4v7yS>UucuszPTn+ z^9c{=0Tuui?wt?Cs5&JtuC&EsqEW3Ew96+Koq8mWI#PaZ?N7J4Y-*{Omd1vLC6KN% zP{OCs%bk%dozN-m+x=0!d0EHi-2PDuIpft-vPO$GmsxZp6(n&K=JPolYBF0JR=b{w zhLG4mgyyOy%Nadk0XMz5YHj-^UOG%jqD3U8&uJ#tE8B%oGD0N2g2=!3w?Q>&jH?tD zmjs`iSW)ZGCs?A|&!_z9Mh_brpXTdzd@$6=iX>V&IQim$E`s%yCnsM#;jdsB_Si`@ z1?NLfzId9;(u}oUW@jz5F-RT8_P};XzR(`5^Q)SeTmCCphS?W=6n$L78qs3xUkfLd z`l>m|Ui03r6(oj^u-SzqQTl#i{tCV?v?VSDe&LKjOHU?vr%{fI=}lc+=i-|e70ZH@?FO= z#27tbAsID0NMo72LXf3F%S)W65ipI6Az8L?mtR50MA^9o%b8Cj2qEQHoOM|P_$7_? z=noFH!1w#|7rj03A5Zb4nf;=(1hn#Rkuu5fv33>7|QdELTfRDS-T^wAN*m+J{h?3u@tS@fKi3J2Vpe z;x^85o5)IHWD8^9FPC6S+xlj9sgD|8(1S06e@ZuVuivMqoU7hQ#vpEqEchZ0WbBT2 z4v%n-FYltWMEK{#Z-=N~IMJTM34z-pG2ye7ZCqz{+=Fk9d$`#O?l|GQN1f(Id*hGo zjb?ZN@zgMd;A1{g3{loR8lZ_Q@L+bD-{Vz4{B~&>ieNb;&qAUEqng`(-&h+r3E-^W z!5+$@O(PaAK=3B8ox`Wyy(7TH`(es}Jmq(=d&=AG0r{7~#Sr_BWiUQujr?QW3sjNW zJ$km&eRjn59D^QbyeSW0kog`6XjBlt;SW%X2ly{Y8%+5HzC*|tFQtxT;Vw_=_z#sP zgUMt(X*IVSFHwm&UF*kXUI7P@WZ{`S=!m(!QJ^XT+ zmDFs>yoEa?Zt=QI+fmhHAjWk``eQgoWjnE)wF^f&UNMuK%*wf}a8Q#ccY%`)W3gjC zd~F!DDE5EF_F*g2+^2Xm>~i+D^)ShQ1hGHCdtiNYuT;R_!_JFP0vCd?JoZ6DA@!az zmz4#^-?_l8y#ue$$QYYx8AY@qM*mET=W*jOIV8zjqq?4;^%dY_*n^jYJ%+Qj_WrVW z&^>PN;=x;$OQ({4IAY4-WM|TwUKn{Qib?i+l*3@>OCh1YmhXp?*_=kG?D=s3kwaNv zWHLU~ObpXV=h3mb7!L8=VrwJwSqRe52vcazz)XJs&@LWqQ?&Fc%G+WV-kI)1jlk>Gp zEeC-N@ZaGozl0E)kSB$vMPlgp0&>Jcwog{_qwp7YQB^-aLqGbk*luG#9O)R1)^v!vQS?|@WrFaK|*$pHbLWiTn z^K+AjNqR@p7mw2Zc~S^0l?-b;$*I*54&#PbRdX!w_Dyo6)y=Zpg-z2@s%n_z+`d_g zu)0x_x3Ed%kjjx`#_by<=&IYptcA@<4ec=M+c*6o&N1Sp^^_^9aYZh2R8@QwFajk- zNR`du{L~!>lNVembKSE7n$6oaEJZlKd4-pu5|bz~>f=vqUrgx4pNpG#j7^iXi&!GZ z7qHt(3xg$Rw&Lwh2E8d>rb*d9AT2Hnmc?AV9tkAQEmMp>)tQ~@jg(djm)*55IUgaa9&uP_)4PrU+lUB<5}e2I(e@^96Lh)P&kJZ3 z*uoU)(Wc9TTKOey_PZHdmKk5QwH9JfgTShiWsm4fXslenl4kAVZL{GeBAQ}1=d{bA z-?L#}d{N6p2z&^^df59kqSez1k2uVs6h($C(_w-R@}u6g!Qvn^Whip8vsyM)tk!5p z?Iw>Tv|<8WE&fs1NGX_L0SaavZ3YNCh0vv-m=P&VOb(Ee%?B}4I4H;XA|^B3?WItB z7}GCzvI+&rpIpk}3NtiUC9kq8HO~|CkQWd_vmbFN?lNO7UHK+xt{P1iFcg>Z>$cv- z4og|qC_lVE`r++X{MNGHHDDKv&{izdlZfqk;S%af5Nx=J>ZvH+hVjKsi`sm*dgj6W zfOZGhS5*tz_GtLrn$Qw!;9M{fFxUsGZLu$N7lQ4{VR@;Ug z7bafHsu9S+3M++nsSgvCEmzS$@}>191@cXus$9@Ar(|vI2B5ojq18F9DS>*o8Hz=f zg=(;on;dQ7Jk%huDpw88bt~x`cX5hXe{v2Y{cc0OiT&iIwsUy0c-%O6-+f}9)V3}+ ziqCnD9G>M$6h+9DX3JXu_}U7L)}i}S1tQ6?(E+#hnYVDY%~}OZ0L={u%Naj3^eQxN zli>ITob*P|Hi_BZAHhM0{kq|Jv|{IlT%gGuq$e&?5^oJ<&7H&TANRZahX17F%BYy%(d@9gjHNk=ZQdC=%JY@W)t*PwBMtGW@3IK%cz zkRp)^kQ|v{_n^L=!)Hg`#*^)%?*8_Vd%J>ncmMdw{_!!sM2OBVa84Qz)pA4h4~@T# za9A4nWv|wS&=4EUF0!iToC&^q>9o3{rx{H;RT?0M#I^V$1~9lJR%nT#Epp8g4D(Tj zAEV6?W-NOgoeD2m+u=Fz=x-*jw;qY+-xowF7R8*@EeQq({v@q#EmefTwTeF(Y7V4~fYe%{{C|M* zKjlQ$Q_}LNXv;{p3Ohi`WpECMoxZZ`EMp8tz5 zZP%Z+@%fpF22_pz5lj?EF>bWX;VL>kYz5lwOREUw>STZjK zYmKlKwuqW;q7s5HWdSmTfr_6}Iw(V3E8*(|Z53IP{?r-|^UHmppf=>POn{(ot#TnJ zmJ>OIAJSP#QBnI*q*e(3MeAwOqVew{hv%q zO82TsNgNfEGHFUDp^DbHaH18Eex6i}v9)65fqwNb8;HA50Q5WPdU^ z8z3BzCopv>HnsIPhf77h z<)k0pW~zCb0Uz!F6yxb7iDrzC5Zb^I!l#sr7Q2i(;r18aD7i7Uu*$+zFI3p zt|X_Il)&Lgf-fcBu_Zrcu;mvVkV9${UDD)|+KEdjtI83-PcB1<6dgzK6~G({5E`E5 zn=u`KuBT)Kh61kU;+zM@Ty9%B)%9b;llDva_wc1P$8y>|Vfu>Axg1KHV})qS0Aib~ zzT)=3bwt-DgecPauIJkU};R3x&w0kYioq)?k1mD*cxS|v|-JTAk6 zq;EmAypvCe8f^{uLt@C}s-+}}UPZhSx&iE zno--QdR#SBQhYf&rBi-)=a_lQkb#xLWU}lP$szSZ4ohh=eh5KAmK9>6Kqw{CY)OfE zrQ(7A1STPEKoZjgB$6dSgd_!UO2>Rlk?g#rqI$lP}g(8CPlo$ zP{I^vmjy4`P)cx$eZ26qX=_hlrtQ z3hdH-Im@w2WG!Qh2(V&#k!6rayqt>pkFF`PtitI(kj`b#wme<(>GA+PcqEHDG zc02N@)QtlM*1q1V4x^UT!f7CF>9(3s$nq}2F)ne#zRu_+4o8I=T;ZuaRVAyn=G`xa zW}35xVJIhBuOo6VdsP5-9aH;L|D=A&1eXB zhcVH%r4lepDFTW`AKMZT;ztqGFyw(_ViBA+DNMPm>!cAOtq9Dcfe9Qx2@*Mm457o$ zCNke_b(xy9OO!z(Olry(Q>9Q$B)UpFK7uH=Whz7TZe_9NGrzQ6tkg?B@h1*Nb|`6V zfedL@sX#?P60*3inPDW7Wx`rgt&h=nwryOIRl03AOicx*qrHi5W1$8Te&%lgLu>tMRmE*TPQ{ilj7(L>FK#^n)$$@{r50n=`%-?$+MsJ8+#m0!k#4qw=Ve|Fu6 zg7ZB>TqZ1w~;vn4_Qn$x>!88eDx+5#wDg3K&V3?WZV?B<-`5$wi;$86Lz@?V#iK z;Uebq@aW=QbdC^XX(_{#EPD)D-T1eVFc6g@Wj?Mpo1THw@T{R;$W@B^{dd&qbBB^~ z$5atv@IJ13Ao^n^S+lGJI7I1xPzq)TM-7&o&>$@G)m!q@pm#kUMYzN`mk!tN253Pp zhRR-mYSf?98(_qF4_`n{W6jvFT5p@xUzPRQCjj%`g@19~9+w5>brY=gB6wK;C(t3S z2uwyy;Q0C3Cy=^6AvhC zp=#k2%(bwXj}}{n^O`nt-J-pANscSa-eVcQ^6}Q;Y1hN*?LF7ydEK32j+$?|MPFK0 zR&HrRY-TRi7l3IZnV_)pD||*|AA2GAo~B&d{7AOIiyyXhSdpFj!GFNYEhR%7BjK=H z`-wh)FN3AIVX-G_ko`OHRc25 zm6-u&SdhUnGk=#K0IW38^Fk8kkyx6E2BXAG0VE~OEujb`HK>G=0Z~jdph(mJ6OtUn zsSzP8#Q=mNA=A~QW#*|J_T+tY$2V{NJKdEJ!x~Ciy6YGYU|>1W7y?9=08Ii!w4+vLL4(rTOrlyr z3lzyA(=>IoRAhLS^;uO#Z9#0?@?N{8&M8n`o0YRnEspu6O$rnBJwgK#K2&7Z;3VA{ zEig+3U?L}zlFC#*R!PQ#jdkqP*?bVZPA<8z9;ZVtTdf3MYkZKTDC*T(M+9n!fy_y{ zj{dglvLwfQ6=#^H7z}%u1BnSK7VC0?S5qJ*^@nfnJ5{VD_jBzk@T?O@>o4%qG9{N` zD&07>MnZb%uZlnwIkNbkOKU|+$t}z2zU%6b7OhcWQhJ0WW~1Q#$x)j?w{!v}x3$ze zf{f}PFDPR^!XO4dG%#cFAkCHv&GF#0jY4789yiDmV<{gix01S)272474fl2tsNDp%&i*5mE>x^bF{b8*6Jp?{G= zk-Zsx0Odo}iJ)1aOavxX0^adYtxB|y%1mm+m@<%zf)Y_+ZJG_=a%Bv-V95wN%lg-< zj*k+8Q!lHeqg##p*d)+tC<&i+1}LDMS9Qr--{nI?M~B3l6Jz`v22g82WLY(gfvroh_@ZS51Tim)9Mf0jD)coW0oTqV8-2zXXkNg7*4}adMpq` zl3p?R2MaI0-NxopHm%h3@hsYT=z>8)8|ppmr!X_5l7GWF!T2P4KzZG|%v5P8zbcOG zGY@ltx$-K55Ck{eBEQ8vkpj_8rc5{W}eXdqs8~n z!S?@)c^Bdc8Cd>#67u~so2wPt@_jU;#l1#D@bfGj>sz$RDVqJK@Lh?2fW9W$X6k^@Rg}Z$RxPR5Z<9YnJFbdmAL~m}?PH z5vWrkHO)P+`BbXN{CV4GtjWu)`YGC6l%gnY?pX5K2HRdrB7tZGxEahnJaNJJ9&g;{ z^Ax+7UKDi>borga*g)A4^=-={0y=$6iHF*lW;F7)vi(Icrlpp>5uM{g!c+8mt$2*e zf3j5E2QKbp%hSlbx2W4n3rI|K(jzjN=kM=oRJM-*^7NXXDXi!ro~UF)uGQ*>8dogo zc;vZ4zJMdLi5;;F45Hr(LfpRek;evvBOnm<*}wWGv!hzE{Cl5BWB}w-vsi^UrvQMg z73W;vX9y@P!2yV-n8)&*b>%rjXuEk7ox`S0t0~uf^&5?5ZV>ez(lx8h?D(!ctuctK zu&P~&3HEqdl3bZ zs4I9kUi_E9htmo6QczmVE}oPm`yHCWbwKgC)+%9Uvh=e+^I08bqK7^tbBWySfS+EV z`Ph7rPzxawGRX~PtqimL50+zwXOS$6V9U$1iY^4+*eB2XK@IS3;Uk4>P|kDjiWlTsCkKBJM?eAAxBY z^Fq@S^Mcb8^TN|oKd}Jyx@&}}XO{%2X9SXdB4HYqx%ytmXQAq{^500P7RE^D+z=0c z!*a~aN}GYTh%QysbwJ^&bvJk#YnZ(){AcgUtTs)ri}1DvRV0N9 zJS403g*M0s|T=v+Mj*v+LJ-S%L zF^@I#lUJ?e(IEAI8ckv2Wc6T^Q0i8<7fS^Xcv4CA3~Lg+Jc|U0uQWc9LbLmFKTn!} z!%5@U8|UYZr`Am!%a_?>ziTIu<%cQcxK-rq#l-g$B4pndr)Tx!Bu8J&GnXF=x~K+ObXiDUxu6OCzDp+vtaO_(QVMwL*-POPeg z;MDzS(^hJdQxx_TsW~Vm$^CySlT6-F-n!>X9Rs%iEkzoJCLDOsvRv6rO`ZuHx4a|~ zd+ol2gU>BOM~_=fOP+v_iH4QPOb=)t$2rMkg)yW0DrQw9+9^6#)L|sQ%T+Co7b^KByV__PTs@oGbR~69LED9XHtONdC-wB7P z=!j=oXZ*9WL!KEsLjx339T^>rdSNl@^CvsF^nqL%KTFuzGKICIz&+!gP1;a zvzdQu+~iTCI<7nlRWi9IEKsSA=d(`bmSj|ipLOcYXKO*)c(O{H@MSZ?zk{? z?@axFA49!ni9q(>E@1q-IK4exnbQyi8{%J~ueUER-(&szu4#qLoKZR27@s$Q&-iWX zo_Jk1`lbDly=MSxX$(xVXqQTWW-{&!c8!UrIIeSQB~NVAe`<+d|vCm z9ZZ~2D^!wanGp8RN;&MAQ51VhK^lAVXT3a};aPCq#+ep~RFW1VvP-Eoc1XdfnXCWm zZ5An3!4;DJSudNLDu`_H2L@M_Co7kSFB>rb4g;ma?6Y3}7~1%Di`~Ot2a{iigV*n~ z*gYPcjYs`ycf12l@~SnpZ+97w?7QO`)IlN9H^kMA-CSSalK(gVCq2D8dOW_FKx-D> z@Lb<)pEonPo8kO&Jf5^1>w|}1udlDJ{*QS&Mo)&L+m!x#eXXTc-uoJ8A|CYQ7I@YS z2OF~{Z4h+-<1Ty6hNqvqfRgXOSx?x>E;$ztuLmPuXU%znvp4t|?vzNAK;_Hxsa$I_ z9Lt}veu*iueKQ*$_kJ}Kz_v0Jn_2||74eDhN%M`W=l$WNgBseeC&SqQAAF7YAmC=Fe+)t{AIu=keN|Cu?_(n|#c;eK!47Hx=3_ z^JsWEkSizKgUO^b9Jj~ldW#ij&DZz!ix3YXXXn|A_kws8L784v99|}QnX)X37?kZcgt-GB#bZ>R& zPUFz&Y&EKHH*}^pAQxu6;RtNQQHi7<(e}K$L45OzLE|Of+#R(snBr06!50qa)}vOl zi!!Doj?X5;Yd$>HzP#z_`0+JVj_Qj6M%98{8Z&VE<>mM_SwCw;zP}pxZ!Y0SL_Vs# znGX7GaGTa?J7}y`+EAKsLJ;sxVg-~hNC;!Bx{jP-F6r&7@y%$KY-o|*$!Fk$G|dJs zN#d|27b6IgIEE2PlIBQa5|OxI))u3kYW$d(b}>FfM)DAmM+LFkWTjybzBct4a68){ zdEXAS#st{K*fiAk#MBa40c}{ckf+&#T#L`)QZ=3t>+uvjZQ&IK zHfXKZR6-6Y;%8dAL5dbLNywGV%h+$>g1dLkz0S%*bVEMXn~A|mfTcd9gc<)yFh{Nr zR7p;FhqRO>b3IAQ`|u~+OHk=Y1mkBBn@}o(@KZrB`b(?lRKJ-*TDgnkp{iqDGqu~2 zhN*~CqcPjWcRm*&VfrHnpGh$t4K_7jh&5_g=0OBOSS}ogIZ%Htkdb{J3~4?ue_k(S zw4FoCNaCnDbI*gRZb-yYZb&)?Q=pE6;5n)}V98f702H+A%&HwI*y7Uxb(RRY_v5rP z;s65Q>MAS7*TdP_g`1MC!_ndSxi!`kJCf)oZ_u&Fd)vF6cPl%Wz3G&c=`@*wb&nG4 z_|@?868YO$mLPhkgGZlx{Xys9dbT^jnVG{;=iQg<+1Km8|DLVA!$}*W5_a;|BzjD&t{^7~#H{Ud$cl=joi5r`np30w|{LPo1ieH}i zyQJ z{u1KP7sx?^{AI%T=+MUcpP1%9naqA}d=(ncpG`9+xQAYiviSXgc|QQHnYu;q{Mu;@pb#FY_lCfg_`d+zgf@L+uta(QSRNBfRq#` z+`F&AWs+iad-unfAar{NJe%#W3`XekZu2Xk{^Qq{Cd5mcYt&OHZGaQ)IVJ{bMB#PC z7%BK}12ba1{q=@mH4?u2(|V=}fowprvH3M20r-2ew+-&+`w6YmG{}sz-LT{=?|i>~ zytlPKx)@+}D9M+UyNtEKux%3etx&HZpUX798h5%6zWk_UQNMqm~cicrd7a|gf-w8r{J2IR03hOu#Ywe zTlP2h&Ju!u+a_tp(m!mbFKCvSWx<+riHB3cZ*CgjR%fxxjIL7f*LC24}CtC~C#mH)0E7S{Q%}xC8VWa235HF+KIX z|7mv0&%Dl1n~NK+;74bpA#Ui7$h8=r3uKe^mLXOn9T5D2cICxn(0dh3Y3msPGr~xE zG_NUrP7$TNiz@(nqq9NlRBq7P$n@`K=wma5fo2wUti5_7$I)r@jBPKXD z%Q!X{;Mi1Oq)3=OQ3LnxnFILDHAZOTA6&oewcZ&)oXB7rZQa^NUgK{$){lcWw|e8q zk4L@p*+y6wyBJ`ZGm92b0&oBhi!1}_cMl$MMNZc& zm{4z`qD=*txTw3x-;h$x1FBbuLW~T}9nF-XiJx1;=^pQ_Xr{MccVHEYV6WJCKr2bHX1q#f4bUkCW1 z-P6r0D}1u~{NBSa*V~(a@&=x5bXw?&lIGr@+TU!fK4@J@`Tr9p>e*%|rad5#5%Fkz zJ?oqqDW-p)%vxs~_cw%Oit*ddSL?1<1(^Tb!wNzt5{BI2{s=&&a0>TL2@Yzy67Y{c zyB*=iW+lcyeWUUa>^SOdWDgqA#oKRSX_rVt({I|X(mL;vj z7KKYD1CG8JOezv#pn@fT1B-Y5zr%yQ)1&R({b$FrRI$w*xeKqESy@UVa3( zsMywu+F_scCa?6VbJrerF^STkbEkjR_+L=CC$0f(hy8CuMs_%Ri#)I@5`@|S#9pVC zVsA3#fQ*@|nJBf@mDrW7-AV7YT;y~XJY3UZf6)H^$7g%lJFEn`?gN`;^g!^xhpNC5 z9-X39u!k@+x~D%J9X@-i%eO6T%RxRpH7AI^rB_-7tr{5HFm1hyE&JVb4yoCE>}D@# zXV}8U&COvnNarK0IZ{27fWMqvW8*?v7$F0`Q^oUgGQPP^p8iRbML9P|6m7;{mOB;i zQ&a1fBi(2ns#9GgKXKpS_9Zs1OxUGpI|{s#rb$(r&bCr=pryI$VlLg1t&LRziXRM+ zW-iN`<`H(2`i-vM?I{<;7i%*3+*+!YR+gnDRDA?TQW!`ucXa)^3vj+gW|}Ksab9iJ zH-i{Ru~FQX8lRtoEi+Xofg?gjC&JLemZfxBEldy_+GM{-C=KUY z(_D^NOwzH4KXf1BPjC`8f6ot`mOK$vgCzslwY8Cb8Mj|VGU#v#=t-^)8&yA z%R;4@KqGTDRNGU&zv~15<$?-MrwfV;xg%Bw6JJRe7g*-9hc8H6LoEnXFzdqJ-I+Fy zdi~y{fqjm~{{!sA%cz8AMhr0B&`zz3+DXzj{E{cp0S0D3z1a+V0E3A_K9tM(*obz? zfu}tM(yhG_ouIy2Z<*pJk1+QTK@#K9_9b=-#OLyf>e#ck;sIYqdq6l|E0Y{9Gy^-sEpWlqWQkk z2^r{le8ub|4agpx89p4vQRs8CO@)MMHmS5$nYC?q?quV+dW9E5V#-iPNlo8zrVC%` zcSlkmY>Ky=f(+T;A($=&VcOcC)FG}j&*|pO)@6hXC1MiIa$hjpEKCZ381>-yS=W7` zfI0ATXi^m_b3IK+tUh!k#H@MMVT{u+M9qwV3|)F-R?d` zD-zX@w?=^ld5E+iY{fKY9U@>ep$Np6Qfhj3F<`{hBf9{w0$>)Vh3Ri4-@mnGr{dFqGVMmzovRb&j5_y<7K15czhtxOV4#xf%1c&`Cx(b+5dLa|-G_RTjB0Nucb$ps{J z+HgjUDw*kGTFBey=Hm&oigVzx_-TESaC+*VSl`8@B^JA@i*rL1WkLc$epL}s6baf= zwa8(n`d$o%)8m<3RC%v#SYm~)mScz1_P+4-EIB7zn z+H>>@603le0~T=-iWtN-5R-NO7D_sd=m0FwFd64i^tij--QQVEQ-uqh{ZX8LdDkB7 z@4-xVu-wm}djQxxYOUfe)i<5>>@EKLt+W2S@f^Hi>JHpFJUrUnKiKZ>(fLSU5?rqj zTiS2I>CCGRwBQmXOB@V(h|JPqWYM@^<@kkJwB&z|ch7AAWj6{7VN`Jp+WO#@;W3BNh?lI^Zw3FxA|la`-jL5>86`T1sNVm z0M~rZ@_=-wFGecMS7l`Uh!xGTFpnP}VrQffv?*zb$>Sh)nor)s@iOT_@aN+feX9Vs zo8Z8=I2<~%b%N+k+LGL4;1^o zWqL6lV=uBlg>bS+0yO#++@uGxsoi;KpR((r4Zp(SGlJ873I`_8=2GBwL6Sh|8e8R_ zg@OLQ!n^i`%Ky#bpSK9;R9eVbS|qFleg5!7B13o9{i`l!qS4-@R9j zVQnWf<#_RSUfLz!stfI(w!+Vl2kw74p1dV~SYe*5j#XQXi+Eu38B7qQghu@Y;gD8L zxZ~|74P|{h5onS_|8VUgGDNY2W_7|Z;&^d+gWWxnxNWjU|FELkAj2yv6b z1d*eO`U$$`@fD?z0v9Avl9)=PTyi3jG(EDA1{pzfB}r-kh)&bBS&|ojh5*tnkCYU~ zu8fs@*!~jMq0o>b*0rSVW9taP*AEk*sa`*hwk?TI`3Q@6qJ71axF$YX6zNaBSA%EQ z%1>!jXhpzjarw0X95~0b?NJ|xT*fnlEv~zw?^=aLnFes>kt0D(noco47~vA=45en) zFm0I72SWseK#4LWB;6>j zUI72iRd<{2eaO$LEmBZk(LJR=a_=ZzwOKKQsC6p!) zHFKIXC4y1gE?ENFE-OT~0~yeDw6GJ}N>R5XzMMtOsQ%zxo-S#PN7zXZ-FzpX;3UWs z`I#&olTwG=PKpL;nsa{6EDYx@8X@R}!Wx(^gpjiX^vQq+fHHnc94Q{El8FWK-=-pI zT~d?AfkxbLk)y8VFh0gX84=|;A5}v|U7Sf~N6Cq?kHeg*2gzQtW;m!mM#W-3Em<#* zufl$Yf*M}&T3Z~i&MsgkN6zt-Acm08!GC1;Kl(M&EYjgWO#Kr#@`>r&b$9^ygTdYY zQ?QHZAtGQPQvsgoT7bX2grovc;~x0c0eTjZ(e}ohtq;Il`eVS0!Owz=2LU2AE<_;4 zX}-^C*s<%7$d*o_32?q)61R_nW3OfB6Us%vZx zHZ*slt2NtKLUYyTLW4EOTWoNe8)&Z;BN(51%*!?`c-YfQ;!7=L9 z;2`*a)_LSfQc*8zhbR-0xb`_+C*=F}oFx}`oKS9%BtDr);d0FV2lP8E7?QP(hzM@E zrgoZ~Hwe&Sii}^6&OFA_Vj%p}?6)yw*(Ac?1_aEXfT-hl;1t0|gPYl;cZs{+)!Cbr z*ia%COCSJNp;wAIx)Ab-Nq)^WJN?KCW%HkAAS8YMM<)-VO*57gjVI=UCFpE)#;oQ< zSlP4fncDC6n6p^ETtUuZ%d}V+9diroPC~%qhPAk>3bwUsZrEoKTVhJfuYxtLK-wo+ z-DSnxFz-Y%vX26uwz~%^sYKn$z(xY z0b|-El_EFA38AMnH8Ol4kuR6(f{4M6d zwS)V|Y;})&_qQ)E_s`V`6Db&l;xu)iXdh+Ho}OZqql*rq_|g*fOSsjcUFq%)&(Aww z);Pvcf$D!VM9oEob04F(`y={!Y!jNirc2Ly3PHM%K5)DxBr=Sjjz&c$unG%YKsl`B zvHT(vs4K6?n#I-!bydfnVtPTD29&<<5DnZORGe|!9+ETXD-G8!oB%3?J(kd^^WxBehI~vxA+jF9@n8+s z^Ya202P2B{!H(PQ@+`a4ltS=I6&Z?e#Iv4zqit&lf>^2aY#r3JzNE|(h?F^F!R z4spteRu1noGusb(S%O%E*U}&su5h4Z9jx~z0qeby9WAC1jNvXEVQq-26ZNm;Pt+>= z9E@M@$f;ljr@<}XelpSAEi}OrXA6z@xF^?!FCn^J2--R2zt&o5s@KB`lFKj%R;*JL zKMQ@=F?vWRnDQ-Dg>+rboE#NTzQ}#v-+11UMhwsM869;-C0mDrtRazXvgUUqwpbFU z`(X7LVcF`x@OT;)bhy2X8N=aFqJkN1UsfiFG%cio_icyP zSovstGqa<3-Ek{zF%Dj5pen1%c2**I!&?PR z>0&H8m1JIRadnt?#p&3&Z?4PbZBu-?;-rt|m9z42TsrAXtcrz8*kXJP0%Iypf=IZG zBg3T>;}^XbLv-{l9KY}pHHK5Uf=U<(VIqt(8}p!PvIXL$+2k;oY=KXzn&tKW=zQGd zJ^xjUYgudO52KW7#xAWmCJjusjmqs?UBxtN6zOMl5+@mtf|MAf>a>aabmZiSRh{@m zg)5RxqH;@0$3^0Snws+;OPu}4ajOUMC(^_R!OwtH!Y_3eBuD@Jd5|1ZnKCLYih?4g zI$e`yZoEuRf-t*7tY37(A*=a4^Jq# zu=&62y?b+8$FVQ^zdi*_m8-x6f+cw;XNTMHDToh=ClbkklofBqg@Q zH~H-M_v^L%P`jX&53HY^|fu#;jqtROD4hTOv!r)`AyE9KrgRA@UZdSBg>`yF- z$di)d3uxlZ?$dnZN<6UZ9C-*ZPN{UCT3b(R+M;D6_(*9MN-U&+u+xC01!7ZaWG%wL z2;lFef(qAUM2(c@@lAL(V+4&uDph;BvYNSC0(X%4#CnRbDm}CDvn*!fBgsPD1}&9N zHZlK`<&ahgivmA@7w}lY@}V5@jY;*8&_oVKiCjyXuyr2`6F(6UKrovZB}lSepvl>) zL5Vq%d`c)G9ZN`&wN%-+bOt6c6U=QaBh9oJ@n*}3{k1~-o~Zm%Ar%yBL0W}oqR9F@ zm8#sZfYmWt-sr_|rZ=Ie;wE>}clOG;Nb7MY#LO27L9m*5Yce)sE!t2TV;xk;qpv7p@>#zLCb0!d`2+o{YU4)tMT*(v=ECFg^{cG9geD&-@F+C95O$LB0CVodfdb-y|5%>LaP&m zNT1GtV+0jm39Khrg&-TdmBMNXS|y%{Eg``e~KvW(gZ@+Ae43~RXM7$iZkp<6`XDujQ`IKPLtv!zXCm*jr9hiG1Fdt z)oQ=MjYN9E(5Ut5@bGw73vOZ=Jy&kva8b-bkt+%I7ti6d<*}Kb&hbc7q#0d{8q!to zW{bBXISy3i2Rqu*SLH3)5>}!d;4MQW`EowHykw#w-?|o5T#PF2Fe>uC{@bLJ9-{ z8cO&Z-@I1AmKD~ji7XD4Uqi_=6CMbQqX6^CJGe~R8!odgOrtCEJk;TnSz^+PI}?KR zeHe-o{K61Jo+5@hQ<7l5w>M-q+p;BoiW9f3GuBE}94wLDLYxrUo?X2gEs==o?A8#j z%6rZuGB;q-+JxsR80Mto;S*1+0{KvlM^fN^bq1;DJlSunD3=El~TC6sT7&J)-@D~Sio1vY9DIH zko`NnSstGEudblfY8iy(cEDvTiX1Oz*C@h2*R^ftL{er;->pL)t(B52$Q#Z}5rZ*S zCecoqZO%y0WZalXP~y+H`V6K9{uv?NF9G0!I7DZ`Fbf}hp#*-7KAG*>&5bse5wjQ| z@=q#;^3viL2P~Du**7|7MZhF)VFR+|D)^&MRi$g;k3OkcodBQMiKGN;?|wyt_*86= zrE43kfv)8z(-Hx~Fr?o8qRvJxeKZ;sGp=4>0EZ;lKZ6fZ4o`IL)L`r?>a1fC06GVA z5fdt{1s!$Pg^r=TGjtF}mpXOKZ4>^#u?^DZUPxqTKFO|-vu;vUcZ+B{RKk@7Q8-J5 zxnazy3#AZhSy^|F%chFDx}0jRd(2fV+GIonnydw){xTUU-B(sOMFB zwCK$Sl+!PI#FyhP!o~q%)ut61yY8jA7LqlqzaJMqg5#0*psZ31ftb<1&S;bm-TaY`N>}TglqM( zg*G-lFB*uHLPk0fq|tt%i*hA?*O%z_wh_fwlj-E@=8EffhH2Y2R6E&{3L{#2*A2`6 ztm!Z(6o%yR2acI+Isk$H9XX1qjJxd?ES;?M$VyFJl+$pQp z$UaFKVIG2|f~OW%ZhjVl3PFw5+HAd8=)5j0OU-2-L#OmuTVq~;xa3SryCy~#3hK8qtDzd?HZ zO=x~jhf}EobSb68#&ee|PA5eU<2}7iYr&vjsv5>z91!b40hhvsRi4<-!A z^f2c=G@m&_S82WBHocBxmVVA?I2c0efs=(btv<%q5*D;?@1ME&6P@YTM! zq%|}+0?K?!tOA$Z=7Y2b{AaITA$&w?%4(Ko0Gu(Q0kt6x=V#+NwJ#T=@~R0AwFS-( zVsTvqi3Q~QN>C<|YT(${HK1J2=1bnQCSHs}=k&DzN$!}a6x10V!q|z&raWpkRzFWp zp~G-rMsVz;`|uoEjrHOAll`fN;I(=AqlASkcgSvKuQJ)()3!I@>cWOe*F-5BHCTYs zu`n#5XQrXGZ>F|o4~NTBSL4wpOp7Cy1?lZh-`hKYbSrTywqqYxA!g$Y7-DV3$9g#^ zLp>OC+;Omcv4B(Gr&{xHO}TZqu)*4f^Ha~zxHC@20|L;-TQ>wCdTg{s?PcGhrm`ZJ zlk@Qq%e;>Mz*<^9nMupwTz*S9}A?@W02KS{nca#tAkpFI)6GNfqyw7Q&W<5DhdF6xy2| z9a)*@A%@G{uKG6a;AGDcNqx<|F&n%Uqi7?9?A3a<@_G36ngaCrxc^+YEl4^{_!#$O zJR6pLvQjqw-%mX6y8T-_5A0YXNPu~e%7RW7T5EuZ60yTCi22({>u%W1UQ+91=2NL= z#OK_FNDPQ>nR@VQ^ncaFDcGu57`w6*&FD zE9-i^02wz0OMeKxssMwe$x8gsQKzyzJWy=$j#N!XwrggEey{jYHc-O80K^o$;B1Q> zLD)gbG>YFKZsIId_Agn@&cs|zVUI+p6OCc#jw+V@+mt7a?*2cy<6HpppYo=lx&FeI zT#I>!7pPS`1L}csnJJ;Ba_6?w%$(&w=rNW17x=P9%YJqHc_wvd&^ z&dXxxl#8K2O&{N!m@FveR!}nmh-BEXum#%!H3Km5>@{^h0p|p*qh|oRilCT#t#f$n zvdD3R8V1~*b4|#>6Soc^^|&a#rwZi`cV3)tVC2x?`Z$|k zo^?lOX9@@B4=&;Pq4%7>Ig6&|bkVNYRbbtc7Y3btQW|L4 z6$@b6fLvrQ{JJ))#idSMDrPHDR*}!1Ns4{m3Q+@CKL$fjJ8$du^AX4vhKx43E#>~v z3lRdrirzwM1*4oN9in5{U?Z*{Cupxb>``Bis*C}$MxrWeuivBO`sM~jfg7E zsKfrV`yc16w*HxGKrAEy?xdhgf3w?wRXyD+=@c738j{dafzod29Su1mt+svq9+x%n zx!3!?#nPJ%h{!N?j+*4X;`)8>Pk-wENd+DHDmw_e%r(9VgP$k_`@|^*QN($ga)}ni zTj5Z*HeoXp`6v!>RD=Cz6lIiFEKO&6JC+NL2)JNu`QlqAcjq)6UO!c7H%;; zYax}mJkv1ei;WJOQ9%+yec+)u#CO>D7-W7o8CNWja+N006)mG|W(hu5&~kK+oAEw_ zlzIIUC4qk`UxY)`7uCm!=u9Zk)n~Fq^3HX^GCsrpOa=|AT7z6ycsYgBQ`crXqH3_Q z2=@}irxT2Uu0;Rj-ccQe3=fGNl3iTa{>>aMR18s2sOpY`0AQh$q#sDTP_h|*sy+Zx!T&% z>F1zIq@XNSn}$;zl>eVt#$JRN2SlT zhw4%hu#`aWVMEvY!c`D)qXEK4IifGyhP|jSIP~dOqLtgxa%tCVN}9LBEI@N+w6&d% zf{7E3!$eA0F`aBFgb+q$C4|oub;U>_ZXt2{bU}7itIy%m@6$1Ou~m@`+LnTPHJal_ z;{6>=4IB>L;-efDM@T#nR1zaYgBYgCwgVD66ba%2ip=CXT;c+&r3zbwspd;toE<4j41#r&5Ss+(HJJSG^ddr`OH?jTVwx-Hj9NqG#YhD%jyuVhni^tWYHQK%`WTC>WL z^}HHP+Lg>I)Q=8=lqk6#ax4lwn%6Ilp73?Li&_{OwA`8@N)0ZJuQt^BlhGc;LxVcz z0?6Jb7RCyDSxz1{Ou(8>xj`${OMQyd&hX(`4$-G7IA!mk(9dAZK}n4+vHf;-%N)Mk zhvOF%&yy2vkhRF3fSgsGK<$%;!f7I>GcQSYM02g9x);j zzS0l{%X`B8fN8DL{(NIsv7>)PRc4XvNcw>|FXX%DnTpjIMAQrQS=`dvt`r@yQc-w# z)kM~n@RB+N(LR24_~+g2!oAIdvyqBshNuYrJDj8&au~GtM zlMP9+@63e-05yy_nf?NzWu7y31J^7O3VeORO`dJs)?hDwwe(W2`v;qR2}v=HS}s_R#H%2$h; zt=1vpP@rjMH}jS_)m#7j28rm-CRkE)jiB!?$LC83of1+}5L;~1=L;Zo(|VQp>Oa?i zF7;D-t}n=0wZw7tq-AkU_8iab6-=xKI^LClsSOd`6NnWo+1kFuHZ80J zm!op9f6OEPcGrl^pV!lx{Un<@mEWuZ($cCIiZKI`t+A3&DU(^j3eb_1syj@$g=~(XQvsAt zx=@JGdpO$7<~2cA#O*;=h$V=I#4;aO)0eM;1g4L*NOj|0YzpsX0 zXji%((n_WDClN^>?|N7`2;8pbJ1Nrhj`Yq4h@+gQZskMCvC!F3=FUBzn4+JLV+r9uIea*gZHI zzR0eKPxgz`OoyC=k-=*q?cmPrWP#zJMB@bv6|34w?Uf)R()LBx?0uWQ(kU3obc}Va z8YHQ}j=IL&Orbmor7R(=mlE7cI;{idU3+8Smqh@^i}u-=IcdY*kd7G^dc`UEh2$^nEF;e6U$Cr*Pvg0gSCEzH2?t)~hXXI>Yu&oJ3uE@|39jg_P=ZXjS=1RDv*Ctnv1hahkJ+m2DDV*|G)=N8%hGx-#a=R6d6Cu1ZQ zt70T=-FlD%qGZgOHOjK0JPwmT{gAS1x|A*u-FafGMbcOf7a4+`!`wH4kFjCGOnJ>P* z*}Z;%;E9V1h}9A9sGLo^i^;`wbSap%uW|HBKT5y1XP2{i_s8)ij)!*ac8n|;aDhpa z?)i{}&IxHOHpthZLlG{IyGi=uN~c{KWuslK+>O>xslF_0&~;oO3I}w#8d*?G*lHdk zo<3(8W1tdpsk#as z0T5e7>{#4-uY~i6Xy{x~I;?W1`e2ODWc3N1?fFDn9=L@bGAlV;uhi*VUq60&R_j{q z1PTn&ya_dzh{`DW_$Y;YzBdONB2VZYwa-RxEY&r|(r%2L*f0U4GdE1kJxvBQ_ zF)uShq6#3LWyxsa5bLYsm_nob!}$H=^m5!q%E19L4n82b9{(YjZ#@w0LiWf7|8(k+ zG3Ap)ImoZd@P-LAy?%XOxsl3X^&Se*^i6WgqPspB((TW!q3wINQKgd<`gk3$C|Hgb zALPb@kJoRJVxPc$YyLSpL(!iTGMb%<_fp4`;m2#`os@{t-ZvGYjiQJK{n3GH)H^|^!uyb?o*5MRA4iQpoMtS|t= zIM~va7!$+ugcy?I|2&qhGm4Hn@lhTfP3PCyTU>Qy4gq&#i$-~St%Kb9Xt6P5c8aCZ zB~F!s!?j|8lgrSWBqMpE_DM9}h?Yn@ZvQqcPb)m3sZ{@C8&ORUe% zh(me)n>O(?DKI9I1JnqxDBCxb7}>6rY-Ajt!t^NKA9K9Dd$2qB(Z}|^hAoOLY!e_HaR;DiL=Xwwcms>Q)Uv(o$yjm0 z?q?rLU01UEX(wpFncSb7XpsxNT4&lfwo@+4%n^#& zxftX4v$I*}S0zb4hMh24{sU>we6nfxEzE(DDwx?19iAVAP(+fQ@Qs6;t9Ll}1-Wd4DJC9s0Q6B#ZrD$r+|ZwF*?uYu%fm)zDgzNI zmOX-C4aYXVndZqonkAS^Aar=z^F3LchL1b;e?np>(BnX=YK}!hdd!!Uq~AC^VvZRd<>EkPqG^xb3SCav-9A0Pi-ex(vDzNp5q=sfv^2~u5x7ImggAYVSdpIq`3!m=gbTd-F2BM2bA4X?A4vX3Pk$%P+rL7R%h^Ty)053jJtK8GTeP3*&+EzWK0V$1{U_XS*PCGW zPtM!(?|bZztdr;8;WxU4o=m>S9)OZiI}vtkIi$vX^9Jc}(fX2dOnX-+|CvY!|0N-} zp5t2mIWZ#(i*bZmqcS;}AxufTrFR4)wCM19JY|l1v#9Ipb2V34pC8t!KhQ`Vkzvx+ zh=G1eV5q^8>D$+Ohk)b&n0p%cE!qm-EsqIWFkz5$e;wOMAskI%ZXqzNse(ejz>I@p zoV8XXD=CzWRI!+ZW0!d%7GT=J$X3m;Bwnovm=stS8uk{OVA<9q7my@OmZ*QjS#87| zjTY5d!*bH40@IbcpYmP&*hJI2a-l$#+-;{*P$T#?KMhpG<3;hTB zax#B63MKSm%9iCTZ-=p8qsgWIT3nAlYLKH8x$&mYPRL)lbLd+jgqtgF#`uDti{*a`S&BxKodR*E{pdQi50`$ znR1V8=fUj!oPD?+E#K=^E6hnMAWy?(J)uL;mRz0n{(uWeI}ccLL90qMetae@qn(vF zhXr7?LHTQ(U}0CR+GO#XMtpcW29ExOy|D9}@ui;7?P2o@7y96^E(t7QjkrSy7h~Ll z5?0+lfjQBN9rRz}?wX|?C*!z+jTmNh^5cp3`Sk4Y)ieL{47Xy04=4SDKl_K1 z(G0oErKicLUJY1Obcl>ZNc)k7S@abfJ!-H#HYyV)ckIQl}HdZxUR1%Bq0TKVYM_jzc z!O8y1mvGsI&wWh5OGG!`#zLf1_)bD4p9cN?gZOPenoQ%z-8UEyYZbrahkm~wKHzu! zaNK_tKHzu!U}Q63LOK17A_x5+`Y(rIg6%~eRn!zn&t^ua)0~qD9ndY-FdUbXW;mXaZCx2_ib%z z*V4H}yp+PnyD#^S28Tzx;LRQ61cAOp*6nLV4IouUPMW{m;374uKgf}nFCmC7FHz#; z#b9^$FR%Lp&;Zin+i(5@FJJUu?)5gFeDhQa><<9>$v5BP!3y$`<^KL)7zEYMGXh>L z#!DPcgf@_$szSgss#f(&cocVcI(krIdO6mU?&I#$QF1$0Xh!n%esq8xQPFNI(^{y$ zB};1Q`k3Oo9RM)vw(b|ECxyaiQS9Lfo3L&l6eCZ=7uJwFEDtzsH?8NLxh zl7*KGPFU*Ua{`Y0F5~%pHg9j(7StO@5Uc8OdiNE)m$qt!-WU5bg$du-{uIUtE*>~M z$M)e#|73sLwq?XxOLemNI~r=sO_wpxL)Yqhz(nW>8l{*>QbM4pDEqkBrUl>BOK!G} zCsYqDyQp%C#Sk$tr$61cg^o*y)U=a-xqOO9!Q*#;0l-2;%F#|#{s8scyqW7!zna9E`j7=fWQ$|EiB(KV(Mraj?6-r?h7=WBDRBKQsENU3NDsVcr@f? zyFL~*q`0jD9(P#cAMyunG>4h&YeiVG$mVxqBkP(~3UJA92sm8bU_h%ZU~?hX=!zBp z--idg)Zowh$C@@*%4Wn<_GRvDiyG?&dtG-x?@&GLjwG!Q`aAotk42&um^(0tCYcNS zZ58NkA}Yd|jO5fKdL67IY`_aBuhS;xjntCHZB}NDC}FBwe)AziXvM( z)1@biMTF#-c;KCMi2uQ+JHCMJ7?kXi1L}45@t7-f8KT9oR_PqR z;D(wY#1yivrUhal4Xjt3m!+YkB&%<{Q4L)eO9SP9SJO0MVZVQ!Vt{6sNH)Opj`Er~ zxp49>WrZVz5OwJ4;z)|DhR?peo?bd}>tM2&^nZ2pq3Fxm$33jI8#*UNv0C`Ku|F)e z;~t6zXpyYPkbkSYp3rH%3bBDBM1*3Qydi(<=7fDDeXp&&L#Uoa?5LjA3%U8K3&zNL&)Rk;XtjcqiQNB3%e1)u+&>tkn$~tAe9;L7%QYo;zVAe0$7KW!2 ztj_qj1I8x)Pdpi7JElcc13ve~hOb;s(J|}Gt`*FtO8)ir86=8aPdAw&4ICU&%HiGM zB&TiV-`0fvgM0j6;(Xg>JAX9za5>nnK>k^}v1U7>R2hvY*F|>BD{c+QEELq@07?jL!R|P^q=&hGosf}gID+~CDL8G zB4CorziD5v@;ioobvS)B!G_Lkx{H$w+~~r&hs!Ztcs8BLfwCzkV<6P_SF$h0e^e;= zYuOxgC{EWEQq)$feuWzcPcJ9c%?=Z83bqPfI;kGvZ`1ZG)d{&W4Sc1EAs zJA?hvmJK%}!Mt*VF4Duk>mHr_yJoN5fr6DcJvmNsed^g;am9 z{RA4;RVwH1(=~~Mg&B@WOc8zz9%-L4pPhK3gxlj|GC;C}9KR$EE6HnlGUIU|==tcH zks5L0t%(`7r|*gxceGbx#&ONCW39VlcZ9vnqGAd=*`ig}>*WjC?=C`0cEQ($1;+s0 zrOd1H5?icorWsj|gL_Hi89Pw$ST4xe3gZNnMT|lA7OHjg0wP@1iCGn$WIq05NLTuh z1>EU)@^kJ`4vzo#5Kpa9i%qCM4!iig3aZ;eXIN)p9cppYSOW48k-wNAUYuTYc3^9` z%k70%dV5@(NHKkCk@kwRtYn2f)Jf}oBL{0R;dWqu@UK>qvmsikjy8?;iMlkmHH3>t zhUS7nT%CvwPoj-3rHwc;Nz^R8?~~q>&O;$Xh(=VT`0nlGC&RXCRGcl8if}?MXK>+n zTQXY_+w8$(vO&-(c2eI?HXlfHcA6o&zlGv55+GuH4d*mfEdNt7Dp(O@#TEjOCZf%K z%+3Mq<>GAw@>8lKG#Jpf`7uq*4cvs8&@l2pz@D@|;G0#p<& zmTzG{1>v5g|2xQtGk9jZc6{v`+8DlS8i=oU|0m>tPIe(J*hXqIq@L~Q%3jAV%pxgK zm_>ZoJd1!G;#wU&V0y)Uuz#beG)v;9nr2dMx!Q?TSEEC!t~-xFU)l{v?)|Uum<&yaEdWOY(a-gL;gLf>y*CR-+h2uOX_xSE*`bC7emhv(pS@#N8 z$F^B>ulot-2lN-Twl*fkUse@=8H&H`hT@EW+haiOe5oQFf;!m@)W#h)3%^`Z_$3OH zc$x-Gz<($83iFJGw4*VCUY=8O1kELQ6gwrwrBnI~oyz1mo9IYQ(>Gi!=tNKG+77}QMe%W8Bf4-u=Iqn;( znhU+6Zgj5KwyS#-6?MbTMr~DhxK~v6Pfb0JOT9AD!nY;>wh2d&(&lUy4q&j*g@1pVZV=2Ec%PvW<;bsgoNj+2p6NNwyTb|TLV%TeMG+=O$l-kdIIY|#iiaru$y*Fy8O&p30w zUf}8zu?BT56AEtd<~^{8aoXK~o%U91Lubw-x$c*5dBT z%N1BYDYsLHM~x;1FebVN813E9c&LyZR>qKIp*q>G0H3A+pH>58nF#P{1Hi=f;5CHE zVX{-~rG@G{8%mj*)evX^=l#DYF`{v{*ej_f7{IOBdZMy|SL6 zp-eHQiyHU2<8X&acrLyTl1g|fkTwwYkPNF4hQ%6&*-+lLlPiIR{3uHg7KfWoS@WSl z5rr~DcHFS>T1^;*(=u?|NU&8PXFM3HAG+B8v6D5mmgC-JdiKTx5BH}#&)SH!3lOUV zRu^v>k5`8yAs!&RH9|xJ;P$J@ROMqLbdsMcf5JrQ494eCQWS)xDhevvg0xIDNNBDX ze$hK@nm)YCzv?~r%3h=C?P&-9BbL9Fs4aBML1-c zlK9|CvjQmL;XlP%*R&b&*zE`}5z#h{bfCmBp>MddzOuv_T-kn{oW;o0xJz2Y88Pof z5QItabZkL)P1woAH4v(3=OePh0YuExswkiZ=58FNlsN2lq-VsSNL7wP3PkKTI02)5 zg_!(g#oOjl_=qOr^_g0pX?+Xt!I~N%g|O*lVK9{)*+q`f+6IIx#PM`?bMZbTT#VB| z(2tKYt5h0V6>I7rH( zN>UwwYLXu!D?}QJtIRu2UL4`SxrMz;P8mcF@J#M=ZgOa@Fku&zOD2~xj8H$1!dI#E z7NvjE4IZoje${gFFB1NMI{KpEQ_i=73d;ubF$?(Yt^x5eO9CBECh$fJfj%Q#QntV* zlR6>ErK;>Unddao4}yohkU6~$20|AuO~N+VDUM0wupP!)`_ctWiB1janZtPL1t=RX87Ih7h*QQa zqdMSsg6096u9zu@xvDh_wDyMEHQ#^tZ zPGcpARzJ%@XblzR9F*f}Z5`ufU6pwIIdY&-9@z2xc#BKrRwcdE`ED^6@jT^@DYL~p zhNbNw+v3zrR`^Y~)dwxatU}?Y`%DknRM$(AiDkE25GEEZ+^T?)Sl&%&nANEeT5Y#8 zdtaZ)Mam*gAa$&mVkA8zrX`b=GRYDg5?`B2kw^>$Vjd3V!|=Y7rXpaIF4u=#!0U=E z+ZBv9V0%+YrstH7xKk3sJ|Ih^z9>H#ErAw=wNA9*j=Ywlgq7GGQmM9KnZ{cCyKfPS zs6r|>BoKuyDX4>FLysax9!iwd<*V3j{wnQt7q|eN$zs^5^zfN%Cw()FSKvA1oTAKB z6=Gz-IqS>&ky=3sx!nb-R3O9(a`QDV@t$1Xi`=MJB6omTCO1B^W}oUI@RCLN$@02(^3v?vfrUL7KsZsfP8R*;Eb^A^{t z_6&xXpOwg~r(f#PjnR&I?fAaFZWFosdW^^_4~G<}H;eWo%T8aRp-lx2U`>Hlib_yM zx#1kHRiiR|tX#y@cjf4zOTrF4fALY^h#5*2y+=pR9OY%PS3`e+}whI(8j4Am>dWtFFV)L2*|4wV zmIf&nFyM+;4k_Z&#J@0;)EPoe#a#;f9ytZL5XD*EDU1xk0b`&Bf9eeq#M})zQyPW!klU!?q|f)gCu*6hjktIGN(dpg z5{);4iHp+ca+7I?@4?{Ig+uU0%HkwIu~m$fk-7~dDQzNEI|uSDay@Ld#Fw(blBLsX z*s*AX=E7wb9(op+L5H2W=_Z@&$QZPN;x!~ZpVw}Y() zW9O~m&SY_o{8>g`o2Po1192FDhQpdDY=njnQJn*sXC!kMn=`=H@lIE*r5*`7C%pXz zvS}6UgF=cj?blbWH^|AvphktQ%>K1pJ+jd;2LSpCRNaf<8D#~F6oqho8E1qNM$X{a zV4&v^0u#(ii#GRJ0(`Vey;O9>lBjE~ z5;zOeb{|>S7RlbNuH&>^um|}V&ID?S3YnU4u#)%;?97Ndy7-3(!WIHX_r5^&nGj-E zeO&A>$!kBmNTMg+%&ATO=_zi5n<2SvQbt~hLw7O8#d`0@(+5_}p=2etL||adIg*Ms zQl40*zZ`OSU4S0mBl=i+*bQ&IWK}DI#&Wg{wbXC!NXTa#%dr%L4tg416$&x|qJw;- zzd})a_8I}bto87=RP6l>F3W&GD96{OvS(Nz^&$n)!lU@a8p)$ouJV};oL9~ubGKUC z9Otdp0THHXK7f>Ef^7Z_z4S*Q0MUaSK@c`JkTq4MV+KTOA-R_K6T6R{Kv4T-e?p7l z8H>W$OCXs%B|q6*@<>wIyif0ARA(1B+St}5CEvJbGhl$2XJt8VmI|}W%-wOm$)y!R zgy9QGz42CNVx6Y08lThz+G;^_;Y5K?(-t;PFaWZzmn`zIH!vN46}+9||8$wa#6{uv zN3Ud|ObtHEM|Qgqnv-1%Y(RL=yI`8d&FFGD8AH9(q$x}adzR6%$;R;jHJqlAR=y=D zq~Jd#@9E#B2`+)gw5XV%e0FdA;NDN&z70%E3WFZQSKYzO3VBS=U~Yu^_*UA(-! zJ)CdPuC6b~%kkyy{#4G+2@;{a_kXe=mUgxeln+$Z%R_MoxF$gDY6?wmFx3``i5OK= z8tep1>AZJK9?&(Vzi?C&0?2qJSs*jmk;sjpgEp>`KF_%g3!8llhoy1-8>P*Mt8dId*JeK1)Om+p-O~>uUTkuz0# zzZ75KwkVXTz$kEBdc`j;u1#PFJ=9{5ivVGZ)=5AqDmcP=lvQcH^qVp|OaVoSV1htE z^dz@&nr%{EvtGBm@er9{pC7`MUnxDk0EFtgvO-J&)N3IBGenD;-K+FJ)0AF50IDvP zY{_q=8Vp0?L$|JH4a;jR69eBZ68D2}C)M|e4vL$lY?$ISA7TjTrvPs%SDTs_M#Z{n z76WB6cU{J=3HG+&PJ2+ZIHB^Sx)a*q)>cYpS+jA$71Hx3}2wQnN4PwlAoRl_o=8#uKdsVP0}NchxgvfaQ8pn#?S< zI5GL7(h%SW7>V*7@b-6te&jtce+nO7;wk&|IQxX_Zj1n|Cb!T0=^OrhESDoGc~wJr zQ*KbaIyw%OUA`;qTwlX`hlANyQQH#ycTbZ2SzZpD2lKy1@5r$5r{SK?pC^~Mh`zZR zUBFMW5w7b|`@z_e6P1eWyC)*h9NA;Odxc1g&F}StW2$;XIJ2pB8X>Bxs~YyMg@6}e zVed>~M@v{{oqc>u^nA6UG=|Z=9G&#&J(8>ILxjmAm(5iG$SYd&du(}Yo6>azm(j0; z0g`!2!QTX@qTMhP(}~XCHIrS$q)+2@JQ10mP}FVC$SjGI#u3jdm}055r&CE_0Mtk^ zil8H)or4bYRGJQ655=bjol>0)r8U}j!UpLTbD7x|A&!5GxXYc^+K-OdY1a#0+5RlDtTeDdxRa46Drq9czrU_f%wq z!16amaQ?pctN+q80RW_HCwgxt_v=huDqf3@%~qFHZcM-TG=62(YxK1iva+ zpKH;^l+{D^YnVv>A)q1jr%gFP3KOi0gws-i7HQ9;cps)p$9im`?iKG;8a?zbYV72>(t1st4+^Js#9)jEKU z!Hf@+>CaHRlMC)mVs%nn6h})X%3F=cN|#{qTM`x>Uj~}TSDB{PA;(9sq3||ENuQ5t zm^OGziX=@)VHoMipt;5dKb9eyibrm?u=r$nAn8Xy{;v0}ppfV+s9p{W2C^8|U!lq- zQ}v`$+m~V`$(OoeW&92%H{p}{s#LyzDtP-fHUqw^)wDV7OqP`@}d)#Vk54Y;wqt+ z2wg=%ZRegoh;{!c2+x)WQ{{P1aJK)#m(b<{O4H zoElS0RR%a3&hoh63S5458(8%V4m}u&%+$=6tEG)7ANnnPK+5to5`1>OkYc^zPXDAY zR}4VW9bPYV zfmi)}q@-mvW@XV3W>;0ZB)=t4=_Vgfj%^}b#Lc*^2@lrvwe(^j#J?<`;9Gkzad$! z@BGwDJ|KDJCtRJIy_rnIMY^}8vbUwOwfCmI zky&d8PO`g+?b1=~tZ-X|6RK)N3=7N#J64~hNTNHZ{X}+)_&vCde4izbuD=OIPH^cF zhddVXH=&68cw!NM6N(I&${oi^V-a~1zAr$E&I12`+gbe5D~@UGk_&iUuZAnfML%P? z%|H$14}ZhmcE7aT-=VGWP65evQX;}9o%?tUwM~*XIQKF-( z7Q!Wlxa}*)tyqSDLx})GXv|LpgTIfwP5B{k)h;>tRs(jBj<~cDO?H(dcCcNyT7=M2 zjK0R@Jh44{kbN;h!rv7AmEyisS|NFi5=4FgO2^U1QbB1*rjj9Z<{=q4 z2-IfVTpT?c-R{lD<2T~t0Yhh++-r3J2bmgK9>2(x$?xCe6-OR_zW3KaMu$In(PKGx zS}A(LWG{Gsh^4A{pxJldl&?0eF72Vf`OITEjk&nU1WQh*?EG?{Z;N+0*-I>Ur4sG% zild|ZJN7xfA+zTXL8=uB^^Gs>fMj z&&q{8o4DGFKH%iD@YBLdntqZAr`L}9m&gI(m(_9339MFlU*N*RSEBC_BF52$*^58P zyKYM;(`r}b-8ySQSgTqiS?t*xyf+)0t0(*Er9XFJCo8e2lDHD%evIN`YqrN}V}VVJ zH@khkz|~qikLY)DHVI{QjmBxA-npI!5$go)%bY?j2E??bZM8JZGjH)$Q3I%-bTOGr z8~G~UWF|$Yq%dtL^~B45bm(I ztD~WgIi^B7RRCBBGTchR$jTtlwdok=iAe_m5|4u_7#|^8QB6QZ4N)$%q z67g_x*91r;WEG$YMnvg1dik8N9seoW2DI{9Wk6RE&H^QxPVM`&^XvnxX4#X6Wl(6S*9(dVW}P?R4R@le0intV87}=gciZ(2LNhwqBDf%K>2WK! zEL}phqg%t;lC}{72uN5|mDoqNu7y-nXP#8QH+O&fGvbS41ef+Vy5AW>VV)jDFu+48 z#Ic+mXpD}mGOjgL@+;z-iR5}A(&#x&-m*^y?9372~*ZdxFCM{4wX1#3w%<{Zo3y zm5orX^;`C+e|)_E{NUB@!O1^kq%b5Vqy8y9+dX-?f4pYLlHIerbKpAtk_Kv7uxf*7 zg;+Za5}R_3QZfkcPRpfJrxm7RM{d*>_QmQT4tym}n6W~JBwLsWp}WxLjmL2iKkS|{ z=@=Tr(Sm~9jg3JIc?vS_1G7_Xv5SMGT#Sr(JrVk?W$vyIGK>P8N}O!9MojB9yx{ZC zQt;I3iI(4dpxFs~`iEYoCl7)-KdB zALKM#v@?>VDHyg4OsXie4>^1twge*5H*pguc@txTZRLxrzAw*X)THo4u%F^)Rxd{9 z;WtxvJ%7(AKNJ(kML~bvbxj@D3R}`>wE&+X# z6K70#ZUwn`;3(DeAF)?Sfo=Q9f-wZ8_c%pIBAf6ALQ;L4=!lJh{OLUSd+s`k6!yR& zH)cslQfotCU1BR)5~+F+jJy?M*BG|P$EivXu5*xqI3-|cP>uY0FHDpxbdqz~VNhD< zyaOeWmdlO5O?RT;{u)QaahgQEn*9A=aZ2fzFsl5DwTdVT_1z@#YcP993#V2$(4k|! zi%S>popRP@s1lkXYgH5v-+f(}N5^nwNP~LCT4+$Y5VE(vp2Ny95KL!V);lD-lOu{D zK#~a^aemt_km6QAlCPP(nq;{)aA;y@_c{xR-XbDR@F-;Ohac*mG zx9`#EC%%lXqG=}Fb5x)s;l2LEvSOqDV)c!I_fAD<&gPsBasuw zZqO)|&HynL3~a#>U~dI~k$v|+IeQdSam;m^`03dSIg|zS7!neK=D#DwWCk`lsH_SW zTo`W|TRDv*@(m&{@v%5?mq7k3l_;-}NLeO$6l1Aj5rtjMwGVoNcRWdZY`@rx%w!vV~Mz^iK&4~2T` z+AW4;+MB(Z#)Vn>2tOTZ5{vyx_3$vXtbOyu5ZpZV?#GdeyQvCp0-<6;PvZLGy=Ft< zb^FF*dJ%Kj>Ua}ZNWB7KgoI}1TQ9aCJWIeX3QoZJdja^iwfJg%OdfO-j??G zZBkjbq2hDpxQf-7SVkw#94BDaZWvr$=Y_o=QNdfo$bPU5Pv;P4ZKafH7*fjV-mw8K zBIt=7)y?Le3FFv|Uy+Rx(iA7iXJ9DA2b-3J$;gA7iLGsQ7_-;S;jIjR|Gjk#D68EIz>sT;CbAemow zK+mO?tv^adwmg+e|CS+B+qAQ@Hps*k-O~*;lRYoB5oVi^5<*c25{dYDm&<=UHTU4T9Y>WJSZJ z{iKuk+1^K)q731v2gYfh-F4G}w_>63$|FqA&94||f^|-oCSP;cfW8VWw=;}A?Gn0Y zi>nK6=ETKSUYhjqfY|JOv(!qGofO8dXm-(qd$5xXRa4gnSZR$VPTX^+s1+-1#-$ay zpccl-EG~b8EfWoYnRL`atU8dhBe6Wv1fSuzE!Ho~np*VLJ-7d%w0+?pk2V16-XiU8FMe|M&alYar*G8+zD;GuU~=-5ZKvgpC(F_cU%TW~W&0DqPu5;dm_y!>dS=KgRU zArnBDwI@vv^@KSQ(1IPE4rD*eFcLLQC+(Se^pdD24aC-21IE{YFb%!zdgxcuf8Hi9 zkGKv30o+ngXIMfi$O+`d!m{PFCy4?*nw);PxrU(To(PbXLHO^(gIyg1;v@;S_zJP7 z137a=>WFHjMnJttk)V5b#@Cm#+k3!u6R8TcP$y8J*WXum8Sd;KAN5bRU+fNuq9oE1 z^g~gXJ&Db;=oGt-`_FQmHy`8;Zh<~8kWlQ71i9a|FUp>DxQIZ*Sjo@?>&7`f2MXv5fS^{@stj!K3l-L0O>tc_d|ULIXd48KVCCE33P5 zLv(qlJTV3if>Y@FeniXp7x8y#1NWSbWk7Qz$ykoLe^jx=3DY=AXtTL^chfZ-Ah>(Kk619vS7lZ&-^?oQV_C!$i-QAAo-AzG#Dv>%CZ?qU_}l{F=MMaV(u>pnz!6) z3ZOyNTEz^$p+Ge98AjE3BuuHDY$sjoBy_2oB|E~RRxd#g!4))xWJorH+wqj!IO93~ zAd@)Uj%uIVTQOIoyQpM(Q33(eiu|~>mU;@=Rs%RUYi}in6bo$LAGn~!Ow`oYMoV%Y zB~2QZ)as=mDm(W!DIF!}PwcZ!cYK!N9VO< zSG(Zhu;RhB(jt??yAN;W2$zV+RA^Dxs@o-L&Im!=Z?1`49Nf(q%HHi+cz4^XE`yjgjFBrKbJp-t5SUtIiAIsg5sdoa&+bUw*7Ea97}krr<^5 zm8Ma0q`veR@%U2h2w|iyX3oVFBL7@Yrt<)td`c|nv6Hl|oW}Z4U~X~YbGk(m=>j)@ zW(jsBd3(k1W9Cg`FmVxSm9PEQoHf|eR5w5<9j5Z6c;r(NPo4|SrG zKPk8Q;MMN&asRnG;fM|bP`6HI*lXH9Pn47FOr>M8pL~>z7pl2JYg@yikBaPAvP-Ji zfb7LAYI(We5SJD<#l1?{U(G}j#)`Yn^~vU>T~gI~Hr+j4$xOYZ zVL`wlE^jny5`6&r-Q(^bI{p3b$!PJR`@{JC-oo_K(VFl|$ zDGi(DhK~u#@W0y80IqRLdE@iKTv9icrePS`UbO=i##2)}ntZauJP1SknagU!ITZ}y z7{}IZYKKfsjN~Sj^;t)-b_HRjKoKdT^GjOPQbuyM@<@)9>r#S1bZPnQl^(9J z6ErbaHo&x+X>Pu}Fez9MF$CI{%_JpK^97U^0>&3H`s4lq>%720W_pt8mf0o)?ig4M zpv0A;? z$51i@4W%j=&cYW91cL?oDuzwW-5;Lb+7$A3G|fK(#|k zN+dB%LY0yv-MhpY(C0|WH8nv>VQNFcB(2LOtXc3^v==~~?&xMYI~$*m!^x8`g5>=S z8P5E~h>3z=e{nEF9$`8v)PmtUUHhREyu!#~ksABvarZxe=k1gvfh)RfN&EcDb1>E{WsxoFWqX07R58{nKPLG2u;MO=8R@?yf{0f z2uWB+xV|7Xx5O^Zbh6Lr61OPQesz;WJGi-eH=ZA!>x^lKd6{Ou2&bl>Q`d$h4R-a_ zdgLHFIM}7!J33uXenv>-8PC8-XALdCQb>#tU{P(!C(MRWYv~N^WwA1Q)aG)#hfOG3q z14nUz2_n)amm^$-(oJ<;=~S0Bu?`nzWXIVv5?-Lo_?NTTtez$8Cx{dEpM$NT}>8w17)?QhDe0( zx=Y9)d7G>(pWT^(C32J|RaDqq)YqFpIR)gXP2m-GHCA&BGL|z&eSLN-ROv)oSI=nKmAgYz6j z?LT+=M&bozQd-Ujg=IWE~sEL<18#0D~a1gz5z_axOJ8t3HBJ!~EC4 zvnl+%>DtqR#t?L%fohEWdv+_%O&x<+&GE4+0WH6#ve-Mc9Lf-NFBe`a9%(aPBrYwx zjvzDo`=s+eC1ygYu~WY+y}Ov4jk|;0G-kfip0Fzep~hALd-n3RW&IH*tb~@_fahp8 zO~cVOu5vA^7cjexa)Ir0CJg+-hAJCT&Rx5WbyTbwdU8Ph)FAV;*jN-@pnj`O^5gQ0 z%}mk7p4E%A=H9?j$3V*M6`gXX6~m;kEt*TE!y6c@3Y7i>?h2&+g2U4K)()L3cPnLP zPPKLVxq_Q}z0h6-EBpM=$v2xrY0IG-HIb`Xx&$%j=Vp)vzD_$V(r2wv8;qT_C6!|g zzy7#kwJ{IikXj%c#dDr?|Luk@>-}0H^xt~0o5yx=GyOQayM{`;S@Y` zP`T(xiOxAzFF~Xv;!1#4fK_#-ew}^|#*CZ!65W~`q%YblSC94LUzWOJ6&5?8K0r&U zX~tR8cnKs9kjyT(<}9Tq#HQH2va*roY2nDc`zAE+GKYw_TyoWs?7t-yaco}fwB(hz zR5WcMnTP&8bxdhk3#zv-`8mKs#w+9|a1Kw823TFdlbba}k%mp; z_}%Izo5Y20(oGQ&@NN3&6HR4F->#bWrbzE4{tHS1{)fWTUKkL~PV#&2eUeB*wlPVLmCA@iA~l zc!j(WAcUZsgK=+nD48*QcpD%(%bH7O6#?KI6~C1X4@UW(L}>lEdvc!+`=G9njXOB` zUNA=lVefuqA-$(k2O?ceRT-Im=ky9;iOt` zSYu2N=#BE(%&k_KV}8I@TJg?icqZq#*Waw)(ugyk2aL8{YuyI@6G!^AhTA)I4g#mP&z8sMQXvk-fHatQ;*?w(C-S9nNA`4VW1i%g# z`yw#`{}gTsn1OSI2dH*%+<;3|zL4;y$U9Y?Yh)c4G}V0MMgk3MdKGW(pkF;6tUumlBMeA~g?8#rBb)j@brhuU{*W5!+zpkWAig9rA zt};cd>z)eFK6s+Op7KVa2=-i=1PTbi)*t`Y^ggl*3JK%>@X<1#0kX+lzG0M$1tsC)xuMY?E0Rhux>K)W*kmj66Eu z`Z#CMpH@2GIt6=pNQ^`5`45_`1(_W-p>P|^5UCKBpO(f=kceV{`sO``nq5f_pE%ea z-{S_^zx{2a+x3@1;Ci1G2$lQP%?yG2nGnS2bWjy>gx@}nfrK(v>UCuQ$o*Is;%u?; zU3q6o(F%@vZMwIy5#urJRiQ{}2QWB+Aa$8SkBt{Ru1S!`+CatcUIJ+z{SXi!;0kjh zu(x(min#foyiz7$k7a6&Z#&38aWjsd@LWXrl8lpP{R>Jnn!Kv0q;kbEqE=)0_Dqx zATj@Iqk|4;uirlT$>h&k#Z@i|fsBmF?`~IcqM#670Plf7(~FSTLc{iT$&0ftzO>R( zn9rJx&bDRed)Ywmafi`-75DNRvMq8Moq+VA|aD$%S^?`Db}_t`Y2 z7{{>79>m>8osF}}ppN3WlSeD{)m-$IPF!lM-uV{Onzc86heb5lBkC?2d=^qQ{Vy`^ zRVzu-u2PZ7%F@PgBhmk*koYg5%7mW7+EEd?A!YP}z~gJ|c8bZ+q4e;<+xCquTb$~+u$u|BVX4{K_^;5gyokpVCkuU{amniRf~(l>-ib}d zcZt^vVjY?wYcH1TZ1hABJ+xB2r@@TXH_!#pAVICG7727ge)g%vD7CHhQ_~!(CDE?e z7hUbORh`$>ec;^{>8F$f_+CKBsL5W1W~ue~qPGDbv$n%WcQ`9P>X!0w+rdt zqM@H+ZvomgWbzd+Wp+}w_5K?pDx||K&NizGU^?Qzvz@t1l%p z*Q)rnq;#^J3h^=Wp=0;icY_gTxqE!j?NQAyd!UTaOV8|u7+U^Oj4*ouPJRVJ-1*Upn;drjDa2+i)6!g`SE>clf@5V9$SmVQ^tldoxa8>l*Y$4~Y3Zo@ScssCY zUHfYTxc9XEtNC|c!|`Juxy2DO(+jDj^+oueytAq2`H(eo!{JCdPu$omrUdj1K4~)5 z<{B~Sj1IZCsusiQEX)bso3puvL#aG@J>8zcMKohdQS~xXsxlAr!=zTsz$_He3r&?c zOumen8hv20kjH37nSq?`0!!iRh!(RiD>jzDS&J0>qE1|ER?A?-{02{hp!1004p?cS z+L1_;Giov{6*LL|kb4U$e+8%~FVuywpvnMYf8FI3<9!Vvg=M8Oay3FJYplS%$P)nW znVTbNWDIJ0hKP(T6?ERMGOSL@DvkYoaIY3IWNS7-wQ?zr$rUt8avFj>oGp@QP+7AE z$uBnGYhx)1Pnt$*J(N5JO!Lr)$Vkt&xtr57!w5;?UDb%R9}|L0_t1mxr{2k5<2Z!Zx4n}eBU$yHRh*cXE; zsUy!;UVaB_IS@TpBfX5_Ba#JXK^Lt5^+j%-60t}{t%_2k%cWIya$nHE>xCQTw?rVc zy$ivGBgLeLwu<*9NwYKvYN|>UU?TA{j1C+a!HVf*M9QVYs>%Kt`uB5Yy_qhum%Dv< zIM~@g=%4I%-;8cW0G#lOhm+BVG0qs8UN%l2Qu@uNy^2aSK5(d-I}TQ-52s4x;3eSA z&d&*3_Q#cNlefJps1>NZfI+X?>kjy%5-!}hBl4`2jLDhTTyid+pUA%^?5?(}CF6&Sd(9 z$^d=!No5Av;Lr>>-7EPJ*T0( zAwpK919~540EoyT!?Bo8#0e;N;uYrBf_H$+=DwEBBrz)Ne`jwdc4PHPIrF$7l`6 zV`>SMBx5L1EeV8WrHl;{EhiE$eWy-K&aM<~mU1qbUm<>8otq^LL@1S}K`O}BlUQtD zVHv745+fLh7Q-U>d}8nRcci1hO{QaV0LFXpX<6Kb)R}!b!p(go20drq1cN&^VC=xM zmd!JNk7PfT&&h=ioFJ47KX)h7^BIgW&+jIc*B{q(I4Xz8QSg4I)^B3!emV1) z7q_+rvojuFZ_lo8ZK>{vC(4{m-V?h&QXy=)n2sw1Rip*Qd!H3Ee|{_^G}%|t;|LUd zck$+8<-4m1+~2b)a~LTbaB62U@dZCP>>^yKdC{_pt)! zdhzJ%$4?Q87#a{+Hc>Oi)`tE~x>&V99MC`V9DNSA$(WUvY>%Y*9{Q9(ma0OzaVRH^ zC(~+nN zCkj9?j8K$`6sMS!Is~&vqci*>%O7BB1cQ!IR;uX?Grq{uR#&9Bt}BWR*I^5QaH7k{ zwczqSrJG{x1S%NwHZn@%EG0&^@#nP{9siY>)c$H=W5gM!a1W=|Lzgld@<0Ay*@7o`*J4-~=P$ON?WwRx7sO{eu zohM%N@R}_T4tED*#;7pM4tPx~>9;;|!(7SXlr(vPSiOdK5=^33Kc#mQebKr4pT>&! zoIFeiuaoCXC5VT)K%nWw4n)pGtdt5KRh?dZcem*UJt5gRCR2&82~8E|l+M?bwQ6}T z<2XyhW}N18$6M+7kHy=_fj`x7pqK5WSU6+dksGUnwhOu@tO;=+qFx7}BZGt)6CB5p z0|3I8YExzz`x==U3*ELaAJxU^Ye^~{|H3Rus&|s;HZ>O-GGYrM&_DoVxdpKhfGzYh zPG>VV(AtZ-Ack~@Z=rb_6TS%1q=4oxO?mYEqPO>Q|M`oPcv8+GAqMM$tEBqgc-k&D`>_O224;z!gqpSEz3d=mGJ{EAF2Uf|5Q5}S&B(2}rQ zGArwk1tVgip%PmZ53DgggQiEaA!_?YyNt|di!?!NO_8Quz$D0&YK=(K!ZMR{1=~b^ zleB*PRAxw?F{L2hq!qH0Wfe?W#2yCDfyywP^USVAPV)V(?f{*fbRImrg*%i7 z^&k`{=0dQQ$1ub`?>y^D;l*17ndtm3>|E85DYhb3y7ZjCoYA=6G2LsHyr=Rm6&*s* z!=jC)_jeXFFQs6+6fH)`N+wj(VQ%Oa%G|YI0}ZFDq8}|?M82767h^HW#@%otN2jb6 z>Un97gS}%AR9!}-x<}K{&#v5D;eZL8Xp&4gdW1a_Xy(3gi7$L5BNc1O& zr{fkLXVkG%1qv&fupb&^MPWs|U+bN$N$~)Xat6rMSsNg)3oC^hhF(Y?( z0{v^@EUi8}fy0t>pH{7bz5#Ewz_py7ZlNzWFz`YU-oMnq=o*^q>k2LN zIkU(*``eF_$-4J9%d;6$O$Hh2*7LeXL5;98xc^dvRphxowhMca&m%XwGKM1`ss1V_r z3GwiHJePShx=imHDrQ>*N?asKQ){L?l$qdD2GJf;w`xI{k*{DYWIRGp)!j?P3uM1bV5t-XZNHumEgAcEO2km?0O;4l7j&}sCalS_wrn~Lvx}yy|hvxde~&*Dn}vEkDFhio+bk8^CdeI z)uuGdVli?@>-xP7#R4)D<4esofr+N=CY~#Hpif8E{&eSA+5;^~GE#^{BZy+ceY;L7 zCy+GQTwIC>eFAm~n>6?gp3|d8nTZ{`(R9J1Vd6!M8SFyqQNhV*myNAJ@W2-)07U*I zA&sG+CNuUc2g!{BS}3=JanXRU04^EhJg=Pkz>>KVI>Yerf7~(TJ)Tgtk6Gt|Rg+ggYez*XLpTJn21ulT-#NsvTJyqjPD|J0i&vhY=Q;;~I@; z!B==I1BY^VbF8jQMRN+J6p)9Sk8Bl8?SiRQCEwipp5`16qTH!?&-FG}3c(>?vD%it zo0TCGoPo@#WOH*QAXHdM9!d0XvYl$c5<5hS6Q|TlQE{ zO7ZXwR+CKI1~FsRxeyP=m`I&d&ljTp^G&hc+!Fb!1r%CvfPnc51#@SmG@ql?slZ;1 z+mz_NNN`AxiYiK>LU)DU(x&w*Nqb%aBXM03i!0w;-+*t+)Zwcp-MC<0yB$^D2>IiG~{- zly&4^xOY+zomi!7CadT`(@=|5s&XSGa{!7I#$qF zqr;*nLq@$IbFd{dh1;u~B>d~hFgdM|p+o#TjvGj+Gh{mc7}-bA$TdS+Oc^K^at5ND zCIVOpaGfvI>e+9{1J9xevi3sDdZh<1;CLVfw54R&e6)029XNXAZQi9FjB7ur^fQ;o`poUn{2boOHutJ*qcj938HBv&nROYYi$h}7bD}y?ipGh1UD{yzM z{a|+1A?;0!t)Y=Q{bXQN%#6$;Uh8(4tcu-W7Dm)B$11A10s=X~b58q*!zjYmN*rS(Ef{St+> z5FNbHd8mL~6HKN^Fo!rJ9CfOTInxRy_ge@zleyAlNr>eM3FN3CN)Xx##(=NdHPA{K zK+zGm5eC;w==}~9$Z_7UUKj5l{Lp{7zZ0N@{RnpTm+>5X8FWBt-p}h94ukL_PUcc; zy&qk}@!0x^9NV4NxO>rU{cQv175}y&@#7HmR+Aw>fx^hpi@g$9NYK@iA{du?Za&Z+ z#n3mOyG8c&z6LE{;J7c*bB;oQ&tPb-Hs{>f2~+K>7G`&RqzU*Hj(JN_LotJ5%ghW} zx9*hn;=!$oLe&H+(_(*dyqsZ7pi+JlLSVcIT}~}jj93*y$)1R)e(aKo6sb%A|3w%# zBRq#M*}|zNq;@6U@s{b@Fn6A#8zvQ;vuh@7c2dTw_?(8a4Gt{sGuC;q*qY$DqOG8r zFmtN{W373v(^hp*Zb%{}3*sU4(j^Z*qzA;g|%cAC#OTVVvHYtQzcaT7CoihL;MP zn){)(7lf*96cjx6CV8Fz^^rP}Ze#BPPS8`3GA+1zP(3er3{)NZP0Bbu`?UWqUNWy$ zL$B3JQ(aJ%nPQ+aMVaP*wxYUI>TNk6aYeL{MG?ji=Sgjaw45#BKj2!)h>K+W$-Z>n z&CQM>=d=n~Mn}I5lJm)lr3Wx;M*44rMgkLCR|%>^W=O$_Y!-nxSX0E6Qmc6qrL5+A z?8UFD7*iT%Rq2+WdS0#CRyCnpRuO)e$#hktk_RoTHpKPfL@{uUa#4i7hn?Zd;t9sqP68V-)|gMNs@zT zhxfCq@i)|$7K+p2&O>D%;_^N$2*d{-k<8z(iKWGqR}nYY>*3Ved-{P#)7uvEvdOs~ zj8`SrRJATL=U3$3;o11wrk!UMIl*fZTJ;&&>ckSO=oVs<-?*0VTvZXIwX(Q(D?aN2 zK`Q5`H#xm#ml%OQx7ZmY25TK@a-!qw*>a&80b)3yc%8ur3=sAu*9L^s;6fVg;^c8t zeF|~jaId#?aH+NEw03rn`h%0#gWau|bcR%ul~@PIAAqg>-q}4qVV=7E!-G|YaUxSx zMraMWA7WQKDV@?U3;hy$4~2zUnU?-em0dRJ3jMUx5f$>JoGHrshXn@lV`?EW85rk= z@Uk%I5Jf-ZR3Z;uTMZPDj8ez~FGNV0Rle3nM0Bp6n~abu8hALG?aP2 zh1ngH=_l~d<#>eqxwzJruC`=j0t!mJf}Kmt>U_!I+)pNN0NacUp`xvlk z1aTIStSTnDN*RWz<1BDhpYgh?&!L{BJE^F8SeLWKDb}`TsfqB@AZ2U%2Bs*{4^RN2 z#f}IWZC*6c(fx1fY!WLqUUrbZ&mXufI5h} zg%ipXEW8^pKk_-WSIsCUDmWt@!!-?CUu-DK#%z^Z`5$Gw6_s}ehp%=kY`Lb=YZ^~) z;AqoxoKaZeSJ6~rnujyQW5bIFRtep1y}NCxq3d40x+!)Kq8)9ordNf*?zymSjKZ>| zOxc*`(KJ`X1gW7DBqO1esnQ4bO*9Ep{*IyobfgOK$-d?&m!@}dUdIt_nBh9J@5uXc zy}Zt7uD>>k1H$<{h09rOc%B5XXhL!))=HDp*)=qm(5-1m%nrC?{|fB3ZpK^8>g_>) z?_|3_80_y39>m$VHiW^e-JN^3j`RVM`IQx2M*cfW=6f+_l3RZD^v`%oYjPS&BCIQ% zb%z0UJ6@KA9}si5X@hKuz-1M4GyO0{;8_WnE}!qnocLNWZ*OV< zg@9HY%Fpzu28f^SRfUig?}M73!_<>e$#or>Hz?Lu(HotAVTkVOT!{XFy8Vq4XNi(` zfW3&TVy+6^C4l8kaCL}(AlWJnR20s0N@T14HCOg^7&onhi`k%mlQn+U8S}uSPsJti zJuJ2T!O_e9!LBNJr`_6IHsCrfwe?DCEhcB{A{|Icu#_;Oi~Vk}mASjznCo+g#|BnYBO$Nm5Yt6e(KmQyScT=aM&(Vq_Ynj|JFSP>oa_>-ds#PDKA(~pg_ zX7vr03J#lDK*#IK2B+Y7*U4KkZO_vyLevOh#2w=2T;yRLYI7Pk37TzP!DlyJX0y3z z2A8IYsBkW0?~A^kDW`^kbpu3cD9vZLV?@$Nm2x+fJ_j9m!pu~>fBRw=ATnve_!3-D zY+}uZU(5t>fJ>zyU8uY6bRKjVsxA^^+7~{Hz1h4k^4k?68Si0I$z@Mb%mu;LOA6w&yU zq3HWNqnvLq)br+p{#2_O4-_)djOofbVJ1eK7)5MN=#nVyM)0iPNG&(^ybD#efT<`& z0QA!^)=!CnC8l9sG4flGFQOM1BRHucLlCabZdiR8&oC}6v;D}ywf=>W)6C;Q)i41*R%zzDQZBAg25C^hZ_lT@knS zxrFgD=OX_}hdQ_*9-ZQZ8uV$ub=1QNkv_R){1_B5D=;TPIJnavH0!VB?c8Bm4WqPNWuC5Mt(2^xza&6_+U4)N(Lu; zBZZ3v@TFXkpi4`1H*C!fEJghoz-%_Ux!Z-qla47_uhBtV@uw}LJf7vo**~A;=m-yR zJ#<58d3kN=@3k!qou*br+EGomj1z48ao?9>tdaNA-+xgb&p9!{8nl~&|L`53&Xh`g z&7UmnFbKJJ1BjKs!`Q<+R4kt&apyqqvjLxoa}LsQ6J%%-x;U#*2w+VRl5xz?lF^Wy zkfxk0^4Jqq)~O(iD9SB|P_ha4xtej&!tMz!+O4pv(r6(7p-%)RixNNyJ0bAzV zdojGc8Pu$SjgYvs@#XCA?8wH76_l4@m~(Rr=6F&@$e)*om$%v1qaH*-k;8Ll{ zj1AEswErSP-n|J$M??Y{#VCmfQm2cF6++|hwXZTg-KxM-$z9zYBexcpAbA(?r7#0G zIs1u<`|w<{-&uxG-~u_Z0vx>Wi7{6iafmF=($H8Ovn92*CAYA1X^hs_lhhIz(RyEdlwWE@2yow_y`|xxOyqHtECVyt?(e^zIGy zBx*;`r51wIm9NDAgEUQnQ!zMa6plyZ(ber$Nsc4QTL7o3Gr$yxA#MWfK?Yft1o4YX zgt}8!Gwj0ja;m9Q$#)a?LgjVU3RYS`lZi1NyqbYKG3RNlVMvxTSvM)(wn|nlh-hfj zyIMcem>dj`8n9ax1~FN!f7op{|9XncJk4%TW;g?oeL$CPRF(3SZVo#o(Y-{wKmj@{#q^E>45UMg?tDDf&p3n^zCF?q}vt{Auw z%+KkP^decn=dLu~UstzZ6CaA1#1A^R3CEyUGpvDX@1(Cn7UKXEe03Z5J%w+3*y31< zu$ao!AY@n&8$*g9S{Q<&4(qKp$~UhXUX;!f0u$hATRzaY zSS_YHQ~m(&gjPdOcg#T}yKtU9(1A z&#{V}wp;yOI*Qu6<4#$dL9B5RPD}PI*+{RCJClvY&4ACB>x$CPcZ3$it2_7YU4??g z3WCG96^-e-zHD<}&&oPh>;F|TPO_;Fl<9rMTBFnMcRNQ%^}VBJm0wJpQh1sRx>Sca zm^5mYs7ym%@7$jV`$!291b9jm_QtZKhP;(UFB6xvxfLb_-cvT5g_HPcK@qY4nHpFn z%So>N&(sjUoGVoR%TJ98J4eI5=BJ1-n#i4hv1vlaYxV6epLw(YnK$zb+GpfwOCI1g zTz=TA_b|0Jp${FminkBOd!sk(vEZPD>S^6hN~)-Tc0i8}H@obyutf3G`U{cp3$QpK z8}V>j*XM6dx%K@j#%Ql|yth0;xUACdopic=anUC2YKd<2>+SECZ=?RQ-cbF0LAQ?5higf_qAf0800ar`vjqkYzueS@(LZA!1C3ZTa8}mFIEU%%(IR-1bY{)I3AnZ~B zV-^pf8zS*j7I^+mwS^N*QsGX<4DjPf6Uv6Qu1gpa3w){(bCw#&Z zOaG8Ll2Ig&RY8P43AFeHxGfeX2MPgwGVQayI=>vjThGUS<{;8D(o%)J)LruOa`K^f zJ@_#0j2m2|$008QpA7}%-?gYVj#|xjzsILPl1tiYMSgl?lvWIkP_Q;*A35}(NL71I z8g^f^=&fH;ZCM@lux&t;6SOcJmS%DY#o?@d+g#AMED^LDolbY3LU=TH+rtlhB2@jz z6=86@kso*K_k>dX`T>GwT@8Pcu+fh7X$mO&OVNP-*AnIA0x#cMEX%5f_D1M6l>qp`bAAMpaznd4hzE;g?qy1aellP1FRRV6M1+Pta+8TJF75f= z5R53ixKPjq@P08o-@!=9ggj%q(y;8(EjU4|2U@$h<4}zxm2Ng@B_0H7C)jUd+ENM| z0XWM&bI+4?`9WrsWSrqzj)^NnOfYSL{U8gO{P-HxJvp@ZQgHVTBK=Ho0_O?9eet<% zwCFPnsUsCKXGC*(x1d`B$T}vRD-3fN_R3f&-!I{f!jAcDQVEysoiW^JkD7MH`bPsN zJfySDj#Kfldh7uu2R1=>IYA;t*%BdMQubuCD5C=1^kQZmu35DtA_EC6$MZIg%Tms% zjO^=13=YkeLS{A8(TnDRV*it3fK!rRtS59ZtjRK+{oxrm!2A|s?o2q<>e>e0aB@rO z$mW|E%Oay;Agnx_Xd+2>Pq?6D?JW4vaS$^i8{#m7g#=m(Z;A^|rHl}aO^qkAjMQn! z$^lFnM77DtNN`;vvD>DcF&ImxAql}myBH30M0*?>OQQ%wRV?%XE=24F+du=vqI!ET zFUikSmAqZSMS&e5XO_gJwQm3DH8TjL^DTW~mw z&T&&Tl1vct4)}=)@(Vn=t!JE-={Drl0{Kmih-MWIAWJ9@kY(r^f0G!`TkeT6pa}I0 z9&N8~@x&)$UaNc&CC;eIUz$TleHqj9T1T}{?j$$-_3RfsQh|uIWYA|l4YAKkq@l>p zfnhK)Oe21%o8Vl8pWCF@#@#Qop>+Y@xV} z%Dzt+<;nT^?KN($(%QssAEP(w*zJcIBWf(j2Q$)4=*N}W)tQ8qcb{-J1_IEGq6TKS z*Ypq_T~7)))XCn~elodFQrM%R6PuYLnAsuiGAO~y-EOs??^1Ln=4@?TCU=U>(~}xV z*ufnh;&Eir8LC+do3q`H+S%Dz<<(=e_+ezYrqgQ=vV`HK*$Up5t>9HiU27m9u{hFZ zkavgjnzB>Rb7xlq&H2!1e$C=P)8x^#%=44l3(br=nd}X1RTn2MwEYq2wNXzFm zw^?q#<|mzSFrJk{>m}Yr&aM_ z>0QGKTW-g%eYLiZs}m|b-wp3Rz;_XyMd0Feh zCcbptmnQTVl}D{JKYY$(OY%I>#KZUg1>YDRBi0V;p$IuzW9s^wLFa#0xBNDhnY2IWjXZ zgu#7fCx$!>>ise>x*9~K1T`v)2gnkbyJ?VUjEq=r4*AI!KEgoZGRQe&GOc*C!x(Oh z6^RDo_21w6B;C0tU7!i6k`a|N}b*maW$dOdna3(ZmMSw=} zO$Z^CGTbM~5wZOH_QOM^eTP-G-s?5{N3C8z6wBl}XP&2+AIz|R(R8Hs;z5c)1LLA9 zQ)CZB^~@8CNa8DTS-XRWE=pxl**K4W3R=;=3lD4F(1V#~9f=*Dh!nb9xq@Hie|=Z` z%Jn0d{c+D-$Cv1UkbgAOagh$pu5m#WWBVODN^;syen@`7i&b`FEd4~-9~)@Wh*)Jb zkQ5WzGFk7CDvUAx!83=pM$x|@C}-K_f-FraQy%-|YRrZ*EsXWx1<22W@C2gbs{Mu6 zE0-V)5Luz)ftKuvhpv}tizVF`Rc0DW^rFXkxy0)P%WuI(_=gi5Q+P!`1I7umOnJ{wjFa_nV8)gdlC*%u zNZO+A@$hoV6|Hvqht2NUY5V(j=VjX|o?&O4{?CDaRmF>F;;){~);N|G_h&aQb3%bd zk*^YN@L_;+;8a0&CUE6mi=RZYr20?*sMDe2Hd_eHIXxfJQc_&#`1bNK1&QRE`(~YJiLgp|<_r9o*=S#SfQVio{ zt;6BqJv_0eSFj{fAzzEr3hULw8U^DbWqN?>sChe{5B5~@9V%Gp+^l3NEAn@~mRvLIO8T1}OJ%hpxjIIAtD4o88k~U6WTN z(Ut}gx|JEEtZZ8rn0GfbND#%#R)%!8T7&EJWon5~J41|ls|Z=OLPbb0S5c+jDz)1O zb@)yHXds*nTmc5I{IrganjQ#^{|fcVKhd>67_3kelF=$cFs@J$;?gQYTCPwLGSn*a zy0${qe)qKD`pH6drONeI+girC?)h}^8o>owY%T+)UB@f0XAm<{pnCP(RB5|B94Ma@ za^B%Xl9lA{@(}74@ZMkqVs?2br|lSy(|Hj*@Hx~u7+Ym)GG7Ym6lC?;@R9Wn4*{WD zO92rICt+ZnTM7iN0v^D<`AEmgez?!nJ%+1ZxEX@B6}Use4Qlf0?|1;u7Jekq&ba0> z$OVTC6)*;4Wzy{GtBLO3WWV&0%2PecsA6kZ{dk|R!r}56-Kg+cqjB}4wRM-Pbyr*q zE`rk5uen;k7T5aKZPDar-r!^TW98TN%E!uG<=1d?wpro-DO8 z#`!mc`kb!9T+A%?*&T)R(dTtDysRpN>wQ2ScbsuJo*-OMc~HHcjwau};(y4hB?+~f zq=@m01{6i`S5BQ|I2aZ+C3jij1`B5cDc~A((_Njg4sM5+dS*MS@F;}VSb_y#{|k76 zc%i_hghOf02wS)<2Cq}@AX$!%&N$J_oPsKPAPa9lCs(*)P3lu)0?3_jVp*+Uju3Go zJiC02F8S>tbr9F)(MMs4d9E9#>T5Ib+Q3yG9Pte=HCQ=av#0CLjD|l`txyrL3lOAV z-|`s1jh;Sk-r{BPw>)fQpOmg^Ucjnz+&B7pwBVuy5O_(C+xtri9b5s;2#3&*jg7Ek zQB3+NFa_&$lyx-uaDdL0xrXpfN(z!RbS%Q>jP^XTXkRDwjuJqta_eE01IX|mg!4Q{?FJ}EogCIyyg6fS`1f$Zp2sW7fh!jNT zI~=47-L$;VF;!F@jwVw@)!og;MH|q?Ad25N5iMJeBmnj;WUM@wK}z5BJb$6fU+7dHA7WNHHwZA zPzp;QPC2vG_c^X?Buukx5oOAJ!F6*8JQLR02BOT}2KKeM-4Swz(+1f4G0TOvo>8VW z7nc_@M`DZK5&zE!k5fa(&dC$`}*L?_qgMfnLUQLktN%T)XFsFTC!Rxdij>= z!Sigxac;I%u90apt|5J7=U@7h4Pkq4?dLH?u|4G%Jt`##*9nJnVF|!-ZNa5B(;?pe zkG=eY*^JMhbmno%b03@c7Zhano|V6zts{50^p(z5@GL4mz@r^ZX!-5=ncL_axfj=D z5|FgLH>yA|!V`Hk!R159FLD4-%q^%Ed46~?<5Ru-5U&9e0{#~!D`YuM)=E4#Dt@;4 z2vx+{2zX;X2!3*hgKt7z#>bYU_{Hl`FBF$(c0-2P;pOTdwwn1(PohS!FD2=n+6?TE zW;h(U>){UVEje+bf$UPcBb+C)pSdZsR{lKR_@w_Ks1~+bw3MdUbItoCfiLLt)-A8C z6pD*c1WB>5vTn+z5O5$#qhf%cgyhBoEZtKg91U9iunb39yj%fUzUCpw5_NiKkT|^8 zEQSS)u3>^p{H~ju zp{+IMw&a=StLmH?;QVqQdYXCz9lMbJwEfFG7RFSyfy-iqYOIIz-}{1zvzryq+FJg9 zAi+Ew`c8Kz{^RE675@yrId1lP_2+VloO)2%+Fak|8!IiiYehq@#&9adolXhk(`y)R zhfePC^%r416U$e)5i^{2#*0LLHw$T!?YbhpPiw`Zb)LRfV; zjuS7He7T2atBi}Mx?7gBIkn?@7eP=s?0`oCH!l&45uD=|i?VAQs9)mRm*71e;v&!N ztq(Cr*k{|o1>BD}|KLSc_@uB9mw`{D0Cf&-F}Jy1$+bkL+CL-`0{@=VhVNleewFr= z+SHu>oZFg~@uib(X@83$?RJGCU<4BJ;eV-V>vSmMept?D@o5Z=h8#IujxMrFcvi~+ z2-#T9c0)Bsry!1FWYS{xCeLH8?l>Re59ruKnTEI3ngiaD&>NJf`A#daGM3=-CCV$# z&}zPjOUsmkyv>zDn&Cph1!k^*+}qYXsLgMNE{nf3hl2xH)AV8K>IGDk1LE(7@HF0ugY*vMsJgehz1WzT|O+UJu?Y+Cue71&-(uF9Jggj;2( zy9bxJ{eTuwq%zvr@{>L_*-1l-o~XRIg=;1v`>o%zUp4zdC~mWVx1~VyqC*T8$Z~-f zMfj^bq${E#lX7Z>RgaelreV0lFk=j7r1yiVE-@mj_bKQE^~rEF<#KK(aHkg{CT?Lq^&83mUxEv2Y9`dst<^9Uw^7`$dA^+@G4)J6-{XtA8 z91h&NE7`GR>`Y z`}>45{ujc@RnHiE_PL{Ro$Fks;x>8w8m|8S5RWPFP+kn*$lx~UmRAwVQvKRcPF25- z>MsBV5@_!epx_FysQNri_E*BBHxHBELQD?lRX>cX7xxMCAjc6%%MEXaCot#o)%Nq% ziW{a`$RGk%q_kYiHqA*yA#~DBjY@5>KvRix(YPF4z2bC{5hT%sZLIRezE&>#+A2_7 zVJH1A8q?d+Y*^dh5Xc)=yO2kh?o5V^)+ggg5+jzDBSfa|KH{=ny^80u;Jdgc_s_8p zwW@tySXBohMA)d4t4o9L?%xrK_&cSKVCg)#q$@h)wyv%Q5U_9wzMUe>4HiIB^;qSS z1?&Z&>D^h|13cKNK30b4^0tD_9@TJDVQQ_`v`?*tC+#m`kJL=_7d#}qwNZN4E0g`= z?%3yl6vjbHZL|7p^Bcm_>RKKyG_df4ze)LpB@RA*vsrzzS=oC0*f`em^rkhs1ReJI z;1;JGAW>L#!-~Q;WF4U@gU?F3+~DK-gNK!{ocoO|k1F2f7_1HI#hHVmKL|n)S1SZ= zl@4>2T<6~o(Ujn%LT49ueJH6vDW7N)1_zH9u~i#wSm8|LnF?=R!&{g+D=}11^^tIr zPtoU1STMi{PDu)3m0pcVUe^zvTVY)G#?>Jp3#R)Y|;K>5oW`xPNT*OMpQj4<+>cy?( z>glR1BVbLY7ahx0St&|9`PMVq8;k@kEc}E1dJ+BhjI|YXP!QM%DCY(iVu=+zU&xVE z%UmcJ1?ROr&$~8`7Z9MTFWRq6apiAcK~SsdeAv*&@UKErB*eNKJCBL>QX=(*j_c+f zKD}UToM_LYf^F0O@xfacv7B@~WADu>M><;PZG@IC$*oPtL0U;;xh* z6<3)DW!|7N1DCj%#~&M1VhWZcv{??gd@(E~zx5*tOUul4EWlaEjqjSGBAsd8_`GF-q7eK} zim;F@>~fdDBbYT=AeyxV5Xc)tBnt#t3jrJ)vEzJ-8oUZD13)xy1`!RbEd#pTECWg; zc(?g|q*!a8 z4fv%aJcMLH_FW};1F1^k9JNjz4{MU@DBJTJz?-~&O|XM#FF|T81vP=nA+B8T8T=~v zqtd;Vk*7+0Ku^|<^Pj#tr)R$WX5ik3>z}^*a2+S^8WG`PRbK1X_gfu`FH6pY+CFW^ zX$wn>6x<7M$?T!qiwZqV_zN(0?X*c4Ya@nA( zS~%HP%?mrLNq>BA_eCX5bXRlGU)yd1`9s@n1-l@}a4O@&erVDk-;YDDlnJl8VbdJ+ z!yyXln)xn7Hb{;U*4a_SXGhrZ&fzc01{-2JIi1g@lS4x|jPpw%ykQ5VA(u$5hM|`E zaJjl~}yYn2hl;Fje*f?M{V!&c1Y3Z zAQ<7-iq}Faa{JsT*e;MpzecZp?Y72mhf~Deq?8J`^Bk1&q-V-8iQ*(egGW(MYx_dU*P?t{Z{X!-ftl0*=_^s%+0X*9y&W779zSm zhvOP^jl1Yw;(Sd*mcu@BHoX z;`VX~Sa7?ieu(4}&T^TPYw?H37Z-^`#jE*DG1ytUAfkXmnIUfeF`lqy;u&Me3IXlV z<8Ni2LkRs#8H6$qP_GKXA~iZ=9vpmYVd8Iic&K~s3S)!5`xV0a@|pCq1&3#&!PIy% zMWm;R>xz1^@1Bm+bdVwoc@H+>354WiO7gkf`!KpWe_J8p>}9cX9~FQwmLkGxNQ#Tr z;!onXN8dc&T%GJ>s#bv{4e}Vs&l)Wb1lm4HWO=@=>Z&1bu9xtsWggO0AFq2=R`4uU zX5QMTM`7Gd66y#Rvb!RIj?|hfT z#hbEgfWt4^Nw$;jyqv3F6|EKFe~UPCGDgIq;glB+fZ%CCP1Py2B$mt)IUg4c{{zR_G$D4h}EyijYn; zinys2pc>2P0b7po`wD~7ni;Y}a{U%+w`Ogu#5%c+6Ux6?8T}W;RS~8MODP)&R7I*7 zlh&Hn6l*f}0oSB4F?h z3mrrclTUU$xjo`KX`ZSdayc8G{&@1CmRL8qL97-!`fE_sc!y14(v16TWn;z)*CAWo z3HW!6HK}#9DA6)?KP)eVq5Yy?({{N9ovdL^g3A=7xsN5)kQDI{pr|v`uW&Y1GZk%E z!tPm`Myw%ZcO?xNW$GK>h4n2htgmSbKqkg5^axy5_Jbpem|Dn_x_ym3Pz@^X(i=z<_U=a9%XDW1_Rh>V&MN9sX3*YY=Xsu-a{E{3b?> z{!pMW=x9Y1_Kr@QGPZ&}{|$nRDM^E9=t7^=ajo)oiMC-Jz@vlbHu#Er`D8kLKbqX) ziQX9=Mf>U%SvtPHsL+LWTxG`(nE)K}gU1x}hEy-Inv~ahsgFpb>Ef(`vQ8{A$`P&b>4!ztB*JGx!MF6kMr1#x7`vY{5)p)RU#tnmhgC`Q3G#T z{yZj1(DitvGf`xf;~fu3kZuMUiB?poEZvSyQtp;RIoE$Tzg_p2MhgW$x$i`{bmwz4 z=4L*n^L`h(beG!0Tf~UC{CT`;dyaZ5bnKHGBM}<@XQpB@d?u{JlnjF~sp(>*<=qY~ z&%viNR0L@azMIA7UamrnE!kPqE;Zo{t?T#1FdLXtp&NMZF_I!8Y~@3aXG_bMF=Zw3T;V1|W6`wV~%G){Z28?-#=sz4Cv z6VFC(WAU(0b$lcjenMpHoNPXx6j?U%4s++L6(s9UiayPsn z>&$HRz8tK)>8YS8VR+Tg?{qGmP4Kg6Km-HZs@F_UrvR$>{1~tKa)6!je!V z5y9bleI3D?4rL{cid0;--^942Syg_{zyuPPf=5h@OBWZC*_a^8&gj)GcT*R!Iz?pSu}+xs_c7`+R{|y}F5bW!T5l8O4Wn>%$7wJg zhAdd)B@-+C?{acX6I;?m^_=nZ?6+ff0^&W@nJ?m&J%1LvV9n+=lZ6K@+%)-9>!1hc z4^nL&V3lx+Y1#ey|KoAx;?-CNa<_vErqIIF$FI%uULeu0UFA0=tfl166 zJ=I9YJ7V4davyEv$|WNLlA4(l8PwuCzygG z6up_xp=e1FihrIVI6zNLGD16Vei25>s56Q_4JS>w&$Ypr6_qU#G!Fws3VI4*M!RQb zA^oV(zr4?Nsq_V@zQC4KoK5xrK#R>EW1tbgla=f-vN18DCT&chw(b{}h8zvUT&v2e zD665NP!xg*<1^63^4kl4=bTIUvp*f=2WdTvnlstr#0?Y>zuGW0SlVTlH*N+z9hDA0 zl6py4K$SQeza>$%gNRkTXtD{-tWY&Nf5%X`C}_L|awZ->#g{o%Uvgs^HRt@0><}t! zGM6bb%+OP>qpe33{QrEXB2$qI8O9$UI$rTfk=zfCQ2(ew`!JChO< zE$xB5gQ=uty-dxR>$Y;q#Cz6K< z5$B`VBODANh+YDvzjzy`9SqMkDmxGk$uF}(B`ejrodL-ysqQRPSYwkGQ@af=xmP;zV=Pl{?T ze<`Srv(-wW_{}-*_h=VurG6r^{d|dKQY2Q&*@5NbjJyFjwxqc_z>|SIMQ~T9iUVcw zFkow8!hJyn%Yg=OwM1`(TMJYDkght?}RJGj^B^{=OuwVL zln|6X>UHQGa3YZKb$~$W?gP&_-^vFz4YRj{%b={L`0|?I>+~ldhEtzErFQrvML_ z9|kxgK?E5>>94cv5l<}s>i$*9uU{BR{?&Kso0WV0zfv-Ov~kJUT45w9!CG<&J@{+k zKt{SjC7|%)5g_Rm--Q^Aio z-_C$8J)+8-DQSIvP(XfP#2+Nw;IYN%6IX&>?}&$kq!}Ic25HL3=Mx0imWqR0JgqVS zyD=F1dOf<#q+oR3YE!}hgrl`iLS-_WjX`oLL#U0;KR{x^LsgQbqa3oIMjQiw(bvL| ziNopOT)w#P(;;RY@djh%la70bl7uTtkl{BIf+{H&cyr)Z+nW6krYIQR2737M{POl< z7^D!r5M}d?b(02XG{e}^4MPD)^Moe^Jt!qfeM}E}-9Wy0s9vL#bumbAxCdztd0=uq z_%NQmy}iNMD?eZKBY_)!09XUhDM+h2t$cz$wC4JXli@eR@%fz;$3RnCmP;L`$qEA@ z{|on=>)G3($p-rLBh6n3B0l;ku!yxsgG)@j!KD)E!|?K)Ui9H{!c@4v9wJihScSAE z^f-ETScH$L!vtKecLGXlozfT!;NNY@tl}a$zr@_o1@O0f^%qa~a4iFW+jT6Shn;S_ z*{`h~)Hk-CHgJ=83Q>#n`om@e#Wo%`HyZdn*m_t;p$+`Av9 z)>4#E2Sj{6V9-eDKYWDm{>H;c_~`ARZf|3Us^T}jvEA%SN~zC+1{uxmeLQHhu}vs6 zwt)fuoIRfo@7~h1M`HJ|)$TOW2({6)gab`DKmru(mw5XdV5rD*qBTduo81=H=C#vauT%$%kIn}r zl-PKT%>TwQ?okI#96cuJP*wJ@7bXy!)Wh#j{|1)mbzaog_-CW@5~-eQC$hYuXINYB zowoOz2d#D!19b|y1bL1VF$_%e;Is+qJ+G}HXg;2Xgz!Le@1zB~P-;(CKO{Cf$Hz_d zkCGdcE6AF2DSg;E+UHceFHYo#p@Is&l%=#pXZ(1kU|7O9#=rSKcXb*JSA3okkbV1f8=biBxyb+&PNXLZE6PT zKrX$$*Q}!#tPY_V$k*%xtSCiDc!`#X9`A@7FcgFdcaBvgq+tqlUNp~s_yG>~tSgm% z@To0;*3=e1AA-k2FK|sVD1k}qOW+Z(S&&)B%+ky{`-+sh5dT5tmo8tyzZ`-0eF4JO z0Fc60(yRHU-t~k=L6JZ5NE{8{;{n#j6e}q5_c}*?n%;YpOS<-?%!!O9roeNs6-d=I z8E?Rmy$@QqnvfLOK0uYhBd!693qmIoD0KIJ*sQJnu!(G<-w#ANB3bL0egLT!$tI#g zokSw_hTR2gek|y$WB>lSC?>RJm?*BJ!kFOF8>z5s0F_T9%6t4 zs@Z5YyGEvgU2yQlo;+(6RP%@YR+xGv#=YVVuA2KA(w)*R<|=&JVM@Ej`>bRkd^LnH z!Lowqb1|1nGu-M1OgxcFsFd)pN;k3(LQg|TBScxnle@zUx(>@$6`U>Loes)`H4z#T z-mRLzpA_HR9MKSALZKpHJ93BZbkwO_V0^@Z)K`OGgW%nCQ@7S?KA^Fr4KnYqY3G{> z{;R`C-TXhLOlw>RnDHi=qH1e*s+uMZ_UAp1ER`v?#0-6LPY#BV%{jHIJ>^X|7tjPU zBhdP*b`j2!?Kl5rW2a|8>8b6>se2TIp@>Tum@0;lp$+lQm3v>>ywa6IUAo=wEop;F z@oKaKz?34GyJsZmEzpMfVpY98KrYtro0Oo)6?Ba z7IxP*6xj^)3?uPU_IyESx@uf#z&w#irj?zx13{K*N3tiV(>183C)Y!Mdc!??9g^z^G`hVm z&Tq&pXBSIsE{7Ks0W9c8E}smRLB7nj`UT!$p?%lwRp`Qwe!=ZlcB~X;kYeHlb@`1# zd1Ng^S{kI=Su#rrrSFXF>uWCoR9baGeU6e6Qi9U?M4Wn%%ivA6b~ej0FJ<>AV>1gn zSiOWf2Q|;>+E5S*ji>)+1^*|5Rh^MxYGEmbHiHH>uPn||Sj4IPX0z zH49F6ysH9}?QRy>;cyBY(yFlhemM)T<#x`i!jx@x7M^UlSB0g!^DHpkr>_c4xAR$O z|Bac1z1phK%o>md*BFdn4X0BQ$tz7)XHCcgjQK>wLimhQMweCf{dD{e*SyE1Mbn86 z-_?Tt`IoHz8Hf?mK@$qIXmNqoz}t%8Vlc}#Ib7noUD!)$YU_A;+# zhohI~tHKJ?8qcdp-kQm3Fx#wN&x}K>*TjLxbaXM9&Z{Ye&5=hnHm+IOT8*?(-R;%t zs?s+PxO=70c06Zy?!t6A^r%v#soKI z?+L>P?&S+uRfDJlA;Sl+=$ekap0Er%SBp@EUj3yl$MgEc0sRJDc#;1^5mZ`1U)+f|d zJ?Il^n%46PRZYeDgsQPVBdqjc8Fu^tJM|X$Fk-?F%P=9;+|Je9S%O2T=1#8W!zF9t zO%22L!PNkkz87GmPUPh>Oy4LaKv*F;II4F; zg@mIsio??Z4p94eEftb*RwgT_39@`ck?hAQcu^Co!T1o6ssM`UyntEuEkC(wEu9?o z8ooE7g{lQ57NnL^#$G2Dox41q{fZ29g` z3uzdlnmt;AB-E+cWJqVtx*Ak){=HS%VK7@y_3k;z8#0i=J~Gv4e>m!#;5fS|RIBAy z){Z80EqUqU+yu(E=uk?v+C)eK&YoLJOPGDl;OaNl^#bTYfeH>Y#r21}GaspBwG%we zhU1zw{%rA#Q4tvl<1e?@qmIYEnQEFMzRg(DBrvEDt7<+Pl4x7Ux!`o>XN;F( zb6vkD|LSW=?PDkTB%O{p9+`daR}v#Qw`b>ZJ_5r9AVlNQ-L7w-MCMU_30_5A#o7y; zUkeG4!@h z9MJg>QZj+mSjx0H;$@{86k3Xf5=MM6=*Nvt#PLV6s9~`TWb6gRtcHQqSPBQSOk1bR zX3?&^#35{Fj3c9W74|WzM0kZxOl&6Mwl$UgmJASKtA>oK=8LD3Z-9zyKN1Uw7Lmqk zzQX`PPWs&t|Om8qD(&e4O5Ycq^|f1<@b z#e>3=YcwDYb1$TUTt!6<81%UT?Aa)^G^i}!-{GIhwdbK-SRdo}+SKM~On}j$@)&+P zQNP3k0q_}s|Ix5CRjokA&x-t|Uj(Q4Gc51&+(pRAEHd3o|GDa&%U{OOGbk z-_~f%wL@`FUe>$hcD3sL9Wusk+>ZbV>Ycvk8b7DLTH5D>>8HZ=7b; zyZxH*P?*{|p~T6xPiw$C0sb!Nh^yQ=IOxIOGD0iJJ!!V(CPR?+0`5&BwH%a}t@eH` z6f?ef@1zM|JeE2vO(~YTsI1i%(rIA`x=yo!Ws{4Mu3scVg_B(MQ#=IHu6fy-$?$@e zuT51)W4LEanAoZnrMWY!*q2*{Eum0b>5+r-bON{BGL$!1zi+Q~Sqxm4Ee9Ru#ew=- z2(c+H&Ut{5@AL8?u~vAHA%YP+CV!YhLj-;}qibL%fwM+6EVK*Y46>|%7j_V`a5mJC zQe7SXm$HQ6W!1qQA9e6A7NGp=JADU?aNM=q&>cD`=WMyIQw*5%}J&=oTyx23ELBQYwcMOT&uq8^9gX)>%c^`TzYKbQXy%t2NyDd3If>%ZE0ZDER09V#q3^cv=|Qb99CXHB$7=b;&%}Y-`ged0n$ql8K1Bx0Dz^J|TBvI*F6JPUU^v z)tM^XtLr$k#+d|uYkMi^S-+q@mM_E7;Vu`B!Yt{)QZ49J6$4rh!DtI8f}1~K2El*0 z6!?r|o0&Pl2=cSW;*j9kw{xUr@r3#6E-LwjKbPf~{)K`A?2vb=TNy7LQt73g3|2m4 zHJpl^`Bug(dsiugUp9L+EWIz`%6P%&Uhg$!F!C9z;zGANpg$c=J{;hebNW-Z_1CPj z1$Y83H@#ygMe$XHZ{LMkV#w3a->{M`Rdl!zRN-bJSDe;KY@A-w!K6L%1gC3#vje`d zR+fL@W%!oYIR7cKRm3x0e#C9vDfXvyP+Xnez6vT9OuAP>+dWm}lS9iLISGt+(vfhvdpV+FUM0+pm9~sIiCnB+{ zrJI*w9*qXhd6$x61)i2>SJGzOWkX9}fzOH^SkJBoA1h~9qjBYM^yY2lu=V_~0*0;} zbzbsVGTqbwTkHT?d{|ptCw)X)Tt0m;TU@^2IJUS#!69sMh4|=|DFxr;Dv`8Ce}o5~ z2y0Ugk+?>Gc<8DRgB;MkM#Pt#XFi9<2GF2gPNo~gQLY^fQw{%7`c`#2NbkI?VfZBd zkq^vWO&}Md5F)o(ed+-cUIB6e3K`_ow~N{dklU<2_OO7MA-7q5#^Pz2H8t&B?V0yz z`1+qt)M!IZf-~UVK;ZeoJ=<(sL5_da5LQ`$h5vLGt1(;6n0p~0_Q5a zD@Y9C?c zFEu38P{JTUh!J#t|5a)iq|&$x38Z}H>Uag0Dn1FZAo@cBvmkdh)ZqMk-J_TRWzrri z2q1(A0X&MMiMMZ6afL}k3vlDiRg`v3ktC?5EE1f=AZh~1Pf|L-AgdVLjV!zw+Q8LC zy5ESs$AI+ZUvd6Wf;%^dXTi9nV(szDVF1ItbZQvLk>rIiUzI-w7Fk)d~-xgxna z_742BO)u0~JOX8zX)YJv8WORmf_OMX{6Tb{27ZM=EiR9iB({H|3WJaEL)DS9_QRTt zpXN}o9|x53d;NA;0=IIRCd=T<@q&DZ`E%@!3>MqM`QD%JZroGKOmtMwyD3+q5fyYs zky*Mg@g>7>$KZAodZRPU0|Cq1bh@{MXl;%M2%OOC_INttq_NwpoRCLm12?j9=eP14 zGhjHa@XdhA^T~uH@N{@FL(1hXj(%}pZVz`EQOeFFDMk|C8kO@EwYreZpHvZ>K=SlR ziNY%(N)cVV=E)irzH*tuEWoXHj6ZxbKr%qkb?p&zDzp@K+yGU_2GB(yV;K=e@1kdK z;-c>P3QCr;E-71Bs8&|$5i1YJ8>c;WJ*sN+Le#SSLxrve&YjE3PzU{>#5mCimFW-a z!d+%_JI+2Dyc)`L+7$A{-J`AACE_);)@QH^oc)*px{=HG7#6dD69TDi-D5T>2W3Q% zq8{^6z6K|k`L7fWM?g3Yxx{ePW9T@(!+&L-`0Hu&w8@N-=-gjv$osfqF~b$R&iL}~ z<>=;Zd~Xn_@d`jI#>wb%a>M-U5oPzjZjZAB+2z*;cWJqRQ*qJOrkV1M_6`-PO0DcF zQt!7PKU}}(SsjAYxSlwve!NCTaGwZ2iGySRVpA1BDYG`^33=rzR}YT;;#pj(j#6fT zv5wJ!Fb|j|_<1hVT04>{LGwH-M1`}gpqYfR40KdlS>d?e?H$&SRN6!(a;y;L&SQnB;ODUd z&l8Fi%27fnlnO;s(IsQXk`LkVEPW|q#vr>IHm__|6(kR>i>YUsE5gFTvF4fT>UBn%NtUse6t(=`Buu0}99OWt833*2dI4V=)lJ^*L^3B?x#|IRY z5us_mo>fX08lIu{b#O~$7X>LZiT37pd?d#dkYaEv6^0G?DBnZ%h zU$a}pYx*d^4Ui9a!$k<#x|J4i!b`h6R;r0{ydc@5+mg<8D!eyklYoO3C}K;2pm7%k z%~qM+eqatD8u{$}QI6vWW7{vt6AE$>UudzFcth(n{;%T??HCXJXYfcK%xCfm$L-PU zQrNH2UDom#XF2Ql=x>ewkjDN{KCiH&t0IYu;L>oII+BW&$0~H&D0i)qa|z5+;`TTj zUx;c30IxLqE+rFQdICdjS(d|*Ri_|_@Lk6I0vzD2OgnlGWF#2SUq_AYu;25kfsKyC zL!mO5jjd&I$;M9lg7`@IVDxM$TymkBs<5cx5*LCFFS$s;S0oeJNlucLMq!L%ayiEt z^HPXqfWn8N5G>-Et^nBe_~5`+XV>NB&5zDqild;G&5cWhRO7!i@k~<)Vn|7dLtWF~AhP(4 z+AP&?Q0h9j!8`=g2(WuQlcr=x9kwn@s6b|pqm_~VQ_~0M_;uDU%m}!Ab5%u{h!&aJ z)3*i?Y6j}{1w{>{+d?Jj4hPGFWBp8BGHxcND8yqFNRO!sdmG`rj3lZfI5pjvD?^UH zt&jo}=*bqV+IzrOEs2WP=#Mgl#<(T`HG{Gg0vs&`dySzB&sGfrs4UZk6AqadhVh!| z`^^|M`B@V-2?h>!9gF&!R`3n@c<#}*m7DqoXnsD@|=eP`0i>j?%|>kVp&7M{=V54BQsgT zrQkv$p?__tl}=v4JF?sw>&+eSOJG7uoOET_Fqp2Sqy(*H-Mab01#6G_oX&|@akP5! zRt@(!#-hqF3$Ci9%Xdyxf4;4H|}qH&^{WE8b_R)@Cp9&u;ou*QY33-=otnW!~r z`=^ykWj+bx9T*)F;m3O5zRqL7B_`u_zuDqsIa4C9c9IEXzt)0YB+PT64{+gnB|E?S z`U2Ow%8ZAHs%Kxqeq0_V7JZCdKXG7Yhaz$|wQB_80vN_1*d&T^odHd2%JMmRI@{kyrDE zlPeKp>55%?k5MEQLJxCXq+g1}3sW)O-9yygE1Y@Lg$Y~dH5Rgrb8rsCCa3i%nV?95 z#>!QS?G{Xos(%TC1~YrQBoY}u?EXOw9`gW+j?eqSG)t|(?`?4T5Q4z$U*BF|&~qx; zK^VRMimRAVr2JW}w#k+sUSEXO#NN_JU{4%J+d#SttM|7a(S=GabWV4UYFzgI4$>)$ zyPLm8o0QUAvp^#E892yH+`~Z6*xD2>Jtl^#`s#TR`i7w+N;*FxUq-n9cE4BM z!%H!5mD3X0wB0Svjl;hdm+K>_G%hI?7vPuJi?f?_brhS-;u4~f9}S@ap29{dEP7_C zuXXL;P4w_sLRmNG^|Q-YK?ty5z)$qu^4;ZWt=~elbygslug)+ju^VFy5TT5x^)_D9 zbYbGOKp9)?1p)zJvJyd!iWpYUlrqS)AkesPFc!iH-|=sK3npZ1@DyIdN%ErO9mnf- z)IYo#q^k&5LM3YU>jTz{UY^jwx*(eh5@vaLCWrSfIk_NJ`6{PuZUTw$Vu;+)&E5Cy zPQM3{AZ1+)1XY8zaebKu*I#YHr2D7T$r(M?B1EyIEKP-$mdh1*%mg+mfG%nE zZ+#}qq1F6|w-s^iS+4sF#gDIXQ~i6KK+nnogSv0MBEk`>ba(z+uno(l2nsCIbR`V+ zWlfUjY)JR!;U=e-k-m4c&hBRb=Z?Fh&IF5U=s%TFU>X{Mk{Hv-oW~7FoHxx$>5*? zQHOPn;Xn^=rL(784PzbhBHBiX!qc6#U4XH0hxgP2%XY}r5Ak}Y4plPLw;_sVpNjU9+B zO3rPQtwhwR6Nz-s3QLTCy+zZVqvoftnXh!suBJI}DDnZxA-ZvQj1!dgZ?DvM|N_@hQ{)jlMhs|-jaO+etydw8>iDUs}0agC;aqNIEHvZf`OE!kPU|Dmj{d) zZ2|S@N_b9+1{-4<5FQk)_-7TRVV2}DX%YjjKyn>HL zI`ry+Dz6fIu|H`amkFAcqIc?4J>%ev{R!QsWETcTs49^oq!-_AGaFESg8Xf5v-(6d zHGugRI)rIxB_qTu9MjSPcfbvc&bFDq)B~W}s2?5m51V`Vhr#EE}@tmsNh3wAuqOtStRR_Vovtbfy}u z)PZj2c@uBe1n5J#HLez(s~VBSPOpp?CmQpI|K^1qODBu|));fGT+oCde!j(Ux13zkh|=1L z^KIX3xFgZ*_rvq8?b>f2Yv zVw|L{0Dd(qqceBZ8Aw>oKA&O@5vb9f_Qw=05@YA85~}OpZi&1l8hm>}bwxUhEDm`U z_kC`MA;Zc`1kARdy`+Ar1@vYz;h6wGFFz|({t(kvOs3NZlf!CASA~YG>t{BdC$iyz zPJ|jIZlqNHb{osp6|Z^gyuH`u1F93Ame6)tQwrPl^Tc$g@VPWU3Ab zbAdLl_(L=Hdwge%QVwY#q?$5_9=ciVKiL)*(m<3piY{blOLr2pPPQD_D-uNuOvLF; z)LB~6~qaEe&76{$R^ymBW~137v7_Lcn0OedjcsE%l!wgIAE%qk4bCIw6`h{kSD zKr0<+&mANx3mO!!#BsMRnSp!F(2{FDu#WVl0qq8zk$IRQvsIG57>=lc2!)F(&~c)) zfCgZqU*k&*gw=Po7_KlhCOl<+8{~CPTo!9I@qUuWNnw){#dPupu|8%Pr%N1Qa)qvj z27_t?+B?V;w$25ts!GH& zR#iM#+70LNSp^Z0wps#eWhLSz*ZA&{%H*@k`>0)Rj71Bl<4i*1i%R84rzX`npdg3M6v}4{ zGk)O!XH*qu?%v$F_<#SOikhkUqaoWwc2;3(+#3Cn^&!`8k03Rbm4xi zSGe1rNf|@0qN;uhkI#tG5SAN3580RIelX}2KK=b>`=OEey)K7*-h=q(*Ru#7g)DVN z$#Irpoy(C6DOr3Fa11QgAM7An%6n0pAvw$MEALT3Wnf^8bR2S1d2i8|Nj5QLn{qV7 zP@#NR5+ac&h^lcU*Dw+M>gFd@c0&3x9%i;o*sf}b!V!2edF=p?*Jtod;Eq?5(@G_X zCZ;qT2VXc#8<1Cn0_n2%#axZ3pe!oVQQIuC z9gj^LPWGEZXr3Cou*Y!xUW-P@-j24hk|d67LD)z#UX{kuO_<8Ojz&&H#%ui5w1rrBIxy!- zLTHnRH0QSIxj?Z+GqKE^ADPl&bde=EQ&v^MxU4&t{$eu{%+1qS)#`Ti6!hQElj~VC&QQS7Mm2#hDP!c}` z5i#|mxz7QMBdL~P>deYdb*^b%4a^W>a~C06w0atKeuGD53yl!ZWgev>-AhgyxQIKW zBss|`)~c+OEGrY~RS69q`eh4LY%`cKsLmWG0%C|2Tjc#FZ#WCrU{w>VQ+S@js2P`cC~OZcTnwO<^UxPl&8(^*_m;%z9v+*LZ*3PySKbQ z)O}x@2RFrx;zcY@BNuhN%}EV_d?Y1`m#ZAdQ~aW9djIGG=4$M=ah3^pJ?x4hnh|e+ zViqkxq0I$xenvyH%<(!^{;*x^Dlx8r1 zU*C9Hc{)ybJ%(lJAWpNgzlfo0vQL;WEnATADIiN&WvQytj}ka zmOzX9KizF$-N&&`db&Wr8PP+g<1y|hNF&h2IC!EwxQFA!DPE+cnBfWGP74R?e z)wZ5i?OC9xh~)a z-a+8QZ41;C6r`K6Pz)F;m;ASMee&hW7DWAAJNa@)BZNL;tn!U%UJ?<`V{8iQ{f#UX zE>B;&Rs!)?>GWEuW0^(7f%`Le&X-GpUPKzJ&4?9K#)>5flgUaUkswP{E2fG)9j;`t zPbG_e7BS71(IqGt;^dMSQ6?ye`o*MiTX=709&J-0OCmuA3PBelICf& z0e6Q)PQF*lHdxvn?E+Y_P;285?#5=Pd?*1lr+@+`$6P6(q1b8Cf*!k4A^w`^`|LUp z=M>f9Hj{O@*Lh6`VceBlUgD;g@&o-;B5>*g5@;Du08WtZW5|>$niyivNr}FO>FM$e zJOr#m9+p0Uzt*Mph<|IBBz-}jtdQ@Nq-1+#sriNz{gT3(QBWfu!JtOWws4Wg6Gz%R z`OxlZ&Vv5AQXv|eD1FSWE;dzNDO6olC_x0Ro}faQ6O$p%QIwP0ao*##Ns0bdw|Ha% zUw+&7IJIR7wtd?;Lw)!xRVdi_?YJ2B(GCSAf~{X__hTi3tzW9xYFocYI!_{807AEZ zsRm^0_oygqyY^V$zldP+4y^VhwG*YRhqY&^-Qwr&_=?%!fF9TaDK8^Vh?t{z|G)Fd z-YAIr7_ovZMp1si1zRcd*$B(_e_n|%$^*%L`Z7H5FvaRLn%lKUMOizw)p1#o)HQAV7+@d zZy{rSXYPSWu+*ghExXVqd*K(l6zG5ELiZ@8NdIS{<2x-BXT{r!{WRdMt?E0OwdsfG zlp~W_u&@1`=~hRWsL}Z!G~ZGxT`=F0tYeC!%(sFW%R9d>G~bd(nE%4_?LUeo+0Or6 z70ds|;)ClAf6V%}V7~3ln@uaNZz(g#vg_MsL1nS#Y!&Uv|5+Qkr~jF6|IYIb5AMAh zPN(q#=O3;9j>86;b^(EYhfVyRBxof7)&LJBQ8seyjc5*MS{FMO0j;7vX3wO;MS{ zdiSW+Bt$(pgy%@1aD*rf&HaN;x7%!xOYDzfjc01k3W4u88{apMI=$w8Sn^4-4{ z2FZXW_Sh38`g@rJK>&KWwrB6kOOW5io=YvBL=%dzzM zPhE>sLs4qtk8akaJU0x{+RsQ%g}}&RUGwpAhlswfL!e8i#X!znNQ$ zQ{5#3GtQJ;#4c~TAV5o zuf++u)sEKUq}C9v#VJiHUW*?kD4bb~6N_3qUW-#&!>6sqTUE1KhohNUi_J+sERtJ` zQ!^p87H?KltAAeJL(xyD09G!&7VjkHnAIp+ixbq2t0b1+mhb_Ha;>vxXo?PxRkRGHot z>>M|bng@|WZfmWT*>9dep93IlIonYWyx_62vdUq@{o==$^%qS9Iq@CYa<`G<(!G;T z|4AUDx2jnz1uB(`im7NZXb1IP)Q>E64?3quw5)rKD6Xcgzy{;6_53i*H#>#px3EbL z^Rt9M>a?Ge=G(!B-uTg~H~t3jtot`WM-YfqH-FB5-i)Htaxr_!y0Y|leRijp~Hfi zb2H=Cu*iH{-F7u)8_C-0Y=TB`)3&-)cNW;{basTmQ?@!kZR3(TTb<28z@&e(kq=Vsxtg#!pVKA$?7Zj!vZ9u`UEP~SO-`ZdSW~4mh zlWIQG)h<}FKpll zG2KCd>|D$AL*e7qr>Fyu@*UuI4gl@Hx#ili6Grvg)P0rTd@ex zni`Sp+@Ycr<~17gSk93+2u%xNOGk)#-x@=>$_?~oT6BB{Uq6i%|AsH?2PnX19rcq? zFbhi9{K4e*a(_vIOJxzjmZWnqc#rc9Wse-f2KR@EgmzOOUv!6fNi5CuSrSVfjkans-55x+9K<^@8Q?ozAtmZ~8vqSg!0$Pc}@^*wo%N1%sG zdQToL(|2u}ZuOO(R8hsX3j1P?vwf=tjjr5g*dmnvT#Mdp6ZW!)CtF6BqhIl|18QWK zi!6D=(yn_@fpd+~u~pa%#hhHG0Vx-N&L~L4uI5G=lPUWhgR6&mt&zf@Se^iRi*TY8o1+jLE!b?-Mt-rSY2W~i}yw(KVT}SyDKrihY|#MWsDy^ z%_#$feDg;qp#=X!AV0iYRYH+}Uk-l1bKIyqfhFMkldJQ=>hyk`-v%9ay6t8L`r%}X z2zjg1CJ+5Z>wCEG#H4>QdWTCwk z*@90haMtB4Fz8}I3vQtq{q+E4qYfo|iWfi8nU#t3jJo2E-*S*I>CLVscc?)}5FL|; zwA;u+!~x#QI5N3Y$9h@d4UsT?yl~Z@=mS|RwnpcJIdMS8gZZ{rR)%z+5IFtkfWF1W z>(Q_eqOXWIwoEjV`IE<76Jo%HZPMO+Sg|GY1Vl^z#4_6gnNs9+yp$ z4mQ6z4)0N98>d&8>M%CF!P^?kG#zw|j$+X%t(zY{Qd`T2VR2kEnS0}j$>?*|;D&)yL@NDo)!mrZz7fnWTF-$DB9U4eu2+4}+q z>9cnR4$^1u4IHFT+#R&_BtJWS+QT=WrWRxloJhQ}7eKy&4+ncIatZkl03MFxv(P%% zt4oxJPXR019T_n{V}F!CPc)#bohfqZ#8DzWZI?N9HgAS3O+ImzCRd2UHoPZ9QEM0p zem7_3wGeR$^4me{Tj)*NoTXx+Jkxlr8(j=bTbrf!ap~6ydBHy2=QR0Gs2fMXw#CM~M z{qR3>w+SU!Oq{R77?eqRJ%upKQ}Q@DhvC`{6p@RaU_!c8=v;^*=^~NN)zH2Us(SgYv~LDlN(#=a_+vwJ{)g-YGSR=d;ulu8FHR>GyZmq*R6 zK4AD}G9!EHD*fv}eALj|GqJ|5QoH`JX|-WTKpd#)O0{1+-Lt0`+0?m8<%6wgZO zh6Pouy}@Kq`|Wpn=2KWgf>a^3%5FT*s!#9n2fT7>&v0jkK00H2_`CL0^ECLDG7aKQ zd2n0YCU`4@DQNuEz%UX|4Gbf}{Zu4$Qf83Y0-hd;tsO+F4|+{VY$51Qm{!}`)G7fb z&=)SFI0#5a0j?t~q4iyI051^O1x$_#x8VJmiEVwpyJqP$Z&#c5pCT%RYS#9~*5>_3 zn-6w2zj0Vt7A&D#PhIi`H0><}>=8Ct0ZkI7ahp oUed+Hf&Z_lIw`NqWpe(V?}B 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%0Do1MZMj*I)oHDtIg_D<%bCx@Qg8 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

UJ02H1;1YRmgD&R@-%8XL*H^X}-*s(;0*{BRS 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®c8`7|N%N8_o9)t88T9F`Zm!rWoSfoaG;+_U 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

+g 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�c4%Y7=qcbj?B} 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 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("{x=%.1f, y=%.1f, z=%.1f},",vec.x,vec.y,vec.z) -end -s=s..string.format("{%.3f, %.3f, %.3f, %.3f},",Color[1],Color[2],Color[3],Color[4]) -s=s..string.format("{%.3f, %.3f, %.3f, %.3f},",FillColor[1],FillColor[2],FillColor[3],FillColor[4]) -s=s..string.format("%d,",LineType or 1) -s=s..string.format("%s",tostring(ReadOnly)) -if Text and type(Text)=="string"and string.len(Text)>0 then -s=s..string.format(", \"%s\"",tostring(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:ToStringLL(Settings) -local LL_Accuracy=Settings and Settings.LL_Accuracy or _SETTINGS.LL_Accuracy -local lat,lon=coord.LOtoLL(self:GetVec3()) -return string.format('%f',lat)..' '..string.format('%f',lon) -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:FilterFunction(ConditionFunction,...) -local condition={} -condition.func=ConditionFunction -condition.arg={} -if arg then -condition.arg=arg -end -if not self.Filter.Functions then self.Filter.Functions={}end -table.insert(self.Filter.Functions,condition) -return self -end -function SET_BASE:_EvalFilterFunctions(Object) -for _,_condition in pairs(self.Filter.Functions or{})do -local condition=_condition -if condition.func(Object,unpack(condition.arg))==false then -return false -end -end -return true -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, -Functions=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 and MUnitInclude 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 and MUnitInclude 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 and MUnitInclude 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 and MUnitInclude 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 and MUnitInclude 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 and MUnitInclude 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 and MUnitInclude 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 and MUnitInclude 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 -if self.Filter.Functions and MUnitInclude then -local MUnitFunc=self:_EvalFilterFunctions(MUnit) -MUnitInclude=MUnitInclude and MUnitFunc -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 -if false then -local coords=self:GetVerticiesCoordinates() -local coord=coords[1] -table.remove(coords,1) -coord:MarkupToAllFreeForm(coords,Coalition,Color,Alpha,FillColor,FillAlpha,LineType,ReadOnly,"Drew Polygon") -if true then -return -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,onland) -self:F2({self.ControllableName}) -local _coord=self:GetCoordinate() -local _radius=radius or 500 -local _speed=speed or 20 -local _tocoord=_coord:GetRandomCoordinateInRadius(_radius,100) -if onland then -for i=1,50 do -local island=_tocoord:GetSurfaceType()==land.SurfaceType.LAND and true or false -if island then break end -_tocoord=_coord:GetRandomCoordinateInRadius(_radius,100) -end -end -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 -function GROUP:IsSAM() -local issam=false -local units=self:GetUnits() -for _,_unit in pairs(units or{})do -local unit=_unit -if unit:HasSEAD()and unit:IsGround()and(not unit:HasAttribute("Mobile AAA"))then -issam=true -break -end -end -return issam -end -function GROUP:IsAAA() -local issam=false -local units=self:GetUnits() -for _,_unit in pairs(units or{})do -local unit=_unit -local desc=unit:GetDesc()or{} -local attr=desc.attributes or{} -if unit:HasSEAD()then return false end -if attr["AAA"]or attr["SAM related"]then -issam=true -end -end -return issam -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:T({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:T() -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:T({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:T({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:T({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:T({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:T({NearUnit=NearUnit}) -local NearUnitCoalition=NearUnit:GetCoalition() -local CargoCoalition=self:GetCoalition() -if NearUnitCoalition==CargoCoalition then -local Attributes=NearUnit:GetDesc() -self:T({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:T({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:T({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:T() -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:T() -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:T() -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:T() -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:T() -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:T() -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:T() -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:T({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:T({"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:T({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:T({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:T({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:T({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:T({From,Event,To,CargoCarrier:GetName(),NearRadius=NearRadius}) -self:T({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:T("Something is wrong") -end -end -function CARGO_UNIT:onenterLoaded(From,Event,To,CargoCarrier) -self:T({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:T({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:T({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:T({Coordinate=Coordinate}) -end -function CARGO_CRATE:IsNear(CargoCarrier,NearRadius) -self:T({NearRadius=NearRadius}) -return self:IsNear(CargoCarrier:GetCoordinate(),NearRadius) -end -function CARGO_CRATE:Respawn() -self:T({"Respawning crate "..self:GetName()}) -if self.CargoObject then -self.CargoObject:ReSpawn() -self:__Reset(-0.1) -end -end -function CARGO_CRATE:onafterReset() -self:T({"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:T({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:T({"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:T("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:T({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:T({"Regroup",GroupTemplate}) -self.CargoObject=_DATABASE:Spawn(GroupTemplate) -end -end -function CARGO_GROUP:OnEventCargoDead(EventData) -self:T(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:T({"Cargo group destroyed"}) -end -end -function CARGO_GROUP:onafterBoard(From,Event,To,CargoCarrier,NearRadius,...) -self:T({CargoCarrier.UnitName,From,Event,To,NearRadius=NearRadius}) -NearRadius=NearRadius or self.NearRadius -self.CargoSet:ForEach( -function(Cargo,...) -self:T({"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:T("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:T({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:T({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:T("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:T({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:T({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.SRSGoogle then -local poptions=self.SRS:GetProviderOptions(MSRS.Provider.GOOGLE) -self.SRSPilot:SetGoogle(poptions.credentials) -self.SRSPilot:SetGoogleAPIKey(poptions.key) -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.SRSOperator:SetLabel("RESCUE") -if self.SRSGoogle then -local poptions=self.SRS:GetProviderOptions(MSRS.Provider.GOOGLE) -self.SRSOperator:SetGoogle(poptions.credentials) -self.SRSOperator:SetGoogleAPIKey(poptions.key) -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"}, -["Chaparral"]={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.16" -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,nil,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,nil,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.6") -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",true) -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 -TIRESIAS={ -ClassName="TIRESIAS", -debug=false, -version="0.0.4", -Interval=20, -GroundSet=nil, -VehicleSet=nil, -AAASet=nil, -SAMSet=nil, -ExceptionSet=nil, -AAARange=60, -HeloSwitchRange=10, -PlaneSwitchRange=25, -SwitchAAA=true, -} -function TIRESIAS:New() -local self=BASE:Inherit(self,FSM:New()) -self:SetStartState("Stopped") -self:AddTransition("Stopped","Start","Running") -self:AddTransition("*","Status","*") -self:AddTransition("*","Stop","Stopped") -self.ExceptionSet=SET_GROUP:New():Clear(false) -self:HandleEvent(EVENTS.PlayerEnterAircraft,self._EventHandler) -self.lid=string.format("TIRESIAS %s | ",self.version) -self:I(self.lid.."Managing ground groups!") -self:__Start(1) -return self -end -function TIRESIAS:SetActivationRanges(HeloMiles,PlaneMiles) -self.HeloSwitchRange=HeloMiles or 10 -self.PlaneSwitchRange=PlaneMiles or 25 -return self -end -function TIRESIAS:SetAAARanges(FiringRange,SwitchAAA) -self.AAARange=FiringRange or 60 -self.SwitchAAA=(SwitchAAA==false)and false or true -return self -end -function TIRESIAS:AddExceptionSet(Set) -self:T(self.lid.."AddExceptionSet") -local exceptions=self.ExceptionSet -Set:ForEachGroupAlive( -function(grp) -if not grp.Tiresias then -grp.Tiresias={ -type="Exception", -exception=true, -} -exceptions:AddGroup(grp,true) -end -BASE:I("TIRESIAS: Added exception group: "..grp:GetName()) -end -) -return self -end -function TIRESIAS._FilterNotAAA(Group) -local grp=Group -local isaaa=grp:IsAAA() -if isaaa==true and grp:IsGround()and not grp:IsShip()then -return false -else -return true -end -end -function TIRESIAS._FilterNotSAM(Group) -local grp=Group -local issam=grp:IsSAM() -if issam==true and grp:IsGround()and not grp:IsShip()then -return false -else -return true -end -end -function TIRESIAS._FilterAAA(Group) -local grp=Group -local isaaa=grp:IsAAA() -if isaaa==true and grp:IsGround()and not grp:IsShip()then -return true -else -return false -end -end -function TIRESIAS._FilterSAM(Group) -local grp=Group -local issam=grp:IsSAM() -if issam==true and grp:IsGround()and not grp:IsShip()then -return true -else -return false -end -end -function TIRESIAS:_InitGroups() -self:T(self.lid.."_InitGroups") -local EngageRange=self.AAARange -local SwitchAAA=self.SwitchAAA -self.AAASet:ForEachGroupAlive( -function(grp) -if not grp.Tiresias then -grp:OptionEngageRange(EngageRange) -grp:SetCommandInvisible(true) -if SwitchAAA then -grp:SetAIOff() -grp:EnableEmission(false) -end -grp.Tiresias={ -type="AAA", -invisible=true, -range=EngageRange, -exception=false, -AIOff=SwitchAAA, -} -end -if grp.Tiresias and(not grp.Tiresias.exception==true)then -if grp.Tiresias.invisible and grp.Tiresias.invisible==false then -grp:SetCommandInvisible(true) -grp.Tiresias.invisible=true -if SwitchAAA then -grp:SetAIOff() -grp:EnableEmission(false) -grp.Tiresias.AIOff=true -end -end -end -end -) -self.VehicleSet:ForEachGroupAlive( -function(grp) -if not grp.Tiresias then -grp:SetAIOff() -grp:SetCommandInvisible(true) -grp.Tiresias={ -type="Vehicle", -invisible=true, -AIOff=true, -exception=false, -} -end -if grp.Tiresias and(not grp.Tiresias.exception==true)then -if grp.Tiresias and grp.Tiresias.invisible and grp.Tiresias.invisible==false then -grp:SetCommandInvisible(true) -grp:SetAIOff() -grp.Tiresias.invisible=true -end -end -end -) -self.SAMSet:ForEachGroupAlive( -function(grp) -if not grp.Tiresias then -grp:SetCommandInvisible(true) -grp.Tiresias={ -type="SAM", -invisible=true, -exception=false, -} -end -if grp.Tiresias and(not grp.Tiresias.exception==true)then -if grp.Tiresias and grp.Tiresias.invisible and grp.Tiresias.invisible==false then -grp:SetCommandInvisible(true) -grp.Tiresias.invisible=true -end -end -end -) -return self -end -function TIRESIAS:_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 unitname=event.IniUnitName or"none" -local _unit=event.IniUnit -local _group=event.IniGroup -if _group and _group:IsAlive()then -local radius=self.PlaneSwitchRange -if _group:IsHelicopter()then -radius=self.HeloSwitchRange -end -self:_SwitchOnGroups(_group,radius) -end -end -return self -end -function TIRESIAS:_SwitchOnGroups(group,radius) -self:T(self.lid.."_SwitchOnGroups "..group:GetName().." Radius "..radius.." NM") -local zone=ZONE_GROUP:New("Zone-"..group:GetName(),group,UTILS.NMToMeters(radius)) -local ground=SET_GROUP:New():FilterCategoryGround():FilterZones({zone}):FilterOnce() -local count=ground:CountAlive() -if self.debug then -local text=string.format("There are %d groups around this plane or helo!",count) -self:I(text) -end -local SwitchAAA=self.SwitchAAA -if ground:CountAlive()>0 then -ground:ForEachGroupAlive( -function(grp) -if grp.Tiresias and grp.Tiresias.type and(not grp.Tiresias.exception==true)then -if grp.Tiresias.invisible==true then -grp:SetCommandInvisible(false) -grp.Tiresias.invisible=false -end -if grp.Tiresias.type=="Vehicle"and grp.Tiresias.AIOff and grp.Tiresias.AIOff==true then -grp:SetAIOn() -grp.Tiresias.AIOff=false -end -if SwitchAAA and grp.Tiresias.type=="AAA"and grp.Tiresias.AIOff and grp.Tiresias.AIOff==true then -grp:SetAIOn() -grp:EnableEmission(true) -grp.Tiresias.AIOff=false -end -else -BASE:E("TIRESIAS - This group has not been initialized or is an exception!") -end -end -) -end -return self -end -function TIRESIAS:onafterStart(From,Event,To) -self:T({From,Event,To}) -local VehicleSet=SET_GROUP:New():FilterCategoryGround():FilterFunction(TIRESIAS._FilterNotAAA):FilterFunction(TIRESIAS._FilterNotSAM):FilterStart() -local AAASet=SET_GROUP:New():FilterCategoryGround():FilterFunction(TIRESIAS._FilterAAA):FilterStart() -local SAMSet=SET_GROUP:New():FilterCategoryGround():FilterFunction(TIRESIAS._FilterSAM):FilterStart() -local OpsGroupSet=SET_OPSGROUP:New():FilterActive(true):FilterStart() -self.FlightSet=SET_GROUP:New():FilterCategories({"plane","helicopter"}):FilterStart() -local EngageRange=self.AAARange -local ExceptionSet=self.ExceptionSet -if self.ExceptionSet then -function ExceptionSet:OnAfterAdded(From,Event,To,ObjectName,Object) -BASE:I("TIRESIAS: EXCEPTION Object Added: "..Object:GetName()) -if Object and Object:IsAlive()then -Object.Tiresias={ -type="Exception", -exception=true, -} -Object:SetAIOn() -Object:SetCommandInvisible(false) -Object:EnableEmission(true) -end -end -local OGS=OpsGroupSet:GetAliveSet() -for _,_OG in pairs(OGS or{})do -local OG=_OG -local grp=OG:GetGroup() -ExceptionSet:AddGroup(grp,true) -end -function OpsGroupSet:OnAfterAdded(From,Event,To,ObjectName,Object) -local grp=Object:GetGroup() -ExceptionSet:AddGroup(grp,true) -end -end -function VehicleSet:OnAfterAdded(From,Event,To,ObjectName,Object) -BASE:I("TIRESIAS: VEHCILE Object Added: "..Object:GetName()) -if Object and Object:IsAlive()then -Object:SetAIOff() -Object:SetCommandInvisible(true) -Object.Tiresias={ -type="Vehicle", -invisible=true, -AIOff=true, -exception=false, -} -end -end -local SwitchAAA=self.SwitchAAA -function AAASet:OnAfterAdded(From,Event,To,ObjectName,Object) -if Object and Object:IsAlive()then -BASE:I("TIRESIAS: AAA Object Added: "..Object:GetName()) -Object:OptionEngageRange(EngageRange) -Object:SetCommandInvisible(true) -if SwitchAAA then -Object:SetAIOff() -Object:EnableEmission(false) -end -Object.Tiresias={ -type="AAA", -invisible=true, -range=EngageRange, -exception=false, -AIOff=SwitchAAA, -} -end -end -function SAMSet:OnAfterAdded(From,Event,To,ObjectName,Object) -if Object and Object:IsAlive()then -BASE:I("TIRESIAS: SAM Object Added: "..Object:GetName()) -Object:SetCommandInvisible(true) -Object.Tiresias={ -type="SAM", -invisible=true, -exception=false, -} -end -end -self.VehicleSet=VehicleSet -self.AAASet=AAASet -self.SAMSet=SAMSet -self.OpsGroupSet=OpsGroupSet -self:_InitGroups() -self:__Status(1) -return self -end -function TIRESIAS:onbeforeStatus(From,Event,To) -self:T({From,Event,To}) -if self:GetState()=="Stopped"then -return false -end -return self -end -function TIRESIAS:onafterStatus(From,Event,To) -self:T({From,Event,To}) -if self.debug then -local count=self.VehicleSet:CountAlive() -local AAAcount=self.AAASet:CountAlive() -local SAMcount=self.SAMSet:CountAlive() -local text=string.format("Overall: %d | Vehicles: %d | AAA: %d | SAM: %d",count+AAAcount+SAMcount,count,AAAcount,SAMcount) -self:I(text) -end -self:_InitGroups() -if self.FlightSet:CountAlive()>0 then -local Set=self.FlightSet:GetAliveSet() -for _,_plane in pairs(Set)do -local plane=_plane -local radius=self.PlaneSwitchRange -if plane:IsHelicopter()then -radius=self.HeloSwitchRange -end -self:_SwitchOnGroups(_plane,radius) -end -end -if self:GetState()~="Stopped"then -self:__Status(self.Interval) -end -return self -end -function TIRESIAS:onafterStop(From,Event,To) -self:T({From,Event,To}) -self:UnHandleEvent(EVENTS.PlayerEnterAircraft) -return self -end -STRATEGO={ -ClassName="STRATEGO", -debug=false, -drawzone=false, -markzone=false, -version="0.2.1", -portweight=3, -POIweight=1, -maxrunways=3, -coalition=nil, -colors=nil, -airbasetable={}, -nonconnectedab={}, -easynames={}, -maxdist=150, -disttable={}, -routexists={}, -routefactor=5, -OpsZones={}, -NeutralBenefit=100, -Budget=0, -usebudget=false, -CaptureUnits=3, -CaptureThreatlevel=1, -} -STRATEGO.Type={ -AIRBASE="AIRBASE", -PORT="PORT", -POI="POI", -FARP="FARP", -SHIP="SHIP", -} -function STRATEGO:New(Name,Coalition,MaxDist) -local self=BASE:Inherit(self,BASE:New()) -self.coalition=Coalition -self.coalitiontext=UTILS.GetCoalitionName(Coalition) -self.name=Name or"Hannibal" -self.maxdist=MaxDist or 150 -self.disttable={} -self.routexists={} -self.lid=string.format("STRATEGO %s %s | ",self.name,self.version) -self.bases=SET_AIRBASE:New():FilterOnce() -self.ports=SET_ZONE:New():FilterPrefixes("Port"):FilterOnce() -self.POIs=SET_ZONE:New():FilterPrefixes("POI"):FilterOnce() -self.colors={ -[1]={0,1,0}, -[2]={1,0,0}, -[3]={0,0,1}, -[4]={1,0.65,0}, -} -return self -end -function STRATEGO:Start() -self:T(self.lid.."Start") -self:AnalyseBases() -self:AnalysePOIs(self.ports,self.portweight,"PORT") -self:AnalysePOIs(self.POIs,self.POIweight,"POI") -for i=self.maxrunways,1,-1 do -self:AnalyseRoutes(i,i*self.routefactor,self.colors[(i%3)+1],i) -end -self:AnalyseUnconnected(self.colors[4]) -self:I(self.lid.."Advisory ready.") -return self -end -function STRATEGO:SetUsingBudget(Usebudget,StartBudget) -self:T(self.lid.."SetUsingBudget") -self.usebudget=Usebudget -self.Budget=StartBudget -return self -end -function STRATEGO:SetDebug(Debug,DrawZones,MarkZones) -self.debug=Debug -self.drawzone=DrawZones -self.markzone=MarkZones -return self -end -function STRATEGO:SetWeights(MaxRunways,PortWeight,POIWeight,RouteFactor) -self.portweight=PortWeight or 3 -self.POIweight=POIWeight or 1 -self.maxrunways=MaxRunways or 3 -self.routefactor=RouteFactor or 5 -return self -end -function STRATEGO:SetNeutralBenefit(NeutralBenefit) -self.NeutralBenefit=NeutralBenefit or 100 -return self -end -function STRATEGO:SetCaptureOptions(CaptureUnits,CaptureThreatlevel) -self.CaptureUnits=CaptureUnits or 3 -self.CaptureThreatlevel=CaptureThreatlevel or 1 -return self -end -function STRATEGO:AnalyseBases() -self:T(self.lid.."AnalyseBases") -local colors=self.colors -local debug=self.debug -local airbasetable=self.airbasetable -local nonconnectedab=self.nonconnectedab -local easynames=self.easynames -self.bases:ForEach( -function(afb) -local ab=afb -local abname=ab:GetName() -local runways=ab:GetRunways() -local numrwys=#runways -if numrwys>=1 then numrwys=numrwys*0.5 end -local abzone=ab:GetZone() -local coa=ab:GetCoalition()+1 -local abtype="AIRBASE" -if ab:IsShip()then -numrwys=1 -abtype="SHIP" -end -if ab:IsHelipad()then -numrwys=1 -abtype="FARP" -end -local coord=abzone:GetCoordinate() -if debug then -abzone:DrawZone(-1,colors[coa],1,colors[coa],0.3,1) -coord:TextToAll(tostring(numrwys),-1,{0,0,0},1,colors[coa],0.3,20) -end -local opszone=self:GetNewOpsZone(abname,coa-1) -local tbl={ -name=abname, -baseweight=numrwys, -weight=0, -coalition=coa-1, -port=false, -zone=abzone, -coord=coord, -type=abtype, -} -airbasetable[abname]=tbl -nonconnectedab[abname]=true -local name=string.gsub(abname,"[%p%s]",".") -easynames[name]=abname -end -) -return self -end -function STRATEGO:UpdateNodeCoalitions() -self:T(self.lid.."UpdateNodeCoalitions") -local newtable={} -for _id,_data in pairs(self.airbasetable)do -local data=_data -if data.type=="AIRBASE"or data.type=="FARP"then -data.coalition=AIRBASE:FindByName(data.name):GetCoalition() -else -data.coalition=data.opszone:GetOwner() -end -newtable[_id]=_data -end -self.airbasetable=nil -self.airbasetable=newtable -return self -end -function STRATEGO:GetNewOpsZone(Zone,Coalition) -self:T(self.lid.."GetNewOpsZone") -local opszone=OPSZONE:New(Zone,Coalition or 0) -opszone:SetCaptureNunits(self.CaptureUnits) -opszone:SetCaptureThreatlevel(self.CaptureThreatlevel) -opszone:SetDrawZone(self.drawzone) -opszone:SetMarkZone(self.markzone) -opszone:Start() -return opszone -end -function STRATEGO:AnalysePOIs(Set,Weight,Key) -self:T(self.lid.."AnalysePOIs") -local colors=self.colors -local debug=self.debug -local airbasetable=self.airbasetable -local nonconnectedab=self.nonconnectedab -local easynames=self.easynames -Set:ForEach( -function(port) -local zone=port -local zname=zone:GetName() -local coord=zone:GetCoordinate() -if debug then -zone:DrawZone(-1,colors[1],1,colors[1],0.3,1) -coord:TextToAll(tostring(Weight),-1,{0,0,0},1,colors[1],0.3,20) -end -local opszone=self:GetNewOpsZone(zone) -local tbl={ -name=zname, -baseweight=Weight, -weight=0, -coalition=coalition.side.NEUTRAL, -port=true, -zone=zone, -coord=coord, -type=Key, -opszone=opszone, -} -airbasetable[zone:GetName()]=tbl -nonconnectedab[zone:GetName()]=true -local name=string.gsub(zname,"[%p%s]",".") -easynames[name]=zname -end -) -return self -end -function STRATEGO:GetToFrom(StartPoint,EndPoint) -self:T(self.lid.."GetToFrom") -local pstart=string.gsub(StartPoint,"[%p%s]",".") -local pend=string.gsub(EndPoint,"[%p%s]",".") -local fromto=pstart..";"..pend -local tofrom=pend..";"..pstart -return fromto,tofrom -end -function STRATEGO:AddRoutesManually(Startpoint,Endpoint,Color,Linetype,Draw) -local fromto,tofrom=self:GetToFrom(Startpoint,Endpoint) -local startcoordinate=self.airbasetable[Startpoint].coord -local targetcoordinate=self.airbasetable[Endpoint].coord -local dist=UTILS.Round(targetcoordinate:Get2DDistance(startcoordinate),-2)/1000 -local color=Color or{136/255,0,1} -local linetype=Linetype or 5 -local data={ -start=Startpoint, -target=Endpoint, -dist=dist, -} -self.disttable[fromto]=data -self.disttable[tofrom]=data -table.insert(self.routexists,fromto) -table.insert(self.routexists,tofrom) -self.nonconnectedab[Endpoint]=false -self.nonconnectedab[Startpoint]=false -local factor=self.airbasetable[Startpoint].baseweight*self.routefactor -self.airbasetable[Startpoint].weight=self.airbasetable[Startpoint].weight+factor -self.airbasetable[Endpoint].weight=self.airbasetable[Endpoint].weight+factor -if self.debug or Draw then -startcoordinate:LineToAll(targetcoordinate,-1,color,1,linetype,nil,string.format("%dkm",dist)) -end -return self -end -function STRATEGO:AnalyseRoutes(tgtrwys,factor,color,linetype) -self:T(self.lid.."AnalyseRoutes") -for _,_ab in pairs(self.airbasetable)do -if _ab.baseweight>=1 then -local startpoint=_ab.name -local startcoord=_ab.coord -for _,_data in pairs(self.airbasetable)do -local fromto,tofrom=self:GetToFrom(startpoint,_data.name) -if _data.name==startpoint then -elseif _data.baseweight==tgtrwys and not(self.routexists[fromto]or self.routexists[tofrom])then -local tgtc=_data.coord -local dist=UTILS.Round(tgtc:Get2DDistance(startcoord),-2)/1000 -if dist<=self.maxdist then -local data={ -start=startpoint, -target=_data.name, -dist=dist, -} -self.disttable[fromto]=data -self.disttable[tofrom]=data -table.insert(self.routexists,fromto) -table.insert(self.routexists,tofrom) -self.nonconnectedab[_data.name]=false -self.nonconnectedab[startpoint]=false -self.airbasetable[startpoint].weight=self.airbasetable[startpoint].weight+factor -self.airbasetable[_data.name].weight=self.airbasetable[_data.name].weight+factor -if self.debug then -startcoord:LineToAll(tgtc,-1,color,1,linetype,nil,string.format("%dkm",dist)) -end -end -end -end -end -end -return self -end -function STRATEGO:AnalyseUnconnected(Color) -self:T(self.lid.."AnalyseUnconnected") -for _name,_noconnect in pairs(self.nonconnectedab)do -if _noconnect then -local startpoint=_name -local startcoord=self.airbasetable[_name].coord -local shortest=1000*1000 -local closest=nil -local closestcoord=nil -for _,_data in pairs(self.airbasetable)do -if _name~=_data.name then -local tgtc=_data.coord -local dist=UTILS.Round(tgtc:Get2DDistance(startcoord),-2)/1000 -if dist=weight then -weight=_data.weight -if not airbases[weight]then airbases[weight]={}end -table.insert(airbases[weight],_name) -end -end -return airbases[weight],weight -end -function STRATEGO:GetNextHighestWeightNodes(Weight) -self:T(self.lid.."GetNextHighestWeightBases") -local weight=0 -local airbases={} -for _name,_data in pairs(self.airbasetable)do -if _data.weight>=weight and _data.weight=targetweight)then -shortest=dist -target=cname -weight=self.airbasetable[cname].weight -coa=self.airbasetable[cname].coalition -end -end -end -return shortest,target,weight,coa -end -function STRATEGO:FindClosestStrategicTarget(Startpoint,BaseWeight) -self:T(self.lid.."FindClosestStrategicTarget") -local shortest=1000*1000 -local target=nil -local weight=0 -local coa=nil -if not BaseWeight then BaseWeight=self.maxrunways end -local startpoint=string.gsub(Startpoint,"[%p%s]",".") -for _,_route in pairs(self.routexists)do -if string.find(_route,startpoint,1,true)then -local dist=self.disttable[_route].dist -local tname=string.gsub(_route,startpoint,"") -local tname=string.gsub(tname,";","") -local cname=self.easynames[tname] -if dist=BaseWeight then -shortest=dist -target=cname -weight=self.airbasetable[cname].weight -coa=self.airbasetable[cname].coalition -end -end -end -return shortest,target,weight,coa -end -function STRATEGO:FindStrategicTargets() -local targets={} -for _,_data in pairs(self.airbasetable)do -local data=_data -if data.coalition==self.coalition then -local dist,name,points,coa=self:FindClosestStrategicTarget(data.name,self.maxrunways) -if coa==coalition.side.NEUTRAL and points~=0 then -local fpoints=points+self.NeutralBenefit -local tries=1 -while targets[fpoints]or tries<100 do -fpoints=points+(self.NeutralBenefit+math.random(1,100)) -tries=tries+1 -end -targets[fpoints]={ -name=name, -dist=dist, -points=fpoints, -coalition=coa, -} -end -local enemycoa=self.coalition==coalition.side.BLUE and coalition.side.RED or coalition.side.BLUE -if coa==enemycoa and points~=0 then -local fpoints=points -local tries=1 -while targets[fpoints]or tries<100 do -fpoints=points+(math.random(1,100)) -tries=tries+1 -end -targets[fpoints]={ -name=name, -dist=dist, -points=fpoints, -coalition=coa, -} -end -end -end -return targets -end -function STRATEGO:FindConsolidationTargets() -local targets={} -for _,_data in pairs(self.airbasetable)do -local data=_data -if data.coalition==self.coalition then -local dist,name,points,coa=self:FindClosestConsolidationTarget(data.name,self.maxrunways-1) -if coa==coalition.side.NEUTRAL and points~=0 then -local fpoints=points+self.NeutralBenefit -local tries=1 -while targets[fpoints]or tries<100 do -fpoints=points-(self.NeutralBenefit+math.random(1,100)) -tries=tries+1 -end -targets[fpoints]={ -name=name, -dist=dist, -points=fpoints, -coalition=coa, -coalitionname=UTILS.GetCoalitionName(coa), -} -end -local enemycoa=self.coalition==coalition.side.BLUE and coalition.side.RED or coalition.side.BLUE -if coa==enemycoa and points~=0 then -local fpoints=points -local tries=1 -while targets[fpoints]or tries<100 do -fpoints=points-(math.random(1,100)) -tries=tries+1 -end -targets[fpoints]={ -name=name, -dist=dist, -points=fpoints, -coalition=coa, -coalitionname=UTILS.GetCoalitionName(coa), -} -end -end -end -return targets -end -function STRATEGO:FindNeighborNodes(Name,Enemies,Friends) -local neighbors={} -local name=string.gsub(Name,"[%p%s]",".") -for _route,_data in pairs(self.disttable)do -if string.find(_route,name,1,true)then -local dist=self.disttable[_route] -local tname=string.gsub(_route,name,"") -local tname=string.gsub(tname,";","") -local cname=self.easynames[tname] -local encoa=self.coalition==coalition.side.BLUE and coalition.side.RED or coalition.side.BLUE -if Enemies==true then -if self.airbasetable[cname].coalition==encoa then -neighbors[cname]=dist -end -elseif Friends==true then -if self.airbasetable[cname].coalition~=encoa then -neighbors[cname]=dist -end -else -neighbors[cname]=dist -end -end -end -return neighbors -end -function STRATEGO:FindRoute(Start,End,Hops,Draw) -self:I({Start,End,Hops}) -local Route={} -local hops=Hops or 4 -local routecomplete=false -local function Checker(neighbors) -for _name,_data in pairs(neighbors)do -if _name==End then -return End -end -end -return nil -end -local function NextClosest(Start,End) -local ecoord=self.airbasetable[End].coord -local nodes=self:FindNeighborNodes(Start) -local closest=nil -local closedist=1000*1000 -for _name,_dist in pairs(nodes)do -local kcoord=self.airbasetable[_name].coord -local dist=math.floor((kcoord:Get2DDistance(ecoord)/1000)+0.5) -if distTstop 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 and(not EventData.IniObjectCategory==Object.Category.STATIC)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:GetProvider()==MSRS.Provider.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 -if Sender==""then -self:E(self.lid..string.format("ERROR: Sender unknown!")) -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] -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:T(self.lid.."Could not get position of target ==> Disengage!") -self:Disengage() -end -else -self:T(self.lid.."Target not ALIVE ==> Disengage!") -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="1.0.0" -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.45" -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) -end -table.insert(self.Spawned_Cargo,realcargo) -end -if not(drop or pack)then -Cargo:RemoveStock() -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.DEAD 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,UseSrs) -local self=BASE:Inherit(self,BASE:New()) -self:F({FileName,Path,Duration,UseSrs}) -self:SetFileName(FileName) -self:SetPlayWithSRS(UseSrs or false) -self:SetPath(Path) -self:SetDuration(Duration) -return self -end -function SOUNDFILE:SetPath(Path) -self:F({Path}) -if not Path then -if self.useSRS then -self.path=lfs.tempdir().."Mission\\l10n\\DEFAULT" -else -self.path="l10n/DEFAULT/" -end -else -self.path=Path -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.."/" -self:T("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) -self:F({Switch}) -if Switch==true or Switch==nil then -self.useSRS=true -else -self.useSRS=false -end -self:T("self.useSRS="..tostring(self.useSRS)) -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", -backend="srsexe", -frequencies={}, -modulations={}, -coalition=0, -gender="female", -culture=nil, -voice=nil, -volume=1, -speed=1, -coordinate=nil, -provider="win", -Label="ROBOT", -ConfigFileName="Moose_MSRS.lua", -ConfigFilePath="Config\\", -ConfigLoaded=false, -poptions={}, -} -MSRS.version="0.3.0" -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.Backend={ -SRSEXE="srsexe", -GRPC="grpc", -} -MSRS.Provider={ -WINDOWS="win", -GOOGLE="gcloud", -AZURE="azure", -AMAZON="aws", -} -function MSRS.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 MSRS:New(Path,Frequency,Modulation,Backend) -local self=BASE:Inherit(self,BASE:New()) -self:F({Path,Frequency,Modulation,Backend}) -Frequency=Frequency or 143 -Modulation=Modulation or radio.modulation.AM -self.lid=string.format("%s-%s | ","unknown",self.version) -if not self.ConfigLoaded then -self:SetPath(Path) -self:SetPort() -self:SetFrequencies(Frequency) -self:SetModulations(Modulation) -self:SetGender() -self:SetCoalition() -self:SetLabel() -self:SetVolume() -self:SetBackend(Backend) -else -if Path then -self:SetPath(Path) -end -if Frequency then -self:SetFrequencies(Frequency) -end -if Modulation then -self:SetModulations(Modulation) -end -if Backend then -self:SetBackend(Backend) -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:SetBackend(Backend) -self:F({Backend=Backend}) -self.backend=Backend or MSRS.Backend.SRSEXE -return self -end -function MSRS:SetBackendGRPC() -self:F() -self:SetBackend(MSRS.Backend.GRPC) -return self -end -function MSRS:SetBackendSRSEXE() -self:F() -self:SetBackend(MSRS.Backend.SRSEXE) -return self -end -function MSRS.SetDefaultBackend(Backend) -MSRS.backend=Backend or MSRS.Backend.SRSEXE -end -function MSRS.SetDefaultBackendGRPC() -MSRS.backend=MSRS.Backend.GRPC -end -function MSRS:GetBackend() -return self.backend -end -function MSRS:SetPath(Path) -self:F({Path=Path}) -self.path=Path or"C:\\Program Files\\DCS-SimpleRadio-Standalone" -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:F(string.format("SRS path=%s",self:GetPath())) -return self -end -function MSRS:GetPath() -return self.path -end -function MSRS:SetVolume(Volume) -self:F({Volume=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:F({Label=Label}) -self.Label=Label or"ROBOT" -return self -end -function MSRS:GetLabel() -return self.Label -end -function MSRS:SetPort(Port) -self:F({Port=Port}) -self.port=Port or 5002 -self:T(string.format("SRS port=%s",self:GetPort())) -return self -end -function MSRS:GetPort() -return self.port -end -function MSRS:SetCoalition(Coalition) -self:F({Coalition=Coalition}) -self.coalition=Coalition or 0 -return self -end -function MSRS:GetCoalition() -return self.coalition -end -function MSRS:SetFrequencies(Frequencies) -self:F(Frequencies) -self.frequencies=UTILS.EnsureTable(Frequencies,false) -return self -end -function MSRS:AddFrequencies(Frequencies) -self:F(Frequencies) -for _,_freq in pairs(UTILS.EnsureTable(Frequencies,false))do -self:T(self.lid..string.format("Adding frequency %s",tostring(_freq))) -table.insert(self.frequencies,_freq) -end -return self -end -function MSRS:GetFrequencies() -return self.frequencies -end -function MSRS:SetModulations(Modulations) -self:F(Modulations) -self.modulations=UTILS.EnsureTable(Modulations,false) -self:T(self.lid.."Modulations:") -self:T(self.modulations) -return self -end -function MSRS:AddModulations(Modulations) -self:F(Modulations) -for _,_mod in pairs(UTILS.EnsureTable(Modulations,false))do -table.insert(self.modulations,_mod) -end -return self -end -function MSRS:GetModulations() -return self.modulations -end -function MSRS:SetGender(Gender) -self:F({Gender=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:F({Culture=Culture}) -self.culture=Culture -return self -end -function MSRS:SetVoice(Voice) -self:F({Voice=Voice}) -self.voice=Voice -return self -end -function MSRS:SetVoiceProvider(Voice,Provider) -self:F({Voice=Voice,Provider=Provider}) -self.poptions=self.poptions or{} -self.poptions[Provider or self:GetProvider()]=Voice -return self -end -function MSRS:SetVoiceWindows(Voice) -self:F({Voice=Voice}) -self:SetVoiceProvider(Voice or"Microsoft Hazel Desktop",MSRS.Provider.WINDOWS) -return self -end -function MSRS:SetVoiceGoogle(Voice) -self:F({Voice=Voice}) -self:SetVoiceProvider(Voice or MSRS.Voices.Google.Standard.en_GB_Standard_A,MSRS.Provider.GOOGLE) -return self -end -function MSRS:SetVoiceAzure(Voice) -self:F({Voice=Voice}) -self:SetVoiceProvider(Voice or"en-US-AriaNeural",MSRS.Provider.AZURE) -return self -end -function MSRS:SetVoiceAmazon(Voice) -self:F({Voice=Voice}) -self:SetVoiceProvider(Voice or"Brian",MSRS.Provider.AMAZON) -return self -end -function MSRS:GetVoice(Provider) -Provider=Provider or self.provider -if Provider and self.poptions[Provider]and self.poptions[Provider].voice then -return self.poptions[Provider].voice -else -return self.voice -end -end -function MSRS:SetCoordinate(Coordinate) -self:F(Coordinate) -self.coordinate=Coordinate -return self -end -function MSRS:SetGoogle(PathToCredentials) -self:F({PathToCredentials=PathToCredentials}) -if PathToCredentials then -self.provider=MSRS.Provider.GOOGLE -self:SetProviderOptionsGoogle(PathToCredentials,PathToCredentials) -end -return self -end -function MSRS:SetGoogleAPIKey(APIKey) -self:F({APIKey=APIKey}) -if APIKey then -self.provider=MSRS.Provider.GOOGLE -if self.poptions[MSRS.Provider.GOOGLE]then -self.poptions[MSRS.Provider.GOOGLE].key=APIKey -else -self:SetProviderOptionsGoogle(nil,APIKey) -end -end -return self -end -function MSRS:SetProvider(Provider) -self:F({Provider=Provider}) -self.provider=Provider or MSRS.Provider.WINDOWS -return self -end -function MSRS:GetProvider() -return self.provider or MSRS.Provider.WINDOWS -end -function MSRS:SetProviderOptions(Provider,CredentialsFile,AccessKey,SecretKey,Region) -self:F({Provider,CredentialsFile,AccessKey,SecretKey,Region}) -local option=MSRS._CreateProviderOptions(Provider,CredentialsFile,AccessKey,SecretKey,Region) -if self then -self.poptions=self.poptions or{} -self.poptions[Provider]=option -else -MSRS.poptions=MSRS.poptions or{} -MSRS.poptions[Provider]=option -end -return option -end -function MSRS._CreateProviderOptions(Provider,CredentialsFile,AccessKey,SecretKey,Region) -BASE:F({Provider,CredentialsFile,AccessKey,SecretKey,Region}) -local option={} -option.provider=Provider -option.credentials=CredentialsFile -option.key=AccessKey -option.secret=SecretKey -option.region=Region -return option -end -function MSRS:SetProviderOptionsGoogle(CredentialsFile,AccessKey) -self:F({CredentialsFile,AccessKey}) -self:SetProviderOptions(MSRS.Provider.GOOGLE,CredentialsFile,AccessKey) -return self -end -function MSRS:SetProviderOptionsAmazon(AccessKey,SecretKey,Region) -self:F({AccessKey,SecretKey,Region}) -self:SetProviderOptions(MSRS.Provider.AMAZON,nil,AccessKey,SecretKey,Region) -return self -end -function MSRS:SetProviderOptionsAzure(AccessKey,Region) -self:F({AccessKey,Region}) -self:SetProviderOptions(MSRS.Provider.AZURE,nil,AccessKey,nil,Region) -return self -end -function MSRS:GetProviderOptions(Provider) -return self.poptions[Provider or self.provider]or{} -end -function MSRS:SetTTSProviderGoogle() -self:F() -self:SetProvider(MSRS.Provider.GOOGLE) -return self -end -function MSRS:SetTTSProviderMicrosoft() -self:F() -self:SetProvider(MSRS.Provider.WINDOWS) -return self -end -function MSRS:SetTTSProviderAzure() -self:F() -self:SetProvider(MSRS.Provider.AZURE) -return self -end -function MSRS:SetTTSProviderAmazon() -self:F() -self:SetProvider(MSRS.Provider.AMAZON) -return self -end -function MSRS:Help() -self:F() -local path=self:GetPath() -local exe="DCS-SR-ExternalAudio.exe" -local filename=os.getenv('TMP').."\\MSRS-help-"..MSRS.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 help output:") -env.info("======================================================================") -env.info(data) -env.info("======================================================================") -return self -end -function MSRS:PlaySoundFile(Soundfile,Delay) -self:F({Soundfile,Delay}) -local soundfile=Soundfile:GetName() -local exists=UTILS.FileExists(soundfile) -if not exists then -self:E("ERROR: MSRS sound file does not exist! File="..soundfile) -return self -end -if Delay and Delay>0 then -self:ScheduleOnce(Delay,MSRS.PlaySoundFile,self,Soundfile,0) -else -local command=self:_GetCommand() -command=command..' --file="'..tostring(soundfile)..'"' -self:_ExecCommand(command) -end -return self -end -function MSRS:PlaySoundText(SoundText,Delay) -self:F({SoundText,Delay}) -if Delay and Delay>0 then -self:ScheduleOnce(Delay,MSRS.PlaySoundText,self,SoundText,0) -else -if self.backend==MSRS.Backend.GRPC then -self:_DCSgRPCtts(SoundText.text,nil,SoundText.gender,SoundText.culture,SoundText.voice,SoundText.volume,SoundText.label,SoundText.coordinate) -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 -end -return self -end -function MSRS:PlayText(Text,Delay,Coordinate) -self:F({Text,Delay,Coordinate}) -if Delay and Delay>0 then -self:ScheduleOnce(Delay,MSRS.PlayText,self,Text,nil,Coordinate) -else -if self.backend==MSRS.Backend.GRPC then -self:T(self.lid.."Transmitting") -self:_DCSgRPCtts(Text,nil,nil,nil,nil,nil,nil,Coordinate) -else -self:PlayTextExt(Text,Delay,nil,nil,nil,nil,nil,nil,nil,Coordinate) -end -end -return self -end -function MSRS:PlayTextExt(Text,Delay,Frequencies,Modulations,Gender,Culture,Voice,Volume,Label,Coordinate) -self:F({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 -Frequencies=Frequencies or self:GetFrequencies() -Modulations=Modulations or self:GetModulations() -if self.backend==MSRS.Backend.SRSEXE then -local command=self:_GetCommand(UTILS.EnsureTable(Frequencies,false),UTILS.EnsureTable(Modulations,false),nil,Gender,Voice,Culture,Volume,nil,nil,Label,Coordinate) -command=command..string.format(" --text=\"%s\"",tostring(Text)) -self:_ExecCommand(command) -elseif self.backend==MSRS.Backend.GRPC then -self:_DCSgRPCtts(Text,Frequencies,Gender,Culture,Voice,Volume,Label,Coordinate) -end -end -return self -end -function MSRS:PlayTextFile(TextFile,Delay) -self:F({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:T(string.format("Command length=%d",l)) -self:_ExecCommand(command) -end -return self -end -function MSRS:_GetLatLongAlt(Coordinate) -self:F({Coordinate=Coordinate}) -local lat=0.0 -local lon=0.0 -local alt=0.0 -if Coordinate then -lat,lon,alt=coord.LOtoLL(Coordinate) -end -return lat,lon,math.floor(alt) -end -function MSRS:_GetCommand(freqs,modus,coal,gender,voice,culture,volume,speed,port,label,coordinate) -self:F({freqs,modus,coal,gender,voice,culture,volume,speed,port,label,coordinate}) -local path=self:GetPath() -local exe="DCS-SR-ExternalAudio.exe" -local fullPath=string.format("%s\\%s",path,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:GetVoice(self.provider)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.provider==MSRS.Provider.GOOGLE then -local pops=self:GetProviderOptions() -command=command..string.format(' --ssml -G "%s"',pops.credentials) -elseif self.provider==MSRS.Provider.WINDOWS then -else -self:E("ERROR: SRS only supports WINWOWS and GOOGLE as TTS providers! Use DCS-gRPC backend for other providers such as ") -end -if not UTILS.FileExists(fullPath)then -self:E("ERROR: MSRS SRS executable does not exist! FullPath="..fullPath) -command="CommandNotFound" -end -self:T("MSRS command from _GetCommand="..command) -return command -end -function MSRS:_ExecCommand(command) -self:F({command=command}) -if string.find(command,"CommandNotFound")then return 0 end -local batContent=command.." && exit" -local filename=os.getenv('TMP').."\\MSRS-"..MSRS.uuid()..".bat" -local script=io.open(filename,"w+") -script:write(batContent) -script:close() -self:T("MSRS batch file created: "..filename) -self:T("MSRS batch content: "..batContent) -local res=nil -if true then -local filenvbs=os.getenv('TMP').."\\MSRS-"..MSRS.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() -self:T("MSRS vbs file created to start batch="..filenvbs) -local runvbs=string.format('cscript.exe //Nologo //B "%s"',filenvbs) -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) -self:T("MSRS vbs and batch file removed") -elseif false then -local filenvbs=os.getenv('TMP').."\\MSRS-"..MSRS.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 -command=string.format('start /b "" "%s"',filename) -self:T("MSRS execute command="..command) -res=os.execute(command) -timer.scheduleFunction(os.remove,filename,timer.getTime()+1) -end -return res -end -function MSRS:_DCSgRPCtts(Text,Frequencies,Gender,Culture,Voice,Volume,Label,Coordinate) -self:F("MSRS_BACKEND_DCSGRPC:_DCSgRPCtts()") -self:F({Text,Frequencies,Gender,Culture,Voice,Volume,Label,Coordinate}) -local options={} -local ssml=Text or'' -Frequencies=UTILS.EnsureTable(Frequencies,true)or self:GetFrequencies() -options.plaintext=Text -options.srsClientName=Label or self.Label -if self.coordinate then -options.position={} -options.position.lat,options.position.lon,options.position.alt=self:_GetLatLongAlt(self.coordinate) -end -options.coalition=UTILS.GetCoalitionName(self.coalition):lower() -local provider=self.provider or MSRS.Provider.WINDOWS -self:F({provider=provider}) -options.provider={} -options.provider[provider]=self:GetProviderOptions(provider) -Voice=Voice or self:GetVoice(self.provider)or self.voice -if Voice then -options.provider[provider].voice=Voice -else -local preTag,genderProp,langProp,postTag='','','','' -local gender="" -if self.gender then -gender=string.format(' gender=\"%s\"',self.gender) -end -local language="" -if self.culture then -language=string.format(' language=\"%s\"',self.culture) -end -if self.gender or self.culture then -ssml=string.format("%s",gender,language,Text) -end -end -for _,freq in pairs(Frequencies)do -self:F("Calling GRPC.tts with the following parameter:") -self:F({ssml=ssml,freq=freq,options=options}) -self:F(options.provider[provider]) -GRPC.tts(ssml,freq*1e6,options) -end -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 -env.info("FF reading config file") -assert(loadfile(path..file))() -if MSRS_Config then -local Self=self or MSRS -Self.path=MSRS_Config.Path or"C:\\Program Files\\DCS-SimpleRadio-Standalone" -Self.port=MSRS_Config.Port or 5002 -Self.backend=MSRS_Config.Backend or MSRS.Backend.SRSEXE -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.Label=MSRS_Config.Label or"MSRS" -Self.voice=MSRS_Config.Voice -Self.provider=MSRS_Config.Provider or MSRS.Provider.WINDOWS -for _,provider in pairs(MSRS.Provider)do -if MSRS_Config[provider]then -Self.poptions[provider]=MSRS_Config[provider] -end -end -Self.ConfigLoaded=true -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 -function MSRS.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 -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 MSRS.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 *** ')

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