mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2026-07-16 22:22:25 +00:00
Compare commits
1871 Commits
2.9.12
...
Apple/Develop
| Author | SHA1 | Date | |
|---|---|---|---|
| 0f9921f16b | |||
| 82282ad2e4 | |||
| b33f17d3df | |||
| d4b0afa635 | |||
| 36c0297344 | |||
| 8c60a8c678 | |||
| 6829d75464 | |||
| 867f919d5f | |||
| f769a0deb5 | |||
| 4efd71e36a | |||
| 7e9dd9deff | |||
| 9820c26d4b | |||
| d1d94a99dc | |||
| 4767ad45cd | |||
| fed9174bcb | |||
| b6ea6606fc | |||
| 9963c737a3 | |||
| 5005203206 | |||
| a3455855b8 | |||
| 73d3ed119c | |||
| d1e4dd3fe9 | |||
| 23112c9954 | |||
| 9326cfad1c | |||
| 7d4432fa47 | |||
| 4b6e860512 | |||
| 2332440f81 | |||
| 23c59d906f | |||
| cc62655ffe | |||
| b8e114101b | |||
| 401e9d9e8e | |||
| f083b77390 | |||
| db3bc3742e | |||
| 5f64ac30f0 | |||
| b5e4846fb1 | |||
| 9b1a3e4894 | |||
| bf10196662 | |||
| 6c32aa6044 | |||
| edf70e971c | |||
| 7c5af54473 | |||
| a0420e1cb6 | |||
| 2146e92154 | |||
| 6b1903466e | |||
| 2aa9b91ce5 | |||
| 27bd27a2e5 | |||
| b430a3450e | |||
| 8e219c5877 | |||
| fdc19a6898 | |||
| d3ebf023fb | |||
| 3d1dcd78a3 | |||
| d40c559d2d | |||
| 9cda1b88ea | |||
| 09f99b9a74 | |||
| ff5205e686 | |||
| ebd105f80d | |||
| cc8a4e0bad | |||
| 5f70e2d6b1 | |||
| 95f38fbf55 | |||
| 22ae3cf796 | |||
| 62cdcf5536 | |||
| aec3466f13 | |||
| 696630fe4d | |||
| ff55fd5a99 | |||
| 8ee86d3128 | |||
| 47aa524677 | |||
| 79f03c164c | |||
| ca7d3a48a7 | |||
| 2556dbe145 | |||
| af1e9546e5 | |||
| 3203a22668 | |||
| b69ac2f86f | |||
| 0d410f3e55 | |||
| cbe6756330 | |||
| 07045be32b | |||
| 52a2132463 | |||
| 9272b1f18c | |||
| b84880c0dd | |||
| a9be18b284 | |||
| 2ab3d7150d | |||
| 93178df67f | |||
| f34bf1e052 | |||
| e159eb83b7 | |||
| 2581836058 | |||
| d9651e1dea | |||
| 0347f185a5 | |||
| 5e2218de38 | |||
| 6db5441bb1 | |||
| 13b7e43729 | |||
| a72f7298d0 | |||
| 957b72ab67 | |||
| d4a00e30e1 | |||
| 74174906f3 | |||
| 801cc105da | |||
| f186b4bd58 | |||
| 79f13fa6ae | |||
| d38fc4ec8f | |||
| ba1575d674 | |||
| 391182e23f | |||
| 6d5fb64fde | |||
| 4e8efc19ac | |||
| 21920f9d2e | |||
| 9a938080b8 | |||
| dae4dc2d40 | |||
| 5df1fb88f1 | |||
| 2e4d4f056a | |||
| a9ceda4edd | |||
| e76fd23396 | |||
| 7df1eb963f | |||
| aff1d3c7db | |||
| d15f6cdb44 | |||
| 85bb9a0566 | |||
| c59b668eec | |||
| 4620a83317 | |||
| 87d0c4e9d4 | |||
| 338d38d2a2 | |||
| dde08f2bbe | |||
| 55224c96ab | |||
| 257801edca | |||
| 74361f6a3a | |||
| b82b882e02 | |||
| 347783d4b7 | |||
| 0b2f07ca6c | |||
| 7c5d5543e0 | |||
| 3d10716b1f | |||
| c48a7d4400 | |||
| 79130d4fe2 | |||
| aebb6ac0db | |||
| 3eb910b47e | |||
| 6223b8e1a7 | |||
| 533a4dfa6c | |||
| dbc752253b | |||
| de71c1d903 | |||
| 7f074781f4 | |||
| 6863398d52 | |||
| a019a511b1 | |||
| ab77fcbc1f | |||
| 56520092c3 | |||
| 85ee0ae38e | |||
| f26cbc5b13 | |||
| 252a1b0761 | |||
| 51e5f53965 | |||
| 23d142a4c9 | |||
| b0976d1613 | |||
| a737ca71e6 | |||
| 1071300a7f | |||
| 4956809c3f | |||
| 04befd1b61 | |||
| 2490958090 | |||
| 5facdd9363 | |||
| 04bea41400 | |||
| a4c4913ead | |||
| 1405e10257 | |||
| d042c778f4 | |||
| b78b76e579 | |||
| d136470149 | |||
| 29dfbbf314 | |||
| c54bd793f2 | |||
| fddac77c79 | |||
| efff22fc18 | |||
| d52ce9af72 | |||
| cace516e91 | |||
| 4b27e92210 | |||
| 3d0061fd83 | |||
| 1f0a154e4d | |||
| 02bfe9a253 | |||
| f2063f41f6 | |||
| c7c07c7075 | |||
| 28e60745b9 | |||
| 830a3b9b1a | |||
| 044a49a61a | |||
| 44aeed0cbd | |||
| 12241e931a | |||
| 3f902d8886 | |||
| 84e20dae29 | |||
| fdffef3baf | |||
| 0806d822ff | |||
| b577b3db29 | |||
| 64433394f5 | |||
| 9fb9b6378e | |||
| 4cb6debef8 | |||
| 435ea42761 | |||
| 36957dce85 | |||
| fe44e966e0 | |||
| 3b4be5396e | |||
| 685c7ac777 | |||
| efe4bf3912 | |||
| f1c70f5713 | |||
| 3608386bd0 | |||
| 548a959e72 | |||
| ff957b72c4 | |||
| 80c55a451d | |||
| bd5816e451 | |||
| d28477a655 | |||
| 9e329abcfa | |||
| 781a2043b7 | |||
| 44136b23b1 | |||
| a5f4272ab7 | |||
| f32b5878ed | |||
| 1a2892d5d0 | |||
| 1811e28fbb | |||
| c6ee556c2b | |||
| 054e362e8c | |||
| c4cb9fb82c | |||
| b48035fb37 | |||
| 5819c31b5e | |||
| d77886d586 | |||
| a9c616120f | |||
| 09849c7a44 | |||
| a66290ab63 | |||
| 75d4b70623 | |||
| 60959e211a | |||
| 6bccd64ade | |||
| 1fcc1598b1 | |||
| 69784226ea | |||
| ff17d17e3e | |||
| 40d9cd2c2b | |||
| a7127f07cb | |||
| b47528028c | |||
| 105c30868b | |||
| 786ddda4ad | |||
| 9d08d667a3 | |||
| a570136a9f | |||
| 58eeabe3fa | |||
| 16583ee35f | |||
| ed17a42737 | |||
| 2f6525c7c4 | |||
| 2cc7356520 | |||
| 5e2e8d1fae | |||
| a05f52e3d0 | |||
| 117a1e8811 | |||
| 0daf486868 | |||
| ab6d4cb9b9 | |||
| 6efbc95043 | |||
| e5fc4bc1dc | |||
| 65eaecd005 | |||
| 7d52fb686a | |||
| 92b4f18cd6 | |||
| 8ede104a3d | |||
| 42b4bd62d9 | |||
| a8bc62608c | |||
| 266aba5d72 | |||
| 1ddcd28371 | |||
| f8c0886fe5 | |||
| ac258fa356 | |||
| 7b21c0cc6b | |||
| 67297aa574 | |||
| 28b7c431e0 | |||
| b5ba31a94d | |||
| 4ca843a14e | |||
| 24043d31ab | |||
| a008e6cc26 | |||
| 1e0afb96af | |||
| 786afd561a | |||
| 5a21e1717c | |||
| d3b550f6a2 | |||
| f5bda1e8d1 | |||
| a5bad3735f | |||
| 2e00c3ad56 | |||
| 3324656516 | |||
| a8b6607c89 | |||
| f93bf6f0e7 | |||
| ccd4d3c9a8 | |||
| 4dee60b0fd | |||
| 85a4e4bd35 | |||
| 227b6337bc | |||
| b6c82316c7 | |||
| 9850b50f1e | |||
| 970f567edf | |||
| a4b47001c4 | |||
| bea05a5b54 | |||
| 7ae60e3cd1 | |||
| 4367a91d4b | |||
| 04accc28c8 | |||
| 210fd237e8 | |||
| 34074a9451 | |||
| 94179bbb33 | |||
| c9549ecd7c | |||
| dc8ebf7faa | |||
| 33bd6cc5a4 | |||
| 5f7975b910 | |||
| 835b7d66a1 | |||
| b99894768b | |||
| 06acd02dbb | |||
| 2cedcff73c | |||
| 360567ccd7 | |||
| 82ec9befd0 | |||
| 514ac30ad1 | |||
| 782cd87d60 | |||
| 3514073433 | |||
| f4345e1005 | |||
| b88a421d2c | |||
| 25d3fb389c | |||
| 9afd516182 | |||
| bfeaa0530f | |||
| bb4692bbf6 | |||
| 73fc73ae52 | |||
| f9d667863e | |||
| f3bc71a4ee | |||
| d30377a7b1 | |||
| 750ad6b5b9 | |||
| e8176eee4f | |||
| 52b1526163 | |||
| 74254e407b | |||
| ca3ccce3f2 | |||
| 4ce820279e | |||
| 3c11045050 | |||
| 64199acefe | |||
| 2d4d4e3865 | |||
| f01a1fb6b9 | |||
| 0a05bb3892 | |||
| 506073022b | |||
| 85b582d6dd | |||
| 8968add126 | |||
| 6967b5f20b | |||
| 4e910fcd7a | |||
| c65d93dd73 | |||
| ea3689791b | |||
| 38bdcfd297 | |||
| b74ec54bea | |||
| 6698bf0c67 | |||
| c346eb7acc | |||
| 414a1d2ca5 | |||
| 6f16d34681 | |||
| 88e6d1c3ff | |||
| 877ca7176a | |||
| 9c3a2470c2 | |||
| ee002fa470 | |||
| 33773950b0 | |||
| 753691f5db | |||
| 216d023c45 | |||
| d5911afde1 | |||
| a57532d7bf | |||
| e02a1f705b | |||
| 2e4d401f61 | |||
| 9c16734141 | |||
| ea1fa521c4 | |||
| 384e0b1212 | |||
| 3d6bd4ea54 | |||
| 085330c930 | |||
| 44cfbb2c30 | |||
| fe0f0a0190 | |||
| f1386dd229 | |||
| 68ac892015 | |||
| 782bd0f164 | |||
| d85b109091 | |||
| 429307ecc3 | |||
| 180d861426 | |||
| 7c15e1119b | |||
| 11d7a31584 | |||
| 26f55b0bb5 | |||
| 2828693112 | |||
| ebd61a70b0 | |||
| e96c265135 | |||
| a96e093204 | |||
| bed8196f10 | |||
| d51ab4483e | |||
| e28d24bfe3 | |||
| 7a9f2f3944 | |||
| 0ce082ed80 | |||
| 1319df63f5 | |||
| 486dc6d218 | |||
| 234345f2ea | |||
| 08bfc1450c | |||
| 8788dc264d | |||
| 5ff5da9d9a | |||
| bfc9d357d6 | |||
| fc79fd18b8 | |||
| a949b7ae8c | |||
| 6899f4b5ac | |||
| e6fb8fa1d4 | |||
| 12e71f4454 | |||
| 6362e37804 | |||
| 8471132fe7 | |||
| 2449ed4c96 | |||
| 3979e6b2af | |||
| 052f055bbf | |||
| a88bc4554f | |||
| ddfdc3e1d1 | |||
| e0487280e5 | |||
| 8bb080dba7 | |||
| c31e8e8130 | |||
| 09fd4d25b7 | |||
| e3c753ba71 | |||
| 1ac0249fe5 | |||
| eb1db21920 | |||
| 60e46bed7e | |||
| a127c8b7c7 | |||
| 982b5392ab | |||
| 843ef4d9d2 | |||
| 52986ce8de | |||
| 5c570b3d8c | |||
| d85e5e7b3e | |||
| 2ebce9fd03 | |||
| 758f2a7103 | |||
| fa163d2a42 | |||
| 8f910f83af | |||
| 5e6204854f | |||
| da65b3ec64 | |||
| c781c2fefb | |||
| d691b8201b | |||
| 2109222d12 | |||
| c335881b2d | |||
| 993fc45e83 | |||
| 1abebc54b2 | |||
| 248af4f9a1 | |||
| 1a70358e21 | |||
| 1729c14acc | |||
| 1b440aa3ad | |||
| 9e2a57a935 | |||
| e52c1199f9 | |||
| 176d614d86 | |||
| 3003d069fe | |||
| dfcbe6fd9b | |||
| 5a9277de90 | |||
| 3b4b385418 | |||
| db49403117 | |||
| 63b916eeca | |||
| 0beb6b2022 | |||
| 62dcfdb7a2 | |||
| 17d33b460f | |||
| 9dc282b1d9 | |||
| 7ec43c9913 | |||
| 9df39f9646 | |||
| d3ad70b019 | |||
| 8bab24adbd | |||
| 3261a59cee | |||
| 42731b5778 | |||
| 80d2c9871d | |||
| d5b7a2e168 | |||
| ee610f5cc7 | |||
| 1830b4c752 | |||
| 486891d861 | |||
| 462b545968 | |||
| d12ad85fbd | |||
| 887b918f4d | |||
| d4de219032 | |||
| 7616b63fae | |||
| aade99a0e6 | |||
| 1781f5e7d6 | |||
| f8b5bd8002 | |||
| 1d4e77cf05 | |||
| b77a7255cb | |||
| 353904d52f | |||
| 01bd1e4428 | |||
| 05b06680bc | |||
| 1f71d11713 | |||
| bd6486fb8a | |||
| d28ab00443 | |||
| 8cb06cc3b0 | |||
| ea330128ca | |||
| cec6663ea3 | |||
| 9e2ad78330 | |||
| 8ce00545ee | |||
| 130d358f4a | |||
| 49476abb9f | |||
| a02b99036b | |||
| 63ba258085 | |||
| 5e2481af7a | |||
| dab6f48fe9 | |||
| d8dc953252 | |||
| 6dc840c24d | |||
| 23c9ea0961 | |||
| d0b04ebbbd | |||
| bd64d084e9 | |||
| f6555a45ae | |||
| c91b59f8cd | |||
| a699300f5f | |||
| 6e12c5c1d0 | |||
| 852fc1d2d5 | |||
| c2c957b859 | |||
| e996b5fc16 | |||
| f20c22ab49 | |||
| 1e200511cb | |||
| 80f2a0ef39 | |||
| ae156259c0 | |||
| 39e3f8b646 | |||
| 08bab8501b | |||
| 3e2fe8a744 | |||
| 802c2065ee | |||
| 02d1f70386 | |||
| 5251041ed2 | |||
| ac61c3a55c | |||
| 38f4b0c53d | |||
| 1054ffa7ac | |||
| 41503ae625 | |||
| 4ff7e74e3c | |||
| 1ee90e67b2 | |||
| 34d2336030 | |||
| 419d85831c | |||
| d0ce7f1aa6 | |||
| 9322b4df99 | |||
| af0e411487 | |||
| 754f090c61 | |||
| c387749a81 | |||
| f68a2e6272 | |||
| bff150a931 | |||
| b980c5d93a | |||
| 49332fafec | |||
| 829860e4f0 | |||
| 2e69911bcc | |||
| 3ab3278db4 | |||
| 70ad9d442b | |||
| 7a3ea2918d | |||
| af67829a41 | |||
| e633c0247d | |||
| 38b385f15f | |||
| c839a015af | |||
| 9c0394f035 | |||
| 33483ae37b | |||
| be42b7e604 | |||
| 9536d368b0 | |||
| a9a827dc40 | |||
| d82f49f727 | |||
| c94f0fa34b | |||
| 9426905959 | |||
| ffaf2efd06 | |||
| 99b7b4b057 | |||
| ce711a3d0c | |||
| 22a63a8fb0 | |||
| 2fdbdb4058 | |||
| 6b3148ec31 | |||
| e5fc887d4a | |||
| 0a509c4723 | |||
| 898405e15f | |||
| 7259038b1c | |||
| 3886351490 | |||
| 59b15930a3 | |||
| f06daedb0c | |||
| c2b8a71000 | |||
| 30f0eca0f4 | |||
| 3aedf8e5d0 | |||
| aed2f1d8a9 | |||
| 3504797147 | |||
| 87082de9c4 | |||
| 3cfaa60ebb | |||
| b29f485e15 | |||
| e821fa1100 | |||
| e5c919700a | |||
| b9b947fa3d | |||
| e51c1d0b85 | |||
| ca540bfd90 | |||
| 2d930ff2dd | |||
| 07714d160c | |||
| e29f545c1e | |||
| 11a8792398 | |||
| e413ca5b62 | |||
| bf155a0beb | |||
| f7556fac0e | |||
| a96c617b01 | |||
| da0ba49a14 | |||
| a3d77a5a71 | |||
| 394f3ffd88 | |||
| c4ad52a456 | |||
| de2c6bec85 | |||
| bff5f79282 | |||
| a3d7b4eba9 | |||
| a49288200e | |||
| b94f36dfdf | |||
| 3eef63b75c | |||
| 8eae7ac26b | |||
| 9df694e12e | |||
| 7bac1d3b92 | |||
| b31b11cda8 | |||
| a3754b7031 | |||
| fc809964c7 | |||
| d000ad76f8 | |||
| ca19352ace | |||
| 3f5936bceb | |||
| 29b244d843 | |||
| e3b405ad2d | |||
| fef1976a6f | |||
| fb8cc4bfeb | |||
| 1eb6dbc7f2 | |||
| d16eb74e1b | |||
| 945b9bdf31 | |||
| b75e383956 | |||
| 15de1d58b0 | |||
| 79ce3871ea | |||
| c119e35901 | |||
| b3005010e7 | |||
| 2cb58da7b7 | |||
| ab14deb2cf | |||
| 646fa8b7e6 | |||
| 84c68ccf14 | |||
| 93d0a8b674 | |||
| 8a33ccd356 | |||
| 7ff8978ca7 | |||
| 9473cc2b27 | |||
| 448d668b44 | |||
| 635c8dc2ad | |||
| 8c0e021e82 | |||
| 5b88ad2967 | |||
| 022854fa37 | |||
| a72d3afd1c | |||
| 79a2e905a8 | |||
| 0e3500f587 | |||
| 1f062fe7b4 | |||
| 68aab10902 | |||
| b9be0341f5 | |||
| b712c44567 | |||
| 12a118d08f | |||
| b1b9db7333 | |||
| 12fa333907 | |||
| 28e08035f2 | |||
| 5f920a46fd | |||
| 23e3aefaf1 | |||
| 74e38f9755 | |||
| 69a781a710 | |||
| 2c82513e54 | |||
| 8eeff7efa2 | |||
| 7fcac6159c | |||
| 17a3f59bdc | |||
| 3cac18f634 | |||
| a5eef3087b | |||
| fc9e31199e | |||
| fefcbbabbb | |||
| 38fcb274a0 | |||
| 3aa3942998 | |||
| 1fbfc8f039 | |||
| af891f3fc2 | |||
| 47bb760d86 | |||
| 8e47a17c96 | |||
| c2c78dfe78 | |||
| c24acd2681 | |||
| 26742432f5 | |||
| 8a958df5f5 | |||
| 6a13e593ec | |||
| b63fe40cc8 | |||
| 8d8b886ad6 | |||
| 2e6cc61450 | |||
| 6859dc9142 | |||
| b871f728df | |||
| e41eec2238 | |||
| 87b87fa2ca | |||
| 3c996b6dd7 | |||
| a39f39646d | |||
| 41a9c22959 | |||
| 49097649de | |||
| 19e94a7fb9 | |||
| 2012e81acb | |||
| 157f9a82b1 | |||
| 795f58dc9b | |||
| 4c841fd1d2 | |||
| 976f92e5b1 | |||
| 25daa67554 | |||
| ef5c7e5972 | |||
| 33ff723bd8 | |||
| 7605c2f4f5 | |||
| ec41d7310b | |||
| feb4f77ef4 | |||
| 21c744b27a | |||
| 34f3e24d3a | |||
| 79654a99d9 | |||
| 38b36c0344 | |||
| b6a90d4633 | |||
| 1d0a273ffb | |||
| 5145dd14ea | |||
| 0e960b77b9 | |||
| 093526f4ff | |||
| 8160aad1bf | |||
| 7fec25ddcd | |||
| 81b227fa9e | |||
| 2eb5374b28 | |||
| 51f40081f3 | |||
| 7e878b5702 | |||
| 804e0ed69d | |||
| 46c0d47175 | |||
| 147df1498e | |||
| f9410a9169 | |||
| 12bde81e67 | |||
| a88750908e | |||
| 0503596414 | |||
| aca3080c9a | |||
| 948158e85a | |||
| 65ab481432 | |||
| 9b3aa423aa | |||
| 07876cb46c | |||
| de48701d15 | |||
| c5678404d5 | |||
| 4e2dcb1c05 | |||
| 98539eee87 | |||
| 223c1650d9 | |||
| aa2133e72a | |||
| 498d58599b | |||
| a0c0fe36ba | |||
| 76b977ff98 | |||
| 878eaf4301 | |||
| 240d3460d7 | |||
| d76e8ddc3f | |||
| ae0d405350 | |||
| 45fc0eb9da | |||
| 3327c3b24d | |||
| 7189154d47 | |||
| c6521d9c38 | |||
| 049529ecf0 | |||
| c2b8eea010 | |||
| e72bd1738c | |||
| 280d2f6517 | |||
| da8856c907 | |||
| 04f7bb7cc7 | |||
| 75c01fbe86 | |||
| 03b79351db | |||
| 33429766a0 | |||
| 83464090d6 | |||
| d77b2655b1 | |||
| 31a537748e | |||
| e873b097cf | |||
| 16e069b9a5 | |||
| 07a3095392 | |||
| e1b5c7f9c2 | |||
| f005cdbc71 | |||
| 230e270f13 | |||
| a87860a110 | |||
| 7fa360f32c | |||
| 7456489fb0 | |||
| 30c6937910 | |||
| 28724f396c | |||
| fb3ca6d9d5 | |||
| 6e75e58952 | |||
| 9992361980 | |||
| 40fee25da5 | |||
| d6a9e4726e | |||
| 9e81e5606a | |||
| 0abb0db2a3 | |||
| edb42bbbe9 | |||
| 3bebe7f69c | |||
| 7605b582d1 | |||
| 6ed031762b | |||
| 01425dac70 | |||
| 8406fd86d5 | |||
| 945efc0516 | |||
| 4e342a59be | |||
| 8e517bb9cf | |||
| dacf5125bf | |||
| 88a8a3318f | |||
| 60b561449b | |||
| 88396b5a5e | |||
| a0c5466f7f | |||
| b62b218257 | |||
| 7a3c23527f | |||
| 2f002703ee | |||
| b0c6ed1d59 | |||
| 950e97b6d7 | |||
| 6d54e5a47f | |||
| 87d65123ba | |||
| 35c8803f8b | |||
| d9c8542993 | |||
| d7a6809fec | |||
| f3c38a4288 | |||
| 626b03c93a | |||
| cf5c55209a | |||
| 19921414cf | |||
| c4de2b6990 | |||
| 7907e4b830 | |||
| 9e55118d3e | |||
| c4f0440b0a | |||
| cbb6d2c712 | |||
| a5076eddba | |||
| bc24d75ad7 | |||
| af11264841 | |||
| 5955c2d61f | |||
| c869d23778 | |||
| 5edffbbca4 | |||
| 01f2b6b783 | |||
| 95e2bf4a21 | |||
| 6f22534b8d | |||
| ed091c4f3e | |||
| 5d1123e7df | |||
| 5929b7b0ed | |||
| f4a2ae8ccc | |||
| 4bc25c6043 | |||
| 0de95b1830 | |||
| d25acf1065 | |||
| 742103ff02 | |||
| f93a1959b4 | |||
| ccaf926c6d | |||
| 26fbae8672 | |||
| d37cd1613d | |||
| 96a923b5a8 | |||
| aebb6a359b | |||
| 500f113f95 | |||
| b983e9f8b4 | |||
| cdbf1e147e | |||
| 5089e7df25 | |||
| 1de19731d0 | |||
| 0c0d1cc794 | |||
| 21b968a9c6 | |||
| d857e3acad | |||
| 856197a5c8 | |||
| 6f05249214 | |||
| c9c6d5dbc2 | |||
| a8de630ecf | |||
| d7b0b3c898 | |||
| 704bb8668f | |||
| 763bfb4bb1 | |||
| e43173de60 | |||
| 16706cd483 | |||
| 505c86399c | |||
| 65ddff13f7 | |||
| bedd105649 | |||
| 7c37160fb7 | |||
| 2e263561d3 | |||
| c10e57ec3b | |||
| 046bd37fd5 | |||
| 55c3e7aba6 | |||
| fbf83b3aed | |||
| 255a652d4d | |||
| ea60795e84 | |||
| aa2cff69e9 | |||
| 0789fb7f08 | |||
| 4a5e262695 | |||
| 22097987dc | |||
| 4fba2e4abd | |||
| 9670f26619 | |||
| 94617e573f | |||
| 98d02d7532 | |||
| 7b44e7a3f6 | |||
| aa4cf37aed | |||
| caf2b4e736 | |||
| da516a0167 | |||
| 1267a64fcb | |||
| 17d1cf673d | |||
| 74c3b9fbcb | |||
| 1fcd70b0d0 | |||
| 832941494b | |||
| 581d812cd4 | |||
| 56dbf6bbe1 | |||
| d5792fb02a | |||
| 0e82cb977e | |||
| e9237146a3 | |||
| b72899b500 | |||
| 2f39e38264 | |||
| db54672fbd | |||
| 30d9a7d703 | |||
| 4ade4c78ba | |||
| 5a088a740e | |||
| be220b6c1d | |||
| e194d6073f | |||
| 38858cd01f | |||
| 645c4a4dbc | |||
| c2a630dabd | |||
| 30cf1f5713 | |||
| e197f88510 | |||
| b07accdb3d | |||
| 31808b7981 | |||
| 50827b3b60 | |||
| 33f7a7084c | |||
| 07a14bc176 | |||
| 3c57177a11 | |||
| 7cc18b8082 | |||
| bef805d694 | |||
| 3479d08193 | |||
| 8ca9474c1c | |||
| 2047e818ef | |||
| 94b8835340 | |||
| 801b4f01b6 | |||
| 8901820026 | |||
| 288b933884 | |||
| 37a5f68339 | |||
| 6058160160 | |||
| 479dfc28b9 | |||
| e3cee8dafe | |||
| 3686e0de63 | |||
| 2931b32ce6 | |||
| f4c7912415 | |||
| 1ecc3c8ff2 | |||
| b28e66fe6d | |||
| 8392788cdb | |||
| 42221f406a | |||
| 4a1b23c52a | |||
| 6e38e214b6 | |||
| 1965e24860 | |||
| 55242edbde | |||
| ebbe64f279 | |||
| 654f752ca0 | |||
| 2c5d9f043e | |||
| 2b2acbe244 | |||
| 8251879162 | |||
| ec9297a5c0 | |||
| 7d4e103660 | |||
| a93ade9813 | |||
| df454cb59e | |||
| 3da6187782 | |||
| 0bd79d518f | |||
| 86798ae9ea | |||
| 13217f764e | |||
| 71400ff24b | |||
| 10371c06d0 | |||
| 33dd158df4 | |||
| dfc63de745 | |||
| 2830e6d199 | |||
| 8c07573f8f | |||
| f3af0262df | |||
| d7841b47a5 | |||
| 0f270a6a35 | |||
| 92db68c36b | |||
| 8432f46e48 | |||
| d74d337d2f | |||
| faccee88e7 | |||
| e8246b3b90 | |||
| eb4e7b9281 | |||
| e91bcdfe5e | |||
| 1f1a068d45 | |||
| a57c0a8c7d | |||
| 1e2190a6cc | |||
| 1644c1dc5b | |||
| 5fc3798c42 | |||
| dcae8eb7f3 | |||
| 3fa5c92058 | |||
| 512c3c57e9 | |||
| 4b1d81d582 | |||
| e223084014 | |||
| 02022685f4 | |||
| 3a95e75c2d | |||
| fae81cdb1b | |||
| dbae37f151 | |||
| f7d58a0b76 | |||
| c415f9082f | |||
| 08fb458aa4 | |||
| 2e0d1fd90f | |||
| aab65765a7 | |||
| c0335a1f86 | |||
| 3694e9649d | |||
| 2c79fa6492 | |||
| 69e6497655 | |||
| c2e81415a1 | |||
| 1d8a03f9d4 | |||
| aca713b8d9 | |||
| ef8c7c9084 | |||
| e4eaf72769 | |||
| 5138ced630 | |||
| 534f445f8c | |||
| a2da4f0e77 | |||
| b2cc3e5329 | |||
| efde321616 | |||
| 3a61581608 | |||
| 3a10f0b946 | |||
| 6dc6972bdb | |||
| 5910c9502a | |||
| befb957b3f | |||
| 405235a59d | |||
| d0d22142b9 | |||
| bd8ac9551c | |||
| 9c148625e4 | |||
| a08d82a3d9 | |||
| 48b51f21de | |||
| ecc94b6b5e | |||
| 977df39ac9 | |||
| c850ea10f5 | |||
| 3260279cb7 | |||
| e0eee614c7 | |||
| 2fcef1f277 | |||
| 1e60a0a32a | |||
| ed52325fe2 | |||
| 68e0537846 | |||
| 7707522ce5 | |||
| 91e26adc6a | |||
| 53b1415e87 | |||
| 146f869aaa | |||
| 75c350cffc | |||
| 484a51a377 | |||
| dc863be38a | |||
| 0bd35727f4 | |||
| 5183fcc316 | |||
| bf7596521c | |||
| a85b6c960c | |||
| 8cb91477cf | |||
| 488996fadb | |||
| 729f987cf1 | |||
| a8a69fdbca | |||
| a1aebf0575 | |||
| 0f42218681 | |||
| 5404f9ef19 | |||
| 8a84be19df | |||
| fd4ea81e46 | |||
| 025bd3237c | |||
| 3df79aedb1 | |||
| 4141aa35ba | |||
| 6e45ee558e | |||
| e867ce4c73 | |||
| db138be5f3 | |||
| 5ae6495e69 | |||
| 7113706a16 | |||
| 45e62a5b2f | |||
| d8281b0103 | |||
| 58f1bc5531 | |||
| 935b52c489 | |||
| aace98545a | |||
| f39236c8fd | |||
| 43b4a6834b | |||
| aa3978b04d | |||
| b51e758516 | |||
| 5a92fb1f65 | |||
| b803371d0b | |||
| dd459b05b5 | |||
| 4a870d0183 | |||
| 5be1832c09 | |||
| 362652ac6c | |||
| aec69884dc | |||
| 47ddc64239 | |||
| 41855fc63b | |||
| 71d93f2b67 | |||
| 2a32be40f6 | |||
| 8fb4d4c7c6 | |||
| c814ed5a04 | |||
| fa10c7d0d4 | |||
| b17748cfeb | |||
| b5524b9a69 | |||
| ec0ff7afcd | |||
| 0ea6f5f71b | |||
| 43c5fee5b3 | |||
| c761d5020b | |||
| d0cf68c2e2 | |||
| 3beb98a5e9 | |||
| dd83ebe0e2 | |||
| 6fdf3957bd | |||
| 608fa1ca6c | |||
| f3d586d455 | |||
| c2f0ce0fa2 | |||
| 8535c39c2c | |||
| da8f023f5b | |||
| 354490d149 | |||
| c5b0be5d21 | |||
| 905b2a7a98 | |||
| 9faa12a681 | |||
| 7123562f85 | |||
| 6301f5a939 | |||
| 5e8676cf8a | |||
| b320ba597a | |||
| 12d16f355b | |||
| f4264cd149 | |||
| 65d1c4187e | |||
| 58b169cb03 | |||
| 04fcb985fe | |||
| 0532ceb1cf | |||
| 5414adc8d8 | |||
| 95210366e9 | |||
| f73dc6c3be | |||
| 0cc3150558 | |||
| 0fc2ce2484 | |||
| 74383e3111 | |||
| 3f8954d21d | |||
| 6abd76a40b | |||
| ebf393c58b | |||
| cda1432d04 | |||
| 09e5fca1a5 | |||
| 4a1acf5d6a | |||
| 7ae926bf5c | |||
| 9c3c27f809 | |||
| 03275938e7 | |||
| c197c842e8 | |||
| a90e321a91 | |||
| e38d73df8b | |||
| 7df90b2d30 | |||
| 8ad03f11ca | |||
| bfb89e80e0 | |||
| e531f12f94 | |||
| a917ee8f1e | |||
| 44f3c776eb | |||
| 5d3f5dac1e | |||
| 873879ff79 | |||
| 4d48b42aea | |||
| 6e01df753f | |||
| c75022038c | |||
| 2f46a63041 | |||
| 6c1907f7e0 | |||
| f43dd389e2 | |||
| 42ecdd3b14 | |||
| 297164a0ee | |||
| 323f09b06c | |||
| e003b91bbe | |||
| d71cd370c0 | |||
| f400d0212a | |||
| efb1d79e77 | |||
| 3ce0a8a45e | |||
| cc295b4633 | |||
| cad66e8241 | |||
| ee23daa651 | |||
| e590701c01 | |||
| da7bc301ea | |||
| 9f1777ca9b | |||
| f1636fc5a9 | |||
| db053398d2 | |||
| 527c4d0de4 | |||
| 2479147fd9 | |||
| 7b7a8c1bab | |||
| 417caf1b62 | |||
| 2ff630d394 | |||
| b6d725920a | |||
| cc76851614 | |||
| 1ddb2aaec0 | |||
| f6b77f51ce | |||
| 4067259e9a | |||
| c30d517a00 | |||
| 27fe314c1e | |||
| 2c12cfe4fd | |||
| 9adf342dd8 | |||
| facac82130 | |||
| c96249906e | |||
| 3e095711f4 | |||
| ca1ddb4013 | |||
| 8169235d2f | |||
| fe9512eb82 | |||
| d213c3defb | |||
| 0a56624657 | |||
| 4553785918 | |||
| 78b3efcf00 | |||
| 67cb844550 | |||
| d13781ec32 | |||
| 1b169f007c | |||
| 8efcad123c | |||
| b9cf1e46af | |||
| 47409b11cb | |||
| 8c5efdbe55 | |||
| 4a04d7cce7 | |||
| 5ef6383c63 | |||
| 35f15435a3 | |||
| e49003940f | |||
| 3c85b920cd | |||
| 83cf6026cb | |||
| 38dcd04334 | |||
| b32a8a2899 | |||
| ece9e008c2 | |||
| 8cac4dbf9e | |||
| ef2bec72a1 | |||
| 03c3a20b1b | |||
| 95e7daa27c | |||
| 5411c4f38d | |||
| 56022f5e1e | |||
| 4c97d966a2 | |||
| f075c02db5 | |||
| bc464008c9 | |||
| 25143386d5 | |||
| af7c9a3caa | |||
| 4a3e60bde9 | |||
| 32e61a7a09 | |||
| 674c6eec81 | |||
| da535eaddc | |||
| c75c3d8777 | |||
| 4fa63986dc | |||
| 029f7a3f5c | |||
| 4c8a93f63f | |||
| eb6eba25f3 | |||
| e9194c59f4 | |||
| c8d693c8e7 | |||
| 72d8b29699 | |||
| 2341014882 | |||
| eb15fadcfe | |||
| c6b78b9e42 | |||
| ea90f56f4d | |||
| 1e7203320f | |||
| e3b587aa95 | |||
| 13fa8f373e | |||
| 8684fc13ac | |||
| c3dc055fb2 | |||
| 7ccdec671a | |||
| 4e024f7173 | |||
| 052e1c3867 | |||
| 1248006f3d | |||
| b318e8ae13 | |||
| ac410e9a56 | |||
| 1e1ed92d52 | |||
| 1af4d44ca5 | |||
| 7e963bef41 | |||
| 933000ffc7 | |||
| 9b217e1c97 | |||
| 324f4944b4 | |||
| b9d738c1ad | |||
| f735f1eb53 | |||
| fc37149e9d | |||
| 7149226283 | |||
| 4164a5288a | |||
| 1992276b07 | |||
| 21a7023b7b | |||
| bd054b26c0 | |||
| f094716b73 | |||
| 413fa31534 | |||
| 5af7bcc677 | |||
| b3100d2f5e | |||
| 4b1888a34d | |||
| f4cd214823 | |||
| 3768d57639 | |||
| e947367fca | |||
| 23105e3cb5 | |||
| 09898d9b4d | |||
| b9be3aa7f8 | |||
| bb6db2b7f8 | |||
| fd2dacaefb | |||
| cc60e85901 | |||
| a49d20dd8f | |||
| 0069d20434 | |||
| 9520782a04 | |||
| f172f6efeb | |||
| b6b6686873 | |||
| 5e724e7a3f | |||
| 90f1d1df2a | |||
| a5726c0ed8 | |||
| 1860366986 | |||
| 9db1d38a15 | |||
| 04ceda693b | |||
| 23ff128ac8 | |||
| 56c8e22392 | |||
| 54ad4495c4 | |||
| 16dff6c24d | |||
| e543ee576f | |||
| 3749142670 | |||
| 7d3fc1740a | |||
| b2a084d669 | |||
| 30203668e4 | |||
| 8db60412ba | |||
| ebecc70693 | |||
| 74712b6e27 | |||
| 2da2dec1da | |||
| 892fcfc2ca | |||
| 40253ea8bb | |||
| 7279da7f09 | |||
| 31c40c96f2 | |||
| 7a833365ce | |||
| 8ff79e4131 | |||
| 2b29ae4f9c | |||
| 4e56078d2a | |||
| 0ac156f2b5 | |||
| 4bbf20ca4e | |||
| a462c5a493 | |||
| 179cb5f589 | |||
| 367014ebf3 | |||
| 04f275e273 | |||
| 19ab249d77 | |||
| f15b1a503a | |||
| a64cc0ba4b | |||
| 9d8d0c2155 | |||
| edfe91274d | |||
| 326b20b08d | |||
| 11b0ce6275 | |||
| 03763e16d6 | |||
| c1e8ee12e0 | |||
| ac8cc408c1 | |||
| dad91cca8c | |||
| 2f9c384ac0 | |||
| f23a2267cf | |||
| ada38fa3ea | |||
| 2ee0597d48 | |||
| 0457fc85c7 | |||
| 2cd071acb2 | |||
| 92f6687429 | |||
| b39a83ad6e | |||
| 7ae4cdc8f1 | |||
| c0b1062ac7 | |||
| 54f6eddfa2 | |||
| 777886a8e7 | |||
| 7d47227fab | |||
| 1f09b8fe0b | |||
| 8c92a578ed | |||
| 992f09d9ba | |||
| 096f2caf9c | |||
| 0b37c909b3 | |||
| bdad38e707 | |||
| 1b18ae1597 | |||
| 6cb262e04d | |||
| 504c531266 | |||
| 77863e64bb | |||
| 61509f35df | |||
| 8dcc3cbe8f | |||
| aa8f3a824c | |||
| b5a19528a1 | |||
| bd20e8fc81 | |||
| 2f806801bc | |||
| be006bd2bf | |||
| 18a08288ec | |||
| c9ac6d73e6 | |||
| 1c48a71cec | |||
| 940abee55c | |||
| f602455d1f | |||
| d5c34a37b0 | |||
| 8a0e9a3400 | |||
| 0e836973fd | |||
| b66a29e209 | |||
| 2d9a99738e | |||
| 22b8cab5e1 | |||
| d40b744728 | |||
| ccb1055c6f | |||
| c78471cc8f | |||
| aa4b4a5e2f | |||
| 33154ebcdc | |||
| 78bfbb11f9 | |||
| a491c34824 | |||
| 1b57af88a0 | |||
| be40d7be9a | |||
| 43c7a65942 | |||
| d18cd0e643 | |||
| 26a03d2c45 | |||
| b035e0835c | |||
| 00de8d911c | |||
| 9df3fcfdf7 | |||
| d675e34f37 | |||
| 2d96ba0f56 | |||
| 21c2bd1103 | |||
| dbe96fa931 | |||
| f3b7740041 | |||
| 7d7488db6f | |||
| b9197d65d5 | |||
| ec6f190b68 | |||
| 5e4f9f035b | |||
| 7b6bf7f39b | |||
| 2e8875dd2f | |||
| 4964cc2f2d | |||
| f0a4c5b008 | |||
| 1b6412821b | |||
| 926a0733e4 | |||
| 124ebd3240 | |||
| 7728609165 | |||
| da70f4ce6c | |||
| 69b3e9abad | |||
| f9f77bfa7b | |||
| dc04d347bb | |||
| a33d71d6ef | |||
| 429db73854 | |||
| e842e11b88 | |||
| 3cf168c7aa | |||
| 1e9c45c115 | |||
| 727cb3276c | |||
| ff02063d54 | |||
| 2e5ffa269f | |||
| a68175448d | |||
| 33e63a4819 | |||
| d7431478d1 | |||
| 3543b2c79a | |||
| 4489efff94 | |||
| 744782fd0b | |||
| 9a4025a566 | |||
| dcdea379ef | |||
| 6a4bddde99 | |||
| 7a8486d7cd | |||
| 25f79e495b | |||
| 2088359756 | |||
| dc2511942c | |||
| f0c257c4a5 | |||
| 068d21612f | |||
| 773461aad9 | |||
| f9257b2b0d | |||
| 9e0f03a3cd | |||
| 8525bf0188 | |||
| cb08e4a577 | |||
| 0ab6a10ec4 | |||
| a28ad6a7c2 | |||
| 7e3c33e242 | |||
| 73f393c542 | |||
| a467fabdc8 | |||
| a2ab84c45a | |||
| e8f30fc98b | |||
| 77b9ded23e | |||
| 7ec7228cb8 | |||
| 9fd6729967 | |||
| f1d4f1753a | |||
| 1f268b3b5d | |||
| 6d9c3fd0aa | |||
| 28ae63bd8d | |||
| 8d6d4b3765 | |||
| 7ddb72885d | |||
| 42e7e3f94f | |||
| 4a5204aecb | |||
| 6466c5e95e | |||
| 829f5af25f | |||
| 0d1147bac4 | |||
| 24b47b02e0 | |||
| 10262fd30b | |||
| 3cabc07d58 | |||
| b0546b1e60 | |||
| a988e67490 | |||
| fa951838d2 | |||
| 2594c5bbf0 | |||
| db70fa341c | |||
| 763e3852ac | |||
| 5d976266c3 | |||
| e03a397d46 | |||
| f08783f800 | |||
| eea75c669f | |||
| 66157d0596 | |||
| 8ec86973c6 | |||
| eb2c6ac6f2 | |||
| 93a8d8bc2d | |||
| cbcc893ce5 | |||
| 2e31c62efb | |||
| e7f953df6d | |||
| c19713949d | |||
| 54450935a1 | |||
| d0f5712ca8 | |||
| 382b049c5f | |||
| b62b39e9e8 | |||
| 6c2cc37abe | |||
| a53763221c | |||
| 83447a3fb4 | |||
| b7bac28113 | |||
| a9edb16554 | |||
| 8717faf7a1 | |||
| 52cbb7202c | |||
| 1d04f7c945 | |||
| f6a8317c42 | |||
| 2a9a7db9b8 | |||
| 3aeec78f79 | |||
| 14300229fb | |||
| 0aeb1fc6af | |||
| 2c15d7ed00 | |||
| 26565d7549 | |||
| 6ac17fa999 | |||
| 768a380b32 | |||
| 62816f217b | |||
| eeeeda4e5e | |||
| 0233fef475 | |||
| 95767c5ef4 | |||
| f5881eda53 | |||
| fca6faa3a8 | |||
| a4aa78ac35 | |||
| ca3fb4e479 | |||
| 40d4dc14ab | |||
| 618a5ca9be | |||
| c1997d9f70 | |||
| bb1caa6642 | |||
| 638f083729 | |||
| 59d41cf98b | |||
| 793adafda7 | |||
| dd5ca93f26 | |||
| f7a86deba5 | |||
| 740f90a513 | |||
| efceb095d2 | |||
| ac7cdb7d16 | |||
| ffc5cff275 | |||
| e456573eec | |||
| daa2b7da4d | |||
| 5351c4f093 | |||
| 5ff314d8e0 | |||
| 45bb0fff8a | |||
| 9a10b34933 | |||
| 1231541053 | |||
| 5e45e4a8ec | |||
| 5a96a6078a | |||
| 8fdeca99be | |||
| 6ff2725a1d | |||
| 2b83d516d5 | |||
| c5ec17a473 | |||
| a81de5e23f | |||
| c1855cefd3 | |||
| e9d812f315 | |||
| f95d27bf80 | |||
| be98ee52b7 | |||
| 4c9210586c | |||
| dc51f83249 | |||
| 23ed39a708 | |||
| 44c59b97e8 | |||
| 9721f743a0 | |||
| 63899923fb | |||
| 4fbbf70b8e | |||
| e38dc77c4b | |||
| f283e68a63 | |||
| 96d52ea393 | |||
| 52c45364fc | |||
| fcfc41252e | |||
| 46f9cf46e4 | |||
| 1c24786723 | |||
| 7ded993c49 | |||
| 2636ca166f | |||
| baa3f3234e | |||
| 5d6e0c333b | |||
| ec2a89fa48 | |||
| ab5a4c43c3 | |||
| ce61f454bf | |||
| 4772dafe7f | |||
| c9414d98da | |||
| 629925e2d8 | |||
| 17f672dad4 | |||
| 7df7a34c32 | |||
| 5d303752fe | |||
| 66cb7dd9b7 | |||
| e483d1bfca | |||
| 0a9717a8c2 | |||
| e0049bea2b | |||
| 73525feb68 | |||
| 7d3bffcfef | |||
| 5b76ec6b99 | |||
| 1456ff37d0 | |||
| abc86a7e9c | |||
| 00c5d82590 | |||
| c902a1f601 | |||
| d92a170304 | |||
| 41a9a1f74b | |||
| 9d7986e8d1 | |||
| ff5adc29d9 | |||
| b77ff793a3 | |||
| 08b95ec8c6 | |||
| 2c255d48c5 | |||
| 367ee31135 | |||
| 4ca43b4779 | |||
| d30b16a2fe | |||
| 6cb306a7e1 | |||
| b63185f1b3 | |||
| 4dc9e78812 | |||
| 22b81318b9 | |||
| 9862811c16 | |||
| af12296e51 | |||
| 0361a95f5d | |||
| d6a16ee8b1 | |||
| e3aade05d4 | |||
| c21c3ecb42 | |||
| 1bfeb0b41b | |||
| 4ec2923f46 | |||
| d2ec9642ca | |||
| e8e4f6bcb9 | |||
| f723f81c6d | |||
| 5aeb3ba42d | |||
| 104d0f3d2d | |||
| 0ec47795bf | |||
| c634023717 | |||
| df9f204b7a | |||
| 1b29a30760 | |||
| 5d07cebdc4 | |||
| b59bd0d4f2 | |||
| a2ccfcf361 | |||
| dccd46367f | |||
| 2a6df8434a | |||
| 1661872bf8 | |||
| 8878d1a8d3 | |||
| 8e017f2ce0 | |||
| d16124213f | |||
| bdff8541b4 | |||
| 0d0cc20ebc | |||
| 6fcb4a3184 | |||
| 2fced89dfc | |||
| 96ad3ff98b | |||
| 6c8fa5585b | |||
| dc8992134e | |||
| 885b329dcb | |||
| 702c5464aa | |||
| ac750aff10 | |||
| c679a77f9f | |||
| 176e4f6217 | |||
| 9e69c1c17e | |||
| 187a620f37 | |||
| 91b2fc7d9c | |||
| 0baa912465 | |||
| 79e7f9940e | |||
| 25845c4d22 | |||
| b66a342642 | |||
| e2054371ed | |||
| e9c1ccedcd | |||
| 367d1c6715 | |||
| 093c3fe7c9 | |||
| 428712147d | |||
| 20fa78bc85 | |||
| d081e115c7 | |||
| 3df9b54c01 | |||
| 72ac957806 | |||
| 8ea746ee19 | |||
| b2bc553e48 | |||
| a91c76a0be | |||
| 5a94be98fe | |||
| 999d67b7af | |||
| 91e7c86ba0 | |||
| f75c464bcd | |||
| a4ae0d88e1 | |||
| 5c5b1383be | |||
| a5e75bf485 | |||
| c6d79db266 | |||
| b394622b92 | |||
| 03c623a0c2 | |||
| a5a0d8ab8a | |||
| 3bb5d3709b | |||
| 32e538a27d | |||
| 02b75bf529 | |||
| f3477a03c6 | |||
| 8f5e519d0a | |||
| 1c4c5b5476 | |||
| 85e4a6cbd6 | |||
| 0f9742aeb4 | |||
| 2d49745215 | |||
| 2b6a039368 | |||
| 83e146f207 | |||
| b115a0a672 | |||
| 0c384a75cc | |||
| c19d20c3f7 | |||
| c7bc55d851 | |||
| 245dd6bb27 | |||
| ff4f162ca2 | |||
| baf3f9a9a4 | |||
| ae65fce9cd | |||
| 5e856b67a1 | |||
| f365c5153b | |||
| a11cda4ed1 | |||
| 9faa1b806d | |||
| f533d4ec8f | |||
| afc0671aa9 | |||
| f77771ae25 | |||
| c62dc17cd8 | |||
| 3ed61e8a2a | |||
| 2a1234f7ff | |||
| 77c6365e37 | |||
| 5c1d367926 | |||
| 869339150a | |||
| 9b5a653940 | |||
| 083bf13fe4 | |||
| 50436b8ced | |||
| b82e15b2ce | |||
| 8ffa1bd692 | |||
| c855198634 | |||
| 8aa8acdad0 | |||
| 382ad6ecd8 | |||
| f009370e07 | |||
| 0e91a658b3 | |||
| 774317f2c5 | |||
| a33e542df5 | |||
| 6adb9f0839 | |||
| 44a433eadf | |||
| 40cd58aa1c | |||
| a2c241da43 | |||
| 34a11e79b3 | |||
| 1e4d2f4427 | |||
| a3d7c9e537 | |||
| 9966c5eadf | |||
| 66bd771662 | |||
| deb0431f93 | |||
| eeca95c77f | |||
| 5cc3fd7238 | |||
| c1c5467b9d | |||
| f67706044a | |||
| 2cb58bd351 | |||
| fc8c1c156f | |||
| 55052f4451 | |||
| 12dd4a918f | |||
| 21a6bcfcfb | |||
| dc9b70db7e | |||
| 3b03b03f3c | |||
| 3923d81c87 | |||
| 3090100265 | |||
| 87356291f7 | |||
| 633186ca8a | |||
| 411d20ba26 | |||
| 6eac89c038 | |||
| 08b6fa520d | |||
| b69cea999e | |||
| 91db9127da | |||
| c4512651cf | |||
| e406fb0c88 | |||
| 26530f25fb | |||
| d51961ac8c | |||
| 80e20e59ec | |||
| 80906ded28 | |||
| 7cb04e65ca | |||
| 8cd625b6f9 | |||
| 152a8cf9da | |||
| bb648b4396 | |||
| ecdca9919e | |||
| 9bf2a013d5 | |||
| 4b2bc909e6 | |||
| b3ed0681a8 | |||
| 0e75f64217 | |||
| 678dc92a66 | |||
| 7f323dd3a5 | |||
| 6ebd36e0b5 | |||
| f7583dbd56 | |||
| d0aa039626 | |||
| 844ad4619e | |||
| a73a12f73b | |||
| 925754f290 | |||
| 3efe02fe37 | |||
| 84b1acc9d5 | |||
| ee144620ef | |||
| f8808e0231 | |||
| 3b02e153ba | |||
| 87a94a72a6 | |||
| de8d356e6a | |||
| 58f5152fe5 | |||
| 5b5bd83d5a | |||
| 90c78f304c | |||
| 6e2a058e6e | |||
| 3c878be568 | |||
| 465f8a5cc9 | |||
| 7dd67bdf0e | |||
| e9e0f82932 | |||
| bb43a0e03c | |||
| 3805ab226b | |||
| f1445cdbdd | |||
| c80f128b0f | |||
| ef78549af0 | |||
| 5b437d6fb1 | |||
| 3a3a3baf93 | |||
| 29efd4c4fb | |||
| 1a9827ec31 | |||
| 4ae5e4ddd0 | |||
| 270eeac084 | |||
| eca130b3af | |||
| 426297812e | |||
| 855ea764b1 | |||
| e53f1aa68d | |||
| 7c06c615d0 | |||
| bc60648458 | |||
| 116527abfe | |||
| a0f58a95db | |||
| c55732a02f | |||
| 25740f7e61 | |||
| 66b6e4475e | |||
| 76a8bc7a33 | |||
| 633f990b36 | |||
| 4511f79609 | |||
| 7ba360a005 | |||
| c4faf39270 | |||
| 1b7dece0a5 | |||
| ddcc20cc47 | |||
| cea3ab291e | |||
| 112a0b2a8e | |||
| 1f4ca22018 | |||
| 954190f6df | |||
| 1307e53833 | |||
| e45d22ad35 | |||
| 48dcca4b7b | |||
| 4e708563eb | |||
| 20b0902768 | |||
| 6a219ab547 | |||
| fd06ce8967 | |||
| 9d46968103 | |||
| a012184dfe | |||
| 972e3550f6 | |||
| 8a83f69e9c | |||
| 98a82c5959 | |||
| ebc7902775 | |||
| 0473276c88 | |||
| 0b30ec7436 | |||
| 84e7efc939 | |||
| 8bdf57d7cb | |||
| d613f59ca8 | |||
| 508decd91b | |||
| ff366fde74 | |||
| 2dce709db1 | |||
| cc99a5ba2a | |||
| e0bf137d70 | |||
| ef4f1a87da | |||
| 8ede1fb351 | |||
| 60ce3685c9 | |||
| a1d06a6fc8 | |||
| 3d5dfc422d | |||
| 52983bc5f1 | |||
| a8a1a0e37c | |||
| e5965f8aec | |||
| 0c8a4c4cf8 | |||
| f1b4bb734d | |||
| 5c3a5ddee3 | |||
| e0e4ba7db3 | |||
| 1f3df107bf | |||
| 6887289466 | |||
| 9ef7a8aa3c | |||
| 900b0049bc | |||
| cadf46ded2 | |||
| b1fd5970d0 | |||
| a4a4b2e6b1 | |||
| d58edf7c38 | |||
| 1f0781f19a | |||
| f53488e8b0 | |||
| 232547a0fb | |||
| f3be326b5e | |||
| 9703d10f03 | |||
| d6ecde96f8 | |||
| 8153052eed | |||
| 7d3a0568a9 | |||
| 4bafdf9408 | |||
| efe3571120 | |||
| bd1fd6f469 | |||
| 69b962b934 | |||
| 1a7117c20d | |||
| c8b0e9e5d2 | |||
| 4b3e361a28 | |||
| 1965e45737 | |||
| a9ecef4f01 | |||
| f11755aad3 | |||
| e42e712e77 | |||
| 84fbd7c4b0 | |||
| 6fb5222358 | |||
| 120a66819c | |||
| d110ef1340 | |||
| 6706fc0af9 | |||
| dc358f9cde | |||
| e285b26b11 | |||
| a62184319f | |||
| add0a0231a | |||
| d00913cb68 | |||
| eef24def61 | |||
| 3813a30def | |||
| 13bde6f2ab | |||
| c38e357652 | |||
| 06d9564b8a | |||
| 0372735a02 | |||
| deb913a95b | |||
| f59dd3e8c7 | |||
| b2ed5c56fc | |||
| 9e29384262 | |||
| 540412d115 | |||
| 694b7afc71 | |||
| cb4a12c5ba | |||
| 733251bf06 | |||
| 42069c5bbb | |||
| 82b4276b33 | |||
| 8f3fc9cb2b | |||
| 89c6358085 | |||
| abb295ecc2 | |||
| 5607aa1cf2 | |||
| 43f1c6fcb1 | |||
| cebf21ec19 | |||
| f4951d09a7 | |||
| 21ec234dd3 | |||
| 43ffad02be | |||
| 4859ea2fb1 | |||
| 86ed8fe9e5 | |||
| d0d709a410 | |||
| 8ecb4372b5 | |||
| cf322c121e | |||
| 1aca716431 | |||
| 83209341ee | |||
| a481e46017 | |||
| 031a0035bd | |||
| 4ca4d2e211 | |||
| 866d4f93cc | |||
| 98eb362628 | |||
| b95f12449e | |||
| e7ab07cec3 | |||
| c61dd606a1 | |||
| 3d029d8f6f | |||
| 9bb18a9df2 | |||
| 823930de40 | |||
| c7201580d6 | |||
| e61856197a | |||
| 37d321bb5c | |||
| 38e16dd07e | |||
| 64bae07877 | |||
| 616a80cf5c | |||
| 18fd22be53 | |||
| c82359f87b | |||
| d354f02507 | |||
| f612ca2fc4 | |||
| 9d0156908c | |||
| 0ee0eb3162 | |||
| 81653cf289 | |||
| 60d68161c4 | |||
| ba5743c057 | |||
| 0ee30532ee | |||
| 093a60050b | |||
| a1605f9a2d | |||
| 6bd5148de8 | |||
| 402e72f728 | |||
| 459ee72426 | |||
| 174054f538 | |||
| 5aba0f025a | |||
| 5b32d39309 | |||
| 8d1adca034 | |||
| 9d55cb6f94 | |||
| 501fafcea6 | |||
| 7b4f7e9295 | |||
| 90fa2abbe8 | |||
| 0532a182bb | |||
| 29bb204e6d | |||
| bf7c49708f | |||
| 9227252976 | |||
| c138f76c8a | |||
| 5f4be987c6 | |||
| 63dc9b2e8e | |||
| f53bd8f11a | |||
| 554809764f | |||
| 084c172e93 | |||
| d86f471361 | |||
| 7fa01b9e3c | |||
| 5ffeecc333 | |||
| 0d88fec4bf | |||
| 06d4e4ac1c | |||
| b3758ae0e7 | |||
| 6a0b7896a4 | |||
| cc0bbf3a17 |
@@ -3,7 +3,7 @@ name: Moose-Docs
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- master-ng
|
||||
- develop
|
||||
paths:
|
||||
- 'Moose Setup/**/*.lua'
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
# Prepare build environment
|
||||
#########################################################################
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Prepare build output folders
|
||||
run: |
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
mkdir -p build/doc
|
||||
|
||||
- name: Checkout FlightControls modified luadocumentor
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: Applevangelist/luadocumentor
|
||||
path: './build/tools/luadocumentor'
|
||||
@@ -77,11 +77,11 @@ jobs:
|
||||
|
||||
- name: Install markdown (prereq for LuaDocumentor)
|
||||
run: |
|
||||
sudo luarocks install markdown 0.32-2
|
||||
sudo luarocks install markdown 0.33-1
|
||||
|
||||
- name: Install penlight (prereq for LuaDocumentor)
|
||||
run: |
|
||||
sudo luarocks install penlight 1.11.0-1
|
||||
sudo luarocks install penlight 1.14.0-3
|
||||
|
||||
- name: Install metalua-compiler (prereq for LuaDocumentor)
|
||||
run: |
|
||||
@@ -113,7 +113,7 @@ jobs:
|
||||
|
||||
- name: Check replacement of head tag
|
||||
run: |
|
||||
head -10 ${{ github.workspace }}/build/doc/AI.AI_A2A_Cap.html
|
||||
head -10 ${{ github.workspace }}/build/doc/Core.Menu.html
|
||||
|
||||
#########################################################################
|
||||
# Push to MOOSE_DOCS
|
||||
@@ -122,19 +122,19 @@ jobs:
|
||||
shell: bash
|
||||
id: set_doc_repo
|
||||
run: |
|
||||
if [[ $GITHUB_REF == 'refs/heads/master' ]]; then
|
||||
if [[ $GITHUB_REF == 'refs/heads/master-ng' ]]; then
|
||||
echo "docrepo=MOOSE_DOCS" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "docrepo=MOOSE_DOCS_DEVELOP" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Checkout ${{ steps.set_doc_repo.outputs.docrepo }} to folder MOOSE_DOCS
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: ${{ github.repository_owner }}/${{ steps.set_doc_repo.outputs.docrepo }}
|
||||
path: './build/MOOSE_DOCS'
|
||||
fetch-depth: 0
|
||||
ref: 'master'
|
||||
ref: 'master-ng'
|
||||
token: ${{ secrets.BOT_TOKEN }}
|
||||
|
||||
- name: Delete folder to remove deleted files
|
||||
@@ -155,7 +155,7 @@ jobs:
|
||||
git config user.email "MooseBotter@users.noreply.github.com"
|
||||
git add .
|
||||
git commit --allow-empty -m "Auto commit by GitHub Actions Workflow"
|
||||
git push --set-upstream origin master
|
||||
git push --set-upstream origin 'master-ng'
|
||||
|
||||
working-directory: ${{ github.workspace }}/build/MOOSE_DOCS
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ name: Moose-Includes
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- master-ng
|
||||
- develop
|
||||
- Apple/Develop
|
||||
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
# Prepare build environment
|
||||
#########################################################################
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Prepare build output folders
|
||||
run: |
|
||||
@@ -105,7 +105,7 @@ jobs:
|
||||
if: ${{ vars.SKIP_LUACHECK != true }}
|
||||
continue-on-error: true
|
||||
run: |
|
||||
luacheck --std=lua51c --config=.luacheckrc -gurasqq "Moose Development/Moose"
|
||||
luacheck --std=lua51c --config=.luacheckrc -guras "Moose Development/Moose"
|
||||
|
||||
- name: Run LuaSrcDiet
|
||||
run: |
|
||||
@@ -115,7 +115,7 @@ jobs:
|
||||
# Push to MOOSE_INCLUDE
|
||||
#########################################################################
|
||||
- name: Checkout MOOSE_INCLUDE
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: ${{ github.repository_owner }}/MOOSE_INCLUDE
|
||||
path: './build/MOOSE_INCLUDE'
|
||||
|
||||
@@ -8,7 +8,7 @@ name: Deploy Jekyll site to Pages
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["master"]
|
||||
branches: ["master-ng"]
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- '.github/workflows/gh-pages.yml'
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
- name: Setup Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
working-directory: docs/
|
||||
- name: Setup Pages
|
||||
id: pages
|
||||
uses: actions/configure-pages@v4
|
||||
uses: actions/configure-pages@v5
|
||||
- name: Build with Jekyll
|
||||
# Outputs to the './_site' directory by default
|
||||
run: bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}"
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
working-directory: docs/
|
||||
- name: Upload artifact
|
||||
# Automatically uploads an artifact from the './_site' directory by default
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
uses: actions/upload-pages-artifact@v4
|
||||
with:
|
||||
path: docs/_site/
|
||||
|
||||
@@ -73,6 +73,6 @@ jobs:
|
||||
needs: deploy
|
||||
steps:
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v5
|
||||
- run: npm install linkinator
|
||||
- run: npx linkinator https://flightcontrol-master.github.io/MOOSE/ --verbosity error --timeout 5000 --recurse --skip "(java.com)" --retry-errors --retry-errors-count 3 --retry-errors-jitter
|
||||
- run: npx linkinator https://flightcontrol-master.github.io/MOOSE/ --verbosity error --timeout 5000 --recurse --skip "(java.com)" --skip 'https:\/\/flightcontrol-master\.github\.io\/MOOSE_DOCS_DEVELOP\/Documentation\/(?:AI|Tasking|Cargo|MissileTrainer)\.[^\/\s]+\.html$' --status-code "404:skip" --retry-errors --retry-errors-count 3 --retry-errors-jitter
|
||||
|
||||
@@ -1,220 +0,0 @@
|
||||
--- **AI** - Models the process of Combat Air Patrol (CAP) for airplanes.
|
||||
--
|
||||
-- This is a class used in the @{AI.AI_A2A_Dispatcher}.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_A2A_Cap
|
||||
-- @image AI_Combat_Air_Patrol.JPG
|
||||
|
||||
-- @type AI_A2A_CAP
|
||||
-- @extends AI.AI_Air_Patrol#AI_AIR_PATROL
|
||||
-- @extends AI.AI_Air_Engage#AI_AIR_ENGAGE
|
||||
|
||||
--- The AI_A2A_CAP class implements the core functions to patrol a @{Core.Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group}
|
||||
-- and automatically engage any airborne enemies that are within a certain range or within a certain zone.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The AI_A2A_CAP is assigned a @{Wrapper.Group} and this must be done before the AI_A2A_CAP process can be started using the **Start** event.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits.
|
||||
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- This cycle will continue.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- When enemies are detected, the AI will automatically engage the enemy.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ## 1. AI_A2A_CAP constructor
|
||||
--
|
||||
-- * @{#AI_A2A_CAP.New}(): Creates a new AI_A2A_CAP object.
|
||||
--
|
||||
-- ## 2. AI_A2A_CAP is a FSM
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ### 2.1 AI_A2A_CAP States
|
||||
--
|
||||
-- * **None** ( Group ): The process is not started yet.
|
||||
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
|
||||
-- * **Engaging** ( Group ): The AI is engaging the bogeys.
|
||||
-- * **Returning** ( Group ): The AI is returning to Base..
|
||||
--
|
||||
-- ### 2.2 AI_A2A_CAP Events
|
||||
--
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone.
|
||||
-- * **@{#AI_A2A_CAP.Engage}**: Let the AI engage the bogeys.
|
||||
-- * **@{#AI_A2A_CAP.Abort}**: Aborts the engagement and return patrolling in the patrol zone.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
|
||||
-- * **@{#AI_A2A_CAP.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
|
||||
-- * **@{#AI_A2A_CAP.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task.
|
||||
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
|
||||
--
|
||||
-- ## 3. Set the Range of Engagement
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- An optional range can be set in meters,
|
||||
-- that will define when the AI will engage with the detected airborne enemy targets.
|
||||
-- The range can be beyond or smaller than the range of the Patrol Zone.
|
||||
-- The range is applied at the position of the AI.
|
||||
-- Use the method @{#AI_A2A_CAP.SetEngageRange}() to define that range.
|
||||
--
|
||||
-- ## 4. Set the Zone of Engagement
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- An optional @{Core.Zone} can be set,
|
||||
-- that will define when the AI will engage with the detected airborne enemy targets.
|
||||
-- Use the method @{#AI_A2A_CAP.SetEngageZone}() to define that Zone.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_A2A_CAP
|
||||
AI_A2A_CAP = {
|
||||
ClassName = "AI_A2A_CAP",
|
||||
}
|
||||
|
||||
--- Creates a new AI_A2A_CAP object
|
||||
-- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Group#GROUP AICap
|
||||
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
|
||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
|
||||
-- @return #AI_A2A_CAP
|
||||
function AI_A2A_CAP:New2( AICap, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType, PatrolZone, PatrolMinSpeed, PatrolMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolAltType )
|
||||
|
||||
-- Multiple inheritance ... :-)
|
||||
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 ) --#AI_A2A_CAP
|
||||
|
||||
self:SetFuelThreshold( .2, 60 )
|
||||
self:SetDamageThreshold( 0.4 )
|
||||
self:SetDisengageRadius( 70000 )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Creates a new AI_A2A_CAP object
|
||||
-- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Group#GROUP AICap
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
|
||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
|
||||
-- @return #AI_A2A_CAP
|
||||
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
|
||||
|
||||
--- onafter State Transition for Event Patrol.
|
||||
-- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Group#GROUP AICap The AI Group managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
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
|
||||
|
||||
--- Set the Engage Zone which defines where the AI will engage bogies.
|
||||
-- @param #AI_A2A_CAP self
|
||||
-- @param Core.Zone#ZONE EngageZone The zone where the AI is performing CAP.
|
||||
-- @return #AI_A2A_CAP self
|
||||
function AI_A2A_CAP:SetEngageZone( EngageZone )
|
||||
self:F2()
|
||||
|
||||
if EngageZone then
|
||||
self.EngageZone = EngageZone
|
||||
else
|
||||
self.EngageZone = nil
|
||||
end
|
||||
end
|
||||
|
||||
--- Set the Engage Range when the AI will engage with airborne enemies.
|
||||
-- @param #AI_A2A_CAP self
|
||||
-- @param #number EngageRange The Engage Range.
|
||||
-- @return #AI_A2A_CAP self
|
||||
function AI_A2A_CAP:SetEngageRange( EngageRange )
|
||||
self:F2()
|
||||
|
||||
if EngageRange then
|
||||
self.EngageRange = EngageRange
|
||||
else
|
||||
self.EngageRange = nil
|
||||
end
|
||||
end
|
||||
|
||||
--- Evaluate the attack and create an AttackUnitTask list.
|
||||
-- @param #AI_A2A_CAP self
|
||||
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
|
||||
-- @param Wrapper.Group#GROUP DefenderGroup The group of defenders.
|
||||
-- @param #number EngageAltitude The altitude to engage the targets.
|
||||
-- @return #AI_A2A_CAP self
|
||||
function AI_A2A_CAP:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude )
|
||||
|
||||
local AttackUnitTasks = {}
|
||||
|
||||
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do
|
||||
local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT
|
||||
if AttackUnit and AttackUnit:IsAlive() and AttackUnit:IsAir() then
|
||||
-- TODO: Add coalition check? Only attack units of if AttackUnit:GetCoalition()~=AICap:GetCoalition()
|
||||
-- Maybe the detected set also contains
|
||||
self:T( { "Attacking Task:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } )
|
||||
AttackUnitTasks[#AttackUnitTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit )
|
||||
end
|
||||
end
|
||||
|
||||
return AttackUnitTasks
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,149 +0,0 @@
|
||||
--- **AI** - Models the process of Ground Controlled Interception (GCI) for airplanes.
|
||||
--
|
||||
-- This is a class used in the @{AI.AI_A2A_Dispatcher}.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_A2A_Gci
|
||||
-- @image AI_Ground_Control_Intercept.JPG
|
||||
|
||||
|
||||
|
||||
-- @type AI_A2A_GCI
|
||||
-- @extends AI.AI_A2A#AI_A2A
|
||||
|
||||
|
||||
--- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The AI_A2A_GCI is assigned a @{Wrapper.Group} and this must be done before the AI_A2A_GCI process can be started using the **Start** event.
|
||||
--
|
||||
-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits.
|
||||
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
|
||||
--
|
||||
-- This cycle will continue.
|
||||
--
|
||||
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
|
||||
--
|
||||
-- When enemies are detected, the AI will automatically engage the enemy.
|
||||
--
|
||||
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||
--
|
||||
-- ## 1. AI_A2A_GCI constructor
|
||||
--
|
||||
-- * @{#AI_A2A_GCI.New}(): Creates a new AI_A2A_GCI object.
|
||||
--
|
||||
-- ## 2. AI_A2A_GCI is a FSM
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ### 2.1 AI_A2A_GCI States
|
||||
--
|
||||
-- * **None** ( Group ): The process is not started yet.
|
||||
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
|
||||
-- * **Engaging** ( Group ): The AI is engaging the bogeys.
|
||||
-- * **Returning** ( Group ): The AI is returning to Base..
|
||||
--
|
||||
-- ### 2.2 AI_A2A_GCI Events
|
||||
--
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone.
|
||||
-- * **@{#AI_A2A_GCI.Engage}**: Let the AI engage the bogeys.
|
||||
-- * **@{#AI_A2A_GCI.Abort}**: Aborts the engagement and return patrolling in the patrol zone.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
|
||||
-- * **@{#AI_A2A_GCI.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
|
||||
-- * **@{#AI_A2A_GCI.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task.
|
||||
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_A2A_GCI
|
||||
AI_A2A_GCI = {
|
||||
ClassName = "AI_A2A_GCI",
|
||||
}
|
||||
|
||||
|
||||
|
||||
--- Creates a new AI_A2A_GCI object
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIIntercept
|
||||
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
|
||||
-- @return #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 ) -- #AI_A2A_GCI
|
||||
|
||||
self:SetFuelThreshold( .2, 60 )
|
||||
self:SetDamageThreshold( 0.4 )
|
||||
self:SetDisengageRadius( 70000 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Creates a new AI_A2A_GCI object
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIIntercept
|
||||
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
|
||||
-- @return #AI_A2A_GCI
|
||||
function AI_A2A_GCI:New( AIIntercept, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
|
||||
|
||||
return self:New2( AIIntercept, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
|
||||
end
|
||||
|
||||
--- onafter State Transition for Event Patrol.
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIIntercept The AI Group managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_A2A_GCI:onafterStart( AIIntercept, From, Event, To )
|
||||
|
||||
self:GetParent( self, AI_A2A_GCI ).onafterStart( self, AIIntercept, From, Event, To )
|
||||
end
|
||||
|
||||
|
||||
--- Evaluate the attack and create an AttackUnitTask list.
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
|
||||
-- @param Wrapper.Group#GROUP DefenderGroup The group of defenders.
|
||||
-- @param #number EngageAltitude The altitude to engage the targets.
|
||||
-- @return #AI_A2A_GCI self
|
||||
function AI_A2A_GCI:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude )
|
||||
|
||||
local AttackUnitTasks = {}
|
||||
|
||||
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do
|
||||
local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT
|
||||
self:T( { "Attacking Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } )
|
||||
if AttackUnit:IsAlive() and AttackUnit:IsAir() then
|
||||
-- TODO: Add coalition check? Only attack units of if AttackUnit:GetCoalition()~=AICap:GetCoalition()
|
||||
-- Maybe the detected set also contains
|
||||
AttackUnitTasks[#AttackUnitTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit )
|
||||
end
|
||||
end
|
||||
|
||||
return AttackUnitTasks
|
||||
end
|
||||
@@ -1,411 +0,0 @@
|
||||
--- **AI** - Models the process of air patrol of airplanes.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_A2A_Patrol
|
||||
-- @image AI_Air_Patrolling.JPG
|
||||
|
||||
|
||||
-- @type AI_A2A_PATROL
|
||||
-- @extends AI.AI_A2A#AI_A2A
|
||||
|
||||
--- Implements the core functions to patrol a @{Core.Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group}.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The AI_A2A_PATROL is assigned a @{Wrapper.Group} and this must be done before the AI_A2A_PATROL process can be started using the **Start** event.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits.
|
||||
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- This cycle will continue.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
---- Note that the enemy is not engaged! To model enemy engagement, either tailor the **Detected** event, or
|
||||
-- use derived AI_ classes to model AI offensive or defensive behaviour.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ## 1. AI_A2A_PATROL constructor
|
||||
--
|
||||
-- * @{#AI_A2A_PATROL.New}(): Creates a new AI_A2A_PATROL object.
|
||||
--
|
||||
-- ## 2. AI_A2A_PATROL is a FSM
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ### 2.1. AI_A2A_PATROL States
|
||||
--
|
||||
-- * **None** ( Group ): The process is not started yet.
|
||||
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
|
||||
-- * **Returning** ( Group ): The AI is returning to Base.
|
||||
-- * **Stopped** ( Group ): The process is stopped.
|
||||
-- * **Crashed** ( Group ): The AI has crashed or is dead.
|
||||
--
|
||||
-- ### 2.2. AI_A2A_PATROL Events
|
||||
--
|
||||
-- * **Start** ( Group ): Start the process.
|
||||
-- * **Stop** ( Group ): Stop the process.
|
||||
-- * **Route** ( Group ): Route the AI to a new random 3D point within the Patrol Zone.
|
||||
-- * **RTB** ( Group ): Route the AI to the home base.
|
||||
-- * **Detect** ( Group ): The AI is detecting targets.
|
||||
-- * **Detected** ( Group ): The AI has detected new targets.
|
||||
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
|
||||
--
|
||||
-- ## 3. Set or Get the AI controllable
|
||||
--
|
||||
-- * @{#AI_A2A_PATROL.SetControllable}(): Set the AIControllable.
|
||||
-- * @{#AI_A2A_PATROL.GetControllable}(): Get the AIControllable.
|
||||
--
|
||||
-- ## 4. Set the Speed and Altitude boundaries of the AI controllable
|
||||
--
|
||||
-- * @{#AI_A2A_PATROL.SetSpeed}(): Set the patrol speed boundaries of the AI, for the next patrol.
|
||||
-- * @{#AI_A2A_PATROL.SetAltitude}(): Set altitude boundaries of the AI, for the next patrol.
|
||||
--
|
||||
-- ## 5. Manage the detection process of the AI controllable
|
||||
--
|
||||
-- The detection process of the AI controllable can be manipulated.
|
||||
-- Detection requires an amount of CPU power, which has an impact on your mission performance.
|
||||
-- Only put detection on when absolutely necessary, and the frequency of the detection can also be set.
|
||||
--
|
||||
-- * @{#AI_A2A_PATROL.SetDetectionOn}(): Set the detection on. The AI will detect for targets.
|
||||
-- * @{#AI_A2A_PATROL.SetDetectionOff}(): Set the detection off, the AI will not detect for targets. The existing target list will NOT be erased.
|
||||
--
|
||||
-- The detection frequency can be set with @{#AI_A2A_PATROL.SetRefreshTimeInterval}( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection.
|
||||
-- Use the method @{#AI_A2A_PATROL.GetDetectedUnits}() to obtain a list of the @{Wrapper.Unit}s detected by the AI.
|
||||
--
|
||||
-- The detection can be filtered to potential targets in a specific zone.
|
||||
-- Use the method @{#AI_A2A_PATROL.SetDetectionZone}() to set the zone where targets need to be detected.
|
||||
-- Note that when the zone is too far away, or the AI is not heading towards the zone, or the AI is too high, no targets may be detected
|
||||
-- according the weather conditions.
|
||||
--
|
||||
-- ## 6. Manage the "out of fuel" in the AI_A2A_PATROL
|
||||
--
|
||||
-- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
|
||||
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
|
||||
-- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit,
|
||||
-- while a new AI is targeted to the AI_A2A_PATROL.
|
||||
-- Once the time is finished, the old AI will return to the base.
|
||||
-- Use the method @{#AI_A2A_PATROL.ManageFuel}() to have this proces in place.
|
||||
--
|
||||
-- ## 7. Manage "damage" behaviour of the AI in the AI_A2A_PATROL
|
||||
--
|
||||
-- When the AI is damaged, it is required that a new Patrol is started. However, damage cannon be foreseen early on.
|
||||
-- Therefore, when the damage threshold is reached, the AI will return immediately to the home base (RTB).
|
||||
-- Use the method @{#AI_A2A_PATROL.ManageDamage}() to have this proces in place.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_A2A_PATROL
|
||||
AI_A2A_PATROL = {
|
||||
ClassName = "AI_A2A_PATROL",
|
||||
}
|
||||
|
||||
--- Creates a new AI_A2A_PATROL object
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The patrol group object.
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
|
||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to BARO
|
||||
-- @return #AI_A2A_PATROL self
|
||||
-- @usage
|
||||
-- -- Define a new AI_A2A_PATROL Object. This PatrolArea will patrol a Group within PatrolZone between 3000 and 6000 meters, with a variying speed between 600 and 900 km/h.
|
||||
-- PatrolZone = ZONE:New( 'PatrolZone' )
|
||||
-- PatrolSpawn = SPAWN:New( 'Patrol Group' )
|
||||
-- PatrolArea = AI_A2A_PATROL:New( PatrolZone, 3000, 6000, 600, 900 )
|
||||
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 ) -- #AI_A2A_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
|
||||
|
||||
-- defafult PatrolAltType to "BARO" if not specified
|
||||
self.PatrolAltType = PatrolAltType or "BARO"
|
||||
|
||||
self:AddTransition( { "Started", "Airborne", "Refuelling" }, "Patrol", "Patrolling" )
|
||||
|
||||
--- OnBefore Transition Handler for Event Patrol.
|
||||
-- @function [parent=#AI_A2A_PATROL] OnBeforePatrol
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Patrol.
|
||||
-- @function [parent=#AI_A2A_PATROL] OnAfterPatrol
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Patrol.
|
||||
-- @function [parent=#AI_A2A_PATROL] Patrol
|
||||
-- @param #AI_A2A_PATROL self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Patrol.
|
||||
-- @function [parent=#AI_A2A_PATROL] __Patrol
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
--- OnLeave Transition Handler for State Patrolling.
|
||||
-- @function [parent=#AI_A2A_PATROL] OnLeavePatrolling
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnEnter Transition Handler for State Patrolling.
|
||||
-- @function [parent=#AI_A2A_PATROL] OnEnterPatrolling
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
self:AddTransition( "Patrolling", "Route", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_PATROL.
|
||||
|
||||
--- OnBefore Transition Handler for Event Route.
|
||||
-- @function [parent=#AI_A2A_PATROL] OnBeforeRoute
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Route.
|
||||
-- @function [parent=#AI_A2A_PATROL] OnAfterRoute
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Route.
|
||||
-- @function [parent=#AI_A2A_PATROL] Route
|
||||
-- @param #AI_A2A_PATROL self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Route.
|
||||
-- @function [parent=#AI_A2A_PATROL] __Route
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
|
||||
|
||||
self:AddTransition( "*", "Reset", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_PATROL.
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- Sets (modifies) the minimum and maximum speed of the patrol.
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @return #AI_A2A_PATROL self
|
||||
function AI_A2A_PATROL:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed )
|
||||
self:F2( { PatrolMinSpeed, PatrolMaxSpeed } )
|
||||
|
||||
self.PatrolMinSpeed = PatrolMinSpeed
|
||||
self.PatrolMaxSpeed = PatrolMaxSpeed
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Sets the floor and ceiling altitude of the patrol.
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @return #AI_A2A_PATROL self
|
||||
function AI_A2A_PATROL:SetAltitude( PatrolFloorAltitude, PatrolCeilingAltitude )
|
||||
self:F2( { PatrolFloorAltitude, PatrolCeilingAltitude } )
|
||||
|
||||
self.PatrolFloorAltitude = PatrolFloorAltitude
|
||||
self.PatrolCeilingAltitude = PatrolCeilingAltitude
|
||||
end
|
||||
|
||||
|
||||
--- Defines a new patrol route using the @{AI.AI_Patrol#AI_PATROL_ZONE} parameters and settings.
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @return #AI_A2A_PATROL self
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
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
|
||||
|
||||
|
||||
--- This static method is called from the route path within the last task at the last waypoint of the AIPatrol.
|
||||
-- Note that this method is required, as triggers the next route when patrolling for the AIPatrol.
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The AI group.
|
||||
-- @param #AI_A2A_PATROL Fsm The FSM.
|
||||
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
|
||||
|
||||
|
||||
--- Defines a new patrol route using the @{AI.AI_Patrol#AI_PATROL_ZONE} parameters and settings.
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The Group managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_A2A_PATROL:onafterRoute( AIPatrol, From, Event, To )
|
||||
self:F2()
|
||||
|
||||
-- When RTB, don't allow anymore the routing.
|
||||
if From == "RTB" then
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
if AIPatrol and AIPatrol:IsAlive() then
|
||||
|
||||
local PatrolRoute = {}
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
local CurrentCoord = AIPatrol:GetCoordinate()
|
||||
|
||||
-- Random altitude.
|
||||
local altitude=math.random(self.PatrolFloorAltitude, self.PatrolCeilingAltitude)
|
||||
|
||||
-- Random speed in km/h.
|
||||
local speedkmh = math.random(self.PatrolMinSpeed, self.PatrolMaxSpeed)
|
||||
|
||||
-- First waypoint is current position.
|
||||
PatrolRoute[1]=CurrentCoord:WaypointAirTurningPoint(nil, speedkmh, {}, "Current")
|
||||
|
||||
if self.racetrack then
|
||||
|
||||
-- Random heading.
|
||||
local heading = math.random(self.racetrackheadingmin, self.racetrackheadingmax)
|
||||
|
||||
-- Random leg length.
|
||||
local leg=math.random(self.racetracklegmin, self.racetracklegmax)
|
||||
|
||||
-- Random duration if any.
|
||||
local duration = self.racetrackdurationmin
|
||||
if self.racetrackdurationmax then
|
||||
duration=math.random(self.racetrackdurationmin, self.racetrackdurationmax)
|
||||
end
|
||||
|
||||
-- CAP coordinate.
|
||||
local c0=self.PatrolZone:GetRandomCoordinate()
|
||||
if self.racetrackcapcoordinates and #self.racetrackcapcoordinates>0 then
|
||||
c0=self.racetrackcapcoordinates[math.random(#self.racetrackcapcoordinates)]
|
||||
end
|
||||
|
||||
-- Race track points.
|
||||
local c1=c0:SetAltitude(altitude) --Core.Point#COORDINATE
|
||||
local c2=c1:Translate(leg, heading):SetAltitude(altitude)
|
||||
|
||||
self:SetTargetDistance(c0) -- For RTB status check
|
||||
|
||||
-- Debug:
|
||||
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)))
|
||||
--c1:MarkToAll("Race track c1")
|
||||
--c2:MarkToAll("Race track c2")
|
||||
|
||||
-- Task to orbit.
|
||||
local taskOrbit=AIPatrol:TaskOrbit(c1, altitude, UTILS.KmphToMps(speedkmh), c2)
|
||||
|
||||
-- Task function to redo the patrol at other random position.
|
||||
local taskPatrol=AIPatrol:TaskFunction("AI_A2A_PATROL.PatrolRoute", self)
|
||||
|
||||
-- Controlled task with task condition.
|
||||
local taskCond=AIPatrol:TaskCondition(nil, nil, nil, nil, duration, nil)
|
||||
local taskCont=AIPatrol:TaskControlled(taskOrbit, taskCond)
|
||||
|
||||
-- Second waypoint
|
||||
PatrolRoute[2]=c1:WaypointAirTurningPoint(self.PatrolAltType, speedkmh, {taskCont, taskPatrol}, "CAP Orbit")
|
||||
|
||||
else
|
||||
|
||||
-- Target coordinate.
|
||||
local ToTargetCoord=self.PatrolZone:GetRandomCoordinate() --Core.Point#COORDINATE
|
||||
ToTargetCoord:SetAltitude(altitude)
|
||||
|
||||
self:SetTargetDistance( ToTargetCoord ) -- For RTB status check
|
||||
|
||||
local taskReRoute=AIPatrol:TaskFunction( "AI_A2A_PATROL.PatrolRoute", self )
|
||||
|
||||
PatrolRoute[2]=ToTargetCoord:WaypointAirTurningPoint(self.PatrolAltType, speedkmh, {taskReRoute}, "Patrol Point")
|
||||
|
||||
end
|
||||
|
||||
-- ROE
|
||||
AIPatrol:OptionROEReturnFire()
|
||||
AIPatrol:OptionROTEvadeFire()
|
||||
|
||||
-- Patrol.
|
||||
AIPatrol:Route( PatrolRoute, 0.5)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
--- **AI** - Models the process of air to ground BAI engagement for airplanes and helicopters.
|
||||
--
|
||||
-- This is a class used in the @{AI.AI_A2G_Dispatcher}.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_A2G_BAI
|
||||
-- @image AI_Air_To_Ground_Engage.JPG
|
||||
|
||||
-- @type AI_A2G_BAI
|
||||
-- @extends AI.AI_A2A_Engage#AI_A2A_Engage -- TODO: Documentation. This class does not exist, unable to determine what it extends.
|
||||
|
||||
--- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_A2G_BAI
|
||||
AI_A2G_BAI = {
|
||||
ClassName = "AI_A2G_BAI",
|
||||
}
|
||||
|
||||
--- Creates a new AI_A2G_BAI object
|
||||
-- @param #AI_A2G_BAI self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
|
||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
|
||||
-- @return #AI_A2G_BAI
|
||||
function AI_A2G_BAI:New2( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
|
||||
|
||||
local AI_Air = AI_AIR:New( AIGroup )
|
||||
local AI_Air_Patrol = AI_AIR_PATROL:New( AI_Air, AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) -- #AI_AIR_PATROL
|
||||
local AI_Air_Engage = AI_AIR_ENGAGE:New( AI_Air_Patrol, AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
|
||||
local self = BASE:Inherit( self, AI_Air_Engage )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Creates a new AI_A2G_BAI object
|
||||
-- @param #AI_A2G_BAI self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
|
||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
|
||||
-- @return #AI_A2G_BAI
|
||||
function AI_A2G_BAI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
|
||||
|
||||
return self:New2( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, PatrolAltType, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType)
|
||||
end
|
||||
|
||||
--- Evaluate the attack and create an AttackUnitTask list.
|
||||
-- @param #AI_A2G_BAI self
|
||||
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
|
||||
-- @param Wrapper.Group#GROUP DefenderGroup The group of defenders.
|
||||
-- @param #number EngageAltitude The altitude to engage the targets.
|
||||
-- @return #AI_A2G_BAI self
|
||||
function AI_A2G_BAI:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude )
|
||||
|
||||
local AttackUnitTasks = {}
|
||||
|
||||
local AttackSetUnitPerThreatLevel = AttackSetUnit:GetSetPerThreatLevel( 10, 0 )
|
||||
for AttackUnitIndex, AttackUnit in ipairs( AttackSetUnitPerThreatLevel or {} ) do
|
||||
if AttackUnit then
|
||||
if AttackUnit:IsAlive() and AttackUnit:IsGround() then
|
||||
self:T( { "BAI Unit:", AttackUnit:GetName() } )
|
||||
AttackUnitTasks[#AttackUnitTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit, true, false, nil, nil, EngageAltitude )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return AttackUnitTasks
|
||||
end
|
||||
@@ -1,98 +0,0 @@
|
||||
--- **AI** - Models the process of air to ground engagement for airplanes and helicopters.
|
||||
--
|
||||
-- This is a class used in the @{AI.AI_A2G_Dispatcher}.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_A2G_CAS
|
||||
-- @image AI_Air_To_Ground_Engage.JPG
|
||||
|
||||
-- @type AI_A2G_CAS
|
||||
-- @extends AI.AI_A2G_Patrol#AI_AIR_PATROL TODO: Documentation. This class does not exist, unable to determine what it extends.
|
||||
|
||||
--- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_A2G_CAS
|
||||
AI_A2G_CAS = {
|
||||
ClassName = "AI_A2G_CAS",
|
||||
}
|
||||
|
||||
--- Creates a new AI_A2G_CAS object
|
||||
-- @param #AI_A2G_CAS self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
|
||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
|
||||
-- @return #AI_A2G_CAS
|
||||
function AI_A2G_CAS:New2( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
|
||||
|
||||
local AI_Air = AI_AIR:New( AIGroup )
|
||||
local AI_Air_Patrol = AI_AIR_PATROL:New( AI_Air, AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) -- #AI_AIR_PATROL
|
||||
local AI_Air_Engage = AI_AIR_ENGAGE:New( AI_Air_Patrol, AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
|
||||
local self = BASE:Inherit( self, AI_Air_Engage )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Creates a new AI_A2G_CAS object
|
||||
-- @param #AI_A2G_CAS self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
|
||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
|
||||
-- @return #AI_A2G_CAS
|
||||
function AI_A2G_CAS:New( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
|
||||
|
||||
return self:New2( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, PatrolAltType, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType)
|
||||
end
|
||||
|
||||
--- Evaluate the attack and create an AttackUnitTask list.
|
||||
-- @param #AI_A2G_CAS self
|
||||
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
|
||||
-- @param Wrapper.Group#GROUP DefenderGroup The group of defenders.
|
||||
-- @param #number EngageAltitude The altitude to engage the targets.
|
||||
-- @return #AI_A2G_CAS self
|
||||
function AI_A2G_CAS:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude )
|
||||
|
||||
local AttackUnitTasks = {}
|
||||
|
||||
local AttackSetUnitPerThreatLevel = AttackSetUnit:GetSetPerThreatLevel( 10, 0 )
|
||||
for AttackUnitIndex, AttackUnit in ipairs( AttackSetUnitPerThreatLevel or {} ) do
|
||||
if AttackUnit then
|
||||
if AttackUnit:IsAlive() and AttackUnit:IsGround() then
|
||||
self:T( { "CAS Unit:", AttackUnit:GetName() } )
|
||||
AttackUnitTasks[#AttackUnitTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit, true, false, nil, nil, EngageAltitude )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return AttackUnitTasks
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,133 +0,0 @@
|
||||
--- **AI** - Models the process of air to ground SEAD engagement for airplanes and helicopters.
|
||||
--
|
||||
-- This is a class used in the @{AI.AI_A2G_Dispatcher}.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_A2G_SEAD
|
||||
-- @image AI_Air_To_Ground_Engage.JPG
|
||||
|
||||
|
||||
|
||||
-- @type AI_A2G_SEAD
|
||||
-- @extends AI.AI_A2G_Patrol#AI_AIR_PATROL
|
||||
|
||||
|
||||
--- Implements the core functions to SEAD intruders. Use the Engage trigger to intercept intruders.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The AI_A2G_SEAD is assigned a @{Wrapper.Group} and this must be done before the AI_A2G_SEAD process can be started using the **Start** event.
|
||||
--
|
||||
-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits.
|
||||
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
|
||||
--
|
||||
-- This cycle will continue.
|
||||
--
|
||||
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
|
||||
--
|
||||
-- When enemies are detected, the AI will automatically engage the enemy.
|
||||
--
|
||||
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||
--
|
||||
-- ## 1. AI_A2G_SEAD constructor
|
||||
--
|
||||
-- * @{#AI_A2G_SEAD.New}(): Creates a new AI_A2G_SEAD object.
|
||||
--
|
||||
-- ## 3. Set the Range of Engagement
|
||||
--
|
||||
-- An optional range can be set in meters,
|
||||
-- that will define when the AI will engage with the detected airborne enemy targets.
|
||||
-- The range can be beyond or smaller than the range of the Patrol Zone.
|
||||
-- The range is applied at the position of the AI.
|
||||
-- Use the method @{#AI_AIR_PATROL.SetEngageRange}() to define that range.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_A2G_SEAD
|
||||
AI_A2G_SEAD = {
|
||||
ClassName = "AI_A2G_SEAD",
|
||||
}
|
||||
|
||||
--- Creates a new AI_A2G_SEAD object
|
||||
-- @param #AI_A2G_SEAD self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
|
||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
|
||||
-- @return #AI_A2G_SEAD
|
||||
function AI_A2G_SEAD:New2( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
|
||||
|
||||
local AI_Air = AI_AIR:New( AIGroup )
|
||||
local AI_Air_Patrol = AI_AIR_PATROL:New( AI_Air, AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
|
||||
local AI_Air_Engage = AI_AIR_ENGAGE:New( AI_Air_Patrol, AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
|
||||
local self = BASE:Inherit( self, AI_Air_Engage )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Creates a new AI_A2G_SEAD object
|
||||
-- @param #AI_A2G_SEAD self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
|
||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
|
||||
-- @return #AI_A2G_SEAD
|
||||
function AI_A2G_SEAD:New( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
|
||||
|
||||
return self:New2( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, PatrolAltType, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
|
||||
end
|
||||
|
||||
|
||||
--- Evaluate the attack and create an AttackUnitTask list.
|
||||
-- @param #AI_A2G_SEAD self
|
||||
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
|
||||
-- @param Wrapper.Group#GROUP DefenderGroup The group of defenders.
|
||||
-- @param #number EngageAltitude The altitude to engage the targets.
|
||||
-- @return #AI_A2G_SEAD self
|
||||
function AI_A2G_SEAD:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude )
|
||||
|
||||
local AttackUnitTasks = {}
|
||||
|
||||
local AttackSetUnitPerThreatLevel = AttackSetUnit:GetSetPerThreatLevel( 10, 0 )
|
||||
for AttackUnitID, AttackUnit in ipairs( AttackSetUnitPerThreatLevel ) do
|
||||
if AttackUnit then
|
||||
if AttackUnit:IsAlive() and AttackUnit:IsGround() then
|
||||
local HasRadar = AttackUnit:HasSEAD()
|
||||
if HasRadar then
|
||||
self:F( { "SEAD Unit:", AttackUnit:GetName() } )
|
||||
AttackUnitTasks[#AttackUnitTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit, true, false, nil, nil, EngageAltitude )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return AttackUnitTasks
|
||||
end
|
||||
|
||||
@@ -1,841 +0,0 @@
|
||||
--- **AI** - Models the process of AI air operations.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Air
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
---
|
||||
-- @type AI_AIR
|
||||
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
||||
|
||||
--- The AI_AIR class implements the core functions to operate an AI @{Wrapper.Group}.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- # 1) AI_AIR constructor
|
||||
--
|
||||
-- * @{#AI_AIR.New}(): Creates a new AI_AIR object.
|
||||
--
|
||||
-- # 2) AI_AIR is a Finite State Machine.
|
||||
--
|
||||
-- This section must be read as follows. Each of the rows indicate a state transition, triggered through an event, and with an ending state of the event was executed.
|
||||
-- The first column is the **From** state, the second column the **Event**, and the third column the **To** state.
|
||||
--
|
||||
-- So, each of the rows have the following structure.
|
||||
--
|
||||
-- * **From** => **Event** => **To**
|
||||
--
|
||||
-- Important to know is that an event can only be executed if the **current state** is the **From** state.
|
||||
-- This, when an **Event** that is being triggered has a **From** state that is equal to the **Current** state of the state machine, the event will be executed,
|
||||
-- and the resulting state will be the **To** state.
|
||||
--
|
||||
-- These are the different possible state transitions of this state machine implementation:
|
||||
--
|
||||
-- * Idle => Start => Monitoring
|
||||
--
|
||||
-- ## 2.1) AI_AIR States.
|
||||
--
|
||||
-- * **Idle**: The process is idle.
|
||||
--
|
||||
-- ## 2.2) AI_AIR Events.
|
||||
--
|
||||
-- * **Start**: Start the transport process.
|
||||
-- * **Stop**: Stop the transport process.
|
||||
-- * **Monitor**: Monitor and take action.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- @field #AI_AIR
|
||||
AI_AIR = {
|
||||
ClassName = "AI_AIR",
|
||||
}
|
||||
|
||||
AI_AIR.TaskDelay = 0.5 -- The delay of each task given to the AI.
|
||||
|
||||
--- Creates a new AI_AIR process.
|
||||
-- @param #AI_AIR self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The group object to receive the A2G Process.
|
||||
-- @return #AI_AIR
|
||||
function AI_AIR:New( AIGroup )
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_AIR
|
||||
|
||||
self:SetControllable( AIGroup )
|
||||
|
||||
self:SetStartState( "Stopped" )
|
||||
|
||||
self:AddTransition( "*", "Queue", "Queued" )
|
||||
|
||||
self:AddTransition( "*", "Start", "Started" )
|
||||
|
||||
--- Start Handler OnBefore for AI_AIR
|
||||
-- @function [parent=#AI_AIR] OnBeforeStart
|
||||
-- @param #AI_AIR self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Start Handler OnAfter for AI_AIR
|
||||
-- @function [parent=#AI_AIR] OnAfterStart
|
||||
-- @param #AI_AIR self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- Start Trigger for AI_AIR
|
||||
-- @function [parent=#AI_AIR] Start
|
||||
-- @param #AI_AIR self
|
||||
|
||||
--- Start Asynchronous Trigger for AI_AIR
|
||||
-- @function [parent=#AI_AIR] __Start
|
||||
-- @param #AI_AIR self
|
||||
-- @param #number Delay
|
||||
|
||||
self:AddTransition( "*", "Stop", "Stopped" )
|
||||
|
||||
--- OnLeave Transition Handler for State Stopped.
|
||||
-- @function [parent=#AI_AIR] OnLeaveStopped
|
||||
-- @param #AI_AIR self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnEnter Transition Handler for State Stopped.
|
||||
-- @function [parent=#AI_AIR] OnEnterStopped
|
||||
-- @param #AI_AIR self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- OnBefore Transition Handler for Event Stop.
|
||||
-- @function [parent=#AI_AIR] OnBeforeStop
|
||||
-- @param #AI_AIR self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Stop.
|
||||
-- @function [parent=#AI_AIR] OnAfterStop
|
||||
-- @param #AI_AIR self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Stop.
|
||||
-- @function [parent=#AI_AIR] Stop
|
||||
-- @param #AI_AIR self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Stop.
|
||||
-- @function [parent=#AI_AIR] __Stop
|
||||
-- @param #AI_AIR self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
self:AddTransition( "*", "Status", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR.
|
||||
|
||||
--- OnBefore Transition Handler for Event Status.
|
||||
-- @function [parent=#AI_AIR] OnBeforeStatus
|
||||
-- @param #AI_AIR self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Status.
|
||||
-- @function [parent=#AI_AIR] OnAfterStatus
|
||||
-- @param #AI_AIR self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Status.
|
||||
-- @function [parent=#AI_AIR] Status
|
||||
-- @param #AI_AIR self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Status.
|
||||
-- @function [parent=#AI_AIR] __Status
|
||||
-- @param #AI_AIR self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
self:AddTransition( "*", "RTB", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR.
|
||||
|
||||
--- OnBefore Transition Handler for Event RTB.
|
||||
-- @function [parent=#AI_AIR] OnBeforeRTB
|
||||
-- @param #AI_AIR self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event RTB.
|
||||
-- @function [parent=#AI_AIR] OnAfterRTB
|
||||
-- @param #AI_AIR self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event RTB.
|
||||
-- @function [parent=#AI_AIR] RTB
|
||||
-- @param #AI_AIR self
|
||||
|
||||
--- Asynchronous Event Trigger for Event RTB.
|
||||
-- @function [parent=#AI_AIR] __RTB
|
||||
-- @param #AI_AIR self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
--- OnLeave Transition Handler for State Returning.
|
||||
-- @function [parent=#AI_AIR] OnLeaveReturning
|
||||
-- @param #AI_AIR self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnEnter Transition Handler for State Returning.
|
||||
-- @function [parent=#AI_AIR] OnEnterReturning
|
||||
-- @param #AI_AIR self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
self:AddTransition( "Patrolling", "Refuel", "Refuelling" )
|
||||
|
||||
--- Refuel Handler OnBefore for AI_AIR
|
||||
-- @function [parent=#AI_AIR] OnBeforeRefuel
|
||||
-- @param #AI_AIR self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Refuel Handler OnAfter for AI_AIR
|
||||
-- @function [parent=#AI_AIR] OnAfterRefuel
|
||||
-- @param #AI_AIR self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- Refuel Trigger for AI_AIR
|
||||
-- @function [parent=#AI_AIR] Refuel
|
||||
-- @param #AI_AIR self
|
||||
|
||||
--- Refuel Asynchronous Trigger for AI_AIR
|
||||
-- @function [parent=#AI_AIR] __Refuel
|
||||
-- @param #AI_AIR self
|
||||
-- @param #number Delay
|
||||
|
||||
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
|
||||
|
||||
-- @param Wrapper.Group#GROUP self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
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
|
||||
|
||||
|
||||
--- Sets (modifies) the minimum and maximum speed of the patrol.
|
||||
-- @param #AI_AIR self
|
||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h.
|
||||
-- @return #AI_AIR self
|
||||
function AI_AIR:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed )
|
||||
self:F2( { PatrolMinSpeed, PatrolMaxSpeed } )
|
||||
|
||||
self.PatrolMinSpeed = PatrolMinSpeed
|
||||
self.PatrolMaxSpeed = PatrolMaxSpeed
|
||||
end
|
||||
|
||||
|
||||
--- Sets (modifies) the minimum and maximum RTB speed of the patrol.
|
||||
-- @param #AI_AIR self
|
||||
-- @param DCS#Speed RTBMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h.
|
||||
-- @param DCS#Speed RTBMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h.
|
||||
-- @return #AI_AIR self
|
||||
function AI_AIR:SetRTBSpeed( RTBMinSpeed, RTBMaxSpeed )
|
||||
self:F( { RTBMinSpeed, RTBMaxSpeed } )
|
||||
|
||||
self.RTBMinSpeed = RTBMinSpeed
|
||||
self.RTBMaxSpeed = RTBMaxSpeed
|
||||
end
|
||||
|
||||
|
||||
--- Sets the floor and ceiling altitude of the patrol.
|
||||
-- @param #AI_AIR self
|
||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @return #AI_AIR self
|
||||
function AI_AIR:SetAltitude( PatrolFloorAltitude, PatrolCeilingAltitude )
|
||||
self:F2( { PatrolFloorAltitude, PatrolCeilingAltitude } )
|
||||
|
||||
self.PatrolFloorAltitude = PatrolFloorAltitude
|
||||
self.PatrolCeilingAltitude = PatrolCeilingAltitude
|
||||
end
|
||||
|
||||
|
||||
--- Sets the home airbase.
|
||||
-- @param #AI_AIR self
|
||||
-- @param Wrapper.Airbase#AIRBASE HomeAirbase
|
||||
-- @return #AI_AIR self
|
||||
function AI_AIR:SetHomeAirbase( HomeAirbase )
|
||||
self:F2( { HomeAirbase } )
|
||||
|
||||
self.HomeAirbase = HomeAirbase
|
||||
end
|
||||
|
||||
--- Sets to refuel at the given tanker.
|
||||
-- @param #AI_AIR self
|
||||
-- @param Wrapper.Group#GROUP TankerName The group name of the tanker as defined within the Mission Editor or spawned.
|
||||
-- @return #AI_AIR self
|
||||
function AI_AIR:SetTanker( TankerName )
|
||||
self:F2( { TankerName } )
|
||||
|
||||
self.TankerName = TankerName
|
||||
end
|
||||
|
||||
|
||||
--- Sets the disengage range, that when engaging a target beyond the specified range, the engagement will be cancelled and the plane will RTB.
|
||||
-- @param #AI_AIR self
|
||||
-- @param #number DisengageRadius The disengage range.
|
||||
-- @return #AI_AIR self
|
||||
function AI_AIR:SetDisengageRadius( DisengageRadius )
|
||||
self:F2( { DisengageRadius } )
|
||||
|
||||
self.DisengageRadius = DisengageRadius
|
||||
end
|
||||
|
||||
--- Set the status checking off.
|
||||
-- @param #AI_AIR self
|
||||
-- @return #AI_AIR self
|
||||
function AI_AIR:SetStatusOff()
|
||||
self:F2()
|
||||
|
||||
self.CheckStatus = false
|
||||
end
|
||||
|
||||
|
||||
--- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
|
||||
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
|
||||
-- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targeted to the AI_AIR.
|
||||
-- Once the time is finished, the old AI will return to the base.
|
||||
-- @param #AI_AIR self
|
||||
-- @param #number FuelThresholdPercentage The threshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.
|
||||
-- @param #number OutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base.
|
||||
-- @return #AI_AIR self
|
||||
function AI_AIR:SetFuelThreshold( FuelThresholdPercentage, OutOfFuelOrbitTime )
|
||||
|
||||
self.FuelThresholdPercentage = FuelThresholdPercentage
|
||||
self.OutOfFuelOrbitTime = OutOfFuelOrbitTime
|
||||
|
||||
self.Controllable:OptionRTBBingoFuel( false )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- When the AI is damaged beyond a certain threshold, it is required that the AI returns to the home base.
|
||||
-- However, damage cannot be foreseen early on.
|
||||
-- Therefore, when the damage threshold is reached,
|
||||
-- the AI will return immediately to the home base (RTB).
|
||||
-- Note that for groups, the average damage of the complete group will be calculated.
|
||||
-- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage threshold will be 0.25.
|
||||
-- @param #AI_AIR self
|
||||
-- @param #number PatrolDamageThreshold The threshold in percentage (between 0 and 1) when the AI is considered to be damaged.
|
||||
-- @return #AI_AIR self
|
||||
function AI_AIR:SetDamageThreshold( PatrolDamageThreshold )
|
||||
|
||||
self.PatrolManageDamage = true
|
||||
self.PatrolDamageThreshold = PatrolDamageThreshold
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Defines a new patrol route using the @{AI.AI_Patrol#AI_PATROL_ZONE} parameters and settings.
|
||||
-- @param #AI_AIR self
|
||||
-- @return #AI_AIR self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_AIR:onafterStart( Controllable, From, Event, To )
|
||||
|
||||
self:__Status( 10 ) -- Check status status every 30 seconds.
|
||||
|
||||
self:HandleEvent( EVENTS.PilotDead, self.OnPilotDead )
|
||||
self:HandleEvent( EVENTS.Crash, self.OnCrash )
|
||||
self:HandleEvent( EVENTS.Ejection, self.OnEjection )
|
||||
|
||||
Controllable:OptionROEHoldFire()
|
||||
Controllable:OptionROTVertical()
|
||||
end
|
||||
|
||||
--- Coordinates the approriate returning action.
|
||||
-- @param #AI_AIR self
|
||||
-- @return #AI_AIR self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_AIR:onafterReturn( Controllable, From, Event, To )
|
||||
|
||||
self:__RTB( self.TaskDelay )
|
||||
|
||||
end
|
||||
|
||||
-- @param #AI_AIR self
|
||||
function AI_AIR:onbeforeStatus()
|
||||
|
||||
return self.CheckStatus
|
||||
end
|
||||
|
||||
-- @param #AI_AIR self
|
||||
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:T( self.Controllable:GetName() .. " is too far from home base, RTB!" )
|
||||
self:Hold( 300 )
|
||||
RTB = false
|
||||
end
|
||||
end
|
||||
|
||||
-- I think this code is not requirement anymore after release 2.5.
|
||||
-- if self:Is( "Fuel" ) or self:Is( "Damaged" ) or self:Is( "LostControl" ) then
|
||||
-- if DistanceFromHomeBase < 5000 then
|
||||
-- self:E( self.Controllable:GetName() .. " is near the home base, RTB!" )
|
||||
-- self:Home( "Destroy" )
|
||||
-- end
|
||||
-- end
|
||||
|
||||
|
||||
if not self:Is( "Fuel" ) and not self:Is( "Home" ) and not self:is( "Refuelling" )then
|
||||
|
||||
local Fuel = self.Controllable:GetFuelMin()
|
||||
|
||||
-- If the fuel in the controllable is below the threshold percentage,
|
||||
-- then send for refuel in case of a tanker, otherwise RTB.
|
||||
if Fuel < self.FuelThresholdPercentage then
|
||||
|
||||
if self.TankerName then
|
||||
self:T( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... Refuelling at Tanker!" )
|
||||
self:Refuel()
|
||||
else
|
||||
self:T( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... RTB!" )
|
||||
local OldAIControllable = self.Controllable
|
||||
|
||||
local OrbitTask = OldAIControllable:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed )
|
||||
local TimedOrbitTask = OldAIControllable:TaskControlled( OrbitTask, OldAIControllable:TaskCondition(nil,nil,nil,nil,self.OutOfFuelOrbitTime,nil ) )
|
||||
OldAIControllable:SetTask( TimedOrbitTask, 10 )
|
||||
|
||||
self:Fuel()
|
||||
RTB = true
|
||||
end
|
||||
else
|
||||
end
|
||||
end
|
||||
|
||||
if self:Is( "Fuel" ) and not self:Is( "Home" ) and not self:is( "Refuelling" ) then
|
||||
RTB = true
|
||||
end
|
||||
|
||||
-- TODO: Check GROUP damage function.
|
||||
local Damage = self.Controllable:GetLife()
|
||||
local InitialLife = self.Controllable:GetLife0()
|
||||
|
||||
-- If the group is damaged, then RTB.
|
||||
-- Note that a group can consist of more units, so if one unit is damaged of a group, the mission may continue.
|
||||
-- The damaged unit will RTB due to DCS logic, and the others will continue to engage.
|
||||
if ( Damage / InitialLife ) < self.PatrolDamageThreshold then
|
||||
self:T( self.Controllable:GetName() .. " is damaged: " .. Damage .. " ... RTB!" )
|
||||
self:Damaged()
|
||||
RTB = true
|
||||
self:SetStatusOff()
|
||||
end
|
||||
|
||||
-- Check if planes went RTB and are out of control.
|
||||
-- We only check if planes are out of control, when they are in duty.
|
||||
if self.Controllable:HasTask() == false then
|
||||
if not self:Is( "Started" ) and
|
||||
not self:Is( "Stopped" ) and
|
||||
not self:Is( "Fuel" ) and
|
||||
not self:Is( "Damaged" ) and
|
||||
not self:Is( "Home" ) then
|
||||
if self.IdleCount >= 10 then
|
||||
if Damage ~= InitialLife then
|
||||
self:Damaged()
|
||||
else
|
||||
self:T( 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
|
||||
|
||||
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
function AI_AIR.RTBRoute( AIGroup, Fsm )
|
||||
|
||||
AIGroup:F( { "AI_AIR.RTBRoute:", AIGroup:GetName() } )
|
||||
|
||||
if AIGroup:IsAlive() then
|
||||
Fsm:RTB()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
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
|
||||
|
||||
--- Set the min and max factors on RTB speed. Use this, if your planes are heading back to base too fast. Default values are 0.5 and 0.6.
|
||||
-- The RTB speed is calculated as the max speed of the unit multiplied by MinFactor (lower bracket) and multiplied by MaxFactor (upper bracket).
|
||||
-- A random value in this bracket is then applied in the waypoint routing generation.
|
||||
-- @param #AI_AIR self
|
||||
-- @param #number MinFactor Lower bracket factor. Defaults to 0.5.
|
||||
-- @param #number MaxFactor Upper bracket factor. Defaults to 0.6.
|
||||
-- @return #AI_AIR self
|
||||
function AI_AIR:SetRTBSpeedFactors(MinFactor,MaxFactor)
|
||||
self.RTBSpeedMaxFactor = MaxFactor or 0.6
|
||||
self.RTBSpeedMinFactor = MinFactor or 0.5
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
-- @param #AI_AIR self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
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:ClearTasks()
|
||||
|
||||
AIGroup:OptionProhibitAfterburner(true)
|
||||
|
||||
local EngageRoute = {}
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
local FromCoord = AIGroup:GetCoordinate()
|
||||
if not FromCoord then return end
|
||||
|
||||
local ToTargetCoord = self.HomeAirbase:GetCoordinate() -- coordinate is on land height(!)
|
||||
|
||||
local ToTargetVec3 = ToTargetCoord:GetVec3()
|
||||
ToTargetVec3.y = ToTargetCoord:GetLandHeight()+3000 -- let's set this 1000m/3000 feet above ground
|
||||
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 ToAirbaseAngle = FromCoord:GetAngleDegrees( FromCoord:GetDirectionVec3( ToTargetCoord2 ) )
|
||||
|
||||
local Distance = FromCoord:Get2DDistance( ToTargetCoord2 )
|
||||
|
||||
--local ToAirbaseCoord = FromCoord:Translate( 5000, ToAirbaseAngle )
|
||||
local ToAirbaseCoord = ToTargetCoord2
|
||||
|
||||
if Distance < 5000 then
|
||||
self:T( "RTB and near the airbase!" )
|
||||
self:Home()
|
||||
return
|
||||
end
|
||||
|
||||
if not AIGroup:InAir() == true then
|
||||
self:T( "Not anymore in the air, considered Home." )
|
||||
self:Home()
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
--- Create a route point of type air.
|
||||
local FromRTBRoutePoint = FromCoord:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
RTBSpeed,
|
||||
true
|
||||
)
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToRTBRoutePoint = ToAirbaseCoord:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.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()
|
||||
|
||||
--- NOW ROUTE THE GROUP!
|
||||
AIGroup:Route( EngageRoute, self.TaskDelay )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- @param #AI_AIR self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
function AI_AIR:onafterHome( AIGroup, From, Event, To )
|
||||
self:F( { AIGroup, From, Event, To } )
|
||||
|
||||
self:T( "Group " .. self.Controllable:GetName() .. " ... Home! ( " .. self:GetState() .. " )" )
|
||||
|
||||
if AIGroup and AIGroup:IsAlive() then
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- @param #AI_AIR self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
function AI_AIR:onafterHold( AIGroup, From, Event, To, HoldTime )
|
||||
self:F( { AIGroup, From, Event, To } )
|
||||
|
||||
self:T( "Group " .. self.Controllable:GetName() .. " ... Holding! ( " .. self:GetState() .. " )" )
|
||||
|
||||
if AIGroup and AIGroup:IsAlive() then
|
||||
local Coordinate = AIGroup:GetCoordinate()
|
||||
if Coordinate == nil then return end
|
||||
local OrbitTask = AIGroup:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed, Coordinate )
|
||||
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:SetState( AIGroup, "AI_AIR", self )
|
||||
|
||||
AIGroup:SetTask( AIGroup:TaskCombo( { TimedOrbitTask, RTBTask, OrbitHoldTask } ), 1 )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
function AI_AIR.Resume( AIGroup, Fsm )
|
||||
|
||||
AIGroup:T( { "AI_AIR.Resume:", AIGroup:GetName() } )
|
||||
if AIGroup:IsAlive() then
|
||||
Fsm:__RTB( Fsm.TaskDelay )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- @param #AI_AIR self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
function AI_AIR:onafterRefuel( AIGroup, From, Event, To )
|
||||
self:F( { AIGroup, From, Event, To } )
|
||||
|
||||
if AIGroup and AIGroup:IsAlive() then
|
||||
|
||||
-- Get tanker group.
|
||||
local Tanker = GROUP:FindByName( self.TankerName )
|
||||
|
||||
if Tanker and Tanker:IsAlive() and Tanker:IsAirPlane() then
|
||||
|
||||
self:T( "Group " .. self.Controllable:GetName() .. " ... Refuelling! State=" .. self:GetState() .. ", Refuelling tanker " .. self.TankerName )
|
||||
|
||||
local RefuelRoute = {}
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
local FromRefuelCoord = AIGroup:GetCoordinate()
|
||||
local ToRefuelCoord = Tanker:GetCoordinate()
|
||||
local ToRefuelSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local FromRefuelRoutePoint = FromRefuelCoord:WaypointAir(self.PatrolAltType, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, ToRefuelSpeed, true)
|
||||
|
||||
--- Create a route point of type air. NOT used!
|
||||
local ToRefuelRoutePoint = Tanker:GetCoordinate():WaypointAir(self.PatrolAltType, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, ToRefuelSpeed, true)
|
||||
|
||||
self:F( { ToRefuelSpeed = ToRefuelSpeed } )
|
||||
|
||||
RefuelRoute[#RefuelRoute+1] = FromRefuelRoutePoint
|
||||
RefuelRoute[#RefuelRoute+1] = ToRefuelRoutePoint
|
||||
|
||||
AIGroup:OptionROEHoldFire()
|
||||
AIGroup:OptionROTEvadeFire()
|
||||
|
||||
-- Get Class name for .Resume function
|
||||
local classname=self:GetClassName()
|
||||
|
||||
-- AI_A2A_CAP can call this function but does not have a .Resume function. Try to fix.
|
||||
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
|
||||
|
||||
-- No tanker defined ==> RTB!
|
||||
self:RTB()
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- @param #AI_AIR self
|
||||
function AI_AIR:onafterDead()
|
||||
self:SetStatusOff()
|
||||
end
|
||||
|
||||
|
||||
-- @param #AI_AIR self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
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
|
||||
|
||||
-- @param #AI_AIR self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function AI_AIR:OnEjection( EventData )
|
||||
|
||||
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
|
||||
self:__Eject( self.TaskDelay, EventData )
|
||||
end
|
||||
end
|
||||
|
||||
-- @param #AI_AIR self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function AI_AIR:OnPilotDead( EventData )
|
||||
|
||||
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
|
||||
self:__PilotDead( self.TaskDelay, EventData )
|
||||
end
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,595 +0,0 @@
|
||||
--- **AI** - Models the process of air to ground engagement for airplanes and helicopters.
|
||||
--
|
||||
-- This is a class used in the @{AI.AI_A2G_Dispatcher}.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Air_Engage
|
||||
-- @image AI_Air_To_Ground_Engage.JPG
|
||||
|
||||
|
||||
|
||||
--- @type AI_AIR_ENGAGE
|
||||
-- @extends AI.AI_AIR#AI_AIR
|
||||
|
||||
|
||||
--- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The AI_AIR_ENGAGE is assigned a @{Wrapper.Group} and this must be done before the AI_AIR_ENGAGE process can be started using the **Start** event.
|
||||
--
|
||||
-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits.
|
||||
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
|
||||
--
|
||||
-- This cycle will continue.
|
||||
--
|
||||
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
|
||||
--
|
||||
-- When enemies are detected, the AI will automatically engage the enemy.
|
||||
--
|
||||
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||
--
|
||||
-- ## 1. AI_AIR_ENGAGE constructor
|
||||
--
|
||||
-- * @{#AI_AIR_ENGAGE.New}(): Creates a new AI_AIR_ENGAGE object.
|
||||
--
|
||||
-- ## 2. Set the Zone of Engagement
|
||||
--
|
||||
-- An optional @{Core.Zone} can be set,
|
||||
-- that will define when the AI will engage with the detected airborne enemy targets.
|
||||
-- Use the method @{AI.AI_CAP#AI_AIR_ENGAGE.SetEngageZone}() to define that Zone.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_AIR_ENGAGE
|
||||
AI_AIR_ENGAGE = {
|
||||
ClassName = "AI_AIR_ENGAGE",
|
||||
}
|
||||
|
||||
|
||||
|
||||
--- Creates a new AI_AIR_ENGAGE object
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param AI.AI_Air#AI_AIR AI_Air The AI_AIR FSM.
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AI group.
|
||||
-- @param DCS#Speed EngageMinSpeed (optional, default = 50% of max speed) The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Speed EngageMaxSpeed (optional, default = 75% of max speed) The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Altitude EngageFloorAltitude (optional, default = 1000m ) The lowest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#Altitude EngageCeilingAltitude (optional, default = 1500m ) The highest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
|
||||
-- @return #AI_AIR_ENGAGE
|
||||
function AI_AIR_ENGAGE:New( AI_Air, AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, AI_Air ) -- #AI_AIR_ENGAGE
|
||||
|
||||
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" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_ENGAGE.
|
||||
|
||||
--- OnBefore Transition Handler for Event EngageRoute.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] OnBeforeEngageRoute
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event EngageRoute.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] OnAfterEngageRoute
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event EngageRoute.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] EngageRoute
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event EngageRoute.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] __EngageRoute
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
--- OnLeave Transition Handler for State Engaging.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] OnLeaveEngaging
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnEnter Transition Handler for State Engaging.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] OnEnterEngaging
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
self:AddTransition( { "Started", "Engaging", "Returning", "Airborne", "Patrolling" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_ENGAGE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Engage.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] OnBeforeEngage
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Engage.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] OnAfterEngage
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Engage.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] Engage
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Engage.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] __Engage
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
--- OnLeave Transition Handler for State Engaging.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] OnLeaveEngaging
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnEnter Transition Handler for State Engaging.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] OnEnterEngaging
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
self:AddTransition( "Engaging", "Fired", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_ENGAGE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Fired.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] OnBeforeFired
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Fired.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] OnAfterFired
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Fired.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] Fired
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Fired.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] __Fired
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_ENGAGE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Destroy.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] OnBeforeDestroy
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Destroy.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] OnAfterDestroy
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Destroy.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] Destroy
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Destroy.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] __Destroy
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
|
||||
self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_ENGAGE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Abort.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] OnBeforeAbort
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Abort.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] OnAfterAbort
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Abort.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] Abort
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Abort.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] __Abort
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_ENGAGE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Accomplish.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] OnBeforeAccomplish
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Accomplish.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] OnAfterAccomplish
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Accomplish.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] Accomplish
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Accomplish.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] __Accomplish
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
self:AddTransition( { "Patrolling", "Engaging" }, "Refuel", "Refuelling" )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- onafter event handler for Start event.
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AI group managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
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
|
||||
|
||||
|
||||
|
||||
--- onafter event handler for Engage event.
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AI Group managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_AIR_ENGAGE:onafterEngage( AIGroup, From, Event, To )
|
||||
-- TODO: This function is overwritten below!
|
||||
self:HandleEvent( EVENTS.Dead )
|
||||
end
|
||||
|
||||
-- todo: need to fix this global function
|
||||
|
||||
|
||||
--- onbefore event handler for Engage event.
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_AIR_ENGAGE:onbeforeEngage( AIGroup, From, Event, To )
|
||||
if self.Accomplished == true then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- onafter event handler for Abort event.
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AI Group managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_AIR_ENGAGE:onafterAbort( AIGroup, From, Event, To )
|
||||
AIGroup:ClearTasks()
|
||||
self:Return()
|
||||
end
|
||||
|
||||
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_AIR_ENGAGE:onafterAccomplish( AIGroup, From, Event, To )
|
||||
self.Accomplished = true
|
||||
--self:SetDetectionOff()
|
||||
end
|
||||
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function AI_AIR_ENGAGE:onafterDestroy( AIGroup, From, Event, To, EventData )
|
||||
|
||||
if EventData.IniUnit then
|
||||
self.AttackUnits[EventData.IniUnit] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
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
|
||||
|
||||
|
||||
-- @param Wrapper.Group#GROUP AIControllable
|
||||
function AI_AIR_ENGAGE.___EngageRoute( AIGroup, Fsm, AttackSetUnit )
|
||||
Fsm:T(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
|
||||
|
||||
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP DefenderGroup The GroupGroup managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @param Core.Set#SET_UNIT AttackSetUnit Unit set to be attacked.
|
||||
function AI_AIR_ENGAGE:onafterEngageRoute( DefenderGroup, From, Event, To, AttackSetUnit )
|
||||
self:T( { DefenderGroup, From, Event, To, AttackSetUnit } )
|
||||
|
||||
local DefenderGroupName = DefenderGroup:GetName()
|
||||
|
||||
self.AttackSetUnit = AttackSetUnit -- Kept in memory in case of resume from refuel in air!
|
||||
|
||||
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 )
|
||||
|
||||
-- Determine the distance to the target.
|
||||
-- If it is less than 10km, then attack without a route.
|
||||
-- Otherwise perform a route attack.
|
||||
|
||||
local DefenderCoord = DefenderGroup:GetPointVec3()
|
||||
DefenderCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
|
||||
|
||||
local TargetCoord = AttackSetUnit:GetRandomSurely():GetPointVec3()
|
||||
|
||||
if TargetCoord == nil then
|
||||
self:Return()
|
||||
return
|
||||
end
|
||||
|
||||
TargetCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
|
||||
|
||||
local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord )
|
||||
local EngageDistance = ( DefenderGroup:IsHelicopter() and 5000 ) or ( DefenderGroup:IsAirPlane() and 10000 )
|
||||
|
||||
-- TODO: A factor of * 3 is way too close. This causes the AI not to engange until merged sometimes!
|
||||
if TargetDistance <= EngageDistance * 9 then
|
||||
|
||||
--self:T(string.format("AI_AIR_ENGAGE onafterEngageRoute ==> __Engage - target distance = %.1f km", TargetDistance/1000))
|
||||
self:__Engage( 0.1, AttackSetUnit )
|
||||
|
||||
else
|
||||
|
||||
--self:T(string.format("FF AI_AIR_ENGAGE onafterEngageRoute ==> Routing - target distance = %.1f km", TargetDistance/1000))
|
||||
|
||||
local EngageRoute = {}
|
||||
local AttackTasks = {}
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
local FromWP = DefenderCoord:WaypointAir(self.PatrolAltType or "RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, EngageSpeed, true)
|
||||
|
||||
EngageRoute[#EngageRoute+1] = FromWP
|
||||
|
||||
self:SetTargetDistance( TargetCoord ) -- For RTB status check
|
||||
|
||||
local FromEngageAngle = DefenderCoord:GetAngleDegrees( DefenderCoord:GetDirectionVec3( TargetCoord ) )
|
||||
local ToCoord=DefenderCoord:Translate( EngageDistance, FromEngageAngle, true )
|
||||
|
||||
local ToWP = ToCoord:WaypointAir(self.PatrolAltType or "RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.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
|
||||
-- TODO: This will make an A2A Dispatcher CAP flight to return rather than going back to patrolling!
|
||||
self:T( DefenderGroupName .. ": No targets found -> Going RTB")
|
||||
self:Return()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- @param Wrapper.Group#GROUP AIControllable
|
||||
function AI_AIR_ENGAGE.___Engage( AIGroup, Fsm, AttackSetUnit )
|
||||
|
||||
Fsm:T(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
|
||||
|
||||
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP DefenderGroup The GroupGroup managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @param Core.Set#SET_UNIT AttackSetUnit Set of units to be attacked.
|
||||
function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit )
|
||||
self:F( { DefenderGroup, From, Event, To, AttackSetUnit} )
|
||||
|
||||
local DefenderGroupName = DefenderGroup:GetName()
|
||||
|
||||
self.AttackSetUnit = AttackSetUnit -- Kept in memory in case of resume from refuel in air!
|
||||
|
||||
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 ) -- Ground targets don't have an altitude.
|
||||
|
||||
local TargetCoord = AttackSetUnit:GetRandomSurely():GetPointVec3()
|
||||
if not TargetCoord then
|
||||
self:Return()
|
||||
return
|
||||
end
|
||||
TargetCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
|
||||
|
||||
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", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, EngageSpeed, true)
|
||||
EngageRoute[#EngageRoute+1] = FromWP
|
||||
|
||||
self:SetTargetDistance( TargetCoord ) -- For RTB status check
|
||||
|
||||
local FromEngageAngle = DefenderCoord:GetAngleDegrees( DefenderCoord:GetDirectionVec3( TargetCoord ) )
|
||||
local ToCoord=DefenderCoord:Translate( EngageDistance, FromEngageAngle, true )
|
||||
|
||||
local ToWP = ToCoord:WaypointAir(self.EngageAltType or "RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, EngageSpeed, true)
|
||||
EngageRoute[#EngageRoute+1] = ToWP
|
||||
|
||||
-- TODO: A factor of * 3 this way too low. This causes the AI NOT to engage until very close or even merged sometimes. Some A2A missiles have a much longer range! Needs more frequent updates of the task!
|
||||
if TargetDistance <= EngageDistance * 9 then
|
||||
|
||||
local AttackUnitTasks = self:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude ) -- Polymorphic
|
||||
|
||||
if #AttackUnitTasks == 0 then
|
||||
self:T( 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:T(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
|
||||
-- TODO: This will make an A2A Dispatcher CAP flight to return rather than going back to patrolling!
|
||||
self:T( DefenderGroupName .. ": No targets found -> returning.")
|
||||
self:Return()
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- @param Wrapper.Group#GROUP AIEngage
|
||||
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
|
||||
@@ -1,393 +0,0 @@
|
||||
--- **AI** - Models the process of A2G patrolling and engaging ground targets for airplanes and helicopters.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Air_Patrol
|
||||
-- @image AI_Air_To_Ground_Patrol.JPG
|
||||
|
||||
-- @type AI_AIR_PATROL
|
||||
-- @extends AI.AI_Air#AI_AIR
|
||||
|
||||
--- The AI_AIR_PATROL class implements the core functions to patrol a @{Core.Zone} by an AI @{Wrapper.Group}
|
||||
-- and automatically engage any airborne enemies that are within a certain range or within a certain zone.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The AI_AIR_PATROL is assigned a @{Wrapper.Group} and this must be done before the AI_AIR_PATROL process can be started using the **Start** event.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits.
|
||||
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- This cycle will continue.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- When enemies are detected, the AI will automatically engage the enemy.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ## 1. AI_AIR_PATROL constructor
|
||||
--
|
||||
-- * @{#AI_AIR_PATROL.New}(): Creates a new AI_AIR_PATROL object.
|
||||
--
|
||||
-- ## 2. AI_AIR_PATROL is a FSM
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ### 2.1 AI_AIR_PATROL States
|
||||
--
|
||||
-- * **None** ( Group ): The process is not started yet.
|
||||
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
|
||||
-- * **Engaging** ( Group ): The AI is engaging the bogeys.
|
||||
-- * **Returning** ( Group ): The AI is returning to Base..
|
||||
--
|
||||
-- ### 2.2 AI_AIR_PATROL Events
|
||||
--
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.PatrolRoute}**: Route the AI to a new random 3D point within the Patrol Zone.
|
||||
-- * **@{#AI_AIR_PATROL.Engage}**: Let the AI engage the bogeys.
|
||||
-- * **@{#AI_AIR_PATROL.Abort}**: Aborts the engagement and return patrolling in the patrol zone.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
|
||||
-- * **@{#AI_AIR_PATROL.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
|
||||
-- * **@{#AI_AIR_PATROL.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task.
|
||||
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
|
||||
--
|
||||
-- ## 3. Set the Range of Engagement
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- An optional range can be set in meters,
|
||||
-- that will define when the AI will engage with the detected airborne enemy targets.
|
||||
-- The range can be beyond or smaller than the range of the Patrol Zone.
|
||||
-- The range is applied at the position of the AI.
|
||||
-- Use the method @{#AI_AIR_PATROL.SetEngageRange}() to define that range.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_AIR_PATROL
|
||||
AI_AIR_PATROL = {
|
||||
ClassName = "AI_AIR_PATROL",
|
||||
}
|
||||
|
||||
--- Creates a new AI_AIR_PATROL object
|
||||
-- @param #AI_AIR_PATROL self
|
||||
-- @param AI.AI_Air#AI_AIR AI_Air The AI_AIR FSM.
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AI group.
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
|
||||
-- @param DCS#Altitude PatrolFloorAltitude (optional, default = 1000m ) The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude (optional, default = 1500m ) The highest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Speed PatrolMinSpeed (optional, default = 50% of max speed) The minimum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed (optional, default = 75% of max speed) The maximum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO.
|
||||
-- @return #AI_AIR_PATROL
|
||||
function AI_AIR_PATROL:New( AI_Air, AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, AI_Air ) -- #AI_AIR_PATROL
|
||||
|
||||
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
|
||||
|
||||
-- defafult PatrolAltType to "RADIO" if not specified
|
||||
self.PatrolAltType = PatrolAltType or "RADIO"
|
||||
|
||||
self:AddTransition( { "Started", "Airborne", "Refuelling" }, "Patrol", "Patrolling" )
|
||||
|
||||
--- OnBefore Transition Handler for Event Patrol.
|
||||
-- @function [parent=#AI_AIR_PATROL] OnBeforePatrol
|
||||
-- @param #AI_AIR_PATROL self
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Patrol.
|
||||
-- @function [parent=#AI_AIR_PATROL] OnAfterPatrol
|
||||
-- @param #AI_AIR_PATROL self
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Patrol.
|
||||
-- @function [parent=#AI_AIR_PATROL] Patrol
|
||||
-- @param #AI_AIR_PATROL self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Patrol.
|
||||
-- @function [parent=#AI_AIR_PATROL] __Patrol
|
||||
-- @param #AI_AIR_PATROL self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
--- OnLeave Transition Handler for State Patrolling.
|
||||
-- @function [parent=#AI_AIR_PATROL] OnLeavePatrolling
|
||||
-- @param #AI_AIR_PATROL self
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnEnter Transition Handler for State Patrolling.
|
||||
-- @function [parent=#AI_AIR_PATROL] OnEnterPatrolling
|
||||
-- @param #AI_AIR_PATROL self
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
self:AddTransition( "Patrolling", "PatrolRoute", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_PATROL.
|
||||
|
||||
--- OnBefore Transition Handler for Event PatrolRoute.
|
||||
-- @function [parent=#AI_AIR_PATROL] OnBeforePatrolRoute
|
||||
-- @param #AI_AIR_PATROL self
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event PatrolRoute.
|
||||
-- @function [parent=#AI_AIR_PATROL] OnAfterPatrolRoute
|
||||
-- @param #AI_AIR_PATROL self
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event PatrolRoute.
|
||||
-- @function [parent=#AI_AIR_PATROL] PatrolRoute
|
||||
-- @param #AI_AIR_PATROL self
|
||||
|
||||
--- Asynchronous Event Trigger for Event PatrolRoute.
|
||||
-- @function [parent=#AI_AIR_PATROL] __PatrolRoute
|
||||
-- @param #AI_AIR_PATROL self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
self:AddTransition( "*", "Reset", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_PATROL.
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the Engage Range when the AI will engage with airborne enemies.
|
||||
-- @param #AI_AIR_PATROL self
|
||||
-- @param #number EngageRange The Engage Range.
|
||||
-- @return #AI_AIR_PATROL self
|
||||
function AI_AIR_PATROL:SetEngageRange( EngageRange )
|
||||
self:F2()
|
||||
|
||||
if EngageRange then
|
||||
self.EngageRange = EngageRange
|
||||
else
|
||||
self.EngageRange = nil
|
||||
end
|
||||
end
|
||||
|
||||
--- Set race track parameters. CAP flights will perform race track patterns rather than randomly patrolling the zone.
|
||||
-- @param #AI_AIR_PATROL self
|
||||
-- @param #number LegMin Min Length of the race track leg in meters. Default 10,000 m.
|
||||
-- @param #number LegMax Max length of the race track leg in meters. Default 15,000 m.
|
||||
-- @param #number HeadingMin Min heading of the race track in degrees. Default 0 deg, i.e. from South to North.
|
||||
-- @param #number HeadingMax Max heading of the race track in degrees. Default 180 deg, i.e. from South to North.
|
||||
-- @param #number DurationMin (Optional) Min duration before switching the orbit position. Default is keep same orbit until RTB or engage.
|
||||
-- @param #number DurationMax (Optional) Max duration before switching the orbit position. Default is keep same orbit until RTB or engage.
|
||||
-- @param #table CapCoordinates Table of coordinates of first race track point. Second point is determined by leg length and heading.
|
||||
-- @return #AI_AIR_PATROL self
|
||||
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
|
||||
|
||||
--- Defines a new patrol route using the @{AI.AI_Patrol#AI_PATROL_ZONE} parameters and settings.
|
||||
-- @param #AI_AIR_PATROL self
|
||||
-- @return #AI_AIR_PATROL self
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
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
|
||||
|
||||
--- This static method is called from the route path within the last task at the last waypoint of the AIPatrol.
|
||||
-- Note that this method is required, as triggers the next route when patrolling for the AIPatrol.
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The AI group.
|
||||
-- @param #AI_AIR_PATROL Fsm The FSM.
|
||||
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
|
||||
|
||||
--- Defines a new patrol route using the @{AI.AI_Patrol#AI_PATROL_ZONE} parameters and settings.
|
||||
-- @param #AI_AIR_PATROL self
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The Group managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_AIR_PATROL:onafterPatrolRoute( AIPatrol, From, Event, To )
|
||||
|
||||
self:F2()
|
||||
|
||||
-- When RTB, don't allow anymore the routing.
|
||||
if From == "RTB" then
|
||||
return
|
||||
end
|
||||
|
||||
if AIPatrol and AIPatrol:IsAlive() then
|
||||
|
||||
local PatrolRoute = {}
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
local CurrentCoord = AIPatrol:GetCoordinate()
|
||||
|
||||
local altitude= math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude )
|
||||
|
||||
local ToTargetCoord = self.PatrolZone:GetRandomPointVec2()
|
||||
ToTargetCoord:SetAlt( altitude )
|
||||
self:SetTargetDistance( ToTargetCoord ) -- For RTB status check
|
||||
|
||||
local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
|
||||
local speedkmh=ToTargetSpeed
|
||||
|
||||
local FromWP = CurrentCoord:WaypointAir(self.PatrolAltType or "RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, ToTargetSpeed, true)
|
||||
PatrolRoute[#PatrolRoute+1] = FromWP
|
||||
|
||||
if self.racetrack then
|
||||
|
||||
-- Random heading.
|
||||
local heading = math.random(self.racetrackheadingmin, self.racetrackheadingmax)
|
||||
|
||||
-- Random leg length.
|
||||
local leg=math.random(self.racetracklegmin, self.racetracklegmax)
|
||||
|
||||
-- Random duration if any.
|
||||
local duration = self.racetrackdurationmin
|
||||
if self.racetrackdurationmax then
|
||||
duration=math.random(self.racetrackdurationmin, self.racetrackdurationmax)
|
||||
end
|
||||
|
||||
-- CAP coordinate.
|
||||
local c0=self.PatrolZone:GetRandomCoordinate()
|
||||
if self.racetrackcapcoordinates and #self.racetrackcapcoordinates>0 then
|
||||
c0=self.racetrackcapcoordinates[math.random(#self.racetrackcapcoordinates)]
|
||||
end
|
||||
|
||||
-- Race track points.
|
||||
local c1=c0:SetAltitude(altitude) --Core.Point#COORDINATE
|
||||
local c2=c1:Translate(leg, heading):SetAltitude(altitude)
|
||||
|
||||
self:SetTargetDistance(c0) -- For RTB status check
|
||||
|
||||
-- Debug:
|
||||
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)))
|
||||
--c1:MarkToAll("Race track c1")
|
||||
--c2:MarkToAll("Race track c2")
|
||||
|
||||
-- Task to orbit.
|
||||
local taskOrbit=AIPatrol:TaskOrbit(c1, altitude, UTILS.KmphToMps(speedkmh), c2)
|
||||
|
||||
-- Task function to redo the patrol at other random position.
|
||||
local taskPatrol=AIPatrol:TaskFunction("AI_AIR_PATROL.___PatrolRoute", self)
|
||||
|
||||
-- Controlled task with task condition.
|
||||
local taskCond=AIPatrol:TaskCondition(nil, nil, nil, nil, duration, nil)
|
||||
local taskCont=AIPatrol:TaskControlled(taskOrbit, taskCond)
|
||||
|
||||
-- Second waypoint
|
||||
PatrolRoute[2]=c1:WaypointAirTurningPoint(self.PatrolAltType, speedkmh, {taskCont, taskPatrol}, "CAP Orbit")
|
||||
|
||||
else
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToWP = ToTargetCoord:WaypointAir(self.PatrolAltType, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.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
|
||||
|
||||
--- Resumes the AIPatrol
|
||||
-- @param Wrapper.Group#GROUP AIPatrol
|
||||
-- @param Core.Fsm#FSM Fsm
|
||||
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
|
||||
@@ -1,296 +0,0 @@
|
||||
--- **AI** - Models squadrons for airplanes and helicopters.
|
||||
--
|
||||
-- This is a class used in the @{AI.AI_Air_Dispatcher} and derived dispatcher classes.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Air_Squadron
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
|
||||
|
||||
--- @type AI_AIR_SQUADRON
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
--- Implements the core functions modeling squadrons for airplanes and helicopters.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_AIR_SQUADRON
|
||||
AI_AIR_SQUADRON = {
|
||||
ClassName = "AI_AIR_SQUADRON",
|
||||
}
|
||||
|
||||
|
||||
|
||||
--- Creates a new AI_AIR_SQUADRON object
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @return #AI_AIR_SQUADRON
|
||||
function AI_AIR_SQUADRON:New( SquadronName, AirbaseName, TemplatePrefixes, ResourceCount )
|
||||
|
||||
self:T( { Air_Squadron = { SquadronName, AirbaseName, TemplatePrefixes, ResourceCount } } )
|
||||
|
||||
local AI_Air_Squadron = BASE:New() -- #AI_AIR_SQUADRON
|
||||
|
||||
AI_Air_Squadron.Name = SquadronName
|
||||
AI_Air_Squadron.Airbase = AIRBASE:FindByName( AirbaseName )
|
||||
AI_Air_Squadron.AirbaseName = AI_Air_Squadron.Airbase:GetName()
|
||||
if not AI_Air_Squadron.Airbase then
|
||||
error( "Cannot find airbase with name:" .. AirbaseName )
|
||||
end
|
||||
|
||||
AI_Air_Squadron.Spawn = {}
|
||||
if type( TemplatePrefixes ) == "string" then
|
||||
local SpawnTemplate = TemplatePrefixes
|
||||
self.DefenderSpawns[SpawnTemplate] = self.DefenderSpawns[SpawnTemplate] or SPAWN:New( SpawnTemplate ) -- :InitCleanUp( 180 )
|
||||
AI_Air_Squadron.Spawn[1] = self.DefenderSpawns[SpawnTemplate]
|
||||
else
|
||||
for TemplateID, SpawnTemplate in pairs( TemplatePrefixes ) do
|
||||
self.DefenderSpawns[SpawnTemplate] = self.DefenderSpawns[SpawnTemplate] or SPAWN:New( SpawnTemplate ) -- :InitCleanUp( 180 )
|
||||
AI_Air_Squadron.Spawn[#AI_Air_Squadron.Spawn+1] = self.DefenderSpawns[SpawnTemplate]
|
||||
end
|
||||
end
|
||||
AI_Air_Squadron.ResourceCount = ResourceCount
|
||||
AI_Air_Squadron.TemplatePrefixes = TemplatePrefixes
|
||||
AI_Air_Squadron.Captured = false -- Not captured. This flag will be set to true, when the airbase where the squadron is located, is captured.
|
||||
|
||||
self:SetSquadronLanguage( SquadronName, "EN" ) -- Squadrons speak English by default.
|
||||
|
||||
return AI_Air_Squadron
|
||||
end
|
||||
|
||||
--- Set the Name of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @param #string Name The Squadron Name.
|
||||
-- @return #AI_AIR_SQUADRON The Squadron.
|
||||
function AI_AIR_SQUADRON:SetName( Name )
|
||||
|
||||
self.Name = Name
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the Name of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @return #string The Squadron Name.
|
||||
function AI_AIR_SQUADRON:GetName()
|
||||
|
||||
return self.Name
|
||||
end
|
||||
|
||||
--- Set the ResourceCount of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @param #number ResourceCount The Squadron ResourceCount.
|
||||
-- @return #AI_AIR_SQUADRON The Squadron.
|
||||
function AI_AIR_SQUADRON:SetResourceCount( ResourceCount )
|
||||
|
||||
self.ResourceCount = ResourceCount
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the ResourceCount of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @return #number The Squadron ResourceCount.
|
||||
function AI_AIR_SQUADRON:GetResourceCount()
|
||||
|
||||
return self.ResourceCount
|
||||
end
|
||||
|
||||
--- Add Resources to the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @param #number Resources The Resources to be added.
|
||||
-- @return #AI_AIR_SQUADRON The Squadron.
|
||||
function AI_AIR_SQUADRON:AddResources( Resources )
|
||||
|
||||
self.ResourceCount = self.ResourceCount + Resources
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove Resources to the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @param #number Resources The Resources to be removed.
|
||||
-- @return #AI_AIR_SQUADRON The Squadron.
|
||||
function AI_AIR_SQUADRON:RemoveResources( Resources )
|
||||
|
||||
self.ResourceCount = self.ResourceCount - Resources
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the Overhead of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @param #number Overhead The Squadron Overhead.
|
||||
-- @return #AI_AIR_SQUADRON The Squadron.
|
||||
function AI_AIR_SQUADRON:SetOverhead( Overhead )
|
||||
|
||||
self.Overhead = Overhead
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the Overhead of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @return #number The Squadron Overhead.
|
||||
function AI_AIR_SQUADRON:GetOverhead()
|
||||
|
||||
return self.Overhead
|
||||
end
|
||||
|
||||
--- Set the Grouping of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @param #number Grouping The Squadron Grouping.
|
||||
-- @return #AI_AIR_SQUADRON The Squadron.
|
||||
function AI_AIR_SQUADRON:SetGrouping( Grouping )
|
||||
|
||||
self.Grouping = Grouping
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the Grouping of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @return #number The Squadron Grouping.
|
||||
function AI_AIR_SQUADRON:GetGrouping()
|
||||
|
||||
return self.Grouping
|
||||
end
|
||||
|
||||
--- Set the FuelThreshold of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @param #number FuelThreshold The Squadron FuelThreshold.
|
||||
-- @return #AI_AIR_SQUADRON The Squadron.
|
||||
function AI_AIR_SQUADRON:SetFuelThreshold( FuelThreshold )
|
||||
|
||||
self.FuelThreshold = FuelThreshold
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the FuelThreshold of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @return #number The Squadron FuelThreshold.
|
||||
function AI_AIR_SQUADRON:GetFuelThreshold()
|
||||
|
||||
return self.FuelThreshold
|
||||
end
|
||||
|
||||
--- Set the EngageProbability of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @param #number EngageProbability The Squadron EngageProbability.
|
||||
-- @return #AI_AIR_SQUADRON The Squadron.
|
||||
function AI_AIR_SQUADRON:SetEngageProbability( EngageProbability )
|
||||
|
||||
self.EngageProbability = EngageProbability
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the EngageProbability of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @return #number The Squadron EngageProbability.
|
||||
function AI_AIR_SQUADRON:GetEngageProbability()
|
||||
|
||||
return self.EngageProbability
|
||||
end
|
||||
|
||||
--- Set the Takeoff of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @param #number Takeoff The Squadron Takeoff.
|
||||
-- @return #AI_AIR_SQUADRON The Squadron.
|
||||
function AI_AIR_SQUADRON:SetTakeoff( Takeoff )
|
||||
|
||||
self.Takeoff = Takeoff
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the Takeoff of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @return #number The Squadron Takeoff.
|
||||
function AI_AIR_SQUADRON:GetTakeoff()
|
||||
|
||||
return self.Takeoff
|
||||
end
|
||||
|
||||
--- Set the Landing of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @param #number Landing The Squadron Landing.
|
||||
-- @return #AI_AIR_SQUADRON The Squadron.
|
||||
function AI_AIR_SQUADRON:SetLanding( Landing )
|
||||
|
||||
self.Landing = Landing
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the Landing of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @return #number The Squadron Landing.
|
||||
function AI_AIR_SQUADRON:GetLanding()
|
||||
|
||||
return self.Landing
|
||||
end
|
||||
|
||||
--- Set the TankerName of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @param #string TankerName The Squadron Tanker Name.
|
||||
-- @return #AI_AIR_SQUADRON The Squadron.
|
||||
function AI_AIR_SQUADRON:SetTankerName( TankerName )
|
||||
|
||||
self.TankerName = TankerName
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the Tanker Name of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @return #string The Squadron Tanker Name.
|
||||
function AI_AIR_SQUADRON:GetTankerName()
|
||||
|
||||
return self.TankerName
|
||||
end
|
||||
|
||||
|
||||
--- Set the Radio of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @param #number RadioFrequency The frequency of communication.
|
||||
-- @param #number RadioModulation The modulation of communication.
|
||||
-- @param #number RadioPower The power in Watts of communication.
|
||||
-- @param #string Language The language of the radio speech.
|
||||
-- @return #AI_AIR_SQUADRON The Squadron.
|
||||
function AI_AIR_SQUADRON:SetRadio( RadioFrequency, RadioModulation, RadioPower, Language )
|
||||
|
||||
self.RadioFrequency = RadioFrequency
|
||||
self.RadioModulation = RadioModulation or radio.modulation.AM
|
||||
self.RadioPower = RadioPower or 100
|
||||
|
||||
if self.RadioSpeech then
|
||||
self.RadioSpeech:Stop()
|
||||
end
|
||||
|
||||
self.RadioSpeech = nil
|
||||
|
||||
self.RadioSpeech = RADIOSPEECH:New( RadioFrequency, RadioModulation )
|
||||
self.RadioSpeech.power = RadioPower
|
||||
self.RadioSpeech:Start( 0.5 )
|
||||
|
||||
self.RadioSpeech:SetLanguage( Language )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@@ -1,653 +0,0 @@
|
||||
--- **AI** - Peform Battlefield Area Interdiction (BAI) within an engagement zone.
|
||||
--
|
||||
-- **Features:**
|
||||
--
|
||||
-- * Hold and standby within a patrol zone.
|
||||
-- * Engage upon command the assigned targets within an engagement zone.
|
||||
-- * Loop the zone until all targets are eliminated.
|
||||
-- * Trigger different events upon the results achieved.
|
||||
-- * After combat, return to the patrol zone and hold.
|
||||
-- * RTB when commanded or after out of fuel.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_BAI)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [YouTube Playlist]()
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- * **Gunterlund**: Test case revision.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_BAI
|
||||
-- @image AI_Battlefield_Air_Interdiction.JPG
|
||||
|
||||
|
||||
--- AI_BAI_ZONE class
|
||||
-- @type AI_BAI_ZONE
|
||||
-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Wrapper.Controllable} patrolling.
|
||||
-- @field Core.Zone#ZONE_BASE TargetZone The @{Core.Zone} where the patrol needs to be executed.
|
||||
-- @extends AI.AI_Patrol#AI_PATROL_ZONE
|
||||
|
||||
--- Implements the core functions to provide BattleGround Air Interdiction in an Engage @{Core.Zone} by an AIR @{Wrapper.Controllable} or @{Wrapper.Group}.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The AI_BAI_ZONE runs a process. It holds an AI in a Patrol Zone and when the AI is commanded to engage, it will fly to an Engage Zone.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The AI_BAI_ZONE is assigned a @{Wrapper.Group} and this must be done before the AI_BAI_ZONE process can be started through the **Start** event.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Upon started, The AI will **Route** itself towards the random 3D point within a patrol zone,
|
||||
-- using a random speed within the given altitude and speed limits.
|
||||
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
|
||||
-- This cycle will continue until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- When the AI is commanded to provide BattleGround Air Interdiction (through the event **Engage**), the AI will fly towards the Engage Zone.
|
||||
-- Any target that is detected in the Engage Zone will be reported and will be destroyed by the AI.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The AI will detect the targets and will only destroy the targets within the Engage Zone.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Every target that is destroyed, is reported< by the AI.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Note that the AI does not know when the Engage Zone is cleared, and therefore will keep circling in the zone.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Until it is notified through the event **Accomplish**, which is to be triggered by an observing party:
|
||||
--
|
||||
-- * a FAC
|
||||
-- * a timed event
|
||||
-- * a menu option selected by a human
|
||||
-- * a condition
|
||||
-- * others ...
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- When the AI has accomplished the Bombing, it will fly back to the Patrol Zone.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- It will keep patrolling there, until it is notified to RTB or move to another BOMB Zone.
|
||||
-- It can be notified to go RTB through the **RTB** event.
|
||||
--
|
||||
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- # 1. AI_BAI_ZONE constructor
|
||||
--
|
||||
-- * @{#AI_BAI_ZONE.New}(): Creates a new AI_BAI_ZONE object.
|
||||
--
|
||||
-- ## 2. AI_BAI_ZONE is a FSM
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ### 2.1. AI_BAI_ZONE States
|
||||
--
|
||||
-- * **None** ( Group ): The process is not started yet.
|
||||
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
|
||||
-- * **Engaging** ( Group ): The AI is engaging the targets in the Engage Zone, executing BOMB.
|
||||
-- * **Returning** ( Group ): The AI is returning to Base..
|
||||
--
|
||||
-- ### 2.2. AI_BAI_ZONE Events
|
||||
--
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone.
|
||||
-- * **@{#AI_BAI_ZONE.Engage}**: Engage the AI to provide BOMB in the Engage Zone, destroying any target it finds.
|
||||
-- * **@{#AI_BAI_ZONE.Abort}**: Aborts the engagement and return patrolling in the patrol zone.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
|
||||
-- * **@{#AI_BAI_ZONE.Destroy}**: The AI has destroyed a target @{Wrapper.Unit}.
|
||||
-- * **@{#AI_BAI_ZONE.Destroyed}**: The AI has destroyed all target @{Wrapper.Unit}s assigned in the BOMB task.
|
||||
-- * **Status**: The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
|
||||
--
|
||||
-- ## 3. Modify the Engage Zone behaviour to pinpoint a **map object** or **scenery object**
|
||||
--
|
||||
-- Use the method @{#AI_BAI_ZONE.SearchOff}() to specify that the EngageZone is not to be searched for potential targets (UNITs), but that the center of the zone
|
||||
-- is the point where a map object is to be destroyed (like a bridge).
|
||||
--
|
||||
-- Example:
|
||||
--
|
||||
-- -- Tell the BAI not to search for potential targets in the BAIEngagementZone, but rather use the center of the BAIEngagementZone as the bombing location.
|
||||
-- AIBAIZone:SearchOff()
|
||||
--
|
||||
-- Searching can be switched back on with the method @{#AI_BAI_ZONE.SearchOn}(). Use the method @{#AI_BAI_ZONE.SearchOnOff}() to flexibily switch searching on or off.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_BAI_ZONE
|
||||
AI_BAI_ZONE = {
|
||||
ClassName = "AI_BAI_ZONE",
|
||||
}
|
||||
|
||||
|
||||
|
||||
--- Creates a new AI_BAI_ZONE object
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
|
||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h.
|
||||
-- @param Core.Zone#ZONE_BASE EngageZone The zone where the engage will happen.
|
||||
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
|
||||
-- @return #AI_BAI_ZONE self
|
||||
function AI_BAI_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageZone, PatrolAltType )
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, AI_PATROL_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) ) -- #AI_BAI_ZONE
|
||||
|
||||
self.EngageZone = EngageZone
|
||||
self.Accomplished = false
|
||||
|
||||
self:SetDetectionZone( self.EngageZone )
|
||||
self:SearchOn()
|
||||
|
||||
self:AddTransition( { "Patrolling", "Engaging" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_BAI_ZONE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Engage.
|
||||
-- @function [parent=#AI_BAI_ZONE] OnBeforeEngage
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Engage.
|
||||
-- @function [parent=#AI_BAI_ZONE] OnAfterEngage
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Engage.
|
||||
-- @function [parent=#AI_BAI_ZONE] Engage
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone.
|
||||
-- @param DCS#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement.
|
||||
-- @param DCS#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack.
|
||||
-- If parameter is not defined the unit / controllable will choose expend on its own discretion.
|
||||
-- Use the structure @{DCS#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack.
|
||||
-- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
|
||||
-- @param DCS#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
|
||||
|
||||
--- Asynchronous Event Trigger for Event Engage.
|
||||
-- @function [parent=#AI_BAI_ZONE] __Engage
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
-- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone.
|
||||
-- @param DCS#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement.
|
||||
-- @param DCS#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack.
|
||||
-- If parameter is not defined the unit / controllable will choose expend on its own discretion.
|
||||
-- Use the structure @{DCS#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack.
|
||||
-- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
|
||||
-- @param DCS#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
|
||||
|
||||
--- OnLeave Transition Handler for State Engaging.
|
||||
-- @function [parent=#AI_BAI_ZONE] OnLeaveEngaging
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnEnter Transition Handler for State Engaging.
|
||||
-- @function [parent=#AI_BAI_ZONE] OnEnterEngaging
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
self:AddTransition( "Engaging", "Target", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_BAI_ZONE.
|
||||
|
||||
self:AddTransition( "Engaging", "Fired", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_BAI_ZONE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Fired.
|
||||
-- @function [parent=#AI_BAI_ZONE] OnBeforeFired
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Fired.
|
||||
-- @function [parent=#AI_BAI_ZONE] OnAfterFired
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Fired.
|
||||
-- @function [parent=#AI_BAI_ZONE] Fired
|
||||
-- @param #AI_BAI_ZONE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Fired.
|
||||
-- @function [parent=#AI_BAI_ZONE] __Fired
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_BAI_ZONE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Destroy.
|
||||
-- @function [parent=#AI_BAI_ZONE] OnBeforeDestroy
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Destroy.
|
||||
-- @function [parent=#AI_BAI_ZONE] OnAfterDestroy
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Destroy.
|
||||
-- @function [parent=#AI_BAI_ZONE] Destroy
|
||||
-- @param #AI_BAI_ZONE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Destroy.
|
||||
-- @function [parent=#AI_BAI_ZONE] __Destroy
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
|
||||
self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_BAI_ZONE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Abort.
|
||||
-- @function [parent=#AI_BAI_ZONE] OnBeforeAbort
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Abort.
|
||||
-- @function [parent=#AI_BAI_ZONE] OnAfterAbort
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Abort.
|
||||
-- @function [parent=#AI_BAI_ZONE] Abort
|
||||
-- @param #AI_BAI_ZONE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Abort.
|
||||
-- @function [parent=#AI_BAI_ZONE] __Abort
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_BAI_ZONE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Accomplish.
|
||||
-- @function [parent=#AI_BAI_ZONE] OnBeforeAccomplish
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Accomplish.
|
||||
-- @function [parent=#AI_BAI_ZONE] OnAfterAccomplish
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Accomplish.
|
||||
-- @function [parent=#AI_BAI_ZONE] Accomplish
|
||||
-- @param #AI_BAI_ZONE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Accomplish.
|
||||
-- @function [parent=#AI_BAI_ZONE] __Accomplish
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set the Engage Zone where the AI is performing BOMB. Note that if the EngageZone is changed, the AI needs to re-detect targets.
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param Core.Zone#ZONE EngageZone The zone where the AI is performing BOMB.
|
||||
-- @return #AI_BAI_ZONE self
|
||||
function AI_BAI_ZONE:SetEngageZone( EngageZone )
|
||||
self:F2()
|
||||
|
||||
if EngageZone then
|
||||
self.EngageZone = EngageZone
|
||||
else
|
||||
self.EngageZone = nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Specifies whether to search for potential targets in the zone, or let the center of the zone be the bombing coordinate.
|
||||
-- AI_BAI_ZONE will search for potential targets by default.
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @return #AI_BAI_ZONE
|
||||
function AI_BAI_ZONE:SearchOnOff( Search )
|
||||
|
||||
self.Search = Search
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- If Search is Off, the current zone coordinate will be the center of the bombing.
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @return #AI_BAI_ZONE
|
||||
function AI_BAI_ZONE:SearchOff()
|
||||
|
||||
self:SearchOnOff( false )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- If Search is On, BAI will search for potential targets in the zone.
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @return #AI_BAI_ZONE
|
||||
function AI_BAI_ZONE:SearchOn()
|
||||
|
||||
self:SearchOnOff( true )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- onafter State Transition for Event Start.
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_BAI_ZONE:onafterStart( Controllable, From, Event, To )
|
||||
|
||||
-- Call the parent Start event handler
|
||||
self:GetParent(self).onafterStart( self, Controllable, From, Event, To )
|
||||
self:HandleEvent( EVENTS.Dead )
|
||||
|
||||
self:SetDetectionDeactivated() -- When not engaging, set the detection off.
|
||||
end
|
||||
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE AIControllable
|
||||
function _NewEngageRoute( AIControllable )
|
||||
|
||||
AIControllable:T( "NewEngageRoute" )
|
||||
local EngageZone = AIControllable:GetState( AIControllable, "EngageZone" ) -- AI.AI_BAI#AI_BAI_ZONE
|
||||
EngageZone:__Engage( 1, EngageZone.EngageSpeed, EngageZone.EngageAltitude, EngageZone.EngageWeaponExpend, EngageZone.EngageAttackQty, EngageZone.EngageDirection )
|
||||
end
|
||||
|
||||
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_BAI_ZONE:onbeforeEngage( Controllable, From, Event, To )
|
||||
|
||||
if self.Accomplished == true then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
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 -- Wrapper.Unit#UNIT
|
||||
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
|
||||
|
||||
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_BAI_ZONE:onafterAbort( Controllable, From, Event, To )
|
||||
Controllable:ClearTasks()
|
||||
self:__Route( 1 )
|
||||
end
|
||||
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone.
|
||||
-- @param DCS#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement.
|
||||
-- @param DCS#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.
|
||||
-- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
|
||||
-- @param DCS#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
|
||||
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 = {}
|
||||
|
||||
--- Calculate the current route point.
|
||||
local CurrentVec2 = self.Controllable:GetVec2()
|
||||
|
||||
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||
local CurrentAltitude = self.Controllable:GetAltitude()
|
||||
local CurrentPointVec3 = COORDINATE:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToEngageZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.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 -- Wrapper.Unit#UNIT
|
||||
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 )
|
||||
|
||||
--- Define a random point in the @{Core.Zone}. The AI will fly to that point within the zone.
|
||||
|
||||
--- Find a random 2D point in EngageZone.
|
||||
local ToTargetVec2 = self.EngageZone:GetRandomVec2()
|
||||
self:T2( ToTargetVec2 )
|
||||
|
||||
--- Obtain a 3D @{Point} from the 2D point + altitude.
|
||||
local ToTargetPointVec3 = COORDINATE:New( ToTargetVec2.x, self.EngageAltitude, ToTargetVec2.y )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToTargetRoutePoint = ToTargetPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
self.EngageSpeed,
|
||||
true
|
||||
)
|
||||
|
||||
EngageRoute[#EngageRoute+1] = ToTargetRoutePoint
|
||||
|
||||
Controllable:OptionROEOpenFire()
|
||||
Controllable:OptionROTVertical()
|
||||
|
||||
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
|
||||
Controllable:WayPointInitialize( EngageRoute )
|
||||
|
||||
--- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ...
|
||||
Controllable:SetState( Controllable, "EngageZone", self )
|
||||
|
||||
Controllable:WayPointFunction( #EngageRoute, 1, "_NewEngageRoute" )
|
||||
|
||||
--- NOW ROUTE THE GROUP!
|
||||
Controllable:WayPointExecute( 1 )
|
||||
|
||||
self:SetRefreshTimeInterval( 2 )
|
||||
self:SetDetectionActivated()
|
||||
self:__Target( -2 ) -- Start targeting
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_BAI_ZONE:onafterAccomplish( Controllable, From, Event, To )
|
||||
self.Accomplished = true
|
||||
self:SetDetectionDeactivated()
|
||||
end
|
||||
|
||||
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function AI_BAI_ZONE:onafterDestroy( Controllable, From, Event, To, EventData )
|
||||
|
||||
if EventData.IniUnit then
|
||||
self.DetectedUnits[EventData.IniUnit] = nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- @param #AI_BAI_ZONE self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
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
|
||||
|
||||
|
||||
@@ -1,308 +0,0 @@
|
||||
--- **AI** - Balance player slots with AI to create an engaging simulation environment, independent of the amount of players.
|
||||
--
|
||||
-- **Features:**
|
||||
--
|
||||
-- * Automatically spawn AI as a replacement of free player slots for a coalition.
|
||||
-- * Make the AI to perform tasks.
|
||||
-- * Define a maximum amount of AI to be active at the same time.
|
||||
-- * Configure the behaviour of AI when a human joins a slot for which an AI is active.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_Balancer)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [YouTube Playlist](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl2CJVIrL1TdAumuVS8n64B7)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- * **Dutch_Baron**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Balancer
|
||||
-- @image AI_Balancing.JPG
|
||||
|
||||
-- @type AI_BALANCER
|
||||
-- @field Core.Set#SET_CLIENT SetClient
|
||||
-- @field Core.Spawn#SPAWN SpawnAI
|
||||
-- @field Wrapper.Group#GROUP Test
|
||||
-- @extends Core.Fsm#FSM_SET
|
||||
|
||||
--- 
|
||||
--
|
||||
-- Monitors and manages as many replacement AI groups as there are
|
||||
-- CLIENTS in a SET\_CLIENT collection, which are not occupied by human players.
|
||||
-- In other words, use AI_BALANCER to simulate human behaviour by spawning in replacement AI in multi player missions.
|
||||
--
|
||||
-- The parent class @{Core.Fsm#FSM_SET} manages the functionality to control the Finite State Machine (FSM).
|
||||
-- The mission designer can tailor the behaviour of the AI_BALANCER, by defining event and state transition methods.
|
||||
-- An explanation about state and event transition methods can be found in the @{Core.Fsm} module documentation.
|
||||
--
|
||||
-- The mission designer can tailor the AI_BALANCER behaviour, by implementing a state or event handling method for the following:
|
||||
--
|
||||
-- * @{#AI_BALANCER.OnAfterSpawned}( AISet, From, Event, To, AIGroup ): Define to add extra logic when an AI is spawned.
|
||||
--
|
||||
-- ## 1. AI_BALANCER construction
|
||||
--
|
||||
-- Create a new AI_BALANCER object with the @{#AI_BALANCER.New}() method:
|
||||
--
|
||||
-- ## 2. AI_BALANCER is a FSM
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ### 2.1. AI_BALANCER States
|
||||
--
|
||||
-- * **Monitoring** ( Set ): Monitoring the Set if all AI is spawned for the Clients.
|
||||
-- * **Spawning** ( Set, ClientName ): There is a new AI group spawned with ClientName as the name of reference.
|
||||
-- * **Spawned** ( Set, AIGroup ): A new AI has been spawned. You can handle this event to customize the AI behaviour with other AI FSMs or own processes.
|
||||
-- * **Destroying** ( Set, AIGroup ): The AI is being destroyed.
|
||||
-- * **Returning** ( Set, AIGroup ): The AI is returning to the airbase specified by the ReturnToAirbase methods. Handle this state to customize the return behaviour of the AI, if any.
|
||||
--
|
||||
-- ### 2.2. AI_BALANCER Events
|
||||
--
|
||||
-- * **Monitor** ( Set ): Every 10 seconds, the Monitor event is triggered to monitor the Set.
|
||||
-- * **Spawn** ( Set, ClientName ): Triggers when there is a new AI group to be spawned with ClientName as the name of reference.
|
||||
-- * **Spawned** ( Set, AIGroup ): Triggers when a new AI has been spawned. You can handle this event to customize the AI behaviour with other AI FSMs or own processes.
|
||||
-- * **Destroy** ( Set, AIGroup ): The AI is being destroyed.
|
||||
-- * **Return** ( Set, AIGroup ): The AI is returning to the airbase specified by the ReturnToAirbase methods.
|
||||
--
|
||||
-- ## 3. AI_BALANCER spawn interval for replacement AI
|
||||
--
|
||||
-- Use the method @{#AI_BALANCER.InitSpawnInterval}() to set the earliest and latest interval in seconds that is waited until a new replacement AI is spawned.
|
||||
--
|
||||
-- ## 4. AI_BALANCER returns AI to Airbases
|
||||
--
|
||||
-- By default, When a human player joins a slot that is AI_BALANCED, the AI group will be destroyed by default.
|
||||
-- However, there are 2 additional options that you can use to customize the destroy behaviour.
|
||||
-- When a human player joins a slot, you can configure to let the AI return to:
|
||||
--
|
||||
-- * @{#AI_BALANCER.ReturnToHomeAirbase}: Returns the AI to the **home** @{Wrapper.Airbase#AIRBASE}.
|
||||
-- * @{#AI_BALANCER.ReturnToNearestAirbases}: Returns the AI to the **nearest friendly** @{Wrapper.Airbase#AIRBASE}.
|
||||
--
|
||||
-- Note that when AI returns to an airbase, the AI_BALANCER will trigger the **Return** event and the AI will return,
|
||||
-- otherwise the AI_BALANCER will trigger a **Destroy** event, and the AI will be destroyed.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- @field #AI_BALANCER
|
||||
AI_BALANCER = {
|
||||
ClassName = "AI_BALANCER",
|
||||
PatrolZones = {},
|
||||
AIGroups = {},
|
||||
Earliest = 5, -- Earliest a new AI can be spawned is in 5 seconds.
|
||||
Latest = 60, -- Latest a new AI can be spawned is in 60 seconds.
|
||||
}
|
||||
|
||||
|
||||
|
||||
--- Creates a new AI_BALANCER object
|
||||
-- @param #AI_BALANCER self
|
||||
-- @param Core.Set#SET_CLIENT SetClient A SET\_CLIENT object that will contain the CLIENT objects to be monitored if they are alive or not (joined by a player).
|
||||
-- @param Core.Spawn#SPAWN SpawnAI The default Spawn object to spawn new AI Groups when needed.
|
||||
-- @return #AI_BALANCER
|
||||
function AI_BALANCER:New( SetClient, SpawnAI )
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, FSM_SET:New( SET_GROUP:New() ) ) -- AI.AI_Balancer#AI_BALANCER
|
||||
|
||||
-- TODO: Define the OnAfterSpawned event
|
||||
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
|
||||
|
||||
--- Sets the earliest to the latest interval in seconds how long AI_BALANCER will wait to spawn a new AI.
|
||||
-- Provide 2 identical seconds if the interval should be a fixed amount of seconds.
|
||||
-- @param #AI_BALANCER self
|
||||
-- @param #number Earliest The earliest a new AI can be spawned in seconds.
|
||||
-- @param #number Latest The latest a new AI can be spawned in seconds.
|
||||
-- @return self
|
||||
function AI_BALANCER:InitSpawnInterval( Earliest, Latest )
|
||||
|
||||
self.Earliest = Earliest
|
||||
self.Latest = Latest
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Returns the AI to the nearest friendly @{Wrapper.Airbase#AIRBASE}.
|
||||
-- @param #AI_BALANCER self
|
||||
-- @param DCS#Distance ReturnThresholdRange If there is an enemy @{Wrapper.Client#CLIENT} within the ReturnThresholdRange given in meters, the AI will not return to the nearest @{Wrapper.Airbase#AIRBASE}.
|
||||
-- @param Core.Set#SET_AIRBASE ReturnAirbaseSet The SET of @{Core.Set#SET_AIRBASE}s to evaluate where to return to.
|
||||
function AI_BALANCER:ReturnToNearestAirbases( ReturnThresholdRange, ReturnAirbaseSet )
|
||||
|
||||
self.ToNearestAirbase = true
|
||||
self.ReturnThresholdRange = ReturnThresholdRange
|
||||
self.ReturnAirbaseSet = ReturnAirbaseSet
|
||||
end
|
||||
|
||||
--- Returns the AI to the home @{Wrapper.Airbase#AIRBASE}.
|
||||
-- @param #AI_BALANCER self
|
||||
-- @param DCS#Distance ReturnThresholdRange If there is an enemy @{Wrapper.Client#CLIENT} within the ReturnThresholdRange given in meters, the AI will not return to the nearest @{Wrapper.Airbase#AIRBASE}.
|
||||
function AI_BALANCER:ReturnToHomeAirbase( ReturnThresholdRange )
|
||||
|
||||
self.ToHomeAirbase = true
|
||||
self.ReturnThresholdRange = ReturnThresholdRange
|
||||
end
|
||||
|
||||
--- AI_BALANCER:onenterSpawning
|
||||
-- @param #AI_BALANCER self
|
||||
-- @param Core.Set#SET_GROUP SetGroup
|
||||
-- @param #string ClientName
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
function AI_BALANCER:onenterSpawning( SetGroup, From, Event, To, ClientName )
|
||||
|
||||
-- OK, Spawn a new group from the default SpawnAI object provided.
|
||||
local AIGroup = self.SpawnAI:Spawn() -- Wrapper.Group#GROUP
|
||||
if AIGroup then
|
||||
AIGroup:T( { "Spawning new AIGroup", ClientName = ClientName } )
|
||||
--TODO: need to rework UnitName thing ...
|
||||
|
||||
SetGroup:Remove( ClientName ) -- Ensure that the previously allocated AIGroup to ClientName is removed in the Set.
|
||||
SetGroup:Add( ClientName, AIGroup )
|
||||
self.SpawnQueue[ClientName] = nil
|
||||
|
||||
-- Fire the Spawned event. The first parameter is the AIGroup just Spawned.
|
||||
-- Mission designers can catch this event to bind further actions to the AIGroup.
|
||||
self:Spawned( AIGroup )
|
||||
end
|
||||
end
|
||||
|
||||
--- AI_BALANCER:onenterDestroying
|
||||
-- @param #AI_BALANCER self
|
||||
-- @param Core.Set#SET_GROUP SetGroup
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
function AI_BALANCER:onenterDestroying( SetGroup, From, Event, To, ClientName, AIGroup )
|
||||
|
||||
AIGroup:Destroy()
|
||||
SetGroup:Flush( self )
|
||||
SetGroup:Remove( ClientName )
|
||||
SetGroup:Flush( self )
|
||||
end
|
||||
|
||||
--- RTB
|
||||
-- @param #AI_BALANCER self
|
||||
-- @param Core.Set#SET_GROUP SetGroup
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
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
|
||||
-- Okay, we need to send this Group back to the nearest base of the Coalition of the AI.
|
||||
local PointVec2 = COORDINATE:New(AIGroup:GetVec2().x, 0, AIGroup:GetVec2().y)
|
||||
local ClosestAirbase = self.ReturnAirbaseSet:FindNearestAirbaseFromPointVec2( PointVec2 )
|
||||
self:T( ClosestAirbase.AirbaseName )
|
||||
AIGroup:RouteRTB(ClosestAirbase)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- AI_BALANCER:onenterMonitoring
|
||||
-- @param #AI_BALANCER self
|
||||
function AI_BALANCER:onenterMonitoring( SetGroup )
|
||||
|
||||
self:T2( { self.SetClient:Count() } )
|
||||
--self.SetClient:Flush()
|
||||
|
||||
self.SetClient:ForEachClient(
|
||||
--- SetClient:ForEachClient
|
||||
-- @param Wrapper.Client#CLIENT Client
|
||||
function( Client )
|
||||
self:T3(Client.ClientName)
|
||||
|
||||
local AIGroup = self.Set:Get( Client.UnitName ) -- Wrapper.Group#GROUP
|
||||
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
|
||||
-- We test if there is no other CLIENT within the self.ReturnThresholdRange of the first unit of the AI group.
|
||||
-- If there is a CLIENT, the AI stays engaged and will not return.
|
||||
-- If there is no CLIENT within the self.ReturnThresholdRange, then the unit will return to the Airbase return method selected.
|
||||
|
||||
local PlayerInRange = { Value = false }
|
||||
local RangeZone = ZONE_RADIUS:New( 'RangeZone', AIGroup:GetVec2(), self.ReturnThresholdRange )
|
||||
|
||||
self:T2( RangeZone )
|
||||
|
||||
_DATABASE:ForEachPlayerUnit(
|
||||
--- Nameless function
|
||||
-- @param Wrapper.Unit#UNIT RangeTestUnit
|
||||
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,
|
||||
|
||||
--- Nameless function
|
||||
-- @param Core.Zone#ZONE_RADIUS RangeZone
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
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
|
||||
-- Spawn a new AI taking into account the spawn interval Earliest, Latest
|
||||
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
|
||||
@@ -1,543 +0,0 @@
|
||||
--- **AI** - Perform Combat Air Patrolling (CAP) for airplanes.
|
||||
--
|
||||
-- **Features:**
|
||||
--
|
||||
-- * Patrol AI airplanes within a given zone.
|
||||
-- * Trigger detected events when enemy airplanes are detected.
|
||||
-- * Manage a fuel threshold to RTB on time.
|
||||
-- * Engage the enemy when detected.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_CAP)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [YouTube Playlist](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl1YCyPxJgoZn-CfhwyeW65L)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- * **Quax**: Concept, Advice & Testing.
|
||||
-- * **Pikey**: Concept, Advice & Testing.
|
||||
-- * **Gunterlund**: Test case revision.
|
||||
-- * **Whisper**: Testing.
|
||||
-- * **Delta99**: Testing.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_CAP
|
||||
-- @image AI_Combat_Air_Patrol.JPG
|
||||
|
||||
-- @type AI_CAP_ZONE
|
||||
-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Wrapper.Controllable} patrolling.
|
||||
-- @field Core.Zone#ZONE_BASE TargetZone The @{Core.Zone} where the patrol needs to be executed.
|
||||
-- @extends AI.AI_Patrol#AI_PATROL_ZONE
|
||||
|
||||
--- Implements the core functions to patrol a @{Core.Zone} by an AI @{Wrapper.Controllable} or @{Wrapper.Group}
|
||||
-- and automatically engage any airborne enemies that are within a certain range or within a certain zone.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The AI_CAP_ZONE is assigned a @{Wrapper.Group} and this must be done before the AI_CAP_ZONE process can be started using the **Start** event.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits.
|
||||
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- This cycle will continue.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- When enemies are detected, the AI will automatically engage the enemy.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ## 1. AI_CAP_ZONE constructor
|
||||
--
|
||||
-- * @{#AI_CAP_ZONE.New}(): Creates a new AI_CAP_ZONE object.
|
||||
--
|
||||
-- ## 2. AI_CAP_ZONE is a FSM
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ### 2.1 AI_CAP_ZONE States
|
||||
--
|
||||
-- * **None** ( Group ): The process is not started yet.
|
||||
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
|
||||
-- * **Engaging** ( Group ): The AI is engaging the bogeys.
|
||||
-- * **Returning** ( Group ): The AI is returning to Base..
|
||||
--
|
||||
-- ### 2.2 AI_CAP_ZONE Events
|
||||
--
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone.
|
||||
-- * **@{#AI_CAP_ZONE.Engage}**: Let the AI engage the bogeys.
|
||||
-- * **@{#AI_CAP_ZONE.Abort}**: Aborts the engagement and return patrolling in the patrol zone.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
|
||||
-- * **@{#AI_CAP_ZONE.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
|
||||
-- * **@{#AI_CAP_ZONE.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task.
|
||||
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
|
||||
--
|
||||
-- ## 3. Set the Range of Engagement
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- An optional range can be set in meters,
|
||||
-- that will define when the AI will engage with the detected airborne enemy targets.
|
||||
-- The range can be beyond or smaller than the range of the Patrol Zone.
|
||||
-- The range is applied at the position of the AI.
|
||||
-- Use the method @{#AI_CAP_ZONE.SetEngageRange}() to define that range.
|
||||
--
|
||||
-- ## 4. Set the Zone of Engagement
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- An optional @{Core.Zone} can be set,
|
||||
-- that will define when the AI will engage with the detected airborne enemy targets.
|
||||
-- Use the method @{#AI_CAP_ZONE.SetEngageZone}() to define that Zone.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_CAP_ZONE
|
||||
AI_CAP_ZONE = {
|
||||
ClassName = "AI_CAP_ZONE",
|
||||
}
|
||||
|
||||
--- Creates a new AI_CAP_ZONE object
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
|
||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h.
|
||||
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
|
||||
-- @return #AI_CAP_ZONE self
|
||||
function AI_CAP_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, AI_PATROL_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) ) -- #AI_CAP_ZONE
|
||||
|
||||
self.Accomplished = false
|
||||
self.Engaging = false
|
||||
|
||||
self:AddTransition( { "Patrolling", "Engaging" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Engage.
|
||||
-- @function [parent=#AI_CAP_ZONE] OnBeforeEngage
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Engage.
|
||||
-- @function [parent=#AI_CAP_ZONE] OnAfterEngage
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Engage.
|
||||
-- @function [parent=#AI_CAP_ZONE] Engage
|
||||
-- @param #AI_CAP_ZONE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Engage.
|
||||
-- @function [parent=#AI_CAP_ZONE] __Engage
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
--- OnLeave Transition Handler for State Engaging.
|
||||
-- @function [parent=#AI_CAP_ZONE] OnLeaveEngaging
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnEnter Transition Handler for State Engaging.
|
||||
-- @function [parent=#AI_CAP_ZONE] OnEnterEngaging
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
self:AddTransition( "Engaging", "Fired", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Fired.
|
||||
-- @function [parent=#AI_CAP_ZONE] OnBeforeFired
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Fired.
|
||||
-- @function [parent=#AI_CAP_ZONE] OnAfterFired
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Fired.
|
||||
-- @function [parent=#AI_CAP_ZONE] Fired
|
||||
-- @param #AI_CAP_ZONE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Fired.
|
||||
-- @function [parent=#AI_CAP_ZONE] __Fired
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Destroy.
|
||||
-- @function [parent=#AI_CAP_ZONE] OnBeforeDestroy
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Destroy.
|
||||
-- @function [parent=#AI_CAP_ZONE] OnAfterDestroy
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Destroy.
|
||||
-- @function [parent=#AI_CAP_ZONE] Destroy
|
||||
-- @param #AI_CAP_ZONE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Destroy.
|
||||
-- @function [parent=#AI_CAP_ZONE] __Destroy
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Abort.
|
||||
-- @function [parent=#AI_CAP_ZONE] OnBeforeAbort
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Abort.
|
||||
-- @function [parent=#AI_CAP_ZONE] OnAfterAbort
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Abort.
|
||||
-- @function [parent=#AI_CAP_ZONE] Abort
|
||||
-- @param #AI_CAP_ZONE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Abort.
|
||||
-- @function [parent=#AI_CAP_ZONE] __Abort
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Accomplish.
|
||||
-- @function [parent=#AI_CAP_ZONE] OnBeforeAccomplish
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Accomplish.
|
||||
-- @function [parent=#AI_CAP_ZONE] OnAfterAccomplish
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Accomplish.
|
||||
-- @function [parent=#AI_CAP_ZONE] Accomplish
|
||||
-- @param #AI_CAP_ZONE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Accomplish.
|
||||
-- @function [parent=#AI_CAP_ZONE] __Accomplish
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the Engage Zone which defines where the AI will engage bogies.
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param Core.Zone#ZONE EngageZone The zone where the AI is performing CAP.
|
||||
-- @return #AI_CAP_ZONE self
|
||||
function AI_CAP_ZONE:SetEngageZone( EngageZone )
|
||||
self:F2()
|
||||
|
||||
if EngageZone then
|
||||
self.EngageZone = EngageZone
|
||||
else
|
||||
self.EngageZone = nil
|
||||
end
|
||||
end
|
||||
|
||||
--- Set the Engage Range when the AI will engage with airborne enemies.
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param #number EngageRange The Engage Range.
|
||||
-- @return #AI_CAP_ZONE self
|
||||
function AI_CAP_ZONE:SetEngageRange( EngageRange )
|
||||
self:F2()
|
||||
|
||||
if EngageRange then
|
||||
self.EngageRange = EngageRange
|
||||
else
|
||||
self.EngageRange = nil
|
||||
end
|
||||
end
|
||||
|
||||
--- onafter State Transition for Event Start.
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_CAP_ZONE:onafterStart( Controllable, From, Event, To )
|
||||
|
||||
-- Call the parent Start event handler
|
||||
self:GetParent(self).onafterStart( self, Controllable, From, Event, To )
|
||||
self:HandleEvent( EVENTS.Dead )
|
||||
|
||||
end
|
||||
|
||||
-- @param AI.AI_CAP#AI_CAP_ZONE
|
||||
-- @param Wrapper.Group#GROUP EngageGroup
|
||||
function AI_CAP_ZONE.EngageRoute( EngageGroup, Fsm )
|
||||
|
||||
EngageGroup:F( { "AI_CAP_ZONE.EngageRoute:", EngageGroup:GetName() } )
|
||||
|
||||
if EngageGroup:IsAlive() then
|
||||
Fsm:__Engage( 1 )
|
||||
end
|
||||
end
|
||||
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_CAP_ZONE:onbeforeEngage( Controllable, From, Event, To )
|
||||
|
||||
if self.Accomplished == true then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_CAP_ZONE:onafterDetected( Controllable, From, Event, To )
|
||||
|
||||
if From ~= "Engaging" then
|
||||
|
||||
local Engage = false
|
||||
|
||||
for DetectedUnit, Detected in pairs( self.DetectedUnits ) do
|
||||
|
||||
local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT
|
||||
self:T( DetectedUnit )
|
||||
if DetectedUnit:IsAlive() and DetectedUnit:IsAir() then
|
||||
Engage = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if Engage == true then
|
||||
self:F( 'Detected -> Engaging' )
|
||||
self:__Engage( 1 )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_CAP_ZONE:onafterAbort( Controllable, From, Event, To )
|
||||
Controllable:ClearTasks()
|
||||
self:__Route( 1 )
|
||||
end
|
||||
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
|
||||
|
||||
if Controllable and Controllable:IsAlive() then
|
||||
|
||||
local EngageRoute = {}
|
||||
|
||||
--- Calculate the current route point.
|
||||
local CurrentVec2 = self.Controllable:GetVec2()
|
||||
|
||||
if not CurrentVec2 then return self end
|
||||
|
||||
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||
local CurrentAltitude = self.Controllable:GetAltitude()
|
||||
local CurrentPointVec3 = COORDINATE:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToEngageZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
ToEngageZoneSpeed,
|
||||
true
|
||||
)
|
||||
|
||||
EngageRoute[#EngageRoute+1] = CurrentRoutePoint
|
||||
|
||||
--- Find a random 2D point in PatrolZone.
|
||||
local ToTargetVec2 = self.PatrolZone:GetRandomVec2()
|
||||
self:T2( ToTargetVec2 )
|
||||
|
||||
--- Define Speed and Altitude.
|
||||
local ToTargetAltitude = math.random( self.EngageFloorAltitude, self.EngageCeilingAltitude )
|
||||
local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
|
||||
self:T2( { self.PatrolMinSpeed, self.PatrolMaxSpeed, ToTargetSpeed } )
|
||||
|
||||
--- Obtain a 3D @{Point} from the 2D point + altitude.
|
||||
local ToTargetPointVec3 = COORDINATE:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToPatrolRoutePoint = ToTargetPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
ToTargetSpeed,
|
||||
true
|
||||
)
|
||||
|
||||
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
|
||||
|
||||
Controllable:OptionROEOpenFire()
|
||||
Controllable:OptionROTEvadeFire()
|
||||
|
||||
local AttackTasks = {}
|
||||
|
||||
for DetectedUnit, Detected in pairs( self.DetectedUnits ) do
|
||||
local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT
|
||||
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
|
||||
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_CAP_ZONE:onafterAccomplish( Controllable, From, Event, To )
|
||||
self.Accomplished = true
|
||||
self:SetDetectionOff()
|
||||
end
|
||||
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function AI_CAP_ZONE:onafterDestroy( Controllable, From, Event, To, EventData )
|
||||
|
||||
if EventData.IniUnit then
|
||||
self.DetectedUnits[EventData.IniUnit] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- @param #AI_CAP_ZONE self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
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
|
||||
@@ -1,572 +0,0 @@
|
||||
--- **AI** - Perform Close Air Support (CAS) near friendlies.
|
||||
--
|
||||
-- **Features:**
|
||||
--
|
||||
-- * Hold and standby within a patrol zone.
|
||||
-- * Engage upon command the enemies within an engagement zone.
|
||||
-- * Loop the zone until all enemies are eliminated.
|
||||
-- * Trigger different events upon the results achieved.
|
||||
-- * After combat, return to the patrol zone and hold.
|
||||
-- * RTB when commanded or after fuel.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_CAS)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [YouTube Playlist](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl3JBO1WDqqpyYRRmIkR2ir2)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- * **Quax**: Concept, Advice & Testing.
|
||||
-- * **Pikey**: Concept, Advice & Testing.
|
||||
-- * **Gunterlund**: Test case revision.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_CAS
|
||||
-- @image AI_Close_Air_Support.JPG
|
||||
|
||||
--- AI_CAS_ZONE class
|
||||
-- @type AI_CAS_ZONE
|
||||
-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Wrapper.Controllable} patrolling.
|
||||
-- @field Core.Zone#ZONE_BASE TargetZone The @{Core.Zone} where the patrol needs to be executed.
|
||||
-- @extends AI.AI_Patrol#AI_PATROL_ZONE
|
||||
|
||||
--- Implements the core functions to provide Close Air Support in an Engage @{Core.Zone} by an AIR @{Wrapper.Controllable} or @{Wrapper.Group}.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The AI_CAS_ZONE runs a process. It holds an AI in a Patrol Zone and when the AI is commanded to engage, it will fly to an Engage Zone.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The AI_CAS_ZONE is assigned a @{Wrapper.Group} and this must be done before the AI_CAS_ZONE process can be started through the **Start** event.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Upon started, The AI will **Route** itself towards the random 3D point within a patrol zone,
|
||||
-- using a random speed within the given altitude and speed limits.
|
||||
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
|
||||
-- This cycle will continue until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- When the AI is commanded to provide Close Air Support (through the event **Engage**), the AI will fly towards the Engage Zone.
|
||||
-- Any target that is detected in the Engage Zone will be reported and will be destroyed by the AI.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The AI will detect the targets and will only destroy the targets within the Engage Zone.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Every target that is destroyed, is reported< by the AI.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Note that the AI does not know when the Engage Zone is cleared, and therefore will keep circling in the zone.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Until it is notified through the event **Accomplish**, which is to be triggered by an observing party:
|
||||
--
|
||||
-- * a FAC
|
||||
-- * a timed event
|
||||
-- * a menu option selected by a human
|
||||
-- * a condition
|
||||
-- * others ...
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- When the AI has accomplished the CAS, it will fly back to the Patrol Zone.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- It will keep patrolling there, until it is notified to RTB or move to another CAS Zone.
|
||||
-- It can be notified to go RTB through the **RTB** event.
|
||||
--
|
||||
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ## AI_CAS_ZONE constructor
|
||||
--
|
||||
-- * @{#AI_CAS_ZONE.New}(): Creates a new AI_CAS_ZONE object.
|
||||
--
|
||||
-- ## AI_CAS_ZONE is a FSM
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ### 2.1. AI_CAS_ZONE States
|
||||
--
|
||||
-- * **None** ( Group ): The process is not started yet.
|
||||
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
|
||||
-- * **Engaging** ( Group ): The AI is engaging the targets in the Engage Zone, executing CAS.
|
||||
-- * **Returning** ( Group ): The AI is returning to Base..
|
||||
--
|
||||
-- ### 2.2. AI_CAS_ZONE Events
|
||||
--
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone.
|
||||
-- * **@{#AI_CAS_ZONE.Engage}**: Engage the AI to provide CAS in the Engage Zone, destroying any target it finds.
|
||||
-- * **@{#AI_CAS_ZONE.Abort}**: Aborts the engagement and return patrolling in the patrol zone.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
|
||||
-- * **@{#AI_CAS_ZONE.Destroy}**: The AI has destroyed a target @{Wrapper.Unit}.
|
||||
-- * **@{#AI_CAS_ZONE.Destroyed}**: The AI has destroyed all target @{Wrapper.Unit}s assigned in the CAS task.
|
||||
-- * **Status**: The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_CAS_ZONE
|
||||
AI_CAS_ZONE = {
|
||||
ClassName = "AI_CAS_ZONE",
|
||||
}
|
||||
|
||||
|
||||
|
||||
--- Creates a new AI_CAS_ZONE object
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
|
||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h.
|
||||
-- @param Core.Zone#ZONE_BASE EngageZone The zone where the engage will happen.
|
||||
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
|
||||
-- @return #AI_CAS_ZONE self
|
||||
function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageZone, PatrolAltType )
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, AI_PATROL_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) ) -- #AI_CAS_ZONE
|
||||
|
||||
self.EngageZone = EngageZone
|
||||
self.Accomplished = false
|
||||
|
||||
self:SetDetectionZone( self.EngageZone )
|
||||
|
||||
self:AddTransition( { "Patrolling", "Engaging" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_CAS_ZONE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Engage.
|
||||
-- @function [parent=#AI_CAS_ZONE] OnBeforeEngage
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Engage.
|
||||
-- @function [parent=#AI_CAS_ZONE] OnAfterEngage
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Engage.
|
||||
-- @function [parent=#AI_CAS_ZONE] Engage
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone.
|
||||
-- @param DCS#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement.
|
||||
-- @param DCS#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack.
|
||||
-- If parameter is not defined the unit / controllable will choose expend on its own discretion.
|
||||
-- Use the structure @{DCS#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack.
|
||||
-- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
|
||||
-- @param DCS#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
|
||||
|
||||
--- Asynchronous Event Trigger for Event Engage.
|
||||
-- @function [parent=#AI_CAS_ZONE] __Engage
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
-- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone.
|
||||
-- @param DCS#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement.
|
||||
-- @param DCS#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack.
|
||||
-- If parameter is not defined the unit / controllable will choose expend on its own discretion.
|
||||
-- Use the structure @{DCS#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack.
|
||||
-- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
|
||||
-- @param DCS#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
|
||||
|
||||
--- OnLeave Transition Handler for State Engaging.
|
||||
-- @function [parent=#AI_CAS_ZONE] OnLeaveEngaging
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnEnter Transition Handler for State Engaging.
|
||||
-- @function [parent=#AI_CAS_ZONE] OnEnterEngaging
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
self:AddTransition( "Engaging", "Target", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_CAS_ZONE.
|
||||
|
||||
self:AddTransition( "Engaging", "Fired", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_CAS_ZONE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Fired.
|
||||
-- @function [parent=#AI_CAS_ZONE] OnBeforeFired
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Fired.
|
||||
-- @function [parent=#AI_CAS_ZONE] OnAfterFired
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Fired.
|
||||
-- @function [parent=#AI_CAS_ZONE] Fired
|
||||
-- @param #AI_CAS_ZONE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Fired.
|
||||
-- @function [parent=#AI_CAS_ZONE] __Fired
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_CAS_ZONE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Destroy.
|
||||
-- @function [parent=#AI_CAS_ZONE] OnBeforeDestroy
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Destroy.
|
||||
-- @function [parent=#AI_CAS_ZONE] OnAfterDestroy
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Destroy.
|
||||
-- @function [parent=#AI_CAS_ZONE] Destroy
|
||||
-- @param #AI_CAS_ZONE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Destroy.
|
||||
-- @function [parent=#AI_CAS_ZONE] __Destroy
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
|
||||
self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_CAS_ZONE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Abort.
|
||||
-- @function [parent=#AI_CAS_ZONE] OnBeforeAbort
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Abort.
|
||||
-- @function [parent=#AI_CAS_ZONE] OnAfterAbort
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Abort.
|
||||
-- @function [parent=#AI_CAS_ZONE] Abort
|
||||
-- @param #AI_CAS_ZONE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Abort.
|
||||
-- @function [parent=#AI_CAS_ZONE] __Abort
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_CAS_ZONE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Accomplish.
|
||||
-- @function [parent=#AI_CAS_ZONE] OnBeforeAccomplish
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Accomplish.
|
||||
-- @function [parent=#AI_CAS_ZONE] OnAfterAccomplish
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Accomplish.
|
||||
-- @function [parent=#AI_CAS_ZONE] Accomplish
|
||||
-- @param #AI_CAS_ZONE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Accomplish.
|
||||
-- @function [parent=#AI_CAS_ZONE] __Accomplish
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set the Engage Zone where the AI is performing CAS. Note that if the EngageZone is changed, the AI needs to re-detect targets.
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param Core.Zone#ZONE EngageZone The zone where the AI is performing CAS.
|
||||
-- @return #AI_CAS_ZONE self
|
||||
function AI_CAS_ZONE:SetEngageZone( EngageZone )
|
||||
self:F2()
|
||||
|
||||
if EngageZone then
|
||||
self.EngageZone = EngageZone
|
||||
else
|
||||
self.EngageZone = nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- onafter State Transition for Event Start.
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_CAS_ZONE:onafterStart( Controllable, From, Event, To )
|
||||
|
||||
-- Call the parent Start event handler
|
||||
self:GetParent(self).onafterStart( self, Controllable, From, Event, To )
|
||||
self:HandleEvent( EVENTS.Dead )
|
||||
|
||||
self:SetDetectionDeactivated() -- When not engaging, set the detection off.
|
||||
end
|
||||
|
||||
-- @param AI.AI_CAS#AI_CAS_ZONE
|
||||
-- @param Wrapper.Group#GROUP EngageGroup
|
||||
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
|
||||
|
||||
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_CAS_ZONE:onbeforeEngage( Controllable, From, Event, To )
|
||||
|
||||
if self.Accomplished == true then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
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 -- Wrapper.Unit#UNIT
|
||||
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
|
||||
|
||||
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_CAS_ZONE:onafterAbort( Controllable, From, Event, To )
|
||||
Controllable:ClearTasks()
|
||||
self:__Route( 1 )
|
||||
end
|
||||
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone.
|
||||
-- @param DCS#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement.
|
||||
-- @param DCS#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.
|
||||
-- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
|
||||
-- @param DCS#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
|
||||
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 = {}
|
||||
|
||||
--- Calculate the current route point.
|
||||
local CurrentVec2 = self.Controllable:GetVec2()
|
||||
|
||||
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||
local CurrentAltitude = self.Controllable:GetAltitude()
|
||||
local CurrentPointVec3 = COORDINATE:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToEngageZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
self.EngageSpeed,
|
||||
true
|
||||
)
|
||||
|
||||
EngageRoute[#EngageRoute+1] = CurrentRoutePoint
|
||||
|
||||
local AttackTasks = {}
|
||||
|
||||
for DetectedUnit, Detected in pairs( self.DetectedUnits ) do
|
||||
local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT
|
||||
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 )
|
||||
|
||||
--- Define a random point in the @{Core.Zone}. The AI will fly to that point within the zone.
|
||||
|
||||
--- Find a random 2D point in EngageZone.
|
||||
local ToTargetVec2 = self.EngageZone:GetRandomVec2()
|
||||
self:T2( ToTargetVec2 )
|
||||
|
||||
--- Obtain a 3D @{Point} from the 2D point + altitude.
|
||||
local ToTargetPointVec3 = COORDINATE:New( ToTargetVec2.x, self.EngageAltitude, ToTargetVec2.y )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToTargetRoutePoint = ToTargetPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
self.EngageSpeed,
|
||||
true
|
||||
)
|
||||
|
||||
EngageRoute[#EngageRoute+1] = ToTargetRoutePoint
|
||||
|
||||
Controllable:Route( EngageRoute, 0.5 )
|
||||
|
||||
self:SetRefreshTimeInterval( 2 )
|
||||
self:SetDetectionActivated()
|
||||
self:__Target( -2 ) -- Start targeting
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_CAS_ZONE:onafterAccomplish( Controllable, From, Event, To )
|
||||
self.Accomplished = true
|
||||
self:SetDetectionDeactivated()
|
||||
end
|
||||
|
||||
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function AI_CAS_ZONE:onafterDestroy( Controllable, From, Event, To, EventData )
|
||||
|
||||
if EventData.IniUnit then
|
||||
self.DetectedUnits[EventData.IniUnit] = nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- @param #AI_CAS_ZONE self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
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
|
||||
|
||||
|
||||
@@ -1,591 +0,0 @@
|
||||
--- **AI** - Models the intelligent transportation of infantry and other cargo.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Cargo
|
||||
-- @image Cargo.JPG
|
||||
|
||||
--- @type AI_CARGO
|
||||
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
||||
|
||||
|
||||
--- Base class for the dynamic cargo handling capability for AI groups.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Carriers can be mobilized to intelligently transport infantry and other cargo within the simulation.
|
||||
-- The AI_CARGO module uses the @{Cargo.Cargo} capabilities within the MOOSE framework.
|
||||
-- CARGO derived objects must be declared within the mission to make the AI_CARGO object recognize the cargo.
|
||||
-- Please consult the @{Cargo.Cargo} module for more information.
|
||||
--
|
||||
-- The derived classes from this module are:
|
||||
--
|
||||
-- * @{AI.AI_Cargo_APC} - Cargo transportation using APCs and other vehicles between zones.
|
||||
-- * @{AI.AI_Cargo_Helicopter} - Cargo transportation using helicopters between zones.
|
||||
-- * @{AI.AI_Cargo_Airplane} - Cargo transportation using airplanes to and from airbases.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- @field #AI_CARGO
|
||||
AI_CARGO = {
|
||||
ClassName = "AI_CARGO",
|
||||
Coordinate = nil, -- Core.Point#COORDINATE,
|
||||
Carrier_Cargo = {},
|
||||
}
|
||||
|
||||
--- Creates a new AI_CARGO object.
|
||||
-- @param #AI_CARGO self
|
||||
-- @param Wrapper.Group#GROUP Carrier Cargo carrier group.
|
||||
-- @param Core.Set#SET_CARGO CargoSet Set of cargo(s) to transport.
|
||||
-- @return #AI_CARGO self
|
||||
function AI_CARGO:New( Carrier, CargoSet )
|
||||
|
||||
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New( Carrier ) ) -- #AI_CARGO
|
||||
|
||||
self.CargoSet = CargoSet -- Core.Set#SET_CARGO
|
||||
self.CargoCarrier = Carrier -- Wrapper.Group#GROUP
|
||||
|
||||
self:SetStartState( "Unloaded" )
|
||||
|
||||
-- Board
|
||||
self:AddTransition( "Unloaded", "Pickup", "Unloaded" )
|
||||
self:AddTransition( "*", "Load", "*" )
|
||||
self:AddTransition( "*", "Reload", "*" )
|
||||
self:AddTransition( "*", "Board", "*" )
|
||||
self:AddTransition( "*", "Loaded", "Loaded" )
|
||||
self:AddTransition( "Loaded", "PickedUp", "Loaded" )
|
||||
|
||||
-- Unload
|
||||
self:AddTransition( "Loaded", "Deploy", "*" )
|
||||
self:AddTransition( "*", "Unload", "*" )
|
||||
self:AddTransition( "*", "Unboard", "*" )
|
||||
self:AddTransition( "*", "Unloaded", "Unloaded" )
|
||||
self:AddTransition( "Unloaded", "Deployed", "Unloaded" )
|
||||
|
||||
|
||||
--- Pickup Handler OnBefore for AI_CARGO
|
||||
-- @function [parent=#AI_CARGO] OnBeforePickup
|
||||
-- @param #AI_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do.
|
||||
-- @return #boolean
|
||||
|
||||
--- Pickup Handler OnAfter for AI_CARGO
|
||||
-- @function [parent=#AI_CARGO] OnAfterPickup
|
||||
-- @param #AI_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do.
|
||||
|
||||
--- Pickup Trigger for AI_CARGO
|
||||
-- @function [parent=#AI_CARGO] Pickup
|
||||
-- @param #AI_CARGO self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do.
|
||||
|
||||
--- Pickup Asynchronous Trigger for AI_CARGO
|
||||
-- @function [parent=#AI_CARGO] __Pickup
|
||||
-- @param #AI_CARGO self
|
||||
-- @param #number Delay
|
||||
-- @param Core.Point#COORDINATE Coordinate Pickup place. If not given, loading starts at the current location.
|
||||
-- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do.
|
||||
|
||||
--- Deploy Handler OnBefore for AI_CARGO
|
||||
-- @function [parent=#AI_CARGO] OnBeforeDeploy
|
||||
-- @param #AI_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do.
|
||||
-- @return #boolean
|
||||
|
||||
--- Deploy Handler OnAfter for AI_CARGO
|
||||
-- @function [parent=#AI_CARGO] OnAfterDeploy
|
||||
-- @param #AI_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do.
|
||||
|
||||
--- Deploy Trigger for AI_CARGO
|
||||
-- @function [parent=#AI_CARGO] Deploy
|
||||
-- @param #AI_CARGO self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do.
|
||||
|
||||
--- Deploy Asynchronous Trigger for AI_CARGO
|
||||
-- @function [parent=#AI_CARGO] __Deploy
|
||||
-- @param #AI_CARGO self
|
||||
-- @param #number Delay
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do.
|
||||
|
||||
|
||||
--- Loaded Handler OnAfter for AI_CARGO
|
||||
-- @function [parent=#AI_CARGO] OnAfterLoaded
|
||||
-- @param #AI_CARGO self
|
||||
-- @param Wrapper.Group#GROUP Carrier
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- Unloaded Handler OnAfter for AI_CARGO
|
||||
-- @function [parent=#AI_CARGO] OnAfterUnloaded
|
||||
-- @param #AI_CARGO self
|
||||
-- @param Wrapper.Group#GROUP Carrier
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- On after Deployed event.
|
||||
-- @function [parent=#AI_CARGO] OnAfterDeployed
|
||||
-- @param #AI_CARGO self
|
||||
-- @param Wrapper.Group#GROUP Carrier
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
-- @param #boolean Defend Defend for APCs.
|
||||
|
||||
|
||||
for _, CarrierUnit in pairs( Carrier:GetUnits() ) do
|
||||
local CarrierUnit = CarrierUnit -- Wrapper.Unit#UNIT
|
||||
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
|
||||
|
||||
|
||||
--- On after Pickup event.
|
||||
-- @param #AI_CARGO self
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Core.Point#COORDINATE Coordinate of the pickup point.
|
||||
-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
||||
-- @param #number Height Height in meters to move to the home coordinate.
|
||||
-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided.
|
||||
function AI_CARGO:onafterPickup( APC, From, Event, To, Coordinate, Speed, Height, PickupZone )
|
||||
|
||||
self.Transporting = false
|
||||
self.Relocating = true
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- On after Deploy event.
|
||||
-- @param #AI_CARGO self
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Core.Point#COORDINATE Coordinate Deploy place.
|
||||
-- @param #number Speed Speed in km/h to drive to the depoly coordinate. Default is 50% of max possible speed the unit can go.
|
||||
-- @param #number Height Height in meters to move to the deploy coordinate.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone where the cargo will be deployed.
|
||||
function AI_CARGO:onafterDeploy( APC, From, Event, To, Coordinate, Speed, Height, DeployZone )
|
||||
|
||||
self.Relocating = false
|
||||
self.Transporting = true
|
||||
|
||||
end
|
||||
|
||||
--- On before Load event.
|
||||
-- @param #AI_CARGO self
|
||||
-- @param Wrapper.Group#GROUP Carrier
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided.
|
||||
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 -- Wrapper.Unit#UNIT
|
||||
|
||||
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 -- Cargo.Cargo#CARGO
|
||||
|
||||
self:F( { IsUnLoaded = Cargo:IsUnLoaded(), IsDeployed = Cargo:IsDeployed(), Cargo:GetName(), Carrier:GetName() } )
|
||||
|
||||
-- Try all Carriers, but start from the one according the Carrier_Index
|
||||
for Carrier_Loop = 1, #Carrier_List do
|
||||
|
||||
local CarrierUnit = Carrier_List[Carrier_Index] -- Wrapper.Unit#UNIT
|
||||
|
||||
-- This counters loop through the available Carriers.
|
||||
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]
|
||||
|
||||
-- Only when there is space within the bay to load the next cargo item!
|
||||
if CarrierSpace > CargoWeight then
|
||||
Carrier:RouteStop()
|
||||
--Cargo:Ungroup()
|
||||
Cargo:__Board( -LoadDelay, CarrierUnit )
|
||||
self:__Board( LoadDelay, Cargo, CarrierUnit, PickupZone )
|
||||
|
||||
LoadDelay = LoadDelay + Cargo:GetCount() * LoadInterval
|
||||
|
||||
-- So now this CarrierUnit has Cargo that is being loaded.
|
||||
-- This will be used further in the logic to follow and to check cargo status.
|
||||
self.Carrier_Cargo[Cargo] = CarrierUnit
|
||||
Boarding = true
|
||||
Carrier_Weight[CarrierUnit] = Carrier_Weight[CarrierUnit] - CargoWeight
|
||||
Loaded = true
|
||||
|
||||
-- Ok, we loaded a cargo, now we can stop the loop.
|
||||
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
|
||||
-- No loading happened, so we need to pickup something else.
|
||||
self.Relocating = false
|
||||
end
|
||||
end
|
||||
|
||||
return Boarding
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- On before Reload event.
|
||||
-- @param #AI_CARGO self
|
||||
-- @param Wrapper.Group#GROUP Carrier
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided.
|
||||
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 -- Wrapper.Unit#UNIT
|
||||
|
||||
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 -- Cargo.Cargo#CARGO
|
||||
|
||||
self:F( { IsUnLoaded = Cargo:IsUnLoaded(), IsDeployed = Cargo:IsDeployed(), Cargo:GetName(), Carrier:GetName() } )
|
||||
|
||||
-- Try all Carriers, but start from the one according the Carrier_Index
|
||||
for Carrier_Loop = 1, #Carrier_List do
|
||||
|
||||
local CarrierUnit = Carrier_List[Carrier_Index] -- Wrapper.Unit#UNIT
|
||||
|
||||
-- This counters loop through the available Carriers.
|
||||
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
|
||||
|
||||
-- So now this CarrierUnit has Cargo that is being loaded.
|
||||
-- This will be used further in the logic to follow and to check cargo status.
|
||||
self.Carrier_Cargo[Cargo] = CarrierUnit
|
||||
Boarding = true
|
||||
Loaded = true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if not Loaded == true then
|
||||
-- No loading happened, so we need to pickup something else.
|
||||
self.Relocating = false
|
||||
end
|
||||
end
|
||||
|
||||
return Boarding
|
||||
|
||||
end
|
||||
|
||||
--- On after Board event.
|
||||
-- @param #AI_CARGO self
|
||||
-- @param Wrapper.Group#GROUP Carrier
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Cargo.Cargo#CARGO Cargo Cargo object.
|
||||
-- @param Wrapper.Unit#UNIT CarrierUnit
|
||||
-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided.
|
||||
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
|
||||
|
||||
--- On after Loaded event.
|
||||
-- @param #AI_CARGO self
|
||||
-- @param Wrapper.Group#GROUP Carrier
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @return #boolean Cargo loaded.
|
||||
-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided.
|
||||
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 -- Cargo.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
|
||||
|
||||
--- On after PickedUp event.
|
||||
-- @param #AI_CARGO self
|
||||
-- @param Wrapper.Group#GROUP Carrier
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided.
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
--- On after Unload event.
|
||||
-- @param #AI_CARGO self
|
||||
-- @param Wrapper.Group#GROUP Carrier
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
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 -- Wrapper.Unit#UNIT
|
||||
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
|
||||
|
||||
--- On after Unboard event.
|
||||
-- @param #AI_CARGO self
|
||||
-- @param Wrapper.Group#GROUP Carrier
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param #string Cargo.Cargo#CARGO Cargo Cargo object.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
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
|
||||
|
||||
--- On after Unloaded event.
|
||||
-- @param #AI_CARGO self
|
||||
-- @param Wrapper.Group#GROUP Carrier
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param #string Cargo.Cargo#CARGO Cargo Cargo object.
|
||||
-- @param #boolean Deployed Cargo is deployed.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
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
|
||||
|
||||
--Cargo:Regroup()
|
||||
|
||||
if Carrier and Carrier:IsAlive() then
|
||||
for _, CarrierUnit in pairs( Carrier:GetUnits() ) do
|
||||
local CarrierUnit = CarrierUnit -- Wrapper.Unit#UNIT
|
||||
local IsEmpty = CarrierUnit:IsCargoEmpty()
|
||||
self:T({ 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
|
||||
|
||||
--- On after Deployed event.
|
||||
-- @param #AI_CARGO self
|
||||
-- @param Wrapper.Group#GROUP Carrier
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
-- @param #boolean Defend Defend for APCs.
|
||||
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
|
||||
@@ -1,609 +0,0 @@
|
||||
--- **AI** - Models the intelligent transportation of cargo using ground vehicles.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Cargo_APC
|
||||
-- @image AI_Cargo_Dispatching_For_APC.JPG
|
||||
|
||||
-- @type AI_CARGO_APC
|
||||
-- @extends AI.AI_Cargo#AI_CARGO
|
||||
|
||||
|
||||
--- Brings a dynamic cargo handling capability for an AI vehicle group.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Armoured Personnel Carriers (APC), Trucks, Jeeps and other ground based carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation.
|
||||
--
|
||||
-- The AI_CARGO_APC class uses the @{Cargo.Cargo} capabilities within the MOOSE framework.
|
||||
-- @{Cargo.Cargo} must be declared within the mission to make the AI_CARGO_APC object recognize the cargo.
|
||||
-- Please consult the @{Cargo.Cargo} module for more information.
|
||||
--
|
||||
-- ## Cargo loading.
|
||||
--
|
||||
-- The module will load automatically cargo when the APCs are within boarding or loading radius.
|
||||
-- The boarding or loading radius is specified when the cargo is created in the simulation, and therefore, this radius depends on the type of cargo
|
||||
-- and the specified boarding radius.
|
||||
--
|
||||
-- ## **Defending** the APCs when enemies nearby.
|
||||
--
|
||||
-- Cargo will defend the carrier with its available arms, and to avoid cargo being lost within the battlefield.
|
||||
--
|
||||
-- When the APCs are approaching enemy units, something special is happening.
|
||||
-- The APCs will stop moving, and the loaded infantry will unboard and follow the APCs and will help to defend the group.
|
||||
-- The carrier will hold the route once the unboarded infantry is further than 50 meters from the APCs,
|
||||
-- to ensure that the APCs are not too far away from the following running infantry.
|
||||
-- Once all enemies are cleared, the infantry will board again automatically into the APCs. Once boarded, the APCs will follow its pre-defined route.
|
||||
--
|
||||
-- A combat radius needs to be specified in meters at the @{#AI_CARGO_APC.New}() method.
|
||||
-- This combat radius will trigger the unboarding of troops when enemies are within the combat radius around the APCs.
|
||||
-- During my tests, I've noticed that there is a balance between ensuring that the infantry is within sufficient hit radius (effectiveness) versus
|
||||
-- vulnerability of the infantry. It all depends on the kind of enemies that are expected to be encountered.
|
||||
-- A combat radius of 350 meters to 500 meters has been proven to be the most effective and efficient.
|
||||
--
|
||||
-- However, when the defense of the carrier, is not required, it must be switched off.
|
||||
-- This is done by disabling the defense of the carrier using the method @{#AI_CARGO_APC.SetCombatRadius}(), and providing a combat radius of 0 meters.
|
||||
-- It can be switched on later when required by reenabling the defense using the method and providing a combat radius larger than 0.
|
||||
--
|
||||
-- ## Infantry or cargo **health**.
|
||||
--
|
||||
-- When infantry is unboarded from the APCs, the infantry is actually respawned into the battlefield.
|
||||
-- As a result, the unboarding infantry is very _healthy_ every time it unboards.
|
||||
-- This is due to the limitation of the DCS simulator, which is not able to specify the health of new spawned units as a parameter.
|
||||
-- However, infantry that was destroyed when unboarded and following the APCs, won't be respawned again. Destroyed is destroyed.
|
||||
-- As a result, there is some additional strength that is gained when an unboarding action happens, but in terms of simulation balance this has
|
||||
-- marginal impact on the overall battlefield simulation. Fortunately, the firing strength of infantry is limited, and thus, respacing healthy infantry every
|
||||
-- time is not so much of an issue ...
|
||||
--
|
||||
-- ## Control the APCs on the map.
|
||||
--
|
||||
-- It is possible also as a human ground commander to influence the path of the APCs, by pointing a new path using the DCS user interface on the map.
|
||||
-- In this case, the APCs will change the direction towards its new indicated route. However, there is a catch!
|
||||
-- Once the APCs are near the enemy, and infantry is unboarded, the APCs won't be able to hold the route until the infantry could catch up.
|
||||
-- The APCs will simply drive on and won't stop! This is a limitation in ED that prevents user actions being controlled by the scripting engine.
|
||||
-- No workaround is possible on this.
|
||||
--
|
||||
-- ## Cargo deployment.
|
||||
--
|
||||
-- Using the @{#AI_CARGO_APC.Deploy}() method, you are able to direct the APCs towards a point on the battlefield to unboard/unload the cargo at the specific coordinate.
|
||||
-- The APCs will follow nearby roads as much as possible, to ensure fast and clean cargo transportation between the objects and villages in the simulation environment.
|
||||
--
|
||||
-- ## Cargo pickup.
|
||||
--
|
||||
-- Using the @{#AI_CARGO_APC.Pickup}() method, you are able to direct the APCs towards a point on the battlefield to board/load the cargo at the specific coordinate.
|
||||
-- The APCs will follow nearby roads as much as possible, to ensure fast and clean cargo transportation between the objects and villages in the simulation environment.
|
||||
--
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
--
|
||||
-- @field #AI_CARGO_APC
|
||||
AI_CARGO_APC = {
|
||||
ClassName = "AI_CARGO_APC",
|
||||
Coordinate = nil, -- Core.Point#COORDINATE,
|
||||
}
|
||||
|
||||
--- Creates a new AI_CARGO_APC object.
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param Wrapper.Group#GROUP APC The carrier APC group.
|
||||
-- @param Core.Set#SET_CARGO CargoSet The set of cargo to be transported.
|
||||
-- @param #number CombatRadius Provide the combat radius to defend the carrier by unboarding the cargo when enemies are nearby. When the combat radius is 0, no defense will happen of the carrier.
|
||||
-- @return #AI_CARGO_APC
|
||||
function AI_CARGO_APC:New( APC, CargoSet, CombatRadius )
|
||||
|
||||
local self = BASE:Inherit( self, AI_CARGO:New( APC, CargoSet ) ) -- #AI_CARGO_APC
|
||||
|
||||
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
|
||||
|
||||
|
||||
--- Set the Carrier.
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param Wrapper.Group#GROUP CargoCarrier
|
||||
-- @return #AI_CARGO_APC
|
||||
function AI_CARGO_APC:SetCarrier( CargoCarrier )
|
||||
|
||||
self.CargoCarrier = CargoCarrier -- Wrapper.Group#GROUP
|
||||
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
|
||||
-- There are enemies within combat radius. Unload the CargoCarrier.
|
||||
AICargoTroops:Destroyed()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- CargoCarrier:HandleEvent( EVENTS.Hit )
|
||||
--
|
||||
-- function CargoCarrier:OnEventHit( EventData )
|
||||
-- self:F({"hit"})
|
||||
-- local AICargoTroops = self:GetState( self, "AI_CARGO_APC" )
|
||||
-- if AICargoTroops then
|
||||
-- self:F( { OnHitLoaded = AICargoTroops:Is( "Loaded" ) } )
|
||||
-- if AICargoTroops:Is( "Loaded" ) or AICargoTroops:Is( "Boarding" ) then
|
||||
-- -- There are enemies within combat radius. Unload the CargoCarrier.
|
||||
-- AICargoTroops:Unload( false )
|
||||
-- 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
|
||||
|
||||
--- Set whether or not the carrier will use roads to *pickup* and *deploy* the cargo.
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param #boolean Offroad If true, carrier will not use roads. If `nil` or `false` the carrier will use roads when available.
|
||||
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
|
||||
-- @return #AI_CARGO_APC self
|
||||
function AI_CARGO_APC:SetOffRoad(Offroad, Formation)
|
||||
|
||||
self:SetPickupOffRoad(Offroad, Formation)
|
||||
self:SetDeployOffRoad(Offroad, Formation)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set whether the carrier will *not* use roads to *pickup* the cargo.
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param #boolean Offroad If true, carrier will not use roads.
|
||||
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
|
||||
-- @return #AI_CARGO_APC self
|
||||
function AI_CARGO_APC:SetPickupOffRoad(Offroad, Formation)
|
||||
|
||||
self.pickupOffroad=Offroad
|
||||
self.pickupFormation=Formation or ENUMS.Formation.Vehicle.OffRoad
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set whether the carrier will *not* use roads to *deploy* the cargo.
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param #boolean Offroad If true, carrier will not use roads.
|
||||
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
|
||||
-- @return #AI_CARGO_APC self
|
||||
function AI_CARGO_APC:SetDeployOffRoad(Offroad, Formation)
|
||||
|
||||
self.deployOffroad=Offroad
|
||||
self.deployFormation=Formation or ENUMS.Formation.Vehicle.OffRoad
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Find a free Carrier within a radius.
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Radius
|
||||
-- @return Wrapper.Group#GROUP NewCarrier
|
||||
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
|
||||
|
||||
--- Enable/Disable unboarding of cargo (infantry) when enemies are nearby (to help defend the carrier).
|
||||
-- This is only valid for APCs and trucks etc, thus ground vehicles.
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param #number CombatRadius Provide the combat radius to defend the carrier by unboarding the cargo when enemies are nearby.
|
||||
-- When the combat radius is 0, no defense will happen of the carrier.
|
||||
-- When the combat radius is not provided, no defense will happen!
|
||||
-- @return #AI_CARGO_APC
|
||||
-- @usage
|
||||
--
|
||||
-- -- Disembark the infantry when the carrier is under attack.
|
||||
-- AICargoAPC:SetCombatRadius( true )
|
||||
--
|
||||
-- -- Keep the cargo in the carrier when the carrier is under attack.
|
||||
-- AICargoAPC:SetCombatRadius( false )
|
||||
function AI_CARGO_APC:SetCombatRadius( CombatRadius )
|
||||
|
||||
self.CombatRadius = CombatRadius or 0
|
||||
|
||||
if self.CombatRadius > 0 then
|
||||
self:__Monitor( -5 )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Follow Infantry to the Carrier.
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param #AI_CARGO_APC Me
|
||||
-- @param Wrapper.Unit#UNIT APCUnit
|
||||
-- @param Cargo.CargoGroup#CARGO_GROUP Cargo
|
||||
-- @return #AI_CARGO_APC
|
||||
function AI_CARGO_APC:FollowToCarrier( Me, APCUnit, CargoGroup )
|
||||
|
||||
local InfantryGroup = CargoGroup:GetGroup()
|
||||
|
||||
self:F( { self = self:GetClassNameAndID(), InfantryGroup = InfantryGroup:GetName() } )
|
||||
|
||||
--if self:Is( "Following" ) then
|
||||
|
||||
if APCUnit:IsAlive() then
|
||||
-- We check if the Cargo is near to the CargoCarrier.
|
||||
if InfantryGroup:IsPartlyInZone( ZONE_UNIT:New( "Radius", APCUnit, 25 ) ) then
|
||||
|
||||
-- The Cargo does not need to follow the Carrier.
|
||||
Me:Guard()
|
||||
|
||||
else
|
||||
|
||||
self:F( { InfantryGroup = InfantryGroup:GetName() } )
|
||||
|
||||
if InfantryGroup:IsAlive() then
|
||||
|
||||
self:F( { InfantryGroup = InfantryGroup:GetName() } )
|
||||
|
||||
local Waypoints = {}
|
||||
|
||||
-- Calculate the new Route.
|
||||
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 ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
|
||||
|
||||
InfantryGroup:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details.
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- On after Monitor event.
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
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
|
||||
-- There are no enemies within combat radius. Reload the CargoCarrier.
|
||||
self:Reload()
|
||||
end
|
||||
else
|
||||
if self:Is( "Loaded" ) then
|
||||
-- There are enemies within combat radius. Unload the CargoCarrier.
|
||||
self:__Unload( 1, nil, true ) -- The 2nd parameter is true, which means that the unload is for defending the carrier, not to deploy!
|
||||
else
|
||||
if self:Is( "Unloaded" ) then
|
||||
--self:Follow()
|
||||
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 -- Cargo.Cargo#CARGO
|
||||
local APCUnit = APCUnit -- Wrapper.Unit#UNIT
|
||||
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
|
||||
|
||||
|
||||
--- On after Follow event.
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
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 -- Cargo.Cargo#CARGO
|
||||
if Cargo:IsUnLoaded() then
|
||||
self:FollowToCarrier( self, APCUnit, Cargo )
|
||||
APCUnit:RouteResume()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Pickup task function. Triggers Load event.
|
||||
-- @param Wrapper.Group#GROUP APC The cargo carrier group.
|
||||
-- @param #AI_CARGO_APC sel `AI_CARGO_APC` class.
|
||||
-- @param Core.Point#COORDINATE Coordinate. The coordinate (not used).
|
||||
-- @param #number Speed Speed (not used).
|
||||
-- @param Core.Zone#ZONE PickupZone Pickup zone.
|
||||
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
|
||||
|
||||
--- Deploy task function. Triggers Unload event.
|
||||
-- @param Wrapper.Group#GROUP APC The cargo carrier group.
|
||||
-- @param #AI_CARGO_APC self `AI_CARGO_APC` class.
|
||||
-- @param Core.Point#COORDINATE Coordinate. The coordinate (not used).
|
||||
-- @param Core.Zone#ZONE DeployZone Deploy zone.
|
||||
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
|
||||
|
||||
|
||||
|
||||
--- On after Pickup event.
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Core.Point#COORDINATE Coordinate of the pickup point.
|
||||
-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
||||
-- @param #number Height Height in meters to move to the pickup coordinate. This parameter is ignored for APCs.
|
||||
-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided.
|
||||
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
|
||||
|
||||
-- Route on road.
|
||||
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 ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
|
||||
|
||||
APC:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details.
|
||||
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
|
||||
|
||||
|
||||
--- On after Deploy event.
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Core.Point#COORDINATE Coordinate Deploy place.
|
||||
-- @param #number Speed Speed in km/h to drive to the depoly coordinate. Default is 50% of max possible speed the unit can go.
|
||||
-- @param #number Height Height in meters to move to the deploy coordinate. This parameter is ignored for APCs.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone where the cargo will be deployed.
|
||||
function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed, Height, DeployZone )
|
||||
|
||||
if APC and APC:IsAlive() then
|
||||
|
||||
self.RouteDeploy = true
|
||||
|
||||
-- Set speed in km/h.
|
||||
local speedmax=APC:GetSpeedMax()
|
||||
local _speed=Speed or speedmax*0.5
|
||||
_speed=math.min(_speed, speedmax)
|
||||
|
||||
-- Route on road.
|
||||
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
|
||||
|
||||
-- Task function
|
||||
local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Deploy", self, Coordinate, DeployZone )
|
||||
|
||||
-- Last waypoint
|
||||
local Waypoint = Waypoints[#Waypoints]
|
||||
|
||||
-- Set task function
|
||||
APC:SetTaskWaypoint(Waypoint, TaskFunction) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
|
||||
|
||||
-- Route group
|
||||
APC:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details.
|
||||
|
||||
-- Call parent function.
|
||||
self:GetParent( self, AI_CARGO_APC ).onafterDeploy( self, APC, From, Event, To, Coordinate, Speed, Height, DeployZone )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- On after Unloaded event.
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param Wrapper.Group#GROUP Carrier
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param #string Cargo.Cargo#CARGO Cargo Cargo object.
|
||||
-- @param #boolean Deployed Cargo is deployed.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
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 we need to scan for possible enemies within combat zone and engage only ground forces.
|
||||
if Defend == true then
|
||||
self.Zone:Scan( { Object.Category.UNIT } )
|
||||
if not self.Zone:IsAllInZoneOfCoalition( self.Coalition ) then
|
||||
-- OK, enemies nearby, now find the enemies and attack them.
|
||||
local AttackUnits = self.Zone:GetScannedUnits() -- #list<DCS#Unit>
|
||||
local Move = {}
|
||||
local CargoGroup = Cargo.CargoObject -- Wrapper.Group#GROUP
|
||||
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" )
|
||||
--MoveTo.Task = CargoGroup:TaskCombo( CargoGroup:TaskAttackUnit( MooseUnit, true ) )
|
||||
self:F( { MooseUnit = MooseUnit:GetName(), CargoGroup = CargoGroup:GetName() } )
|
||||
end
|
||||
end
|
||||
CargoGroup:RoutePush( Move, 0.1 )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- On after Deployed event.
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param Wrapper.Group#GROUP Carrier
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
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
|
||||
|
||||
|
||||
--- On after Home event.
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Core.Point#COORDINATE Coordinate Home place.
|
||||
-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
||||
-- @param #number Height Height in meters to move to the home coordinate. This parameter is ignored for APCs.
|
||||
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 ) -- Move after a random seconds to the Route. See the Route method for details.
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,512 +0,0 @@
|
||||
--- **AI** - Models the intelligent transportation of cargo using airplanes.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Cargo_Airplane
|
||||
-- @image AI_Cargo_Dispatching_For_Airplanes.JPG
|
||||
|
||||
-- @type AI_CARGO_AIRPLANE
|
||||
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
||||
|
||||
|
||||
--- Brings a dynamic cargo handling capability for an AI airplane group.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Airplane carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation between airbases.
|
||||
--
|
||||
-- The AI_CARGO_AIRPLANE module uses the @{Cargo.Cargo} capabilities within the MOOSE framework.
|
||||
-- @{Cargo.Cargo} must be declared within the mission to make AI_CARGO_AIRPLANE recognize the cargo.
|
||||
-- Please consult the @{Cargo.Cargo} module for more information.
|
||||
--
|
||||
-- ## Cargo pickup.
|
||||
--
|
||||
-- Using the @{#AI_CARGO_AIRPLANE.Pickup}() method, you are able to direct the helicopters towards a point on the battlefield to board/load the cargo at the specific coordinate.
|
||||
-- Ensure that the landing zone is horizontally flat, and that trees cannot be found in the landing vicinity, or the helicopters won't land or will even crash!
|
||||
--
|
||||
-- ## Cargo deployment.
|
||||
--
|
||||
-- Using the @{#AI_CARGO_AIRPLANE.Deploy}() method, you are able to direct the helicopters towards a point on the battlefield to unboard/unload the cargo at the specific coordinate.
|
||||
-- Ensure that the landing zone is horizontally flat, and that trees cannot be found in the landing vicinity, or the helicopters won't land or will even crash!
|
||||
--
|
||||
-- ## Infantry health.
|
||||
--
|
||||
-- When infantry is unboarded from the APCs, the infantry is actually respawned into the battlefield.
|
||||
-- As a result, the unboarding infantry is very _healthy_ every time it unboards.
|
||||
-- This is due to the limitation of the DCS simulator, which is not able to specify the health of new spawned units as a parameter.
|
||||
-- However, infantry that was destroyed when unboarded, won't be respawned again. Destroyed is destroyed.
|
||||
-- As a result, there is some additional strength that is gained when an unboarding action happens, but in terms of simulation balance this has
|
||||
-- marginal impact on the overall battlefield simulation. Fortunately, the firing strength of infantry is limited, and thus, respacing healthy infantry every
|
||||
-- time is not so much of an issue ...
|
||||
--
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- @field #AI_CARGO_AIRPLANE
|
||||
AI_CARGO_AIRPLANE = {
|
||||
ClassName = "AI_CARGO_AIRPLANE",
|
||||
Coordinate = nil, -- Core.Point#COORDINATE
|
||||
}
|
||||
|
||||
--- Creates a new AI_CARGO_AIRPLANE object.
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane Plane used for transportation of cargo.
|
||||
-- @param Core.Set#SET_CARGO CargoSet Cargo set to be transported.
|
||||
-- @return #AI_CARGO_AIRPLANE
|
||||
function AI_CARGO_AIRPLANE:New( Airplane, CargoSet )
|
||||
|
||||
local self = BASE:Inherit( self, AI_CARGO:New( Airplane, CargoSet ) ) -- #AI_CARGO_AIRPLANE
|
||||
|
||||
self:AddTransition( "*", "Landed", "*" )
|
||||
self:AddTransition( "*", "Home" , "*" )
|
||||
|
||||
self:AddTransition( "*", "Destroyed", "Destroyed" )
|
||||
|
||||
--- Pickup Handler OnBefore for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] OnBeforePickup
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane Cargo transport plane.
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase Airbase where troops are picked up.
|
||||
-- @param #number Speed in km/h for travelling to pickup base.
|
||||
-- @return #boolean
|
||||
|
||||
--- Pickup Handler OnAfter for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] OnAfterPickup
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane Cargo transport plane.
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate where to pickup stuff.
|
||||
-- @param #number Speed Speed in km/h for travelling to pickup base.
|
||||
-- @param #number Height Height in meters to move to the pickup coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE PickupZone The airbase zone where the cargo will be picked up.
|
||||
|
||||
--- Pickup Trigger for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] Pickup
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate where to pickup stuff.
|
||||
-- @param #number Speed Speed in km/h for travelling to pickup base.
|
||||
-- @param #number Height Height in meters to move to the pickup coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE PickupZone The airbase zone where the cargo will be picked up.
|
||||
|
||||
--- Pickup Asynchronous Trigger for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] __Pickup
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param #number Delay Delay in seconds.
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate where to pickup stuff.
|
||||
-- @param #number Speed Speed in km/h for travelling to pickup base.
|
||||
-- @param #number Height Height in meters to move to the pickup coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE PickupZone The airbase zone where the cargo will be picked up.
|
||||
|
||||
--- Deploy Handler OnBefore for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] OnBeforeDeploy
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane Cargo plane.
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase Destination airbase where troops are deployed.
|
||||
-- @param #number Speed Speed in km/h for travelling to deploy base.
|
||||
-- @return #boolean
|
||||
|
||||
--- Deploy Handler OnAfter for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] OnAfterDeploy
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane Cargo plane.
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate where to deploy stuff.
|
||||
-- @param #number Speed Speed in km/h for travelling to the deploy base.
|
||||
-- @param #number Height Height in meters to move to the home coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE DeployZone The airbase zone where the cargo will be deployed.
|
||||
|
||||
--- Deploy Trigger for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] Deploy
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate where to deploy stuff.
|
||||
-- @param #number Speed Speed in km/h for travelling to the deploy base.
|
||||
-- @param #number Height Height in meters to move to the home coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE DeployZone The airbase zone where the cargo will be deployed.
|
||||
|
||||
--- Deploy Asynchronous Trigger for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] __Deploy
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param #number Delay Delay in seconds.
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate where to deploy stuff.
|
||||
-- @param #number Speed Speed in km/h for travelling to the deploy base.
|
||||
-- @param #number Height Height in meters to move to the home coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE DeployZone The airbase zone where the cargo will be deployed.
|
||||
|
||||
--- On after Loaded event, i.e. triggered when the cargo is inside the carrier.
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] OnAfterLoaded
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane Cargo plane.
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
|
||||
|
||||
--- On after Deployed event.
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] OnAfterDeployed
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane Cargo plane.
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed.
|
||||
|
||||
-- Set carrier.
|
||||
self:SetCarrier( Airplane )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set the Carrier (controllable). Also initializes events for carrier and defines the coalition.
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane Transport plane.
|
||||
-- @return #AI_CARGO_AIRPLANE self
|
||||
function AI_CARGO_AIRPLANE:SetCarrier( Airplane )
|
||||
|
||||
local AICargo = self
|
||||
|
||||
self.Airplane = Airplane -- Wrapper.Group#GROUP
|
||||
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
|
||||
-- There are enemies within combat range. Unload the Airplane.
|
||||
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
|
||||
-- There are enemies within combat range. Unload the Airplane.
|
||||
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
|
||||
|
||||
|
||||
--- Find a free Carrier within a range.
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase
|
||||
-- @param #number Radius
|
||||
-- @return Wrapper.Group#GROUP NewCarrier
|
||||
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
|
||||
|
||||
--- On after "Landed" event. Called on engine shutdown and initiates the pickup mission or unloading event.
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane Cargo transport plane.
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
function AI_CARGO_AIRPLANE:onafterLanded( Airplane, From, Event, To )
|
||||
|
||||
self:F({Airplane, From, Event, To})
|
||||
|
||||
if Airplane and Airplane:IsAlive()~=nil then
|
||||
|
||||
-- Aircraft was sent to this airbase to pickup troops. Initiate loadling.
|
||||
if self.RoutePickup == true then
|
||||
self:Load( self.PickupZone )
|
||||
end
|
||||
|
||||
-- Aircraft was send to this airbase to deploy troops. Initiate unloading.
|
||||
if self.RouteDeploy == true then
|
||||
self:Unload()
|
||||
self.RouteDeploy = false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- On after "Pickup" event. Routes transport to pickup airbase.
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane Cargo transport plane.
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate where to pickup stuff.
|
||||
-- @param #number Speed Speed in km/h for travelling to pickup base.
|
||||
-- @param #number Height Height in meters to move to the pickup coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE PickupZone The airbase zone where the cargo will be picked up.
|
||||
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())
|
||||
|
||||
-- Get closest airbase of current position.
|
||||
local ClosestAirbase, DistToAirbase=Airplane:GetCoordinate():GetClosestAirbase()
|
||||
|
||||
-- Two cases. Aircraft spawned in air or at an airbase.
|
||||
if Airplane:InAir() then
|
||||
self.Airbase=nil --> route will start in air
|
||||
else
|
||||
self.Airbase=ClosestAirbase
|
||||
end
|
||||
|
||||
-- Set pickup airbase.
|
||||
local Airbase = self.PickupZone:GetAirbase()
|
||||
|
||||
-- Distance from closest to pickup airbase ==> we need to know if we are already at the pickup airbase.
|
||||
local Dist = Airbase:GetCoordinate():Get2DDistance(ClosestAirbase:GetCoordinate())
|
||||
|
||||
if Airplane:InAir() or Dist>500 then
|
||||
|
||||
-- Route aircraft to pickup airbase.
|
||||
self:Route( Airplane, Airbase, Speed, Height )
|
||||
|
||||
-- Set airbase as starting point in the next Route() call.
|
||||
self.Airbase = Airbase
|
||||
|
||||
-- Aircraft is on a pickup mission.
|
||||
self.RoutePickup = true
|
||||
|
||||
else
|
||||
|
||||
-- We are already at the right airbase ==> Landed ==> triggers loading of troops. Is usually called at engine shutdown event.
|
||||
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
|
||||
|
||||
--- On after Depoly event. Routes plane to the airbase where the troops are deployed.
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane Cargo transport plane.
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate where to deploy stuff.
|
||||
-- @param #number Speed Speed in km/h for travelling to the deploy base.
|
||||
-- @param #number Height Height in meters to move to the home coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE DeployZone The airbase zone where the cargo will be deployed.
|
||||
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
|
||||
|
||||
-- Activate uncontrolled airplane.
|
||||
if Airplane:IsAlive()==false then
|
||||
Airplane:SetCommand({id = 'Start', params = {}})
|
||||
end
|
||||
|
||||
-- Route to destination airbase.
|
||||
self:Route( Airplane, Airbase, Speed, Height )
|
||||
|
||||
-- Aircraft is on a depoly mission.
|
||||
self.RouteDeploy = true
|
||||
|
||||
-- Set destination airbase for next :Route() command.
|
||||
self.Airbase = Airbase
|
||||
|
||||
self:GetParent( self, AI_CARGO_AIRPLANE ).onafterDeploy( self, Airplane, From, Event, To, Coordinate, Speed, Height, DeployZone )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- On after Unload event. Cargo is beeing unloaded, i.e. the unboarding process is started.
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane Cargo transport plane.
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Zone#ZONE_AIRBASE DeployZone The airbase zone where the cargo will be deployed.
|
||||
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() -- Get Heading of object in degrees.
|
||||
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
|
||||
|
||||
--- Route the airplane from one airport or it's current position to another airbase.
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane Airplane group to be routed.
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase Destination airbase.
|
||||
-- @param #number Speed Speed in km/h. Default is 80% of max possible speed the group can do.
|
||||
-- @param #number Height Height in meters to move to the Airbase.
|
||||
-- @param #boolean Uncontrolled If true, spawn group in uncontrolled state.
|
||||
function AI_CARGO_AIRPLANE:Route( Airplane, Airbase, Speed, Height, Uncontrolled )
|
||||
|
||||
if Airplane and Airplane:IsAlive() then
|
||||
|
||||
-- Set takeoff type.
|
||||
local Takeoff = SPAWN.Takeoff.Cold
|
||||
|
||||
-- Get template of group.
|
||||
local Template = Airplane:GetTemplate()
|
||||
|
||||
-- Nil check
|
||||
if Template==nil then
|
||||
return
|
||||
end
|
||||
|
||||
-- Waypoints of the route.
|
||||
local Points={}
|
||||
|
||||
-- To point.
|
||||
local AirbasePointVec2 = Airbase:GetPointVec2()
|
||||
local ToWaypoint = AirbasePointVec2:WaypointAir(COORDINATE.WaypointAltType.BARO, "Land", "Landing", Speed or Airplane:GetSpeedMax()*0.8, true, Airbase)
|
||||
|
||||
--ToWaypoint["airdromeId"] = Airbase:GetID()
|
||||
--ToWaypoint["speed_locked"] = true
|
||||
|
||||
|
||||
-- If self.Airbase~=nil then group is currently at an airbase, where it should be respawned.
|
||||
if self.Airbase then
|
||||
|
||||
-- Second point of the route. First point is done in RespawnAtCurrentAirbase() routine.
|
||||
Template.route.points[2] = ToWaypoint
|
||||
|
||||
-- Respawn group at the current airbase.
|
||||
Airplane:RespawnAtCurrentAirbase(Template, Takeoff, Uncontrolled)
|
||||
|
||||
else
|
||||
|
||||
-- From point.
|
||||
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
|
||||
|
||||
-- The two route points.
|
||||
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
|
||||
|
||||
--- On after Home event. Aircraft will be routed to their home base.
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane The cargo plane.
|
||||
-- @param From From state.
|
||||
-- @param Event Event.
|
||||
-- @param To To State.
|
||||
-- @param Core.Point#COORDINATE Coordinate Home place (not used).
|
||||
-- @param #number Speed Speed in km/h to fly to the home airbase (zone). Default is 80% of max possible speed the unit can go.
|
||||
-- @param #number Height Height in meters to move to the home coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE HomeZone The home airbase (zone) where the plane should return to.
|
||||
function AI_CARGO_AIRPLANE:onafterHome(Airplane, From, Event, To, Coordinate, Speed, Height, HomeZone )
|
||||
if Airplane and Airplane:IsAlive() then
|
||||
|
||||
-- We are going home!
|
||||
self.RouteHome = true
|
||||
|
||||
-- Home Base.
|
||||
local HomeBase=HomeZone:GetAirbase()
|
||||
self.Airbase=HomeBase
|
||||
|
||||
-- Now route the airplane home
|
||||
self:Route( Airplane, HomeBase, Speed, Height )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,265 +0,0 @@
|
||||
--- **AI** - Models the intelligent transportation of infantry and other cargo using APCs.
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Quickly transport cargo to various deploy zones using ground vehicles (APCs, trucks ...).
|
||||
-- * Various @{Cargo.Cargo#CARGO} types can be transported. These are infantry groups and crates.
|
||||
-- * Define a list of deploy zones of various types to transport the cargo to.
|
||||
-- * The vehicles follow the roads to ensure the fastest possible cargo transportation over the ground.
|
||||
-- * Multiple vehicles can transport multiple cargo as one vehicle group.
|
||||
-- * Multiple vehicle groups can be enabled as one collaborating transportation process.
|
||||
-- * Infantry loaded as cargo, will unboard in case enemies are nearby and will help defending the vehicles.
|
||||
-- * Different ranges can be setup for enemy defenses.
|
||||
-- * Different options can be setup to tweak the cargo transporation behaviour.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Test Missions:
|
||||
--
|
||||
-- Test missions can be located on the main GITHUB site.
|
||||
--
|
||||
-- [FlightControl-Master/MOOSE_MISSIONS/AID - AI Dispatching/AID-CGO - AI Cargo Dispatching/]
|
||||
-- (https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/AID%20-%20AI%20Dispatching/AID-CGO%20-%20AI%20Cargo%20Dispatching)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Cargo_Dispatcher_APC
|
||||
-- @image AI_Cargo_Dispatching_For_APC.JPG
|
||||
|
||||
-- @type AI_CARGO_DISPATCHER_APC
|
||||
-- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER
|
||||
|
||||
|
||||
--- A dynamic cargo transportation capability for AI groups.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Armoured Personnel APCs (APC), Trucks, Jeeps and other carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation.
|
||||
--
|
||||
-- The AI_CARGO_DISPATCHER_APC module is derived from the AI_CARGO_DISPATCHER module.
|
||||
--
|
||||
-- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_APC class, it is recommended that you first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher} module!!!
|
||||
--
|
||||
-- Especially to learn how to **Tailor the different cargo handling events**, this will be very useful!
|
||||
--
|
||||
-- On top, the AI_CARGO_DISPATCHER_APC class uses the @{Cargo.Cargo} capabilities within the MOOSE framework.
|
||||
-- Also ensure that you fully understand how to declare and setup Cargo objects within the MOOSE framework before using this class.
|
||||
-- CARGO derived objects must be declared within the mission to make the AI_CARGO_DISPATCHER_HELICOPTER object recognize the cargo.
|
||||
--
|
||||
--
|
||||
-- # 1) AI_CARGO_DISPATCHER_APC constructor.
|
||||
--
|
||||
-- * @{#AI_CARGO_DISPATCHER_APC.New}(): Creates a new AI_CARGO_DISPATCHER_APC object.
|
||||
--
|
||||
-- ---
|
||||
--
|
||||
-- # 2) AI_CARGO_DISPATCHER_APC is a Finite State Machine.
|
||||
--
|
||||
-- This section must be read as follows. Each of the rows indicate a state transition, triggered through an event, and with an ending state of the event was executed.
|
||||
-- The first column is the **From** state, the second column the **Event**, and the third column the **To** state.
|
||||
--
|
||||
-- So, each of the rows have the following structure.
|
||||
--
|
||||
-- * **From** => **Event** => **To**
|
||||
--
|
||||
-- Important to know is that an event can only be executed if the **current state** is the **From** state.
|
||||
-- This, when an **Event** that is being triggered has a **From** state that is equal to the **Current** state of the state machine, the event will be executed,
|
||||
-- and the resulting state will be the **To** state.
|
||||
--
|
||||
-- These are the different possible state transitions of this state machine implementation:
|
||||
--
|
||||
-- * Idle => Start => Monitoring
|
||||
-- * Monitoring => Monitor => Monitoring
|
||||
-- * Monitoring => Stop => Idle
|
||||
--
|
||||
-- * Monitoring => Pickup => Monitoring
|
||||
-- * Monitoring => Load => Monitoring
|
||||
-- * Monitoring => Loading => Monitoring
|
||||
-- * Monitoring => Loaded => Monitoring
|
||||
-- * Monitoring => PickedUp => Monitoring
|
||||
-- * Monitoring => Deploy => Monitoring
|
||||
-- * Monitoring => Unload => Monitoring
|
||||
-- * Monitoring => Unloaded => Monitoring
|
||||
-- * Monitoring => Deployed => Monitoring
|
||||
-- * Monitoring => Home => Monitoring
|
||||
--
|
||||
--
|
||||
-- ## 2.1) AI_CARGO_DISPATCHER States.
|
||||
--
|
||||
-- * **Monitoring**: The process is dispatching.
|
||||
-- * **Idle**: The process is idle.
|
||||
--
|
||||
-- ## 2.2) AI_CARGO_DISPATCHER Events.
|
||||
--
|
||||
-- * **Start**: Start the transport process.
|
||||
-- * **Stop**: Stop the transport process.
|
||||
-- * **Monitor**: Monitor and take action.
|
||||
--
|
||||
-- * **Pickup**: Pickup cargo.
|
||||
-- * **Load**: Load the cargo.
|
||||
-- * **Loading**: The dispatcher is coordinating the loading of a cargo.
|
||||
-- * **Loaded**: Flag that the cargo is loaded.
|
||||
-- * **PickedUp**: The dispatcher has loaded all requested cargo into the CarrierGroup.
|
||||
-- * **Deploy**: Deploy cargo to a location.
|
||||
-- * **Unload**: Unload the cargo.
|
||||
-- * **Unloaded**: Flag that the cargo is unloaded.
|
||||
-- * **Deployed**: All cargo is unloaded from the carriers in the group.
|
||||
-- * **Home**: A Carrier is going home.
|
||||
--
|
||||
-- ## 2.3) Enhance your mission scripts with **Tailored** Event Handling!
|
||||
--
|
||||
-- Within your mission, you can capture these events when triggered, and tailor the events with your own code!
|
||||
-- Check out the @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} class at chapter 3 for details on the different event handlers that are available and how to use them.
|
||||
--
|
||||
-- **There are a lot of templates available that allows you to quickly setup an event handler for a specific event type!**
|
||||
--
|
||||
-- ---
|
||||
--
|
||||
-- # 3) Set the pickup parameters.
|
||||
--
|
||||
-- Several parameters can be set to pickup cargo:
|
||||
--
|
||||
-- * @{#AI_CARGO_DISPATCHER_APC.SetPickupRadius}(): Sets or randomizes the pickup location for the APC around the cargo coordinate in a radius defined an outer and optional inner radius.
|
||||
-- * @{#AI_CARGO_DISPATCHER_APC.SetPickupSpeed}(): Set the speed or randomizes the speed in km/h to pickup the cargo.
|
||||
--
|
||||
-- # 4) Set the deploy parameters.
|
||||
--
|
||||
-- Several parameters can be set to deploy cargo:
|
||||
--
|
||||
-- * @{#AI_CARGO_DISPATCHER_APC.SetDeployRadius}(): Sets or randomizes the deploy location for the APC around the cargo coordinate in a radius defined an outer and an optional inner radius.
|
||||
-- * @{#AI_CARGO_DISPATCHER_APC.SetDeploySpeed}(): Set the speed or randomizes the speed in km/h to deploy the cargo.
|
||||
--
|
||||
-- # 5) Set the home zone when there isn't any more cargo to pickup.
|
||||
--
|
||||
-- A home zone can be specified to where the APCs will move when there isn't any cargo left for pickup.
|
||||
-- Use @{#AI_CARGO_DISPATCHER_APC.SetHomeZone}() to specify the home zone.
|
||||
--
|
||||
-- If no home zone is specified, the APCs will wait near the deploy zone for a new pickup command.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_CARGO_DISPATCHER_APC
|
||||
AI_CARGO_DISPATCHER_APC = {
|
||||
ClassName = "AI_CARGO_DISPATCHER_APC",
|
||||
}
|
||||
|
||||
--- Creates a new AI_CARGO_DISPATCHER_APC object.
|
||||
-- @param #AI_CARGO_DISPATCHER_APC self
|
||||
-- @param Core.Set#SET_GROUP APCSet The set of @{Wrapper.Group#GROUP} objects of vehicles, trucks, APCs that will transport the cargo.
|
||||
-- @param Core.Set#SET_CARGO CargoSet The set of @{Cargo.Cargo#CARGO} objects, which can be CARGO_GROUP, CARGO_CRATE, CARGO_SLINGLOAD objects.
|
||||
-- @param Core.Set#SET_ZONE PickupZoneSet (optional) The set of pickup zones, which are used to where the cargo can be picked up by the APCs. If nil, then cargo can be picked up everywhere.
|
||||
-- @param Core.Set#SET_ZONE DeployZoneSet The set of deploy zones, which are used to where the cargo will be deployed by the APCs.
|
||||
-- @param DCS#Distance CombatRadius The cargo will be unloaded from the APC and engage the enemy if the enemy is within CombatRadius range. The radius is in meters, the default value is 500 meters.
|
||||
-- @return #AI_CARGO_DISPATCHER_APC
|
||||
-- @usage
|
||||
--
|
||||
-- -- An AI dispatcher object for a vehicle squadron, moving infantry from pickup zones to deploy zones.
|
||||
--
|
||||
-- local SetCargoInfantry = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart()
|
||||
-- local SetAPC = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart()
|
||||
-- local SetDeployZones = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart()
|
||||
--
|
||||
-- AICargoDispatcherAPC = AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargoInfantry, nil, SetDeployZones )
|
||||
-- AICargoDispatcherAPC:Start()
|
||||
--
|
||||
function AI_CARGO_DISPATCHER_APC:New( APCSet, CargoSet, PickupZoneSet, DeployZoneSet, CombatRadius )
|
||||
|
||||
local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( APCSet, CargoSet, PickupZoneSet, DeployZoneSet ) ) -- #AI_CARGO_DISPATCHER_APC
|
||||
|
||||
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
|
||||
|
||||
|
||||
--- AI cargo
|
||||
-- @param #AI_CARGO_DISPATCHER_APC self
|
||||
-- @param Wrapper.Group#GROUP APC The APC carrier.
|
||||
-- @param Core.Set#SET_CARGO CargoSet Cargo set.
|
||||
-- @return AI.AI_Cargo_APC#AI_CARGO_DISPATCHER_APC AI cargo APC object.
|
||||
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
|
||||
|
||||
--- Enable/Disable unboarding of cargo (infantry) when enemies are nearby (to help defend the carrier).
|
||||
-- This is only valid for APCs and trucks etc, thus ground vehicles.
|
||||
-- @param #AI_CARGO_DISPATCHER_APC self
|
||||
-- @param #number CombatRadius Provide the combat radius to defend the carrier by unboarding the cargo when enemies are nearby.
|
||||
-- When the combat radius is 0 (default), no defense will happen of the carrier.
|
||||
-- When the combat radius is not provided, no defense will happen!
|
||||
-- @return #AI_CARGO_DISPATCHER_APC
|
||||
-- @usage
|
||||
--
|
||||
-- -- Disembark the infantry when the carrier is under attack.
|
||||
-- AICargoDispatcher:SetCombatRadius( 500 )
|
||||
--
|
||||
-- -- Keep the cargo in the carrier when the carrier is under attack.
|
||||
-- AICargoDispatcher:SetCombatRadius( 0 )
|
||||
function AI_CARGO_DISPATCHER_APC:SetCombatRadius( CombatRadius )
|
||||
|
||||
self.CombatRadius = CombatRadius or 0
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set whether the carrier will *not* use roads to *pickup* and *deploy* the cargo.
|
||||
-- @param #AI_CARGO_DISPATCHER_APC self
|
||||
-- @param #boolean Offroad If true, carrier will not use roads.
|
||||
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
|
||||
-- @return #AI_CARGO_DISPATCHER_APC self
|
||||
function AI_CARGO_DISPATCHER_APC:SetOffRoad(Offroad, Formation)
|
||||
|
||||
self:SetPickupOffRoad(Offroad, Formation)
|
||||
self:SetDeployOffRoad(Offroad, Formation)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set whether the carrier will *not* use roads to *pickup* the cargo.
|
||||
-- @param #AI_CARGO_DISPATCHER_APC self
|
||||
-- @param #boolean Offroad If true, carrier will not use roads.
|
||||
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
|
||||
-- @return #AI_CARGO_DISPATCHER_APC self
|
||||
function AI_CARGO_DISPATCHER_APC:SetPickupOffRoad(Offroad, Formation)
|
||||
|
||||
self.pickupOffroad=Offroad
|
||||
self.pickupFormation=Formation or ENUMS.Formation.Vehicle.OffRoad
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set whether the carrier will *not* use roads to *deploy* the cargo.
|
||||
-- @param #AI_CARGO_DISPATCHER_APC self
|
||||
-- @param #boolean Offroad If true, carrier will not use roads.
|
||||
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
|
||||
-- @return #AI_CARGO_DISPATCHER_APC self
|
||||
function AI_CARGO_DISPATCHER_APC:SetDeployOffRoad(Offroad, Formation)
|
||||
|
||||
self.deployOffroad=Offroad
|
||||
self.deployFormation=Formation or ENUMS.Formation.Vehicle.OffRoad
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -1,171 +0,0 @@
|
||||
--- **AI** - Models the intelligent transportation of infantry and other cargo using Planes.
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * The airplanes will fly towards the pickup airbases to pickup the cargo.
|
||||
-- * The airplanes will fly towards the deploy airbases to deploy the cargo.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Test Missions:
|
||||
--
|
||||
-- Test missions can be located on the main GITHUB site.
|
||||
--
|
||||
-- [FlightControl-Master/MOOSE_MISSIONS/AID - AI Dispatching/AID-CGO - AI Cargo Dispatching/]
|
||||
-- (https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/AID%20-%20AI%20Dispatching/AID-CGO%20-%20AI%20Cargo%20Dispatching)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Cargo_Dispatcher_Airplane
|
||||
-- @image AI_Cargo_Dispatching_For_Airplanes.JPG
|
||||
|
||||
|
||||
-- @type AI_CARGO_DISPATCHER_AIRPLANE
|
||||
-- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER
|
||||
|
||||
|
||||
--- Brings a dynamic cargo handling capability for AI groups.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Airplanes can be mobilized to intelligently transport infantry and other cargo within the simulation.
|
||||
--
|
||||
-- The AI_CARGO_DISPATCHER_AIRPLANE module is derived from the AI_CARGO_DISPATCHER module.
|
||||
--
|
||||
-- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_AIRPLANE class, it is recommended that you first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher} module!!!**
|
||||
--
|
||||
-- Especially to learn how to **Tailor the different cargo handling events**, this will be very useful!
|
||||
--
|
||||
-- On top, the AI_CARGO_DISPATCHER_AIRPLANE class uses the @{Cargo.Cargo} capabilities within the MOOSE framework.
|
||||
-- Also ensure that you fully understand how to declare and setup Cargo objects within the MOOSE framework before using this class.
|
||||
-- CARGO derived objects must be declared within the mission to make the AI_CARGO_DISPATCHER_HELICOPTER object recognize the cargo.
|
||||
--
|
||||
-- # 1) AI_CARGO_DISPATCHER_AIRPLANE constructor.
|
||||
--
|
||||
-- * @{#AI_CARGO_DISPATCHER_AIRPLANE.New}(): Creates a new AI_CARGO_DISPATCHER_AIRPLANE object.
|
||||
--
|
||||
-- ---
|
||||
--
|
||||
-- # 2) AI_CARGO_DISPATCHER_AIRPLANE is a Finite State Machine.
|
||||
--
|
||||
-- This section must be read as follows. Each of the rows indicate a state transition, triggered through an event, and with an ending state of the event was executed.
|
||||
-- The first column is the **From** state, the second column the **Event**, and the third column the **To** state.
|
||||
--
|
||||
-- So, each of the rows have the following structure.
|
||||
--
|
||||
-- * **From** => **Event** => **To**
|
||||
--
|
||||
-- Important to know is that an event can only be executed if the **current state** is the **From** state.
|
||||
-- This, when an **Event** that is being triggered has a **From** state that is equal to the **Current** state of the state machine, the event will be executed,
|
||||
-- and the resulting state will be the **To** state.
|
||||
--
|
||||
-- These are the different possible state transitions of this state machine implementation:
|
||||
--
|
||||
-- * Idle => Start => Monitoring
|
||||
-- * Monitoring => Monitor => Monitoring
|
||||
-- * Monitoring => Stop => Idle
|
||||
--
|
||||
-- * Monitoring => Pickup => Monitoring
|
||||
-- * Monitoring => Load => Monitoring
|
||||
-- * Monitoring => Loading => Monitoring
|
||||
-- * Monitoring => Loaded => Monitoring
|
||||
-- * Monitoring => PickedUp => Monitoring
|
||||
-- * Monitoring => Deploy => Monitoring
|
||||
-- * Monitoring => Unload => Monitoring
|
||||
-- * Monitoring => Unloaded => Monitoring
|
||||
-- * Monitoring => Deployed => Monitoring
|
||||
-- * Monitoring => Home => Monitoring
|
||||
--
|
||||
--
|
||||
-- ## 2.1) AI_CARGO_DISPATCHER States.
|
||||
--
|
||||
-- * **Monitoring**: The process is dispatching.
|
||||
-- * **Idle**: The process is idle.
|
||||
--
|
||||
-- ## 2.2) AI_CARGO_DISPATCHER Events.
|
||||
--
|
||||
-- * **Start**: Start the transport process.
|
||||
-- * **Stop**: Stop the transport process.
|
||||
-- * **Monitor**: Monitor and take action.
|
||||
--
|
||||
-- * **Pickup**: Pickup cargo.
|
||||
-- * **Load**: Load the cargo.
|
||||
-- * **Loading**: The dispatcher is coordinating the loading of a cargo.
|
||||
-- * **Loaded**: Flag that the cargo is loaded.
|
||||
-- * **PickedUp**: The dispatcher has loaded all requested cargo into the CarrierGroup.
|
||||
-- * **Deploy**: Deploy cargo to a location.
|
||||
-- * **Unload**: Unload the cargo.
|
||||
-- * **Unloaded**: Flag that the cargo is unloaded.
|
||||
-- * **Deployed**: All cargo is unloaded from the carriers in the group.
|
||||
-- * **Home**: A Carrier is going home.
|
||||
--
|
||||
-- ## 2.3) Enhance your mission scripts with **Tailored** Event Handling!
|
||||
--
|
||||
-- Within your mission, you can capture these events when triggered, and tailor the events with your own code!
|
||||
-- Check out the @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} class at chapter 3 for details on the different event handlers that are available and how to use them.
|
||||
--
|
||||
-- **There are a lot of templates available that allows you to quickly setup an event handler for a specific event type!**
|
||||
--
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
--
|
||||
-- @field #AI_CARGO_DISPATCHER_AIRPLANE
|
||||
AI_CARGO_DISPATCHER_AIRPLANE = {
|
||||
ClassName = "AI_CARGO_DISPATCHER_AIRPLANE",
|
||||
}
|
||||
|
||||
--- Creates a new AI_CARGO_DISPATCHER_AIRPLANE object.
|
||||
-- @param #AI_CARGO_DISPATCHER_AIRPLANE self
|
||||
-- @param Core.Set#SET_GROUP AirplaneSet The set of @{Wrapper.Group#GROUP} objects of airplanes that will transport the cargo.
|
||||
-- @param Core.Set#SET_CARGO CargoSet The set of @{Cargo.Cargo#CARGO} objects, which can be CARGO_GROUP, CARGO_CRATE, CARGO_SLINGLOAD objects.
|
||||
-- @param Core.Zone#SET_ZONE PickupZoneSet The set of zone airbases where the cargo has to be picked up.
|
||||
-- @param Core.Zone#SET_ZONE DeployZoneSet The set of zone airbases where the cargo is deployed. Choice for each cargo is random.
|
||||
-- @return #AI_CARGO_DISPATCHER_AIRPLANE self
|
||||
-- @usage
|
||||
--
|
||||
-- -- An AI dispatcher object for an airplane squadron, moving infantry and vehicles from pickup airbases to deploy airbases.
|
||||
--
|
||||
-- local CargoInfantrySet = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart()
|
||||
-- local AirplanesSet = SET_GROUP:New():FilterPrefixes( "Airplane" ):FilterStart()
|
||||
-- local PickupZoneSet = SET_ZONE:New()
|
||||
-- local DeployZoneSet = SET_ZONE:New()
|
||||
--
|
||||
-- PickupZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Gudauta ) )
|
||||
-- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Sochi_Adler ) )
|
||||
-- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Maykop_Khanskaya ) )
|
||||
-- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Mineralnye_Vody ) )
|
||||
-- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Vaziani ) )
|
||||
--
|
||||
-- AICargoDispatcherAirplanes = AI_CARGO_DISPATCHER_AIRPLANE:New( AirplanesSet, CargoInfantrySet, PickupZoneSet, DeployZoneSet )
|
||||
-- AICargoDispatcherAirplanes:Start()
|
||||
--
|
||||
function AI_CARGO_DISPATCHER_AIRPLANE:New( AirplaneSet, CargoSet, PickupZoneSet, DeployZoneSet )
|
||||
|
||||
local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( AirplaneSet, CargoSet, PickupZoneSet, DeployZoneSet ) ) -- #AI_CARGO_DISPATCHER_AIRPLANE
|
||||
|
||||
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
|
||||
@@ -1,201 +0,0 @@
|
||||
--- **AI** - Models the intelligent transportation of infantry and other cargo using Helicopters.
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * The helicopters will fly towards the pickup locations to pickup the cargo.
|
||||
-- * The helicopters will fly towards the deploy zones to deploy the cargo.
|
||||
-- * Precision deployment as well as randomized deployment within the deploy zones are possible.
|
||||
-- * Helicopters will orbit the deploy zones when there is no space for landing until the deploy zone is free.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Test Missions:
|
||||
--
|
||||
-- Test missions can be located on the main GITHUB site.
|
||||
--
|
||||
-- [FlightControl-Master/MOOSE_MISSIONS/AID - AI Dispatching/AID-CGO - AI Cargo Dispatching/]
|
||||
-- (https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/AID%20-%20AI%20Dispatching/AID-CGO%20-%20AI%20Cargo%20Dispatching)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Cargo_Dispatcher_Helicopter
|
||||
-- @image AI_Cargo_Dispatching_For_Helicopters.JPG
|
||||
|
||||
-- @type AI_CARGO_DISPATCHER_HELICOPTER
|
||||
-- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER
|
||||
|
||||
|
||||
--- A dynamic cargo handling capability for AI helicopter groups.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Helicopters can be mobilized to intelligently transport infantry and other cargo within the simulation.
|
||||
--
|
||||
--
|
||||
-- The AI_CARGO_DISPATCHER_HELICOPTER module is derived from the AI_CARGO_DISPATCHER module.
|
||||
--
|
||||
-- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_HELICOPTER class, it is recommended that you first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher} module!!!**
|
||||
--
|
||||
-- Especially to learn how to **Tailor the different cargo handling events**, this will be very useful!
|
||||
--
|
||||
-- On top, the AI_CARGO_DISPATCHER_HELICOPTER class uses the @{Cargo.Cargo} capabilities within the MOOSE framework.
|
||||
-- Also ensure that you fully understand how to declare and setup Cargo objects within the MOOSE framework before using this class.
|
||||
-- CARGO derived objects must be declared within the mission to make the AI_CARGO_DISPATCHER_HELICOPTER object recognize the cargo.
|
||||
--
|
||||
-- ---
|
||||
--
|
||||
-- # 1. AI\_CARGO\_DISPATCHER\_HELICOPTER constructor.
|
||||
--
|
||||
-- * @{#AI_CARGO_DISPATCHER\_HELICOPTER.New}(): Creates a new AI\_CARGO\_DISPATCHER\_HELICOPTER object.
|
||||
--
|
||||
-- ---
|
||||
--
|
||||
-- # 2. AI\_CARGO\_DISPATCHER\_HELICOPTER is a Finite State Machine.
|
||||
--
|
||||
-- This section must be read as follows. Each of the rows indicate a state transition, triggered through an event, and with an ending state of the event was executed.
|
||||
-- The first column is the **From** state, the second column the **Event**, and the third column the **To** state.
|
||||
--
|
||||
-- So, each of the rows have the following structure.
|
||||
--
|
||||
-- * **From** => **Event** => **To**
|
||||
--
|
||||
-- Important to know is that an event can only be executed if the **current state** is the **From** state.
|
||||
-- This, when an **Event** that is being triggered has a **From** state that is equal to the **Current** state of the state machine, the event will be executed,
|
||||
-- and the resulting state will be the **To** state.
|
||||
--
|
||||
-- These are the different possible state transitions of this state machine implementation:
|
||||
--
|
||||
-- * Idle => Start => Monitoring
|
||||
-- * Monitoring => Monitor => Monitoring
|
||||
-- * Monitoring => Stop => Idle
|
||||
--
|
||||
-- * Monitoring => Pickup => Monitoring
|
||||
-- * Monitoring => Load => Monitoring
|
||||
-- * Monitoring => Loading => Monitoring
|
||||
-- * Monitoring => Loaded => Monitoring
|
||||
-- * Monitoring => PickedUp => Monitoring
|
||||
-- * Monitoring => Deploy => Monitoring
|
||||
-- * Monitoring => Unload => Monitoring
|
||||
-- * Monitoring => Unloaded => Monitoring
|
||||
-- * Monitoring => Deployed => Monitoring
|
||||
-- * Monitoring => Home => Monitoring
|
||||
--
|
||||
--
|
||||
-- ## 2.1) AI_CARGO_DISPATCHER States.
|
||||
--
|
||||
-- * **Monitoring**: The process is dispatching.
|
||||
-- * **Idle**: The process is idle.
|
||||
--
|
||||
-- ## 2.2) AI_CARGO_DISPATCHER Events.
|
||||
--
|
||||
-- * **Start**: Start the transport process.
|
||||
-- * **Stop**: Stop the transport process.
|
||||
-- * **Monitor**: Monitor and take action.
|
||||
--
|
||||
-- * **Pickup**: Pickup cargo.
|
||||
-- * **Load**: Load the cargo.
|
||||
-- * **Loading**: The dispatcher is coordinating the loading of a cargo.
|
||||
-- * **Loaded**: Flag that the cargo is loaded.
|
||||
-- * **PickedUp**: The dispatcher has loaded all requested cargo into the CarrierGroup.
|
||||
-- * **Deploy**: Deploy cargo to a location.
|
||||
-- * **Unload**: Unload the cargo.
|
||||
-- * **Unloaded**: Flag that the cargo is unloaded.
|
||||
-- * **Deployed**: All cargo is unloaded from the carriers in the group.
|
||||
-- * **Home**: A Carrier is going home.
|
||||
--
|
||||
-- ## 2.3) Enhance your mission scripts with **Tailored** Event Handling!
|
||||
--
|
||||
-- Within your mission, you can capture these events when triggered, and tailor the events with your own code!
|
||||
-- Check out the @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} class at chapter 3 for details on the different event handlers that are available and how to use them.
|
||||
--
|
||||
-- **There are a lot of templates available that allows you to quickly setup an event handler for a specific event type!**
|
||||
--
|
||||
-- ---
|
||||
--
|
||||
-- ## 3. Set the pickup parameters.
|
||||
--
|
||||
-- Several parameters can be set to pickup cargo:
|
||||
--
|
||||
-- * @{#AI_CARGO_DISPATCHER_HELICOPTER.SetPickupRadius}(): Sets or randomizes the pickup location for the helicopter around the cargo coordinate in a radius defined an outer and optional inner radius.
|
||||
-- * @{#AI_CARGO_DISPATCHER_HELICOPTER.SetPickupSpeed}(): Set the speed or randomizes the speed in km/h to pickup the cargo.
|
||||
-- * @{#AI_CARGO_DISPATCHER_HELICOPTER.SetPickupHeight}(): Set the height or randomizes the height in meters to pickup the cargo.
|
||||
--
|
||||
-- ---
|
||||
--
|
||||
-- ## 4. Set the deploy parameters.
|
||||
--
|
||||
-- Several parameters can be set to deploy cargo:
|
||||
--
|
||||
-- * @{#AI_CARGO_DISPATCHER_HELICOPTER.SetDeployRadius}(): Sets or randomizes the deploy location for the helicopter around the cargo coordinate in a radius defined an outer and an optional inner radius.
|
||||
-- * @{#AI_CARGO_DISPATCHER_HELICOPTER.SetDeploySpeed}(): Set the speed or randomizes the speed in km/h to deploy the cargo.
|
||||
-- * @{#AI_CARGO_DISPATCHER_HELICOPTER.SetDeployHeight}(): Set the height or randomizes the height in meters to deploy the cargo.
|
||||
--
|
||||
-- ---
|
||||
--
|
||||
-- ## 5. Set the home zone when there isn't any more cargo to pickup.
|
||||
--
|
||||
-- A home zone can be specified to where the Helicopters will move when there isn't any cargo left for pickup.
|
||||
-- Use @{#AI_CARGO_DISPATCHER_HELICOPTER.SetHomeZone}() to specify the home zone.
|
||||
--
|
||||
-- If no home zone is specified, the helicopters will wait near the deploy zone for a new pickup command.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_CARGO_DISPATCHER_HELICOPTER
|
||||
AI_CARGO_DISPATCHER_HELICOPTER = {
|
||||
ClassName = "AI_CARGO_DISPATCHER_HELICOPTER",
|
||||
}
|
||||
|
||||
--- Creates a new AI_CARGO_DISPATCHER_HELICOPTER object.
|
||||
-- @param #AI_CARGO_DISPATCHER_HELICOPTER self
|
||||
-- @param Core.Set#SET_GROUP HelicopterSet The set of @{Wrapper.Group#GROUP} objects of helicopters that will transport the cargo.
|
||||
-- @param Core.Set#SET_CARGO CargoSet The set of @{Cargo.Cargo#CARGO} objects, which can be CARGO_GROUP, CARGO_CRATE, CARGO_SLINGLOAD objects.
|
||||
-- @param Core.Set#SET_ZONE PickupZoneSet (optional) The set of pickup zones, which are used to where the cargo can be picked up by the APCs. If nil, then cargo can be picked up everywhere.
|
||||
-- @param Core.Set#SET_ZONE DeployZoneSet The set of deploy zones, which are used to where the cargo will be deployed by the Helicopters.
|
||||
-- @return #AI_CARGO_DISPATCHER_HELICOPTER
|
||||
-- @usage
|
||||
--
|
||||
-- -- An AI dispatcher object for a helicopter squadron, moving infantry from pickup zones to deploy zones.
|
||||
--
|
||||
-- local SetCargoInfantry = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart()
|
||||
-- local SetHelicopter = SET_GROUP:New():FilterPrefixes( "Helicopter" ):FilterStart()
|
||||
-- local SetPickupZones = SET_ZONE:New():FilterPrefixes( "Pickup" ):FilterStart()
|
||||
-- local SetDeployZones = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart()
|
||||
--
|
||||
-- AICargoDispatcherHelicopter = AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargoInfantry, SetPickupZones, SetDeployZones )
|
||||
-- AICargoDispatcherHelicopter:Start()
|
||||
--
|
||||
function AI_CARGO_DISPATCHER_HELICOPTER:New( HelicopterSet, CargoSet, PickupZoneSet, DeployZoneSet )
|
||||
|
||||
local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( HelicopterSet, CargoSet, PickupZoneSet, DeployZoneSet ) ) -- #AI_CARGO_DISPATCHER_HELICOPTER
|
||||
|
||||
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
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
--- **AI** - Models the intelligent transportation of infantry and other cargo using Ships.
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Transport cargo to various deploy zones using naval vehicles.
|
||||
-- * Various @{Cargo.Cargo#CARGO} types can be transported, including infantry, vehicles, and crates.
|
||||
-- * Define a deploy zone of various types to determine the destination of the cargo.
|
||||
-- * Ships will follow shipping lanes as defined in the Mission Editor.
|
||||
-- * Multiple ships can transport multiple cargo as a single group.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Test Missions:
|
||||
--
|
||||
-- NEED TO DO
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **acrojason** (derived from AI_Cargo_Dispatcher_APC by FlightControl)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Cargo_Dispatcher_Ship
|
||||
-- @image AI_Cargo_Dispatcher.JPG
|
||||
|
||||
-- @type AI_CARGO_DISPATCHER_SHIP
|
||||
-- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER
|
||||
|
||||
|
||||
--- A dynamic cargo transportation capability for AI groups.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Naval vessels can be mobilized to semi-intelligently transport cargo within the simulation.
|
||||
--
|
||||
-- The AI_CARGO_DISPATCHER_SHIP module is derived from the AI_CARGO_DISPATCHER module.
|
||||
--
|
||||
-- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_SHIP class, it is recommended that you first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher} module!!!
|
||||
--
|
||||
-- This will be particularly helpful in order to determine how to **Tailor the different cargo handling events**.
|
||||
--
|
||||
-- The AI_CARGO_DISPATCHER_SHIP class uses the @{Cargo.Cargo} capabilities within the MOOSE framework.
|
||||
-- Also ensure that you fully understand how to declare and setup Cargo objects within the MOOSE framework before using this class.
|
||||
-- CARGO derived objects must generally be declared within the mission to make the AI_CARGO_DISPATCHER_SHIP object recognize the cargo.
|
||||
--
|
||||
--
|
||||
-- # 1) AI_CARGO_DISPATCHER_SHIP constructor.
|
||||
--
|
||||
-- * @{#AI_CARGO_DISPATCHER_SHIP.New}(): Creates a new AI_CARGO_DISPATCHER_SHIP object.
|
||||
--
|
||||
-- ---
|
||||
--
|
||||
-- # 2) AI_CARGO_DISPATCHER_SHIP is a Finite State Machine.
|
||||
--
|
||||
-- This section must be read as follows... Each of the rows indicate a state transition, triggered through an event, and with an ending state of the event was executed.
|
||||
-- The first column is the **From** state, the second column the **Event**, and the third column the **To** state.
|
||||
--
|
||||
-- So, each of the rows have the following structure.
|
||||
--
|
||||
-- * **From** => **Event** => **To**
|
||||
--
|
||||
-- Important to know is that an event can only be executed if the **current state** is the **From** state.
|
||||
-- This, when an **Event** that is being triggered has a **From** state that is equal to the **Current** state of the state machine, the event will be executed,
|
||||
-- and the resulting state will be the **To** state.
|
||||
--
|
||||
-- These are the different possible state transitions of this state machine implementation:
|
||||
--
|
||||
-- * Idle => Start => Monitoring
|
||||
-- * Monitoring => Monitor => Monitoring
|
||||
-- * Monitoring => Stop => Idle
|
||||
--
|
||||
-- * Monitoring => Pickup => Monitoring
|
||||
-- * Monitoring => Load => Monitoring
|
||||
-- * Monitoring => Loading => Monitoring
|
||||
-- * Monitoring => Loaded => Monitoring
|
||||
-- * Monitoring => PickedUp => Monitoring
|
||||
-- * Monitoring => Deploy => Monitoring
|
||||
-- * Monitoring => Unload => Monitoring
|
||||
-- * Monitoring => Unloaded => Monitoring
|
||||
-- * Monitoring => Deployed => Monitoring
|
||||
-- * Monitoring => Home => Monitoring
|
||||
--
|
||||
--
|
||||
-- ## 2.1) AI_CARGO_DISPATCHER States.
|
||||
--
|
||||
-- * **Monitoring**: The process is dispatching.
|
||||
-- * **Idle**: The process is idle.
|
||||
--
|
||||
-- ## 2.2) AI_CARGO_DISPATCHER Events.
|
||||
--
|
||||
-- * **Start**: Start the transport process.
|
||||
-- * **Stop**: Stop the transport process.
|
||||
-- * **Monitor**: Monitor and take action.
|
||||
--
|
||||
-- * **Pickup**: Pickup cargo.
|
||||
-- * **Load**: Load the cargo.
|
||||
-- * **Loading**: The dispatcher is coordinating the loading of a cargo.
|
||||
-- * **Loaded**: Flag that the cargo is loaded.
|
||||
-- * **PickedUp**: The dispatcher has loaded all requested cargo into the CarrierGroup.
|
||||
-- * **Deploy**: Deploy cargo to a location.
|
||||
-- * **Unload**: Unload the cargo.
|
||||
-- * **Unloaded**: Flag that the cargo is unloaded.
|
||||
-- * **Deployed**: All cargo is unloaded from the carriers in the group.
|
||||
-- * **Home**: A Carrier is going home.
|
||||
--
|
||||
-- ## 2.3) Enhance your mission scripts with **Tailored** Event Handling!
|
||||
--
|
||||
-- Within your mission, you can capture these events when triggered, and tailor the events with your own code!
|
||||
-- Check out the @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} class at chapter 3 for details on the different event handlers that are available and how to use them.
|
||||
--
|
||||
-- **There are a lot of templates available that allows you to quickly setup an event handler for a specific event type!**
|
||||
--
|
||||
-- ---
|
||||
--
|
||||
-- # 3) Set the pickup parameters.
|
||||
--
|
||||
-- Several parameters can be set to pickup cargo:
|
||||
--
|
||||
-- * @{#AI_CARGO_DISPATCHER_SHIP.SetPickupRadius}(): Sets or randomizes the pickup location for the Ship around the cargo coordinate in a radius defined an outer and optional inner radius.
|
||||
-- * @{#AI_CARGO_DISPATCHER_SHIP.SetPickupSpeed}(): Set the speed or randomizes the speed in km/h to pickup the cargo.
|
||||
--
|
||||
-- # 4) Set the deploy parameters.
|
||||
--
|
||||
-- Several parameters can be set to deploy cargo:
|
||||
--
|
||||
-- * @{#AI_CARGO_DISPATCHER_SHIP.SetDeployRadius}(): Sets or randomizes the deploy location for the Ship around the cargo coordinate in a radius defined an outer and an optional inner radius.
|
||||
-- * @{#AI_CARGO_DISPATCHER_SHIP.SetDeploySpeed}(): Set the speed or randomizes the speed in km/h to deploy the cargo.
|
||||
--
|
||||
-- # 5) Set the home zone when there isn't any more cargo to pickup.
|
||||
--
|
||||
-- A home zone can be specified to where the Ship will move when there isn't any cargo left for pickup.
|
||||
-- Use @{#AI_CARGO_DISPATCHER_SHIP.SetHomeZone}() to specify the home zone.
|
||||
--
|
||||
-- If no home zone is specified, the Ship will wait near the deploy zone for a new pickup command.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_CARGO_DISPATCHER_SHIP
|
||||
AI_CARGO_DISPATCHER_SHIP = {
|
||||
ClassName = "AI_CARGO_DISPATCHER_SHIP"
|
||||
}
|
||||
|
||||
--- Creates a new AI_CARGO_DISPATCHER_SHIP object.
|
||||
-- @param #AI_CARGO_DISPATCHER_SHIP self
|
||||
-- @param Core.Set#SET_GROUP ShipSet The set of @{Wrapper.Group#GROUP} objects of Ships that will transport the cargo
|
||||
-- @param Core.Set#SET_CARGO CargoSet The set of @{Cargo.Cargo#CARGO} objects, which can be CARGO_GROUP, CARGO_CRATE, or CARGO_SLINGLOAD objects.
|
||||
-- @param Core.Set#SET_ZONE PickupZoneSet The set of pickup zones which are used to determine from where the cargo can be picked up by the Ship.
|
||||
-- @param Core.Set#SET_ZONE DeployZoneSet The set of deploy zones which determine where the cargo will be deployed by the Ship.
|
||||
-- @param #table ShippingLane Table containing list of Shipping Lanes to be used
|
||||
-- @return #AI_CARGO_DISPATCHER_SHIP
|
||||
-- @usage
|
||||
--
|
||||
-- -- An AI dispatcher object for a naval group, moving cargo from pickup zones to deploy zones via a predetermined Shipping Lane
|
||||
--
|
||||
-- local SetCargoInfantry = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart()
|
||||
-- local SetShip = SET_GROUP:New():FilterPrefixes( "Ship" ):FilterStart()
|
||||
-- local SetPickupZones = SET_ZONE:New():FilterPrefixes( "Pickup" ):FilterStart()
|
||||
-- local SetDeployZones = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart()
|
||||
-- NEED MORE THOUGHT - ShippingLane is part of Warehouse.......
|
||||
-- local ShippingLane = SET_GROUP:New():FilterPrefixes( "ShippingLane" ):FilterOnce():GetSetObjects()
|
||||
--
|
||||
-- AICargoDispatcherShip = AI_CARGO_DISPATCHER_SHIP:New( SetShip, SetCargoInfantry, SetPickupZones, SetDeployZones, ShippingLane )
|
||||
-- AICargoDispatcherShip:Start()
|
||||
--
|
||||
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
|
||||
@@ -1,663 +0,0 @@
|
||||
--- **AI** - Models the intelligent transportation of cargo using helicopters.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Cargo_Helicopter
|
||||
-- @image AI_Cargo_Dispatching_For_Helicopters.JPG
|
||||
|
||||
-- @type AI_CARGO_HELICOPTER
|
||||
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
||||
|
||||
|
||||
--- Brings a dynamic cargo handling capability for an AI helicopter group.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Helicopter carriers can be mobilized to intelligently transport infantry and other cargo within the simulation.
|
||||
--
|
||||
-- The AI_CARGO_HELICOPTER class uses the @{Cargo.Cargo} capabilities within the MOOSE framework.
|
||||
-- @{Cargo.Cargo} must be declared within the mission to make the AI_CARGO_HELICOPTER object recognize the cargo.
|
||||
-- Please consult the @{Cargo.Cargo} module for more information.
|
||||
--
|
||||
-- ## Cargo pickup.
|
||||
--
|
||||
-- Using the @{#AI_CARGO_HELICOPTER.Pickup}() method, you are able to direct the helicopters towards a point on the battlefield to board/load the cargo at the specific coordinate.
|
||||
-- Ensure that the landing zone is horizontally flat, and that trees cannot be found in the landing vicinity, or the helicopters won't land or will even crash!
|
||||
--
|
||||
-- ## Cargo deployment.
|
||||
--
|
||||
-- Using the @{#AI_CARGO_HELICOPTER.Deploy}() method, you are able to direct the helicopters towards a point on the battlefield to unboard/unload the cargo at the specific coordinate.
|
||||
-- Ensure that the landing zone is horizontally flat, and that trees cannot be found in the landing vicinity, or the helicopters won't land or will even crash!
|
||||
--
|
||||
-- ## Infantry health.
|
||||
--
|
||||
-- When infantry is unboarded from the helicopters, the infantry is actually respawned into the battlefield.
|
||||
-- As a result, the unboarding infantry is very _healthy_ every time it unboards.
|
||||
-- This is due to the limitation of the DCS simulator, which is not able to specify the health of new spawned units as a parameter.
|
||||
-- However, infantry that was destroyed when unboarded, won't be respawned again. Destroyed is destroyed.
|
||||
-- As a result, there is some additional strength that is gained when an unboarding action happens, but in terms of simulation balance this has
|
||||
-- marginal impact on the overall battlefield simulation. Fortunately, the firing strength of infantry is limited, and thus, respacing healthy infantry every
|
||||
-- time is not so much of an issue ...
|
||||
--
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_CARGO_HELICOPTER
|
||||
AI_CARGO_HELICOPTER = {
|
||||
ClassName = "AI_CARGO_HELICOPTER",
|
||||
Coordinate = nil, -- Core.Point#COORDINATE,
|
||||
}
|
||||
|
||||
AI_CARGO_QUEUE = {}
|
||||
|
||||
--- Creates a new AI_CARGO_HELICOPTER object.
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter
|
||||
-- @param Core.Set#SET_CARGO CargoSet
|
||||
-- @return #AI_CARGO_HELICOPTER
|
||||
function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
|
||||
|
||||
local self = BASE:Inherit( self, AI_CARGO:New( Helicopter, CargoSet ) ) -- #AI_CARGO_HELICOPTER
|
||||
|
||||
self.Zone = ZONE_GROUP:New( Helicopter:GetName(), Helicopter, 300 )
|
||||
|
||||
self:SetStartState( "Unloaded" )
|
||||
-- Boarding
|
||||
self:AddTransition( "Unloaded", "Pickup", "Unloaded" )
|
||||
self:AddTransition( "*", "Landed", "*" )
|
||||
self:AddTransition( "*", "Load", "*" )
|
||||
self:AddTransition( "*", "Loaded", "Loaded" )
|
||||
self:AddTransition( "Loaded", "PickedUp", "Loaded" )
|
||||
|
||||
-- Unboarding
|
||||
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" )
|
||||
|
||||
-- RTB
|
||||
self:AddTransition( "*", "Home" , "*" )
|
||||
|
||||
--- Pickup Handler OnBefore for AI_CARGO_HELICOPTER
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] OnBeforePickup
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @return #boolean
|
||||
|
||||
--- Pickup Handler OnAfter for AI_CARGO_HELICOPTER
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] OnAfterPickup
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
||||
|
||||
--- PickedUp Handler OnAfter for AI_CARGO_HELICOPTER - Cargo set has been picked up, ready to deploy
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] OnAfterPickedUp
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter The helicopter #GROUP object
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Unit#UNIT Unit The helicopter #UNIT object
|
||||
|
||||
--- Unloaded Handler OnAfter for AI_CARGO_HELICOPTER - Cargo unloaded, carrier is empty
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] OnAfterUnloaded
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Cargo.CargoGroup#CARGO_GROUP Cargo The #CARGO_GROUP object.
|
||||
-- @param Wrapper.Unit#UNIT Unit The helicopter #UNIT object
|
||||
|
||||
--- Pickup Trigger for AI_CARGO_HELICOPTER
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] Pickup
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
||||
|
||||
--- Pickup Asynchronous Trigger for AI_CARGO_HELICOPTER
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] __Pickup
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param #number Delay Delay in seconds.
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed Speed in km/h to go to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
||||
|
||||
--- Deploy Handler OnBefore for AI_CARGO_HELICOPTER
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] OnBeforeDeploy
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Core.Point#COORDINATE Coordinate Place at which cargo is deployed.
|
||||
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
||||
-- @return #boolean
|
||||
|
||||
--- Deploy Handler OnAfter for AI_CARGO_HELICOPTER
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] OnAfterDeploy
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
||||
|
||||
--- Deployed Handler OnAfter for AI_CARGO_HELICOPTER
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] OnAfterDeployed
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- Deploy Trigger for AI_CARGO_HELICOPTER
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] Deploy
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Core.Point#COORDINATE Coordinate Place at which the cargo is deployed.
|
||||
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
||||
|
||||
--- Deploy Asynchronous Trigger for AI_CARGO_HELICOPTER
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] __Deploy
|
||||
-- @param #number Delay Delay in seconds.
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Core.Point#COORDINATE Coordinate Place at which the cargo is deployed.
|
||||
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
||||
|
||||
--- Home Trigger for AI_CARGO_HELICOPTER
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] Home
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Core.Point#COORDINATE Coordinate Place to which the helicopter will go.
|
||||
-- @param #number Speed (optional) Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
||||
-- @param #number Height (optional) Height the Helicopter should be flying at.
|
||||
|
||||
--- Home Asynchronous Trigger for AI_CARGO_HELICOPTER
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] __Home
|
||||
-- @param #number Delay Delay in seconds.
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Core.Point#COORDINATE Coordinate Place to which the helicopter will go.
|
||||
-- @param #number Speed (optional) Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
||||
-- @param #number Height (optional) Height the Helicopter should be flying at.
|
||||
|
||||
-- We need to capture the Crash events for the helicopters.
|
||||
-- The helicopter reference is used in the semaphore AI_CARGO_QUEUE.
|
||||
-- So, we need to unlock this when the helo is not anymore ...
|
||||
Helicopter:HandleEvent( EVENTS.Crash,
|
||||
function( Helicopter, EventData )
|
||||
AI_CARGO_QUEUE[Helicopter] = nil
|
||||
end
|
||||
)
|
||||
|
||||
-- We need to capture the Land events for the helicopters.
|
||||
-- The helicopter reference is used in the semaphore AI_CARGO_QUEUE.
|
||||
-- So, we need to unlock this when the helo has landed, which can be anywhere ...
|
||||
-- But only free the landing coordinate after 1 minute, to ensure that all helos have left.
|
||||
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 -- kph
|
||||
self.landingheight = 5.5 -- meter
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--- Set the Carrier.
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter
|
||||
-- @return #AI_CARGO_HELICOPTER
|
||||
function AI_CARGO_HELICOPTER:SetCarrier( Helicopter )
|
||||
|
||||
local AICargo = self
|
||||
|
||||
self.Helicopter = Helicopter -- Wrapper.Group#GROUP
|
||||
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
|
||||
-- There are enemies within combat range. Unload the Helicopter.
|
||||
AICargoTroops:Destroyed()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Helicopter:OnEventLand( EventData )
|
||||
AICargo:Landed()
|
||||
end
|
||||
|
||||
self.Coalition = self.Helicopter:GetCoalition()
|
||||
|
||||
self:SetControllable( Helicopter )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set landingspeed and -height for helicopter landings. Adjust after tracing if your helis get stuck after landing.
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param #number speed Landing speed in kph(!), e.g. 15
|
||||
-- @param #number height Landing height in meters(!), e.g. 5.5
|
||||
-- @return #AI_CARGO_HELICOPTER self
|
||||
-- @usage If your choppers get stuck, add tracing to your script to determine if they hit the right parameters like so:
|
||||
--
|
||||
-- BASE:TraceOn()
|
||||
-- BASE:TraceClass("AI_CARGO_HELICOPTER")
|
||||
--
|
||||
-- Watch the DCS.log for entries stating `Helicopter:<name>, Height = Helicopter:<number>, Velocity = Helicopter:<number>`
|
||||
-- Adjust if necessary.
|
||||
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
|
||||
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
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
|
||||
|
||||
-- S_EVENT_LAND is directly called in two situations:
|
||||
-- 1 - When the helo lands normally on the ground.
|
||||
-- 2 - when the helo is hit and goes RTB or even when it is destroyed.
|
||||
-- For point 2, this is an issue, the infantry may not unload in this case!
|
||||
-- So we check if the helo is on the ground, and velocity< 15.
|
||||
-- Only then the infantry can unload (and load too, for consistency)!
|
||||
|
||||
self:T( { Helicopter:GetName(), Height = Helicopter:GetHeight( true ), Velocity = Helicopter:GetVelocityKMH() } )
|
||||
|
||||
if self.RoutePickup == true then
|
||||
if Helicopter:GetHeight( true ) <= self.landingheight then --and Helicopter:GetVelocityKMH() < self.landingspeed then
|
||||
--self:Load( Helicopter:GetPointVec2() )
|
||||
self:Load( self.PickupZone )
|
||||
self.RoutePickup = false
|
||||
end
|
||||
end
|
||||
|
||||
if self.RouteDeploy == true then
|
||||
if Helicopter:GetHeight( true ) <= self.landingheight then --and Helicopter:GetVelocityKMH() < self.landingspeed then
|
||||
self:Unload( self.DeployZone )
|
||||
self.RouteDeploy = false
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed
|
||||
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 -- Core.Zone#ZONE_RADIUS
|
||||
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 CoordinateFrom = Helicopter:GetCoordinate()
|
||||
-- local WaypointFrom = CoordinateFrom:WaypointAir(
|
||||
-- "RADIO",
|
||||
-- COORDINATE.WaypointType.TurningPoint,
|
||||
-- COORDINATE.WaypointAction.TurningPoint,
|
||||
-- Speed,
|
||||
-- true
|
||||
-- )
|
||||
-- Route[#Route+1] = WaypointFrom
|
||||
local CoordinateTo = Coordinate
|
||||
|
||||
local landheight = CoordinateTo:GetLandHeight() -- get target height
|
||||
CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground
|
||||
|
||||
local WaypointTo = CoordinateTo:WaypointAir(
|
||||
"RADIO",
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.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
|
||||
|
||||
-- Now route the helicopter
|
||||
Helicopter:Route( Route, 0 )
|
||||
|
||||
-- Keep the DeployZone, because when the helo has landed, we want to provide the DeployZone to the mission designer as part of the Unloaded event.
|
||||
self.DeployZone = DeployZone
|
||||
|
||||
else
|
||||
self:__Queue( -10, Coordinate, Speed, DeployZone )
|
||||
end
|
||||
end
|
||||
else
|
||||
AI_CARGO_QUEUE[Helicopter] = nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed
|
||||
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() -- get target height
|
||||
CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground
|
||||
|
||||
local WaypointTo = CoordinateTo:WaypointAir("RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.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
|
||||
|
||||
-- Now route the helicopter
|
||||
Helicopter:Route(Route, 0)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- On after Deployed event.
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Cargo.Cargo#CARGO Cargo Cargo object.
|
||||
-- @param #boolean Deployed Cargo is deployed.
|
||||
-- @return #boolean True if all cargo has been unloaded.
|
||||
function AI_CARGO_HELICOPTER:onafterDeployed( Helicopter, From, Event, To, DeployZone )
|
||||
self:F( { From, Event, To, DeployZone = DeployZone } )
|
||||
|
||||
self:Orbit( Helicopter:GetCoordinate(), 50 )
|
||||
|
||||
-- Free the coordinate zone after 30 seconds, so that the original helicopter can fly away first.
|
||||
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
|
||||
|
||||
--- On after Pickup event.
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Core.Point#COORDINATE Coordinate Pickup place.
|
||||
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
||||
-- @param #number Height Height in meters to move to the pickup coordinate. This parameter is ignored for APCs.
|
||||
-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided.
|
||||
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 = {}
|
||||
|
||||
--- Calculate the target route point.
|
||||
local CoordinateFrom = Helicopter:GetCoordinate()
|
||||
|
||||
--- Create a route point of type air.
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, _speed, true)
|
||||
|
||||
--- Create a route point of type air.
|
||||
local CoordinateTo = Coordinate
|
||||
local landheight = CoordinateTo:GetLandHeight() -- get target height
|
||||
CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground
|
||||
|
||||
local WaypointTo = CoordinateTo:WaypointAir("RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint,_speed, true)
|
||||
|
||||
Route[#Route+1] = WaypointFrom
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
|
||||
Helicopter:WayPointInitialize( Route )
|
||||
|
||||
local Tasks = {}
|
||||
|
||||
Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() )
|
||||
Route[#Route].task = Helicopter:TaskCombo( Tasks )
|
||||
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
-- Now route the helicopter
|
||||
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
|
||||
|
||||
--- Depoloy function and queue.
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP AICargoHelicopter
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate
|
||||
function AI_CARGO_HELICOPTER:_Deploy( AICargoHelicopter, Coordinate, DeployZone )
|
||||
AICargoHelicopter:__Queue( -10, Coordinate, 100, DeployZone )
|
||||
end
|
||||
|
||||
--- On after Deploy event.
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter Transport helicopter.
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Core.Point#COORDINATE Coordinate Place at which the cargo is deployed.
|
||||
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
||||
-- @param #number Height Height in meters to move to the deploy coordinate.
|
||||
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 = {}
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
Coordinate.y = Height
|
||||
|
||||
local _speed=Speed or Helicopter:GetSpeedMax()*0.5
|
||||
|
||||
--- Create a route point of type air.
|
||||
local CoordinateFrom = Helicopter:GetCoordinate()
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, _speed, true)
|
||||
Route[#Route+1] = WaypointFrom
|
||||
Route[#Route+1] = WaypointFrom
|
||||
|
||||
--- Create a route point of type air.
|
||||
|
||||
local CoordinateTo = Coordinate
|
||||
local landheight = CoordinateTo:GetLandHeight() -- get target height
|
||||
CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground
|
||||
|
||||
local WaypointTo = CoordinateTo:WaypointAir("RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, _speed, true)
|
||||
|
||||
Route[#Route+1] = WaypointTo
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
|
||||
Helicopter:WayPointInitialize( Route )
|
||||
|
||||
local Tasks = {}
|
||||
|
||||
-- The _Deploy function does not exist.
|
||||
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 ) )
|
||||
|
||||
--Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() )
|
||||
Route[#Route].task = Helicopter:TaskCombo( Tasks )
|
||||
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
-- Now route the helicopter
|
||||
Helicopter:Route( Route, 0 )
|
||||
|
||||
self:GetParent( self, AI_CARGO_HELICOPTER ).onafterDeploy( self, Helicopter, From, Event, To, Coordinate, Speed, Height, DeployZone )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- On after Home event.
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Core.Point#COORDINATE Coordinate Home place.
|
||||
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
||||
-- @param #number Height Height in meters to move to the home coordinate.
|
||||
-- @param Core.Zone#ZONE HomeZone The zone wherein the carrier will return when all cargo has been transported. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
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 = {}
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
--Coordinate.y = Height
|
||||
Height = Height or 50
|
||||
|
||||
Speed = Speed or Helicopter:GetSpeedMax()*0.5
|
||||
|
||||
--- Create a route point of type air.
|
||||
local CoordinateFrom = Helicopter:GetCoordinate()
|
||||
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, Speed, true)
|
||||
Route[#Route+1] = WaypointFrom
|
||||
|
||||
--- Create a route point of type air.
|
||||
local CoordinateTo = Coordinate
|
||||
local landheight = CoordinateTo:GetLandHeight() -- get target height
|
||||
CoordinateTo.y = landheight + Height -- flight height should be 50m above ground
|
||||
|
||||
local WaypointTo = CoordinateTo:WaypointAir("RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, Speed, true)
|
||||
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
|
||||
Helicopter:WayPointInitialize( Route )
|
||||
|
||||
local Tasks = {}
|
||||
|
||||
Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() )
|
||||
Route[#Route].task = Helicopter:TaskCombo( Tasks )
|
||||
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
-- Now route the helicopter
|
||||
Helicopter:Route(Route, 0)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,404 +0,0 @@
|
||||
--- **AI** - Models the intelligent transportation of infantry and other cargo.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **acrojason** (derived from AI_Cargo_APC by FlightControl)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Cargo_Ship
|
||||
-- @image AI_Cargo_Dispatcher.JPG
|
||||
|
||||
-- @type AI_CARGO_SHIP
|
||||
-- @extends AI.AI_Cargo#AI_CARGO
|
||||
|
||||
--- Brings a dynamic cargo handling capability for an AI naval group.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Naval ships can be utilized to transport cargo around the map following naval shipping lanes.
|
||||
-- The AI_CARGO_SHIP class uses the @{Cargo.Cargo} capabilities within the MOOSE framework.
|
||||
-- @{Cargo.Cargo} must be declared within the mission or warehouse to make the AI_CARGO_SHIP recognize the cargo.
|
||||
-- Please consult the @{Cargo.Cargo} module for more information.
|
||||
--
|
||||
-- ## Cargo loading.
|
||||
--
|
||||
-- The module will automatically load cargo when the Ship is within boarding or loading radius.
|
||||
-- The boarding or loading radius is specified when the cargo is created in the simulation and depends on the type of
|
||||
-- cargo and the specified boarding radius.
|
||||
--
|
||||
-- ## Defending the Ship when enemies are nearby
|
||||
-- This is not supported for naval cargo because most tanks don't float. Protect your transports...
|
||||
--
|
||||
-- ## Infantry or cargo **health**.
|
||||
-- When cargo is unboarded from the Ship, the cargo is actually respawned into the battlefield.
|
||||
-- As a result, the unboarding cargo is very _healthy_ every time it unboards.
|
||||
-- This is due to the limitation of the DCS simulator, which is not able to specify the health of newly spawned units as a parameter.
|
||||
-- However, cargo that was destroyed when unboarded and following the Ship won't be respawned again (this is likely not a thing for
|
||||
-- naval cargo due to the lack of support for defending the Ship mentioned above). Destroyed is destroyed.
|
||||
-- As a result, there is some additional strength that is gained when an unboarding action happens, but in terms of simulation balance
|
||||
-- this has marginal impact on the overall battlefield simulation. Given the relatively short duration of DCS missions and the somewhat
|
||||
-- lengthy naval transport times, most units entering the Ship as cargo will be freshly en route to an amphibious landing or transporting
|
||||
-- between warehouses.
|
||||
--
|
||||
-- ## Control the Ships on the map.
|
||||
--
|
||||
-- Currently, naval transports can only be controlled via scripts due to their reliance upon predefined Shipping Lanes created in the Mission
|
||||
-- Editor. An interesting future enhancement could leverage new pathfinding functionality for ships in the Ops module.
|
||||
--
|
||||
-- ## Cargo deployment.
|
||||
--
|
||||
-- Using the @{#AI_CARGO_SHIP.Deploy}() method, you are able to direct the Ship towards a Deploy zone to unboard/unload the cargo at the
|
||||
-- specified coordinate. The Ship will follow the Shipping Lane to ensure consistent cargo transportation within the simulation environment.
|
||||
--
|
||||
-- ## Cargo pickup.
|
||||
--
|
||||
-- Using the @{#AI_CARGO_SHIP.Pickup}() method, you are able to direct the Ship towards a Pickup zone to board/load the cargo at the specified
|
||||
-- coordinate. The Ship will follow the Shipping Lane to ensure consistent cargo transportation within the simulation environment.
|
||||
--
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- @field #AI_CARGO_SHIP
|
||||
AI_CARGO_SHIP = {
|
||||
ClassName = "AI_CARGO_SHIP",
|
||||
Coordinate = nil -- Core.Point#COORDINATE
|
||||
}
|
||||
|
||||
--- Creates a new AI_CARGO_SHIP object.
|
||||
-- @param #AI_CARGO_SHIP self
|
||||
-- @param Wrapper.Group#GROUP Ship The carrier Ship group
|
||||
-- @param Core.Set#SET_CARGO CargoSet The set of cargo to be transported
|
||||
-- @param #number CombatRadius Provide the combat radius to defend the carrier by unboarding the cargo when enemies are nearby. When CombatRadius is 0, no defense will occur.
|
||||
-- @param #table ShippingLane Table containing list of Shipping Lanes to be used
|
||||
-- @return #AI_CARGO_SHIP
|
||||
function AI_CARGO_SHIP:New( Ship, CargoSet, CombatRadius, ShippingLane )
|
||||
|
||||
local self = BASE:Inherit( self, AI_CARGO:New( Ship, CargoSet ) ) -- #AI_CARGO_SHIP
|
||||
|
||||
self:AddTransition( "*", "Monitor", "*" )
|
||||
self:AddTransition( "*", "Destroyed", "Destroyed" )
|
||||
self:AddTransition( "*", "Home", "*" )
|
||||
|
||||
self:SetCombatRadius( 0 ) -- Don't want to deploy cargo in middle of water to defend Ship, so set CombatRadius to 0
|
||||
self:SetShippingLane ( ShippingLane )
|
||||
|
||||
self:SetCarrier( Ship )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the Carrier
|
||||
-- @param #AI_CARGO_SHIP self
|
||||
-- @param Wrapper.Group#GROUP CargoCarrier
|
||||
-- @return #AI_CARGO_SHIP
|
||||
function AI_CARGO_SHIP:SetCarrier( CargoCarrier )
|
||||
self.CargoCarrier = CargoCarrier -- Wrapper.Group#GROUIP
|
||||
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
|
||||
-- Better hope they can swim!
|
||||
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
|
||||
|
||||
|
||||
--- FInd a free Carrier within a radius
|
||||
-- @param #AI_CARGO_SHIP self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Radius
|
||||
-- @return Wrapper.Group#GROUP NewCarrier
|
||||
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
|
||||
|
||||
|
||||
--- Follow Infantry to the Carrier
|
||||
-- @param #AI_CARGO_SHIP self
|
||||
-- @param #AI_CARGO_SHIP Me
|
||||
-- @param Wrapper.Unit#UNIT ShipUnit
|
||||
-- @param Cargo.CargoGroup#CARGO_GROUP Cargo
|
||||
-- @return #AI_CARGO_SHIP
|
||||
function AI_CARGO_SHIP:FollowToCarrier( Me, ShipUnit, CargoGroup )
|
||||
|
||||
local InfantryGroup = CargoGroup:GetGroup()
|
||||
|
||||
self:F( { self=self:GetClassNameAndID(), InfantryGroup = InfantryGroup:GetName() } )
|
||||
|
||||
if ShipUnit:IsAlive() then
|
||||
-- Check if the Cargo is near the CargoCarrier
|
||||
if InfantryGroup:IsPartlyInZone( ZONE_UNIT:New( "Radius", ShipUnit, 1000 ) ) then
|
||||
|
||||
-- Cargo does not need to navigate to Carrier
|
||||
Me:Guard()
|
||||
else
|
||||
|
||||
self:F( { InfantryGroup = InfantryGroup:GetName() } )
|
||||
if InfantryGroup:IsAlive() then
|
||||
|
||||
self:F( { InfantryGroup = InfantryGroup:GetName() } )
|
||||
local Waypoints = {}
|
||||
|
||||
-- Calculate new route
|
||||
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 ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone
|
||||
|
||||
InfantryGroup:Route( Waypoints, 1 ) -- Move after a random number of seconds to the Route. See Route method for details
|
||||
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
|
||||
-- We really shouldn't find ourselves in here for Ships since the CombatRadius should always be 0.
|
||||
-- This is to avoid Unloading the Ship in the middle of the sea.
|
||||
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
|
||||
-- There are no enemies within combat radius. Reload the CargoCarrier.
|
||||
self:Reload()
|
||||
end
|
||||
else
|
||||
if self:Is( "Loaded" ) then
|
||||
-- There are enemies within combat radius. Unload the CargoCarrier.
|
||||
self:__Unload( 1, nil, true ) -- The 2nd parameter is true, which means that the unload is for defending the carrier, not to deploy!
|
||||
else
|
||||
if self:Is( "Unloaded" ) then
|
||||
--self:Follow()
|
||||
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 -- Cargo.Cargo#CARGO
|
||||
local ShipUnit = ShipUnit -- Wrapper.Unit#UNIT
|
||||
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
|
||||
|
||||
--- Check if cargo ship is alive and trigger Load event
|
||||
-- @param Wrapper.Group#Group Ship
|
||||
-- @param #AI_CARGO_SHIP self
|
||||
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
|
||||
|
||||
--- Check if cargo ship is alive and trigger Unload event. Good time to remind people that Lua is case sensitive and Unload != UnLoad
|
||||
-- @param Wrapper.Group#GROUP Ship
|
||||
-- @param #AI_CARGO_SHIP self
|
||||
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
|
||||
|
||||
--- on after Pickup event.
|
||||
-- @param AI_CARGO_SHIP Ship
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Core.Point#COORDINATE Coordinate of the pickup point
|
||||
-- @param #number Speed Speed in km/h to sail to the pickup coordinate. Default is 50% of max speed for the unit
|
||||
-- @param #number Height Altitude in meters to move to the pickup coordinate. This parameter is ignored for Ships
|
||||
-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil if there was no PickupZoneSet provided
|
||||
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
|
||||
|
||||
--- On after Deploy event.
|
||||
-- @param #AI_CARGO_SHIP self
|
||||
-- @param Wrapper.Group#GROUP SHIP
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate of the deploy point
|
||||
-- @param #number Speed Speed in km/h to sail to the deploy coordinate. Default is 50% of max speed for the unit
|
||||
-- @param #number Height Altitude in meters to move to the deploy coordinate. This parameter is ignored for Ships
|
||||
-- @param Core.Zone#ZONE DeployZone The zone where the cargo will be deployed.
|
||||
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
|
||||
|
||||
--- On after Unload event.
|
||||
-- @param #AI_CARGO_SHIP self
|
||||
-- @param Wrapper.Group#GROUP Ship
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
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 -- Wrapper.Unit#UNIT
|
||||
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 = {}
|
||||
|
||||
-- Need to find a more generalized way to do this instead of reversing the shipping lane.
|
||||
-- This only works if the Source/Dest route waypoints are numbered 1..n and not n..1
|
||||
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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,192 +0,0 @@
|
||||
--- **AI** - Models the automatic assignment of AI escorts to player flights.
|
||||
--
|
||||
-- ## Features:
|
||||
-- --
|
||||
-- * Provides the facilities to trigger escorts when players join flight slots.
|
||||
-- *
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Escort_Dispatcher
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
|
||||
-- @type AI_ESCORT_DISPATCHER
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
|
||||
--- Models the automatic assignment of AI escorts to player flights.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_ESCORT_DISPATCHER
|
||||
AI_ESCORT_DISPATCHER = {
|
||||
ClassName = "AI_ESCORT_DISPATCHER",
|
||||
}
|
||||
|
||||
-- @field #list
|
||||
AI_ESCORT_DISPATCHER.AI_Escorts = {}
|
||||
|
||||
|
||||
--- Creates a new AI_ESCORT_DISPATCHER object.
|
||||
-- @param #AI_ESCORT_DISPATCHER self
|
||||
-- @param Core.Set#SET_GROUP CarrierSet The set of @{Wrapper.Group#GROUP} objects of carriers for which escorts are spawned in.
|
||||
-- @param Core.Spawn#SPAWN EscortSpawn The spawn object that will spawn in the Escorts.
|
||||
-- @param Wrapper.Airbase#AIRBASE EscortAirbase The airbase where the escorts are spawned.
|
||||
-- @param #string EscortName Name of the escort, which will also be the name of the escort menu.
|
||||
-- @param #string EscortBriefing A text showing the briefing to the player. Note that if no EscortBriefing is provided, the default briefing will be shown.
|
||||
-- @return #AI_ESCORT_DISPATCHER
|
||||
-- @usage
|
||||
--
|
||||
-- -- Create a new escort when a player joins an SU-25T plane.
|
||||
-- Create a carrier set, which contains the player slots that can be joined by the players, for which escorts will be defined.
|
||||
-- local Red_SU25T_CarrierSet = SET_GROUP:New():FilterPrefixes( "Red A2G Player Su-25T" ):FilterStart()
|
||||
--
|
||||
-- -- Create a spawn object that will spawn in the escorts, once the player has joined the player slot.
|
||||
-- local Red_SU25T_EscortSpawn = SPAWN:NewWithAlias( "Red A2G Su-25 Escort", "Red AI A2G SU-25 Escort" ):InitLimit( 10, 10 )
|
||||
--
|
||||
-- -- Create an airbase object, where the escorts will be spawned.
|
||||
-- local Red_SU25T_Airbase = AIRBASE:FindByName( AIRBASE.Caucasus.Maykop_Khanskaya )
|
||||
--
|
||||
-- -- Park the airplanes at the airbase, visible before start.
|
||||
-- Red_SU25T_EscortSpawn:ParkAtAirbase( Red_SU25T_Airbase, AIRBASE.TerminalType.OpenMedOrBig )
|
||||
--
|
||||
-- -- New create the escort dispatcher, using the carrier set, the escort spawn object at the escort airbase.
|
||||
-- -- Provide a name of the escort, which will be also the name appearing on the radio menu for the group.
|
||||
-- -- And a briefing to appear when the player joins the player slot.
|
||||
-- Red_SU25T_EscortDispatcher = AI_ESCORT_DISPATCHER:New( Red_SU25T_CarrierSet, Red_SU25T_EscortSpawn, Red_SU25T_Airbase, "Escort Su-25", "You Su-25T is escorted by one Su-25. Use the radio menu to control the escorts." )
|
||||
--
|
||||
-- -- The dispatcher needs to be started using the :Start() method.
|
||||
-- Red_SU25T_EscortDispatcher:Start()
|
||||
function AI_ESCORT_DISPATCHER:New( CarrierSet, EscortSpawn, EscortAirbase, EscortName, EscortBriefing )
|
||||
|
||||
local self = BASE:Inherit( self, FSM:New() ) -- #AI_ESCORT_DISPATCHER
|
||||
|
||||
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" )
|
||||
|
||||
-- Put a Dead event handler on CarrierSet, to ensure that when a carrier is destroyed, that all internal parameters are reset.
|
||||
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
|
||||
|
||||
-- @param #AI_ESCORT_DISPATCHER self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function AI_ESCORT_DISPATCHER:OnEventExit( EventData )
|
||||
|
||||
local PlayerGroupName = EventData.IniGroupName
|
||||
local PlayerGroup = EventData.IniGroup
|
||||
local PlayerUnit = EventData.IniUnit
|
||||
|
||||
self:T({EscortAirbase= self.EscortAirbase } )
|
||||
self:T({PlayerGroupName = PlayerGroupName } )
|
||||
self:T({PlayerGroup = PlayerGroup})
|
||||
self:T({FirstGroup = self.CarrierSet:GetFirst()})
|
||||
self:T({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
|
||||
|
||||
-- @param #AI_ESCORT_DISPATCHER self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function AI_ESCORT_DISPATCHER:OnEventBirth( EventData )
|
||||
|
||||
local PlayerGroupName = EventData.IniGroupName
|
||||
local PlayerGroup = EventData.IniGroup
|
||||
local PlayerUnit = EventData.IniUnit
|
||||
|
||||
self:T({EscortAirbase= self.EscortAirbase } )
|
||||
self:T({PlayerGroupName = PlayerGroupName } )
|
||||
self:T({PlayerGroup = PlayerGroup})
|
||||
self:T({FirstGroup = self.CarrierSet:GetFirst()})
|
||||
self:T({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:T({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
|
||||
|
||||
|
||||
--- Start Trigger for AI_ESCORT_DISPATCHER
|
||||
-- @function [parent=#AI_ESCORT_DISPATCHER] Start
|
||||
-- @param #AI_ESCORT_DISPATCHER self
|
||||
|
||||
--- Start Asynchronous Trigger for AI_ESCORT_DISPATCHER
|
||||
-- @function [parent=#AI_ESCORT_DISPATCHER] __Start
|
||||
-- @param #AI_ESCORT_DISPATCHER self
|
||||
-- @param #number Delay
|
||||
|
||||
--- Stop Trigger for AI_ESCORT_DISPATCHER
|
||||
-- @function [parent=#AI_ESCORT_DISPATCHER] Stop
|
||||
-- @param #AI_ESCORT_DISPATCHER self
|
||||
|
||||
--- Stop Asynchronous Trigger for AI_ESCORT_DISPATCHER
|
||||
-- @function [parent=#AI_ESCORT_DISPATCHER] __Stop
|
||||
-- @param #AI_ESCORT_DISPATCHER self
|
||||
-- @param #number Delay
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
--- **AI** - Models the assignment of AI escorts to player flights upon request using the radio menu.
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Provides the facilities to trigger escorts when players join flight units.
|
||||
-- * Provide a menu for which escorts can be requested.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Escort_Dispatcher_Request
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
|
||||
-- @type AI_ESCORT_DISPATCHER_REQUEST
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
|
||||
--- Models the assignment of AI escorts to player flights upon request using the radio menu.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_ESCORT_DISPATCHER_REQUEST
|
||||
AI_ESCORT_DISPATCHER_REQUEST = {
|
||||
ClassName = "AI_ESCORT_DISPATCHER_REQUEST",
|
||||
}
|
||||
|
||||
-- @field #list
|
||||
AI_ESCORT_DISPATCHER_REQUEST.AI_Escorts = {}
|
||||
|
||||
|
||||
--- Creates a new AI_ESCORT_DISPATCHER_REQUEST object.
|
||||
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
|
||||
-- @param Core.Set#SET_GROUP CarrierSet The set of @{Wrapper.Group#GROUP} objects of carriers for which escorts are requested.
|
||||
-- @param Core.Spawn#SPAWN EscortSpawn The spawn object that will spawn in the Escorts.
|
||||
-- @param Wrapper.Airbase#AIRBASE EscortAirbase The airbase where the escorts are spawned.
|
||||
-- @param #string EscortName Name of the escort, which will also be the name of the escort menu.
|
||||
-- @param #string EscortBriefing A text showing the briefing to the player. Note that if no EscortBriefing is provided, the default briefing will be shown.
|
||||
-- @return #AI_ESCORT_DISPATCHER_REQUEST
|
||||
function AI_ESCORT_DISPATCHER_REQUEST:New( CarrierSet, EscortSpawn, EscortAirbase, EscortName, EscortBriefing )
|
||||
|
||||
local self = BASE:Inherit( self, FSM:New() ) -- #AI_ESCORT_DISPATCHER_REQUEST
|
||||
|
||||
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" )
|
||||
|
||||
-- Put a Dead event handler on CarrierSet, to ensure that when a carrier is destroyed, that all internal parameters are reset.
|
||||
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
|
||||
|
||||
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
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
|
||||
|
||||
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
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
|
||||
|
||||
|
||||
--- Start Trigger for AI_ESCORT_DISPATCHER_REQUEST
|
||||
-- @function [parent=#AI_ESCORT_DISPATCHER_REQUEST] Start
|
||||
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
|
||||
|
||||
--- Start Asynchronous Trigger for AI_ESCORT_DISPATCHER_REQUEST
|
||||
-- @function [parent=#AI_ESCORT_DISPATCHER_REQUEST] __Start
|
||||
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
|
||||
-- @param #number Delay
|
||||
|
||||
--- Stop Trigger for AI_ESCORT_DISPATCHER_REQUEST
|
||||
-- @function [parent=#AI_ESCORT_DISPATCHER_REQUEST] Stop
|
||||
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
|
||||
|
||||
--- Stop Asynchronous Trigger for AI_ESCORT_DISPATCHER_REQUEST
|
||||
-- @function [parent=#AI_ESCORT_DISPATCHER_REQUEST] __Stop
|
||||
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
|
||||
-- @param #number Delay
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,323 +0,0 @@
|
||||
--- **AI** - Taking the lead of AI escorting your flight or of other AI, upon request using the menu.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Escort navigation commands.
|
||||
-- * Escort hold at position commands.
|
||||
-- * Escorts reporting detected targets.
|
||||
-- * Escorts scanning targets in advance.
|
||||
-- * Escorts attacking specific targets.
|
||||
-- * Request assistance from other groups for attack.
|
||||
-- * Manage rule of engagement of escorts.
|
||||
-- * Manage the allowed evasion techniques of escorts.
|
||||
-- * Make escort to execute a defined mission or path.
|
||||
-- * Escort tactical situation reporting.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Missions:
|
||||
--
|
||||
-- [ESC - Escorting](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_Escort)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- Allows you to interact with escorting AI on your flight and take the lead.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Each escorting group can be commanded with a complete set of radio commands (radio menu in your flight, and then F10).
|
||||
--
|
||||
-- The radio commands will vary according the category of the group. The richest set of commands are with helicopters and airPlanes.
|
||||
-- Ships and Ground troops will have a more limited set, but they can provide support through the bombing of targets designated by the other escorts.
|
||||
--
|
||||
-- Escorts detect targets using a built-in detection mechanism. The detected targets are reported at a specified time interval.
|
||||
-- Once targets are reported, each escort has these targets as menu options to command the attack of these targets.
|
||||
-- Targets are by default grouped per area of 5000 meters, but the kind of detection and the grouping range can be altered.
|
||||
--
|
||||
-- Different formations can be selected in the Flight menu: Trail, Stack, Left Line, Right Line, Left Wing, Right Wing, Central Wing and Boxed formations are available.
|
||||
-- The Flight menu also allows for a mass attack, where all of the escorts are commanded to attack a target.
|
||||
--
|
||||
-- Escorts can emit flares to reports their location. They can be commanded to hold at a location, which can be their current or the leader location.
|
||||
-- In this way, you can spread out the escorts over the battle field before a coordinated attack.
|
||||
--
|
||||
-- But basically, the escort class provides 4 modes of operation, and depending on the mode, you are either leading the flight, or following the flight.
|
||||
--
|
||||
-- ## Leading the flight
|
||||
--
|
||||
-- When leading the flight, you are expected to guide the escorts towards the target areas,
|
||||
-- and carefully coordinate the attack based on the threat levels reported, and the available weapons
|
||||
-- carried by the escorts. Ground ships or ground troops can execute A-assisted attacks, when they have long-range ground precision weapons for attack.
|
||||
--
|
||||
-- ## Following the flight
|
||||
--
|
||||
-- Escorts can be commanded to execute a specific mission path. In this mode, the escorts are in the lead.
|
||||
-- You as a player, are following the escorts, and are commanding them to progress the mission while
|
||||
-- ensuring that the escorts survive. You are joining the escorts in the battlefield. They will detect and report targets
|
||||
-- and you will ensure that the attacks are well coordinated, assigning the correct escort type for the detected target
|
||||
-- type. Once the attack is finished, the escort will resume the mission it was assigned.
|
||||
-- In other words, you can use the escorts for reconnaissance, and for guiding the attack.
|
||||
-- Imagine you as a mi-8 pilot, assigned to pickup cargo. Two ka-50s are guiding the way, and you are
|
||||
-- following. You are in control. The ka-50s detect targets, report them, and you command how the attack
|
||||
-- will commence and from where. You can control where the escorts are holding position and which targets
|
||||
-- are attacked first. You are in control how the ka-50s will follow their mission path.
|
||||
--
|
||||
-- Escorts can act as part of a AI A2G dispatcher offensive. In this way, You was a player are in control.
|
||||
-- The mission is defined by the A2G dispatcher, and you are responsible to join the flight and ensure that the
|
||||
-- attack is well coordinated.
|
||||
--
|
||||
-- It is with great proud that I present you this class, and I hope you will enjoy the functionality and the dynamism
|
||||
-- it brings in your DCS world simulations.
|
||||
--
|
||||
-- # RADIO MENUs that can be created:
|
||||
--
|
||||
-- Find a summary below of the current available commands:
|
||||
--
|
||||
-- ## Navigation ...:
|
||||
--
|
||||
-- Escort group navigation functions:
|
||||
--
|
||||
-- * **"Join-Up":** The escort group fill follow you in the assigned formation.
|
||||
-- * **"Flare":** Provides menu commands to let the escort group shoot a flare in the air in a color.
|
||||
-- * **"Smoke":** Provides menu commands to let the escort group smoke the air in a color. Note that smoking is only available for ground and naval troops.
|
||||
--
|
||||
-- ## Hold position ...:
|
||||
--
|
||||
-- Escort group navigation functions:
|
||||
--
|
||||
-- * **"At current location":** The escort group will hover above the ground at the position they were. The altitude can be specified as a parameter.
|
||||
-- * **"At my location":** The escort group will hover or orbit at the position where you are. The escort will fly to your location and hold position. The altitude can be specified as a parameter.
|
||||
--
|
||||
-- ## Report targets ...:
|
||||
--
|
||||
-- Report targets will make the escort group to report any target that it identifies within detection range. Any detected target can be attacked using the "Attack Targets" menu function. (see below).
|
||||
--
|
||||
-- * **"Report now":** Will report the current detected targets.
|
||||
-- * **"Report targets on":** Will make the escorts to report the detected targets and will fill the "Attack Targets" menu list.
|
||||
-- * **"Report targets off":** Will stop detecting targets.
|
||||
--
|
||||
-- ## Attack targets ...:
|
||||
--
|
||||
-- This menu item will list all detected targets within a 15km range. Depending on the level of detection (known/unknown) and visuality, the targets type will also be listed.
|
||||
-- This menu will be available in Flight menu or in each Escort menu.
|
||||
--
|
||||
-- ## Scan targets ...:
|
||||
--
|
||||
-- Menu items to pop-up the escort group for target scanning. After scanning, the escort group will resume with the mission or rejoin formation.
|
||||
--
|
||||
-- * **"Scan targets 30 seconds":** Scan 30 seconds for targets.
|
||||
-- * **"Scan targets 60 seconds":** Scan 60 seconds for targets.
|
||||
--
|
||||
-- ## Request assistance from ...:
|
||||
--
|
||||
-- This menu item will list all detected targets within a 15km range, similar as with the menu item **Attack Targets**.
|
||||
-- This menu item allows to request attack support from other ground based escorts supporting the current escort.
|
||||
-- eg. the function allows a player to request support from the Ship escort to attack a target identified by the Plane escort with its Tomahawk missiles.
|
||||
-- eg. the function allows a player to request support from other Planes escorting to bomb the unit with illumination missiles or bombs, so that the main plane escort can attack the area.
|
||||
--
|
||||
-- ## ROE ...:
|
||||
--
|
||||
-- Sets the Rules of Engagement (ROE) of the escort group when in flight.
|
||||
--
|
||||
-- * **"Hold Fire":** The escort group will hold fire.
|
||||
-- * **"Return Fire":** The escort group will return fire.
|
||||
-- * **"Open Fire":** The escort group will open fire on designated targets.
|
||||
-- * **"Weapon Free":** The escort group will engage with any target.
|
||||
--
|
||||
-- ## Evasion ...:
|
||||
--
|
||||
-- Will define the evasion techniques that the escort group will perform during flight or combat.
|
||||
--
|
||||
-- * **"Fight until death":** The escort group will have no reaction to threats.
|
||||
-- * **"Use flares, chaff and jammers":** The escort group will use passive defense using flares and jammers. No evasive manoeuvres are executed.
|
||||
-- * **"Evade enemy fire":** The rescort group will evade enemy fire before firing.
|
||||
-- * **"Go below radar and evade fire":** The escort group will perform evasive vertical manoeuvres.
|
||||
--
|
||||
-- ## Resume Mission ...:
|
||||
--
|
||||
-- Escort groups can have their own mission. This menu item will allow the escort group to resume their Mission from a given waypoint.
|
||||
-- Note that this is really fantastic, as you now have the dynamic of taking control of the escort groups, and allowing them to resume their path or mission.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Authors: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Escort_Request
|
||||
-- @image Escorting.JPG
|
||||
|
||||
|
||||
|
||||
-- @type AI_ESCORT_REQUEST
|
||||
-- @extends AI.AI_Escort#AI_ESCORT
|
||||
|
||||
--- AI_ESCORT_REQUEST class
|
||||
--
|
||||
-- # AI_ESCORT_REQUEST construction methods.
|
||||
--
|
||||
-- Create a new AI_ESCORT_REQUEST object with the @{#AI_ESCORT_REQUEST.New} method:
|
||||
--
|
||||
-- * @{#AI_ESCORT_REQUEST.New}: Creates a new AI_ESCORT_REQUEST object from a @{Wrapper.Group#GROUP} for a @{Wrapper.Client#CLIENT}, with an optional briefing text.
|
||||
--
|
||||
-- @usage
|
||||
-- -- Declare a new EscortPlanes object as follows:
|
||||
--
|
||||
-- -- First find the GROUP object and the CLIENT object.
|
||||
-- local EscortUnit = CLIENT:FindByName( "Unit Name" ) -- The Unit Name is the name of the unit flagged with the skill Client in the mission editor.
|
||||
-- local EscortGroup = GROUP:FindByName( "Group Name" ) -- The Group Name is the name of the group that will escort the Escort Client.
|
||||
--
|
||||
-- -- Now use these 2 objects to construct the new EscortPlanes object.
|
||||
-- EscortPlanes = AI_ESCORT_REQUEST:New( EscortUnit, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." )
|
||||
--
|
||||
-- @field #AI_ESCORT_REQUEST
|
||||
AI_ESCORT_REQUEST = {
|
||||
ClassName = "AI_ESCORT_REQUEST",
|
||||
}
|
||||
|
||||
--- AI_ESCORT_REQUEST.Mode class
|
||||
-- @type AI_ESCORT_REQUEST.MODE
|
||||
-- @field #number FOLLOW
|
||||
-- @field #number MISSION
|
||||
|
||||
--- MENUPARAM type
|
||||
-- @type MENUPARAM
|
||||
-- @field #AI_ESCORT_REQUEST ParamSelf
|
||||
-- @field #Distance ParamDistance
|
||||
-- @field #function ParamFunction
|
||||
-- @field #string ParamMessage
|
||||
|
||||
--- AI_ESCORT_REQUEST class constructor for an AI group
|
||||
-- @param #AI_ESCORT_REQUEST self
|
||||
-- @param Wrapper.Client#CLIENT EscortUnit The client escorted by the EscortGroup.
|
||||
-- @param Core.Spawn#SPAWN EscortSpawn The spawn object of AI, escorting the EscortUnit.
|
||||
-- @param Wrapper.Airbase#AIRBASE EscortAirbase The airbase where escorts will be spawned once requested.
|
||||
-- @param #string EscortName Name of the escort.
|
||||
-- @param #string EscortBriefing A text showing the AI_ESCORT_REQUEST briefing to the player. Note that if no EscortBriefing is provided, the default briefing will be shown.
|
||||
-- @return #AI_ESCORT_REQUEST
|
||||
-- @usage
|
||||
-- EscortSpawn = SPAWN:NewWithAlias( "Red A2G Escort Template", "Red A2G Escort AI" ):InitLimit( 10, 10 )
|
||||
-- EscortSpawn:ParkAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Sochi_Adler ), AIRBASE.TerminalType.OpenBig )
|
||||
--
|
||||
-- local EscortUnit = UNIT:FindByName( "Red A2G Pilot" )
|
||||
--
|
||||
-- Escort = AI_ESCORT_REQUEST:New( EscortUnit, EscortSpawn, AIRBASE:FindByName(AIRBASE.Caucasus.Sochi_Adler), "A2G", "Briefing" )
|
||||
-- Escort:FormationTrail( 50, 100, 100 )
|
||||
-- Escort:Menus()
|
||||
-- Escort:__Start( 5 )
|
||||
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 ) ) -- #AI_ESCORT_REQUEST
|
||||
|
||||
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
|
||||
|
||||
-- @param #AI_ESCORT_REQUEST self
|
||||
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() -- Wrapper.Group#GROUP
|
||||
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 )
|
||||
|
||||
-- @param #AI_ESCORT self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
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
|
||||
|
||||
-- @param #AI_ESCORT_REQUEST self
|
||||
-- @param Core.Set#SET_GROUP EscortGroupSet
|
||||
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
|
||||
|
||||
-- @param #AI_ESCORT_REQUEST self
|
||||
-- @param Core.Set#SET_GROUP EscortGroupSet
|
||||
function AI_ESCORT_REQUEST:onafterStop( EscortGroupSet )
|
||||
|
||||
self:F()
|
||||
|
||||
EscortGroupSet:ForEachGroup(
|
||||
-- @param Core.Group#GROUP EscortGroup
|
||||
function( EscortGroup )
|
||||
EscortGroup:WayPointInitialize()
|
||||
|
||||
EscortGroup:OptionROTVertical()
|
||||
EscortGroup:OptionROEOpenFire()
|
||||
end
|
||||
)
|
||||
|
||||
self.Detection:Stop()
|
||||
|
||||
self.MainMenu:Remove()
|
||||
|
||||
end
|
||||
|
||||
--- Set the spawn mode to be mission execution.
|
||||
-- @param #AI_ESCORT_REQUEST self
|
||||
function AI_ESCORT_REQUEST:SetEscortSpawnMission()
|
||||
|
||||
self.SpawnMode = self.__Enum.Mode.Mission
|
||||
|
||||
end
|
||||
@@ -1,950 +0,0 @@
|
||||
--- **AI** - Perform Air Patrolling for airplanes.
|
||||
--
|
||||
-- **Features:**
|
||||
--
|
||||
-- * Patrol AI airplanes within a given zone.
|
||||
-- * Trigger detected events when enemy airplanes are detected.
|
||||
-- * Manage a fuel threshold to RTB on time.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- AI PATROL classes makes AI Controllables execute an Patrol.
|
||||
--
|
||||
-- There are the following types of PATROL classes defined:
|
||||
--
|
||||
-- * @{#AI_PATROL_ZONE}: Perform a PATROL in a zone.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_Patrol)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [YouTube Playlist](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl35HvYZKA6G22WMt7iI3zky)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- * **Dutch_Baron**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-)
|
||||
-- * **Pikey**: Testing and API concept review.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Patrol
|
||||
-- @image AI_Air_Patrolling.JPG
|
||||
|
||||
--- AI_PATROL_ZONE class
|
||||
-- @type AI_PATROL_ZONE
|
||||
-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Wrapper.Controllable} patrolling.
|
||||
-- @field Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
|
||||
-- @field DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @field DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @field DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h.
|
||||
-- @field DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h.
|
||||
-- @field Core.Spawn#SPAWN CoordTest
|
||||
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
||||
|
||||
--- Implements the core functions to patrol a @{Core.Zone} by an AI @{Wrapper.Controllable} or @{Wrapper.Group}.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The AI_PATROL_ZONE is assigned a @{Wrapper.Group} and this must be done before the AI_PATROL_ZONE process can be started using the **Start** event.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits.
|
||||
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- This cycle will continue.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
---- Note that the enemy is not engaged! To model enemy engagement, either tailor the **Detected** event, or
|
||||
-- use derived AI_ classes to model AI offensive or defensive behaviour.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ## 1. AI_PATROL_ZONE constructor
|
||||
--
|
||||
-- * @{#AI_PATROL_ZONE.New}(): Creates a new AI_PATROL_ZONE object.
|
||||
--
|
||||
-- ## 2. AI_PATROL_ZONE is a FSM
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ### 2.1. AI_PATROL_ZONE States
|
||||
--
|
||||
-- * **None** ( Group ): The process is not started yet.
|
||||
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
|
||||
-- * **Returning** ( Group ): The AI is returning to Base.
|
||||
-- * **Stopped** ( Group ): The process is stopped.
|
||||
-- * **Crashed** ( Group ): The AI has crashed or is dead.
|
||||
--
|
||||
-- ### 2.2. AI_PATROL_ZONE Events
|
||||
--
|
||||
-- * **Start** ( Group ): Start the process.
|
||||
-- * **Stop** ( Group ): Stop the process.
|
||||
-- * **Route** ( Group ): Route the AI to a new random 3D point within the Patrol Zone.
|
||||
-- * **RTB** ( Group ): Route the AI to the home base.
|
||||
-- * **Detect** ( Group ): The AI is detecting targets.
|
||||
-- * **Detected** ( Group ): The AI has detected new targets.
|
||||
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
|
||||
--
|
||||
-- ## 3. Set or Get the AI controllable
|
||||
--
|
||||
-- * @{#AI_PATROL_ZONE.SetControllable}(): Set the AIControllable.
|
||||
-- * @{#AI_PATROL_ZONE.GetControllable}(): Get the AIControllable.
|
||||
--
|
||||
-- ## 4. Set the Speed and Altitude boundaries of the AI controllable
|
||||
--
|
||||
-- * @{#AI_PATROL_ZONE.SetSpeed}(): Set the patrol speed boundaries of the AI, for the next patrol.
|
||||
-- * @{#AI_PATROL_ZONE.SetAltitude}(): Set altitude boundaries of the AI, for the next patrol.
|
||||
--
|
||||
-- ## 5. Manage the detection process of the AI controllable
|
||||
--
|
||||
-- The detection process of the AI controllable can be manipulated.
|
||||
-- Detection requires an amount of CPU power, which has an impact on your mission performance.
|
||||
-- Only put detection on when absolutely necessary, and the frequency of the detection can also be set.
|
||||
--
|
||||
-- * @{#AI_PATROL_ZONE.SetDetectionOn}(): Set the detection on. The AI will detect for targets.
|
||||
-- * @{#AI_PATROL_ZONE.SetDetectionOff}(): Set the detection off, the AI will not detect for targets. The existing target list will NOT be erased.
|
||||
--
|
||||
-- The detection frequency can be set with @{#AI_PATROL_ZONE.SetRefreshTimeInterval}( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection.
|
||||
-- Use the method @{#AI_PATROL_ZONE.GetDetectedUnits}() to obtain a list of the @{Wrapper.Unit}s detected by the AI.
|
||||
--
|
||||
-- The detection can be filtered to potential targets in a specific zone.
|
||||
-- Use the method @{#AI_PATROL_ZONE.SetDetectionZone}() to set the zone where targets need to be detected.
|
||||
-- Note that when the zone is too far away, or the AI is not heading towards the zone, or the AI is too high, no targets may be detected
|
||||
-- according the weather conditions.
|
||||
--
|
||||
-- ## 6. Manage the "out of fuel" in the AI_PATROL_ZONE
|
||||
--
|
||||
-- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
|
||||
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
|
||||
-- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit,
|
||||
-- while a new AI is targeted to the AI_PATROL_ZONE.
|
||||
-- Once the time is finished, the old AI will return to the base.
|
||||
-- Use the method @{#AI_PATROL_ZONE.ManageFuel}() to have this process in place.
|
||||
--
|
||||
-- ## 7. Manage "damage" behaviour of the AI in the AI_PATROL_ZONE
|
||||
--
|
||||
-- When the AI is damaged, it is required that a new AIControllable is started. However, damage cannon be foreseen early on.
|
||||
-- Therefore, when the damage threshold is reached, the AI will return immediately to the home base (RTB).
|
||||
-- Use the method @{#AI_PATROL_ZONE.ManageDamage}() to have this process in place.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_PATROL_ZONE
|
||||
AI_PATROL_ZONE = {
|
||||
ClassName = "AI_PATROL_ZONE",
|
||||
}
|
||||
|
||||
--- Creates a new AI_PATROL_ZONE object
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Core.Zone} where the patrol needs to be executed.
|
||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h.
|
||||
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
|
||||
-- @return #AI_PATROL_ZONE self
|
||||
-- @usage
|
||||
-- -- Define a new AI_PATROL_ZONE Object. This PatrolArea will patrol an AIControllable within PatrolZone between 3000 and 6000 meters, with a variying speed between 600 and 900 km/h.
|
||||
-- PatrolZone = ZONE:New( 'PatrolZone' )
|
||||
-- PatrolSpawn = SPAWN:New( 'Patrol Group' )
|
||||
-- PatrolArea = AI_PATROL_ZONE:New( PatrolZone, 3000, 6000, 600, 900 )
|
||||
function AI_PATROL_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_PATROL_ZONE
|
||||
|
||||
|
||||
self.PatrolZone = PatrolZone
|
||||
self.PatrolFloorAltitude = PatrolFloorAltitude
|
||||
self.PatrolCeilingAltitude = PatrolCeilingAltitude
|
||||
self.PatrolMinSpeed = PatrolMinSpeed
|
||||
self.PatrolMaxSpeed = PatrolMaxSpeed
|
||||
|
||||
-- defafult PatrolAltType to "BARO" if not specified
|
||||
self.PatrolAltType = PatrolAltType or "BARO"
|
||||
|
||||
self:SetRefreshTimeInterval( 30 )
|
||||
|
||||
self.CheckStatus = true
|
||||
|
||||
self:ManageFuel( .2, 60 )
|
||||
self:ManageDamage( 1 )
|
||||
|
||||
|
||||
self.DetectedUnits = {} -- This table contains the targets detected during patrol.
|
||||
|
||||
self:SetStartState( "None" )
|
||||
|
||||
self:AddTransition( "*", "Stop", "Stopped" )
|
||||
|
||||
--- OnLeave Transition Handler for State Stopped.
|
||||
-- @function [parent=#AI_PATROL_ZONE] OnLeaveStopped
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnEnter Transition Handler for State Stopped.
|
||||
-- @function [parent=#AI_PATROL_ZONE] OnEnterStopped
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- OnBefore Transition Handler for Event Stop.
|
||||
-- @function [parent=#AI_PATROL_ZONE] OnBeforeStop
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Stop.
|
||||
-- @function [parent=#AI_PATROL_ZONE] OnAfterStop
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Stop.
|
||||
-- @function [parent=#AI_PATROL_ZONE] Stop
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Stop.
|
||||
-- @function [parent=#AI_PATROL_ZONE] __Stop
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
self:AddTransition( "None", "Start", "Patrolling" )
|
||||
|
||||
--- OnBefore Transition Handler for Event Start.
|
||||
-- @function [parent=#AI_PATROL_ZONE] OnBeforeStart
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Start.
|
||||
-- @function [parent=#AI_PATROL_ZONE] OnAfterStart
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Start.
|
||||
-- @function [parent=#AI_PATROL_ZONE] Start
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Start.
|
||||
-- @function [parent=#AI_PATROL_ZONE] __Start
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
--- OnLeave Transition Handler for State Patrolling.
|
||||
-- @function [parent=#AI_PATROL_ZONE] OnLeavePatrolling
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnEnter Transition Handler for State Patrolling.
|
||||
-- @function [parent=#AI_PATROL_ZONE] OnEnterPatrolling
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
self:AddTransition( "Patrolling", "Route", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_PATROL_ZONE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Route.
|
||||
-- @function [parent=#AI_PATROL_ZONE] OnBeforeRoute
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Route.
|
||||
-- @function [parent=#AI_PATROL_ZONE] OnAfterRoute
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Route.
|
||||
-- @function [parent=#AI_PATROL_ZONE] Route
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Route.
|
||||
-- @function [parent=#AI_PATROL_ZONE] __Route
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
self:AddTransition( "*", "Status", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_PATROL_ZONE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Status.
|
||||
-- @function [parent=#AI_PATROL_ZONE] OnBeforeStatus
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Status.
|
||||
-- @function [parent=#AI_PATROL_ZONE] OnAfterStatus
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Status.
|
||||
-- @function [parent=#AI_PATROL_ZONE] Status
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Status.
|
||||
-- @function [parent=#AI_PATROL_ZONE] __Status
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
self:AddTransition( "*", "Detect", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_PATROL_ZONE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Detect.
|
||||
-- @function [parent=#AI_PATROL_ZONE] OnBeforeDetect
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Detect.
|
||||
-- @function [parent=#AI_PATROL_ZONE] OnAfterDetect
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Detect.
|
||||
-- @function [parent=#AI_PATROL_ZONE] Detect
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Detect.
|
||||
-- @function [parent=#AI_PATROL_ZONE] __Detect
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
self:AddTransition( "*", "Detected", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_PATROL_ZONE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Detected.
|
||||
-- @function [parent=#AI_PATROL_ZONE] OnBeforeDetected
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Detected.
|
||||
-- @function [parent=#AI_PATROL_ZONE] OnAfterDetected
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event Detected.
|
||||
-- @function [parent=#AI_PATROL_ZONE] Detected
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Detected.
|
||||
-- @function [parent=#AI_PATROL_ZONE] __Detected
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
self:AddTransition( "*", "RTB", "Returning" ) -- FSM_CONTROLLABLE Transition for type #AI_PATROL_ZONE.
|
||||
|
||||
--- OnBefore Transition Handler for Event RTB.
|
||||
-- @function [parent=#AI_PATROL_ZONE] OnBeforeRTB
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event RTB.
|
||||
-- @function [parent=#AI_PATROL_ZONE] OnAfterRTB
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
--- Synchronous Event Trigger for Event RTB.
|
||||
-- @function [parent=#AI_PATROL_ZONE] RTB
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event RTB.
|
||||
-- @function [parent=#AI_PATROL_ZONE] __RTB
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
--- OnLeave Transition Handler for State Returning.
|
||||
-- @function [parent=#AI_PATROL_ZONE] OnLeaveReturning
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnEnter Transition Handler for State Returning.
|
||||
-- @function [parent=#AI_PATROL_ZONE] OnEnterReturning
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
self:AddTransition( "*", "Reset", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_PATROL_ZONE.
|
||||
|
||||
self:AddTransition( "*", "Eject", "*" )
|
||||
self:AddTransition( "*", "Crash", "Crashed" )
|
||||
self:AddTransition( "*", "PilotDead", "*" )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- Sets (modifies) the minimum and maximum speed of the patrol.
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h.
|
||||
-- @return #AI_PATROL_ZONE self
|
||||
function AI_PATROL_ZONE:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed )
|
||||
self:F2( { PatrolMinSpeed, PatrolMaxSpeed } )
|
||||
|
||||
self.PatrolMinSpeed = PatrolMinSpeed
|
||||
self.PatrolMaxSpeed = PatrolMaxSpeed
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Sets the floor and ceiling altitude of the patrol.
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @return #AI_PATROL_ZONE self
|
||||
function AI_PATROL_ZONE:SetAltitude( PatrolFloorAltitude, PatrolCeilingAltitude )
|
||||
self:F2( { PatrolFloorAltitude, PatrolCeilingAltitude } )
|
||||
|
||||
self.PatrolFloorAltitude = PatrolFloorAltitude
|
||||
self.PatrolCeilingAltitude = PatrolCeilingAltitude
|
||||
end
|
||||
|
||||
-- * @{#AI_PATROL_ZONE.SetDetectionOn}(): Set the detection on. The AI will detect for targets.
|
||||
-- * @{#AI_PATROL_ZONE.SetDetectionOff}(): Set the detection off, the AI will not detect for targets. The existing target list will NOT be erased.
|
||||
|
||||
--- Set the detection on. The AI will detect for targets.
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @return #AI_PATROL_ZONE self
|
||||
function AI_PATROL_ZONE:SetDetectionOn()
|
||||
self:F2()
|
||||
|
||||
self.DetectOn = true
|
||||
end
|
||||
|
||||
--- Set the detection off. The AI will NOT detect for targets.
|
||||
-- However, the list of already detected targets will be kept and can be enquired!
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @return #AI_PATROL_ZONE self
|
||||
function AI_PATROL_ZONE:SetDetectionOff()
|
||||
self:F2()
|
||||
|
||||
self.DetectOn = false
|
||||
end
|
||||
|
||||
--- Set the status checking off.
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @return #AI_PATROL_ZONE self
|
||||
function AI_PATROL_ZONE:SetStatusOff()
|
||||
self:F2()
|
||||
|
||||
self.CheckStatus = false
|
||||
end
|
||||
|
||||
--- Activate the detection. The AI will detect for targets if the Detection is switched On.
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @return #AI_PATROL_ZONE self
|
||||
function AI_PATROL_ZONE:SetDetectionActivated()
|
||||
self:F2()
|
||||
|
||||
self:ClearDetectedUnits()
|
||||
self.DetectActivated = true
|
||||
self:__Detect( -self.DetectInterval )
|
||||
end
|
||||
|
||||
--- Deactivate the detection. The AI will NOT detect for targets.
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @return #AI_PATROL_ZONE self
|
||||
function AI_PATROL_ZONE:SetDetectionDeactivated()
|
||||
self:F2()
|
||||
|
||||
self:ClearDetectedUnits()
|
||||
self.DetectActivated = false
|
||||
end
|
||||
|
||||
--- Set the interval in seconds between each detection executed by the AI.
|
||||
-- The list of already detected targets will be kept and updated.
|
||||
-- Newly detected targets will be added, but already detected targets that were
|
||||
-- not detected in this cycle, will NOT be removed!
|
||||
-- The default interval is 30 seconds.
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param #number Seconds The interval in seconds.
|
||||
-- @return #AI_PATROL_ZONE self
|
||||
function AI_PATROL_ZONE:SetRefreshTimeInterval( Seconds )
|
||||
self:F2()
|
||||
|
||||
if Seconds then
|
||||
self.DetectInterval = Seconds
|
||||
else
|
||||
self.DetectInterval = 30
|
||||
end
|
||||
end
|
||||
|
||||
--- Set the detection zone where the AI is detecting targets.
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Core.Zone#ZONE DetectionZone The zone where to detect targets.
|
||||
-- @return #AI_PATROL_ZONE self
|
||||
function AI_PATROL_ZONE:SetDetectionZone( DetectionZone )
|
||||
self:F2()
|
||||
|
||||
if DetectionZone then
|
||||
self.DetectZone = DetectionZone
|
||||
else
|
||||
self.DetectZone = nil
|
||||
end
|
||||
end
|
||||
|
||||
--- Gets a list of @{Wrapper.Unit#UNIT}s that were detected by the AI.
|
||||
-- No filtering is applied, so, ANY detected UNIT can be in this list.
|
||||
-- It is up to the mission designer to use the @{Wrapper.Unit} class and methods to filter the targets.
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @return #table The list of @{Wrapper.Unit#UNIT}s
|
||||
function AI_PATROL_ZONE:GetDetectedUnits()
|
||||
self:F2()
|
||||
|
||||
return self.DetectedUnits
|
||||
end
|
||||
|
||||
--- Clears the list of @{Wrapper.Unit#UNIT}s that were detected by the AI.
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
function AI_PATROL_ZONE:ClearDetectedUnits()
|
||||
self:F2()
|
||||
self.DetectedUnits = {}
|
||||
end
|
||||
|
||||
--- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
|
||||
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
|
||||
-- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targeted to the AI_PATROL_ZONE.
|
||||
-- Once the time is finished, the old AI will return to the base.
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param #number PatrolFuelThresholdPercentage The threshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.
|
||||
-- @param #number PatrolOutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base.
|
||||
-- @return #AI_PATROL_ZONE self
|
||||
function AI_PATROL_ZONE:ManageFuel( PatrolFuelThresholdPercentage, PatrolOutOfFuelOrbitTime )
|
||||
|
||||
self.PatrolFuelThresholdPercentage = PatrolFuelThresholdPercentage
|
||||
self.PatrolOutOfFuelOrbitTime = PatrolOutOfFuelOrbitTime
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- When the AI is damaged beyond a certain threshold, it is required that the AI returns to the home base.
|
||||
-- However, damage cannot be foreseen early on.
|
||||
-- Therefore, when the damage threshold is reached,
|
||||
-- the AI will return immediately to the home base (RTB).
|
||||
-- Note that for groups, the average damage of the complete group will be calculated.
|
||||
-- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage threshold will be 0.25.
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param #number PatrolDamageThreshold The threshold in percentage (between 0 and 1) when the AI is considered to be damaged.
|
||||
-- @return #AI_PATROL_ZONE self
|
||||
function AI_PATROL_ZONE:ManageDamage( PatrolDamageThreshold )
|
||||
|
||||
self.PatrolManageDamage = true
|
||||
self.PatrolDamageThreshold = PatrolDamageThreshold
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Defines a new patrol route using the @{#AI_PATROL_ZONE} parameters and settings.
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @return #AI_PATROL_ZONE self
|
||||
function AI_PATROL_ZONE:onafterStart( Controllable, From, Event, To )
|
||||
self:F2()
|
||||
|
||||
self:__Route( 1 ) -- Route to the patrol point. The asynchronous trigger is important, because a spawned group and units takes at least one second to come live.
|
||||
self:__Status( 60 ) -- Check status status every 30 seconds.
|
||||
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
|
||||
|
||||
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable+
|
||||
function AI_PATROL_ZONE:onbeforeDetect( Controllable, From, Event, To )
|
||||
|
||||
return self.DetectOn and self.DetectActivated
|
||||
end
|
||||
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable
|
||||
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 )
|
||||
|
||||
-- Check that target is alive due to issue https://github.com/FlightControl-Master/MOOSE/issues/1234
|
||||
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
|
||||
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE AIControllable
|
||||
-- This static method is called from the route path within the last task at the last waypoint of the Controllable.
|
||||
-- Note that this method is required, as triggers the next route when patrolling for the Controllable.
|
||||
function AI_PATROL_ZONE:_NewPatrolRoute( AIControllable )
|
||||
|
||||
local PatrolZone = AIControllable:GetState( AIControllable, "PatrolZone" ) -- PatrolCore.Zone#AI_PATROL_ZONE
|
||||
PatrolZone:__Route( 1 )
|
||||
end
|
||||
|
||||
|
||||
--- Defines a new patrol route using the @{#AI_PATROL_ZONE} parameters and settings.
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
|
||||
|
||||
self:F2()
|
||||
|
||||
-- When RTB, don't allow anymore the routing.
|
||||
if From == "RTB" then
|
||||
return
|
||||
end
|
||||
|
||||
local life = self.Controllable:GetLife() or 0
|
||||
if self.Controllable:IsAlive() and life > 1 then
|
||||
-- Determine if the AIControllable is within the PatrolZone.
|
||||
-- If not, make a waypoint within the to that the AIControllable will fly at maximum speed to that point.
|
||||
|
||||
local PatrolRoute = {}
|
||||
|
||||
-- Calculate the current route point of the controllable as the start point of the route.
|
||||
-- However, when the controllable is not in the air,
|
||||
-- the controllable current waypoint is probably the airbase...
|
||||
-- Thus, if we would take the current waypoint as the startpoint, upon take-off, the controllable flies
|
||||
-- immediately back to the airbase, and this is not correct.
|
||||
-- Therefore, when on a runway, get as the current route point a random point within the PatrolZone.
|
||||
-- This will make the plane fly immediately to the patrol zone.
|
||||
|
||||
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
|
||||
--Done: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||
local CurrentAltitude = self.Controllable:GetAltitude()
|
||||
local CurrentPointVec3 = COORDINATE:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
COORDINATE.WaypointType.TakeOffParking,
|
||||
COORDINATE.WaypointAction.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
|
||||
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||
local CurrentAltitude = self.Controllable:GetAltitude()
|
||||
local CurrentPointVec3 = COORDINATE:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
ToPatrolZoneSpeed,
|
||||
true
|
||||
)
|
||||
PatrolRoute[#PatrolRoute+1] = CurrentRoutePoint
|
||||
end
|
||||
|
||||
|
||||
--- Define a random point in the @{Core.Zone}. The AI will fly to that point within the zone.
|
||||
|
||||
--- Find a random 2D point in PatrolZone.
|
||||
local ToTargetVec2 = self.PatrolZone:GetRandomVec2()
|
||||
self:T2( ToTargetVec2 )
|
||||
|
||||
--- Define Speed and Altitude.
|
||||
local ToTargetAltitude = math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude )
|
||||
local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
|
||||
self:T2( { self.PatrolMinSpeed, self.PatrolMaxSpeed, ToTargetSpeed } )
|
||||
|
||||
--- Obtain a 3D @{Point} from the 2D point + altitude.
|
||||
local ToTargetPointVec3 = COORDINATE:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToTargetRoutePoint = ToTargetPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
ToTargetSpeed,
|
||||
true
|
||||
)
|
||||
|
||||
--self.CoordTest:SpawnFromVec3( ToTargetPointVec3:GetVec3() )
|
||||
|
||||
--ToTargetPointVec3:SmokeRed()
|
||||
|
||||
PatrolRoute[#PatrolRoute+1] = ToTargetRoutePoint
|
||||
|
||||
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
|
||||
self.Controllable:WayPointInitialize( PatrolRoute )
|
||||
|
||||
--- Do a trick, link the NewPatrolRoute function of the PATROLGROUP object to the AIControllable in a temporary variable ...
|
||||
self.Controllable:SetState( self.Controllable, "PatrolZone", self )
|
||||
self.Controllable:WayPointFunction( #PatrolRoute, 1, "AI_PATROL_ZONE:_NewPatrolRoute" )
|
||||
|
||||
--- NOW ROUTE THE GROUP!
|
||||
self.Controllable:WayPointExecute( 1, 2 )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
function AI_PATROL_ZONE:onbeforeStatus()
|
||||
|
||||
return self.CheckStatus
|
||||
end
|
||||
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
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 < self.PatrolFuelThresholdPercentage then
|
||||
self:T( self.Controllable:GetName() .. " is out of fuel:" .. Fuel .. ", RTB!" )
|
||||
local OldAIControllable = self.Controllable
|
||||
|
||||
local OrbitTask = OldAIControllable:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed )
|
||||
local TimedOrbitTask = OldAIControllable:TaskControlled( OrbitTask, OldAIControllable:TaskCondition(nil,nil,nil,nil,self.PatrolOutOfFuelOrbitTime,nil ) )
|
||||
OldAIControllable:SetTask( TimedOrbitTask, 10 )
|
||||
|
||||
RTB = true
|
||||
end
|
||||
|
||||
-- TODO: Check GROUP damage function.
|
||||
local Damage = self.Controllable:GetLife()
|
||||
if Damage <= self.PatrolDamageThreshold then
|
||||
self:T( self.Controllable:GetName() .. " is damaged:" .. Damage .. ", RTB!" )
|
||||
RTB = true
|
||||
end
|
||||
|
||||
if self:IsInstanceOf("AI_CAS") or self:IsInstanceOf("AI_BAI") then
|
||||
local atotal,shells,rockets,bombs,missiles = self.Controllable:GetAmmunition()
|
||||
local arelevant = rockets+bombs
|
||||
if arelevant == 0 or missiles == 0 then
|
||||
RTB = true
|
||||
self:T({total=atotal,shells=shells,rockets=rockets,bombs=bombs,missiles=missiles})
|
||||
self:T( self.Controllable:GetName() .. " is out of ammo, RTB!" )
|
||||
end
|
||||
end
|
||||
|
||||
if RTB == true then
|
||||
self:RTB()
|
||||
else
|
||||
self:__Status( 60 ) -- Execute the Patrol event after 30 seconds.
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
function AI_PATROL_ZONE:onafterRTB()
|
||||
self:F2()
|
||||
|
||||
if self.Controllable and self.Controllable:IsAlive() then
|
||||
|
||||
self:SetDetectionOff()
|
||||
self.CheckStatus = false
|
||||
|
||||
local PatrolRoute = {}
|
||||
|
||||
--- Calculate the current route point.
|
||||
local CurrentVec2 = self.Controllable:GetVec2()
|
||||
if not CurrentVec2 then return end
|
||||
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||
--local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
||||
local CurrentAltitude = self.Controllable:GetAltitude()
|
||||
local CurrentPointVec3 = COORDINATE:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
ToPatrolZoneSpeed,
|
||||
true
|
||||
)
|
||||
|
||||
PatrolRoute[#PatrolRoute+1] = CurrentRoutePoint
|
||||
|
||||
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
|
||||
self.Controllable:WayPointInitialize( PatrolRoute )
|
||||
|
||||
--- NOW ROUTE THE GROUP!
|
||||
self.Controllable:WayPointExecute( 1, 1 )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
function AI_PATROL_ZONE:onafterDead()
|
||||
self:SetDetectionOff()
|
||||
self:SetStatusOff()
|
||||
end
|
||||
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function AI_PATROL_ZONE:OnCrash( EventData )
|
||||
|
||||
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
|
||||
if #self.Controllable:GetUnits() == 1 then
|
||||
self:__Crash( 1, EventData )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function AI_PATROL_ZONE:OnEjection( EventData )
|
||||
|
||||
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
|
||||
self:__Eject( 1, EventData )
|
||||
end
|
||||
end
|
||||
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function AI_PATROL_ZONE:OnPilotDead( EventData )
|
||||
|
||||
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
|
||||
self:__PilotDead( 1, EventData )
|
||||
end
|
||||
end
|
||||
@@ -1,312 +0,0 @@
|
||||
--- **Actions** - ACT_ACCOUNT_ classes **account for** (detect, count & report) various DCS events occurring on UNITs.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Actions.Act_Account
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
do -- ACT_ACCOUNT
|
||||
|
||||
--- # @{#ACT_ACCOUNT} FSM class, extends @{Core.Fsm#FSM_PROCESS}
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ## ACT_ACCOUNT state machine:
|
||||
--
|
||||
-- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur.
|
||||
-- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below.
|
||||
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
|
||||
-- but will have **different implementation behaviour** upon each event or state transition.
|
||||
--
|
||||
-- ### ACT_ACCOUNT States
|
||||
--
|
||||
-- * **Assigned**: The player is assigned.
|
||||
-- * **Waiting**: Waiting for an event.
|
||||
-- * **Report**: Reporting.
|
||||
-- * **Account**: Account for an event.
|
||||
-- * **Accounted**: All events have been accounted for, end of the process.
|
||||
-- * **Failed**: Failed the process.
|
||||
--
|
||||
-- ### ACT_ACCOUNT Events
|
||||
--
|
||||
-- * **Start**: Start the process.
|
||||
-- * **Wait**: Wait for an event.
|
||||
-- * **Report**: Report the status of the accounting.
|
||||
-- * **Event**: An event happened, process the event.
|
||||
-- * **More**: More targets.
|
||||
-- * **NoMore (*)**: No more targets.
|
||||
-- * **Fail (*)**: The action process has failed.
|
||||
--
|
||||
-- (*) End states of the process.
|
||||
--
|
||||
-- ### ACT_ACCOUNT state transition methods:
|
||||
--
|
||||
-- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state.
|
||||
-- There are 2 moments when state transition methods will be called by the state machine:
|
||||
--
|
||||
-- * **Before** the state transition.
|
||||
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
|
||||
-- If the state transition method returns false, then the processing of the state transition will not be done!
|
||||
-- If you want to change the behaviour of the AIControllable at this event, return false,
|
||||
-- but then you'll need to specify your own logic using the AIControllable!
|
||||
--
|
||||
-- * **After** the state transition.
|
||||
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
|
||||
-- These state transition methods need to provide a return value, which is specified at the function description.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- @type ACT_ACCOUNT
|
||||
-- @field Core.Set#SET_UNIT TargetSetUnit
|
||||
-- @extends Core.Fsm#FSM_PROCESS
|
||||
ACT_ACCOUNT = {
|
||||
ClassName = "ACT_ACCOUNT",
|
||||
TargetSetUnit = nil,
|
||||
}
|
||||
|
||||
--- Creates a new DESTROY process.
|
||||
-- @param #ACT_ACCOUNT self
|
||||
-- @return #ACT_ACCOUNT
|
||||
function ACT_ACCOUNT:New()
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, FSM_PROCESS:New() ) -- Core.Fsm#FSM_PROCESS
|
||||
|
||||
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
|
||||
|
||||
--- Process Events
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
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
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
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 -- Process always the event.
|
||||
end
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ACCOUNT:onafterEvent( ProcessUnit, From, Event, To )
|
||||
|
||||
self:__NoMore( 1 )
|
||||
end
|
||||
|
||||
end -- ACT_ACCOUNT
|
||||
|
||||
do -- ACT_ACCOUNT_DEADS
|
||||
|
||||
--- # @{#ACT_ACCOUNT_DEADS} FSM class, extends @{#ACT_ACCOUNT}
|
||||
--
|
||||
-- The ACT_ACCOUNT_DEADS class accounts (detects, counts and reports) successful kills of DCS units.
|
||||
-- The process is given a @{Core.Set} of units that will be tracked upon successful destruction.
|
||||
-- The process will end after each target has been successfully destroyed.
|
||||
-- Each successful dead will trigger an Account state transition that can be scored, modified or administered.
|
||||
--
|
||||
--
|
||||
-- ## ACT_ACCOUNT_DEADS constructor:
|
||||
--
|
||||
-- * @{#ACT_ACCOUNT_DEADS.New}(): Creates a new ACT_ACCOUNT_DEADS object.
|
||||
--
|
||||
-- @type ACT_ACCOUNT_DEADS
|
||||
-- @field Core.Set#SET_UNIT TargetSetUnit
|
||||
-- @extends #ACT_ACCOUNT
|
||||
ACT_ACCOUNT_DEADS = {
|
||||
ClassName = "ACT_ACCOUNT_DEADS",
|
||||
}
|
||||
|
||||
--- Creates a new DESTROY process.
|
||||
-- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Core.Set#SET_UNIT TargetSetUnit
|
||||
-- @param #string TaskName
|
||||
function ACT_ACCOUNT_DEADS:New()
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, ACT_ACCOUNT:New() ) -- #ACT_ACCOUNT_DEADS
|
||||
|
||||
self.DisplayInterval = 30
|
||||
self.DisplayCount = 30
|
||||
self.DisplayMessage = true
|
||||
self.DisplayTime = 10 -- 10 seconds is the default
|
||||
self.DisplayCategory = "HQ" -- Targets is the default display category
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function ACT_ACCOUNT_DEADS:Init( FsmAccount )
|
||||
|
||||
self.Task = self:GetTask()
|
||||
self.TaskName = self.Task:GetName()
|
||||
end
|
||||
|
||||
--- Process Events
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
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
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param Tasking.Task#TASK Task
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
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
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param Tasking.Task#TASK Task
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
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
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param Tasking.Task#TASK Task
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
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
|
||||
|
||||
--- DCS Events
|
||||
|
||||
-- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
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
|
||||
|
||||
-- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function ACT_ACCOUNT_DEADS:onfuncEventDead( EventData )
|
||||
self:T( { "EventDead", EventData } )
|
||||
|
||||
if EventData.IniDCSUnit then
|
||||
self:Event( EventData )
|
||||
end
|
||||
end
|
||||
|
||||
--- DCS Events
|
||||
|
||||
-- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function ACT_ACCOUNT_DEADS:onfuncEventCrash( EventData )
|
||||
self:T( { "EventDead", EventData } )
|
||||
|
||||
if EventData.IniDCSUnit then
|
||||
self:Event( EventData )
|
||||
end
|
||||
end
|
||||
|
||||
end -- ACT_ACCOUNT DEADS
|
||||
@@ -1,294 +0,0 @@
|
||||
--- (SP) (MP) (FSM) Accept or reject process for player (task) assignments.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- # @{#ACT_ASSIGN} FSM template class, extends @{Core.Fsm#FSM_PROCESS}
|
||||
--
|
||||
-- ## ACT_ASSIGN state machine:
|
||||
--
|
||||
-- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur.
|
||||
-- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below.
|
||||
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
|
||||
-- but will have **different implementation behaviour** upon each event or state transition.
|
||||
--
|
||||
-- ### ACT_ASSIGN **Events**:
|
||||
--
|
||||
-- These are the events defined in this class:
|
||||
--
|
||||
-- * **Start**: Start the tasking acceptance process.
|
||||
-- * **Assign**: Assign the task.
|
||||
-- * **Reject**: Reject the task..
|
||||
--
|
||||
-- ### ACT_ASSIGN **Event methods**:
|
||||
--
|
||||
-- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process.
|
||||
-- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine:
|
||||
--
|
||||
-- * **Immediate**: The event method has exactly the name of the event.
|
||||
-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed.
|
||||
--
|
||||
-- ### ACT_ASSIGN **States**:
|
||||
--
|
||||
-- * **UnAssigned**: The player has not accepted the task.
|
||||
-- * **Assigned (*)**: The player has accepted the task.
|
||||
-- * **Rejected (*)**: The player has not accepted the task.
|
||||
-- * **Waiting**: The process is awaiting player feedback.
|
||||
-- * **Failed (*)**: The process has failed.
|
||||
--
|
||||
-- (*) End states of the process.
|
||||
--
|
||||
-- ### ACT_ASSIGN state transition methods:
|
||||
--
|
||||
-- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state.
|
||||
-- There are 2 moments when state transition methods will be called by the state machine:
|
||||
--
|
||||
-- * **Before** the state transition.
|
||||
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
|
||||
-- If the state transition method returns false, then the processing of the state transition will not be done!
|
||||
-- If you want to change the behaviour of the AIControllable at this event, return false,
|
||||
-- but then you'll need to specify your own logic using the AIControllable!
|
||||
--
|
||||
-- * **After** the state transition.
|
||||
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
|
||||
-- These state transition methods need to provide a return value, which is specified at the function description.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # 1) @{#ACT_ASSIGN_ACCEPT} class, extends @{Core.Fsm#ACT_ASSIGN}
|
||||
--
|
||||
-- The ACT_ASSIGN_ACCEPT class accepts by default a task for a player. No player intervention is allowed to reject the task.
|
||||
--
|
||||
-- ## 1.1) ACT_ASSIGN_ACCEPT constructor:
|
||||
--
|
||||
-- * @{#ACT_ASSIGN_ACCEPT.New}(): Creates a new ACT_ASSIGN_ACCEPT object.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # 2) @{#ACT_ASSIGN_MENU_ACCEPT} class, extends @{Core.Fsm#ACT_ASSIGN}
|
||||
--
|
||||
-- The ACT_ASSIGN_MENU_ACCEPT class accepts a task when the player accepts the task through an added menu option.
|
||||
-- This assignment type is useful to conditionally allow the player to choose whether or not he would accept the task.
|
||||
-- The assignment type also allows to reject the task.
|
||||
--
|
||||
-- ## 2.1) ACT_ASSIGN_MENU_ACCEPT constructor:
|
||||
-- -----------------------------------------
|
||||
--
|
||||
-- * @{#ACT_ASSIGN_MENU_ACCEPT.New}(): Creates a new ACT_ASSIGN_MENU_ACCEPT object.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Actions.Act_Assign
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
|
||||
do -- ACT_ASSIGN
|
||||
|
||||
--- ACT_ASSIGN class
|
||||
-- @type ACT_ASSIGN
|
||||
-- @field Tasking.Task#TASK Task
|
||||
-- @field Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @field Core.Zone#ZONE_BASE TargetZone
|
||||
-- @extends Core.Fsm#FSM_PROCESS
|
||||
ACT_ASSIGN = {
|
||||
ClassName = "ACT_ASSIGN",
|
||||
}
|
||||
|
||||
|
||||
--- Creates a new task assignment state machine. The process will accept the task by default, no player intervention accepted.
|
||||
-- @param #ACT_ASSIGN self
|
||||
-- @return #ACT_ASSIGN The task acceptance process.
|
||||
function ACT_ASSIGN:New()
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, FSM_PROCESS:New( "ACT_ASSIGN" ) ) -- Core.Fsm#FSM_PROCESS
|
||||
|
||||
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 -- ACT_ASSIGN
|
||||
|
||||
|
||||
|
||||
do -- ACT_ASSIGN_ACCEPT
|
||||
|
||||
--- ACT_ASSIGN_ACCEPT class
|
||||
-- @type ACT_ASSIGN_ACCEPT
|
||||
-- @field Tasking.Task#TASK Task
|
||||
-- @field Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @field Core.Zone#ZONE_BASE TargetZone
|
||||
-- @extends #ACT_ASSIGN
|
||||
ACT_ASSIGN_ACCEPT = {
|
||||
ClassName = "ACT_ASSIGN_ACCEPT",
|
||||
}
|
||||
|
||||
|
||||
--- Creates a new task assignment state machine. The process will accept the task by default, no player intervention accepted.
|
||||
-- @param #ACT_ASSIGN_ACCEPT self
|
||||
-- @param #string TaskBriefing
|
||||
function ACT_ASSIGN_ACCEPT:New( TaskBriefing )
|
||||
|
||||
local self = BASE:Inherit( self, ACT_ASSIGN:New() ) -- #ACT_ASSIGN_ACCEPT
|
||||
|
||||
self.TaskBriefing = TaskBriefing
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function ACT_ASSIGN_ACCEPT:Init( FsmAssign )
|
||||
|
||||
self.TaskBriefing = FsmAssign.TaskBriefing
|
||||
end
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ASSIGN_ACCEPT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ASSIGN_ACCEPT:onafterStart( ProcessUnit, Task, From, Event, To )
|
||||
|
||||
self:__Assign( 1 )
|
||||
end
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ASSIGN_ACCEPT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ASSIGN_ACCEPT:onenterAssigned( ProcessUnit, Task, From, Event, To, TaskGroup )
|
||||
|
||||
self.Task:Assign( ProcessUnit, ProcessUnit:GetPlayerName() )
|
||||
end
|
||||
|
||||
end -- ACT_ASSIGN_ACCEPT
|
||||
|
||||
|
||||
do -- ACT_ASSIGN_MENU_ACCEPT
|
||||
|
||||
--- ACT_ASSIGN_MENU_ACCEPT class
|
||||
-- @type ACT_ASSIGN_MENU_ACCEPT
|
||||
-- @field Tasking.Task#TASK Task
|
||||
-- @field Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @field Core.Zone#ZONE_BASE TargetZone
|
||||
-- @extends #ACT_ASSIGN
|
||||
ACT_ASSIGN_MENU_ACCEPT = {
|
||||
ClassName = "ACT_ASSIGN_MENU_ACCEPT",
|
||||
}
|
||||
|
||||
--- Init.
|
||||
-- @param #ACT_ASSIGN_MENU_ACCEPT self
|
||||
-- @param #string TaskBriefing
|
||||
-- @return #ACT_ASSIGN_MENU_ACCEPT self
|
||||
function ACT_ASSIGN_MENU_ACCEPT:New( TaskBriefing )
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, ACT_ASSIGN:New() ) -- #ACT_ASSIGN_MENU_ACCEPT
|
||||
|
||||
self.TaskBriefing = TaskBriefing
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Creates a new task assignment state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator.
|
||||
-- @param #ACT_ASSIGN_MENU_ACCEPT self
|
||||
-- @param #string TaskBriefing
|
||||
-- @return #ACT_ASSIGN_MENU_ACCEPT self
|
||||
function ACT_ASSIGN_MENU_ACCEPT:Init( TaskBriefing )
|
||||
|
||||
self.TaskBriefing = TaskBriefing
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ASSIGN_MENU_ACCEPT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
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
|
||||
|
||||
--- Menu function.
|
||||
-- @param #ACT_ASSIGN_MENU_ACCEPT self
|
||||
function ACT_ASSIGN_MENU_ACCEPT:MenuAssign( TaskGroup )
|
||||
|
||||
self:__Assign( -1, TaskGroup )
|
||||
end
|
||||
|
||||
--- Menu function.
|
||||
-- @param #ACT_ASSIGN_MENU_ACCEPT self
|
||||
function ACT_ASSIGN_MENU_ACCEPT:MenuReject( TaskGroup )
|
||||
|
||||
self:__Reject( -1, TaskGroup )
|
||||
end
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ASSIGN_MENU_ACCEPT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ASSIGN_MENU_ACCEPT:onafterAssign( ProcessUnit, Task, From, Event, To, TaskGroup )
|
||||
|
||||
self.Menu:Remove()
|
||||
end
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ASSIGN_MENU_ACCEPT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ASSIGN_MENU_ACCEPT:onafterReject( ProcessUnit, Task, From, Event, To, TaskGroup )
|
||||
self:F( { TaskGroup = TaskGroup } )
|
||||
|
||||
self.Menu:Remove()
|
||||
--TODO: need to resolve this problem ... it has to do with the events ...
|
||||
--self.Task:UnAssignFromUnit( ProcessUnit )needs to become a callback funtion call upon the event
|
||||
self.Task:RejectGroup( TaskGroup )
|
||||
end
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ASSIGN_ACCEPT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ASSIGN_MENU_ACCEPT:onenterAssigned( ProcessUnit, Task, From, Event, To, TaskGroup )
|
||||
|
||||
--self.Task:AssignToGroup( TaskGroup )
|
||||
self.Task:Assign( ProcessUnit, ProcessUnit:GetPlayerName() )
|
||||
end
|
||||
|
||||
end -- ACT_ASSIGN_MENU_ACCEPT
|
||||
@@ -1,220 +0,0 @@
|
||||
--- (SP) (MP) (FSM) Route AI or players through waypoints or to zones.
|
||||
--
|
||||
-- 
|
||||
-- ## ACT_ASSIST state machine:
|
||||
--
|
||||
-- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur.
|
||||
-- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below.
|
||||
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
|
||||
-- but will have **different implementation behaviour** upon each event or state transition.
|
||||
--
|
||||
-- ### ACT_ASSIST **Events**:
|
||||
--
|
||||
-- These are the events defined in this class:
|
||||
--
|
||||
-- * **Start**: The process is started.
|
||||
-- * **Next**: The process is smoking the targets in the given zone.
|
||||
--
|
||||
-- ### ACT_ASSIST **Event methods**:
|
||||
--
|
||||
-- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process.
|
||||
-- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine:
|
||||
--
|
||||
-- * **Immediate**: The event method has exactly the name of the event.
|
||||
-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed.
|
||||
--
|
||||
-- ### ACT_ASSIST **States**:
|
||||
--
|
||||
-- * **None**: The controllable did not receive route commands.
|
||||
-- * **AwaitSmoke (*)**: The process is awaiting to smoke the targets in the zone.
|
||||
-- * **Smoking (*)**: The process is smoking the targets in the zone.
|
||||
-- * **Failed (*)**: The process has failed.
|
||||
--
|
||||
-- (*) End states of the process.
|
||||
--
|
||||
-- ### ACT_ASSIST state transition methods:
|
||||
--
|
||||
-- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state.
|
||||
-- There are 2 moments when state transition methods will be called by the state machine:
|
||||
--
|
||||
-- * **Before** the state transition.
|
||||
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
|
||||
-- If the state transition method returns false, then the processing of the state transition will not be done!
|
||||
-- If you want to change the behaviour of the AIControllable at this event, return false,
|
||||
-- but then you'll need to specify your own logic using the AIControllable!
|
||||
--
|
||||
-- * **After** the state transition.
|
||||
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
|
||||
-- These state transition methods need to provide a return value, which is specified at the function description.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # 1) @{#ACT_ASSIST_SMOKE_TARGETS_ZONE} class, extends @{#ACT_ASSIST}
|
||||
--
|
||||
-- The ACT_ASSIST_SMOKE_TARGETS_ZONE class implements the core functions to smoke targets in a @{Core.Zone}.
|
||||
-- The targets are smoked within a certain range around each target, simulating a realistic smoking behaviour.
|
||||
-- At random intervals, a new target is smoked.
|
||||
--
|
||||
-- # 1.1) ACT_ASSIST_SMOKE_TARGETS_ZONE constructor:
|
||||
--
|
||||
-- * @{#ACT_ASSIST_SMOKE_TARGETS_ZONE.New}(): Creates a new ACT_ASSIST_SMOKE_TARGETS_ZONE object.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Actions.Act_Assist
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
|
||||
do -- ACT_ASSIST
|
||||
|
||||
--- ACT_ASSIST class
|
||||
-- @type ACT_ASSIST
|
||||
-- @extends Core.Fsm#FSM_PROCESS
|
||||
ACT_ASSIST = {
|
||||
ClassName = "ACT_ASSIST",
|
||||
}
|
||||
|
||||
--- Creates a new target smoking state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator.
|
||||
-- @param #ACT_ASSIST self
|
||||
-- @return #ACT_ASSIST
|
||||
function ACT_ASSIST:New()
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, FSM_PROCESS:New( "ACT_ASSIST" ) ) -- Core.Fsm#FSM_PROCESS
|
||||
|
||||
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
|
||||
|
||||
--- Task Events
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ASSIST self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
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
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ASSIST self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ASSIST:onafterStop( ProcessUnit, From, Event, To )
|
||||
|
||||
self.Menu:Remove() -- When stopped, remove the menus
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
do -- ACT_ASSIST_SMOKE_TARGETS_ZONE
|
||||
|
||||
--- ACT_ASSIST_SMOKE_TARGETS_ZONE class
|
||||
-- @type ACT_ASSIST_SMOKE_TARGETS_ZONE
|
||||
-- @field Core.Set#SET_UNIT TargetSetUnit
|
||||
-- @field Core.Zone#ZONE_BASE TargetZone
|
||||
-- @extends #ACT_ASSIST
|
||||
ACT_ASSIST_SMOKE_TARGETS_ZONE = {
|
||||
ClassName = "ACT_ASSIST_SMOKE_TARGETS_ZONE",
|
||||
}
|
||||
|
||||
-- function ACT_ASSIST_SMOKE_TARGETS_ZONE:_Destructor()
|
||||
-- self:E("_Destructor")
|
||||
--
|
||||
-- self.Menu:Remove()
|
||||
-- self:EventRemoveAll()
|
||||
-- end
|
||||
|
||||
--- Creates a new target smoking state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator.
|
||||
-- @param #ACT_ASSIST_SMOKE_TARGETS_ZONE self
|
||||
-- @param Core.Set#SET_UNIT TargetSetUnit
|
||||
-- @param Core.Zone#ZONE_BASE TargetZone
|
||||
function ACT_ASSIST_SMOKE_TARGETS_ZONE:New( TargetSetUnit, TargetZone )
|
||||
local self = BASE:Inherit( self, ACT_ASSIST:New() ) -- #ACT_ASSIST
|
||||
|
||||
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
|
||||
|
||||
--- Creates a new target smoking state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator.
|
||||
-- @param #ACT_ASSIST_SMOKE_TARGETS_ZONE self
|
||||
-- @param Core.Set#SET_UNIT TargetSetUnit
|
||||
-- @param Core.Zone#ZONE_BASE TargetZone
|
||||
-- @return #ACT_ASSIST_SMOKE_TARGETS_ZONE self
|
||||
function ACT_ASSIST_SMOKE_TARGETS_ZONE:Init( TargetSetUnit, TargetZone )
|
||||
|
||||
self.TargetSetUnit = TargetSetUnit
|
||||
self.TargetZone = TargetZone
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ASSIST_SMOKE_TARGETS_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ASSIST_SMOKE_TARGETS_ZONE:onenterSmoking( ProcessUnit, From, Event, To )
|
||||
|
||||
self.TargetSetUnit:ForEachUnit(
|
||||
-- @param Wrapper.Unit#UNIT SmokeUnit
|
||||
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
|
||||
@@ -1,485 +0,0 @@
|
||||
--- (SP) (MP) (FSM) Route AI or players through waypoints or to zones.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- # @{#ACT_ROUTE} FSM class, extends @{Core.Fsm#FSM_PROCESS}
|
||||
--
|
||||
-- ## ACT_ROUTE state machine:
|
||||
--
|
||||
-- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur.
|
||||
-- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below.
|
||||
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
|
||||
-- but will have **different implementation behaviour** upon each event or state transition.
|
||||
--
|
||||
-- ### ACT_ROUTE **Events**:
|
||||
--
|
||||
-- These are the events defined in this class:
|
||||
--
|
||||
-- * **Start**: The process is started. The process will go into the Report state.
|
||||
-- * **Report**: The process is reporting to the player the route to be followed.
|
||||
-- * **Route**: The process is routing the controllable.
|
||||
-- * **Pause**: The process is pausing the route of the controllable.
|
||||
-- * **Arrive**: The controllable has arrived at a route point.
|
||||
-- * **More**: There are more route points that need to be followed. The process will go back into the Report state.
|
||||
-- * **NoMore**: There are no more route points that need to be followed. The process will go into the Success state.
|
||||
--
|
||||
-- ### ACT_ROUTE **Event methods**:
|
||||
--
|
||||
-- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process.
|
||||
-- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine:
|
||||
--
|
||||
-- * **Immediate**: The event method has exactly the name of the event.
|
||||
-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed.
|
||||
--
|
||||
-- ### ACT_ROUTE **States**:
|
||||
--
|
||||
-- * **None**: The controllable did not receive route commands.
|
||||
-- * **Arrived (*)**: The controllable has arrived at a route point.
|
||||
-- * **Aborted (*)**: The controllable has aborted the route path.
|
||||
-- * **Routing**: The controllable is understay to the route point.
|
||||
-- * **Pausing**: The process is pausing the routing. AI air will go into hover, AI ground will stop moving. Players can fly around.
|
||||
-- * **Success (*)**: All route points were reached.
|
||||
-- * **Failed (*)**: The process has failed.
|
||||
--
|
||||
-- (*) End states of the process.
|
||||
--
|
||||
-- ### ACT_ROUTE state transition methods:
|
||||
--
|
||||
-- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state.
|
||||
-- There are 2 moments when state transition methods will be called by the state machine:
|
||||
--
|
||||
-- * **Before** the state transition.
|
||||
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
|
||||
-- If the state transition method returns false, then the processing of the state transition will not be done!
|
||||
-- If you want to change the behaviour of the AIControllable at this event, return false,
|
||||
-- but then you'll need to specify your own logic using the AIControllable!
|
||||
--
|
||||
-- * **After** the state transition.
|
||||
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
|
||||
-- These state transition methods need to provide a return value, which is specified at the function description.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # 1) @{#ACT_ROUTE_ZONE} class, extends @{#ACT_ROUTE}
|
||||
--
|
||||
-- The ACT_ROUTE_ZONE class implements the core functions to route an AIR @{Wrapper.Controllable} player @{Wrapper.Unit} to a @{Core.Zone}.
|
||||
-- The player receives on perioding times messages with the coordinates of the route to follow.
|
||||
-- Upon arrival at the zone, a confirmation of arrival is sent, and the process will be ended.
|
||||
--
|
||||
-- # 1.1) ACT_ROUTE_ZONE constructor:
|
||||
--
|
||||
-- * @{#ACT_ROUTE_ZONE.New}(): Creates a new ACT_ROUTE_ZONE object.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Actions.Act_Route
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
|
||||
do -- ACT_ROUTE
|
||||
|
||||
--- ACT_ROUTE class
|
||||
-- @type ACT_ROUTE
|
||||
-- @field Tasking.Task#TASK TASK
|
||||
-- @field Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @field Core.Zone#ZONE_BASE Zone
|
||||
-- @field Core.Point#COORDINATE Coordinate
|
||||
-- @extends Core.Fsm#FSM_PROCESS
|
||||
ACT_ROUTE = {
|
||||
ClassName = "ACT_ROUTE",
|
||||
}
|
||||
|
||||
|
||||
--- Creates a new routing state machine. The process will route a CLIENT to a ZONE until the CLIENT is within that ZONE.
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @return #ACT_ROUTE self
|
||||
function ACT_ROUTE:New()
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, FSM_PROCESS:New( "ACT_ROUTE" ) ) -- Core.Fsm#FSM_PROCESS
|
||||
|
||||
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
|
||||
|
||||
--- Set a Cancel Menu item.
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @return #ACT_ROUTE
|
||||
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
|
||||
|
||||
--- Set the route mode.
|
||||
-- There are 2 route modes supported:
|
||||
--
|
||||
-- * SetRouteMode( "B" ): Route mode is Bearing and Range.
|
||||
-- * SetRouteMode( "C" ): Route mode is LL or MGRS according coordinate system setup.
|
||||
--
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @return #ACT_ROUTE
|
||||
function ACT_ROUTE:SetRouteMode( RouteMode )
|
||||
|
||||
self.RouteMode = RouteMode
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the routing text to be displayed.
|
||||
-- The route mode determines the text displayed.
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @param Wrapper.Unit#UNIT Controllable
|
||||
-- @return #string
|
||||
function ACT_ROUTE:GetRouteText( Controllable )
|
||||
|
||||
local RouteText = ""
|
||||
|
||||
local Coordinate = nil -- Core.Point#COORDINATE
|
||||
|
||||
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() -- This is to dermine that the coordinates are for a specific task mode (A2A or A2G).
|
||||
local CC = self:GetTask():GetMission():GetCommandCenter()
|
||||
if CC then
|
||||
if CC:IsModeWWII() then
|
||||
-- Find closest reference point to the target.
|
||||
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 -- Core.Zone#ZONE
|
||||
local ZoneCoord = Zone:GetCoordinate()
|
||||
local ZoneDistance = ZoneCoord:Get2DDistance( Coordinate )
|
||||
self:F( { ShortestDistance, ShortestReferenceName } )
|
||||
if ShortestDistance == 0 or ZoneDistance < ShortestDistance then
|
||||
ShortestDistance = ZoneDistance
|
||||
ShortestReferencePoint = ZoneCoord
|
||||
ShortestReferenceName = CC.ReferenceNames[ZoneName]
|
||||
end
|
||||
end
|
||||
if ShortestReferencePoint then
|
||||
RouteText = Coordinate:ToStringFromRP( ShortestReferencePoint, ShortestReferenceName, Controllable )
|
||||
end
|
||||
else
|
||||
RouteText = Coordinate:ToString( Controllable, nil, Task )
|
||||
end
|
||||
end
|
||||
|
||||
return RouteText
|
||||
end
|
||||
|
||||
|
||||
function ACT_ROUTE:MenuCancel()
|
||||
self:F("Cancelled")
|
||||
self.CancelMenuGroupCommand:Remove()
|
||||
self:__Cancel( 1 )
|
||||
end
|
||||
|
||||
--- Task Events
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ROUTE:onafterStart( ProcessUnit, From, Event, To )
|
||||
|
||||
|
||||
self:__Route( 1 )
|
||||
end
|
||||
|
||||
--- Check if the controllable has arrived.
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @return #boolean
|
||||
function ACT_ROUTE:onfuncHasArrived( ProcessUnit )
|
||||
return false
|
||||
end
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ROUTE:onbeforeRoute( ProcessUnit, From, Event, To )
|
||||
|
||||
if ProcessUnit:IsAlive() then
|
||||
local HasArrived = self:onfuncHasArrived( ProcessUnit ) -- Polymorphic
|
||||
if self.DisplayCount >= 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 -- if false, then the event will not be executed...
|
||||
end
|
||||
|
||||
return false
|
||||
|
||||
end
|
||||
|
||||
end -- ACT_ROUTE
|
||||
|
||||
|
||||
do -- ACT_ROUTE_POINT
|
||||
|
||||
--- ACT_ROUTE_POINT class
|
||||
-- @type ACT_ROUTE_POINT
|
||||
-- @field Tasking.Task#TASK TASK
|
||||
-- @extends #ACT_ROUTE
|
||||
ACT_ROUTE_POINT = {
|
||||
ClassName = "ACT_ROUTE_POINT",
|
||||
}
|
||||
|
||||
|
||||
--- Creates a new routing state machine.
|
||||
-- The task will route a controllable to a Coordinate until the controllable is within the Range.
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
-- @param Core.Point#COORDINATE The Coordinate to Target.
|
||||
-- @param #number Range The Distance to Target.
|
||||
-- @param Core.Zone#ZONE_BASE Zone
|
||||
function ACT_ROUTE_POINT:New( Coordinate, Range )
|
||||
local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_POINT
|
||||
|
||||
self.Coordinate = Coordinate
|
||||
self.Range = Range or 0
|
||||
|
||||
self.DisplayInterval = 30
|
||||
self.DisplayCount = 30
|
||||
self.DisplayMessage = true
|
||||
self.DisplayTime = 10 -- 10 seconds is the default
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Creates a new routing state machine.
|
||||
-- The task will route a controllable to a Coordinate until the controllable is within the Range.
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
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 -- 10 seconds is the default
|
||||
self:SetStartState("None")
|
||||
end
|
||||
|
||||
--- Set Coordinate
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
-- @param Core.Point#COORDINATE Coordinate The Coordinate to route to.
|
||||
function ACT_ROUTE_POINT:SetCoordinate( Coordinate )
|
||||
self:F2( { Coordinate } )
|
||||
self.Coordinate = Coordinate
|
||||
end
|
||||
|
||||
--- Get Coordinate
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
-- @return Core.Point#COORDINATE Coordinate The Coordinate to route to.
|
||||
function ACT_ROUTE_POINT:GetCoordinate()
|
||||
self:F2( { self.Coordinate } )
|
||||
return self.Coordinate
|
||||
end
|
||||
|
||||
--- Set Range around Coordinate
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
-- @param #number Range The Range to consider the arrival. Default is 10000 meters.
|
||||
function ACT_ROUTE_POINT:SetRange( Range )
|
||||
self:F2( { Range } )
|
||||
self.Range = Range or 10000
|
||||
end
|
||||
|
||||
--- Get Range around Coordinate
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
-- @return #number The Range to consider the arrival. Default is 10000 meters.
|
||||
function ACT_ROUTE_POINT:GetRange()
|
||||
self:F2( { self.Range } )
|
||||
return self.Range
|
||||
end
|
||||
|
||||
--- Method override to check if the controllable has arrived.
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @return #boolean
|
||||
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
|
||||
|
||||
--- Task Events
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
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 -- ACT_ROUTE_POINT
|
||||
|
||||
|
||||
do -- ACT_ROUTE_ZONE
|
||||
|
||||
--- ACT_ROUTE_ZONE class
|
||||
-- @type ACT_ROUTE_ZONE
|
||||
-- @field Tasking.Task#TASK TASK
|
||||
-- @field Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @field Core.Zone#ZONE_BASE Zone
|
||||
-- @extends #ACT_ROUTE
|
||||
ACT_ROUTE_ZONE = {
|
||||
ClassName = "ACT_ROUTE_ZONE",
|
||||
}
|
||||
|
||||
|
||||
--- Creates a new routing state machine. The task will route a controllable to a ZONE until the controllable is within that ZONE.
|
||||
-- @param #ACT_ROUTE_ZONE self
|
||||
-- @param Core.Zone#ZONE_BASE Zone
|
||||
function ACT_ROUTE_ZONE:New( Zone )
|
||||
local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_ZONE
|
||||
|
||||
self.Zone = Zone
|
||||
|
||||
self.DisplayInterval = 30
|
||||
self.DisplayCount = 30
|
||||
self.DisplayMessage = true
|
||||
self.DisplayTime = 10 -- 10 seconds is the default
|
||||
|
||||
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 -- 10 seconds is the default
|
||||
end
|
||||
|
||||
--- Set Zone
|
||||
-- @param #ACT_ROUTE_ZONE self
|
||||
-- @param Core.Zone#ZONE_BASE Zone The Zone object where to route to.
|
||||
-- @param #number Altitude
|
||||
-- @param #number Heading
|
||||
function ACT_ROUTE_ZONE:SetZone( Zone, Altitude, Heading ) -- R2.2 Added altitude and heading
|
||||
self.Zone = Zone
|
||||
self.Altitude = Altitude
|
||||
self.Heading = Heading
|
||||
end
|
||||
|
||||
--- Get Zone
|
||||
-- @param #ACT_ROUTE_ZONE self
|
||||
-- @return Core.Zone#ZONE_BASE Zone The Zone object where to route to.
|
||||
function ACT_ROUTE_ZONE:GetZone()
|
||||
return self.Zone
|
||||
end
|
||||
|
||||
--- Method override to check if the controllable has arrived.
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @return #boolean
|
||||
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
|
||||
|
||||
--- Task Events
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ROUTE_ZONE self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
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 -- ACT_ROUTE_ZONE
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,340 +0,0 @@
|
||||
--- **Cargo** - Management of single cargo crates, which are based on a STATIC object.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [Demo Missions]()
|
||||
--
|
||||
-- ### [YouTube Playlist]()
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Cargo.CargoCrate
|
||||
-- @image Cargo_Crates.JPG
|
||||
|
||||
do -- CARGO_CRATE
|
||||
|
||||
--- Models the behaviour of cargo crates, which can be slingloaded and boarded on helicopters.
|
||||
-- @type CARGO_CRATE
|
||||
-- @extends Cargo.Cargo#CARGO_REPRESENTABLE
|
||||
|
||||
---
|
||||
-- 
|
||||
--
|
||||
--- Defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier.
|
||||
-- Use the event functions as described above to Load, UnLoad, Board, UnBoard the CARGO\_CRATE objects to and from carriers.
|
||||
--
|
||||
-- The above cargo classes are used by the following AI_CARGO_ classes to allow AI groups to transport cargo:
|
||||
--
|
||||
-- * AI Armoured Personnel Carriers to transport cargo and engage in battles, using the @{AI.AI_Cargo_APC} module.
|
||||
-- * AI Helicopters to transport cargo, using the @{AI.AI_Cargo_Helicopter} module.
|
||||
-- * AI Planes to transport cargo, using the @{AI.AI_Cargo_Airplane} module.
|
||||
-- * AI Ships is planned.
|
||||
--
|
||||
-- The above cargo classes are also used by the TASK_CARGO_ classes to allow human players to transport cargo as part of a tasking:
|
||||
--
|
||||
-- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT} to transport cargo by human players.
|
||||
-- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_CSAR} to transport downed pilots by human players.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #CARGO_CRATE
|
||||
CARGO_CRATE = {
|
||||
ClassName = "CARGO_CRATE"
|
||||
}
|
||||
|
||||
--- CARGO_CRATE Constructor.
|
||||
-- @param #CARGO_CRATE self
|
||||
-- @param Wrapper.Static#STATIC CargoStatic
|
||||
-- @param #string Type
|
||||
-- @param #string Name
|
||||
-- @param #number LoadRadius (optional)
|
||||
-- @param #number NearRadius (optional)
|
||||
-- @return #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 ) ) -- #CARGO_CRATE
|
||||
self:T( { Type, Name, NearRadius } )
|
||||
|
||||
self.CargoObject = CargoStatic -- Wrapper.Static#STATIC
|
||||
|
||||
-- Cargo objects are added to the _DATABASE and SET_CARGO objects.
|
||||
_EVENTDISPATCHER:CreateEventNewCargo( self )
|
||||
|
||||
self:HandleEvent( EVENTS.Dead, self.OnEventCargoDead )
|
||||
self:HandleEvent( EVENTS.Crash, self.OnEventCargoDead )
|
||||
--self:HandleEvent( EVENTS.RemoveUnit, self.OnEventCargoDead )
|
||||
self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventCargoDead )
|
||||
|
||||
self:SetEventPriority( 4 )
|
||||
|
||||
self.NearRadius = NearRadius or 25
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-- @param #CARGO_CRATE self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
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
|
||||
|
||||
|
||||
--- Enter UnLoaded State.
|
||||
-- @param #CARGO_CRATE self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#COORDINATE
|
||||
function CARGO_CRATE:onenterUnLoaded( From, Event, To, ToPointVec2 )
|
||||
--self:T( { ToPointVec2, From, Event, To } )
|
||||
|
||||
local Angle = 180
|
||||
local Speed = 10
|
||||
local Distance = 10
|
||||
|
||||
if From == "Loaded" then
|
||||
local StartCoordinate = self.CargoCarrier:GetCoordinate()
|
||||
local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees.
|
||||
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 } )
|
||||
|
||||
-- Respawn the group...
|
||||
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
|
||||
|
||||
|
||||
--- Loaded State.
|
||||
-- @param #CARGO_CRATE self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Unit#UNIT CargoCarrier
|
||||
function CARGO_CRATE:onenterLoaded( From, Event, To, CargoCarrier )
|
||||
--self:T( { From, Event, To, CargoCarrier } )
|
||||
|
||||
self.CargoCarrier = CargoCarrier
|
||||
|
||||
-- Only destroy the CargoObject is if there is a CargoObject (packages don't have CargoObjects).
|
||||
if self.CargoObject then
|
||||
self:T("Destroying")
|
||||
self.NoDestroy = true
|
||||
self.CargoObject:Destroy( false ) -- Do not generate a remove unit event, because we want to keep the template for later respawn in the database.
|
||||
--local Coordinate = self.CargoObject:GetCoordinate():GetRandomCoordinateInRadius( 50, 20 )
|
||||
--self.CargoObject:ReSpawnAt( Coordinate, 0 )
|
||||
end
|
||||
end
|
||||
|
||||
--- Check if the cargo can be Boarded.
|
||||
-- @param #CARGO_CRATE self
|
||||
function CARGO_CRATE:CanBoard()
|
||||
return false
|
||||
end
|
||||
|
||||
--- Check if the cargo can be Unboarded.
|
||||
-- @param #CARGO_CRATE self
|
||||
function CARGO_CRATE:CanUnboard()
|
||||
return false
|
||||
end
|
||||
|
||||
--- Check if the cargo can be sling loaded.
|
||||
-- @param #CARGO_CRATE self
|
||||
function CARGO_CRATE:CanSlingload()
|
||||
return false
|
||||
end
|
||||
|
||||
--- Check if Cargo Crate is in the radius for the Cargo to be reported.
|
||||
-- @param #CARGO_CRATE self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @return #boolean true if the Cargo Crate is within the report radius.
|
||||
function CARGO_CRATE:IsInReportRadius( Coordinate )
|
||||
--self:T( { Coordinate, LoadRadius = self.LoadRadius } )
|
||||
|
||||
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
|
||||
|
||||
|
||||
--- Check if Cargo Crate is in the radius for the Cargo to be Boarded or Loaded.
|
||||
-- @param #CARGO_CRATE self
|
||||
-- @param Core.Point#Coordinate Coordinate
|
||||
-- @return #boolean true if the Cargo Crate is within the loading radius.
|
||||
function CARGO_CRATE:IsInLoadRadius( Coordinate )
|
||||
--self:T( { Coordinate, LoadRadius = self.NearRadius } )
|
||||
|
||||
local Distance = 0
|
||||
if self:IsUnLoaded() then
|
||||
Distance = Coordinate:Get2DDistance( self.CargoObject:GetCoordinate() )
|
||||
--self:T( Distance )
|
||||
if Distance <= self.NearRadius then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Get the current Coordinate of the CargoGroup.
|
||||
-- @param #CARGO_CRATE self
|
||||
-- @return Core.Point#COORDINATE The current Coordinate of the first Cargo of the CargoGroup.
|
||||
-- @return #nil There is no valid Cargo in the CargoGroup.
|
||||
function CARGO_CRATE:GetCoordinate()
|
||||
--self:T()
|
||||
|
||||
return self.CargoObject:GetCoordinate()
|
||||
end
|
||||
|
||||
--- Check if the CargoGroup is alive.
|
||||
-- @param #CARGO_CRATE self
|
||||
-- @return #boolean true if the CargoGroup is alive.
|
||||
-- @return #boolean false if the CargoGroup is dead.
|
||||
function CARGO_CRATE:IsAlive()
|
||||
|
||||
local Alive = true
|
||||
|
||||
-- When the Cargo is Loaded, the Cargo is in the CargoCarrier, so we check if the CargoCarrier is alive.
|
||||
-- When the Cargo is not Loaded, the Cargo is the CargoObject, so we check if the CargoObject is alive.
|
||||
if self:IsLoaded() then
|
||||
Alive = Alive == true and self.CargoCarrier:IsAlive()
|
||||
else
|
||||
Alive = Alive == true and self.CargoObject:IsAlive()
|
||||
end
|
||||
|
||||
return Alive
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Route Cargo to Coordinate and randomize locations.
|
||||
-- @param #CARGO_CRATE self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
function CARGO_CRATE:RouteTo( Coordinate )
|
||||
self:T( {Coordinate = Coordinate } )
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Check if Cargo is near to the Carrier.
|
||||
-- The Cargo is near to the Carrier within NearRadius.
|
||||
-- @param #CARGO_CRATE self
|
||||
-- @param Wrapper.Group#GROUP CargoCarrier
|
||||
-- @param #number NearRadius
|
||||
-- @return #boolean The Cargo is near to the Carrier.
|
||||
-- @return #nil The Cargo is not near to the Carrier.
|
||||
function CARGO_CRATE:IsNear( CargoCarrier, NearRadius )
|
||||
self:T( {NearRadius = NearRadius } )
|
||||
|
||||
return self:IsNear( CargoCarrier:GetCoordinate(), NearRadius )
|
||||
end
|
||||
|
||||
--- Respawn the CargoGroup.
|
||||
-- @param #CARGO_CRATE self
|
||||
function CARGO_CRATE:Respawn()
|
||||
|
||||
self:T( { "Respawning crate " .. self:GetName() } )
|
||||
|
||||
|
||||
-- Respawn the group...
|
||||
if self.CargoObject then
|
||||
self.CargoObject:ReSpawn() -- A cargo destroy crates a DEAD event.
|
||||
self:__Reset( -0.1 )
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Respawn the CargoGroup.
|
||||
-- @param #CARGO_CRATE self
|
||||
function CARGO_CRATE:onafterReset()
|
||||
|
||||
self:T( { "Reset crate " .. self:GetName() } )
|
||||
|
||||
|
||||
-- Respawn the group...
|
||||
if self.CargoObject then
|
||||
self:SetDeployed( false )
|
||||
self:SetStartState( "UnLoaded" )
|
||||
self.CargoCarrier = nil
|
||||
-- Cargo objects are added to the _DATABASE and SET_CARGO objects.
|
||||
_EVENTDISPATCHER:CreateEventNewCargo( self )
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Get the transportation method of the Cargo.
|
||||
-- @param #CARGO_CRATE self
|
||||
-- @return #string The transportation method of the Cargo.
|
||||
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
|
||||
|
||||
@@ -1,777 +0,0 @@
|
||||
--- **Cargo** - Management of grouped cargo logistics, which are based on a GROUP object.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [Demo Missions]()
|
||||
--
|
||||
-- ### [YouTube Playlist]()
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Cargo.CargoGroup
|
||||
-- @image Cargo_Groups.JPG
|
||||
|
||||
|
||||
do -- CARGO_GROUP
|
||||
|
||||
--- @type CARGO_GROUP
|
||||
-- @field Core.Set#SET_CARGO CargoSet The collection of derived CARGO objects.
|
||||
-- @field #string GroupName The name of the CargoGroup.
|
||||
-- @field Wrapper.Group#GROUÜ CargoCarrier The carrier group.
|
||||
-- @extends Cargo.Cargo#CARGO_REPORTABLE
|
||||
|
||||
--- Defines a cargo that is represented by a @{Wrapper.Group} object within the simulator.
|
||||
--
|
||||
-- 
|
||||
-- The cargo can be Loaded, UnLoaded, Boarded, UnBoarded to and from Carriers.
|
||||
--
|
||||
-- The above cargo classes are used by the following AI_CARGO_ classes to allow AI groups to transport cargo:
|
||||
--
|
||||
-- * AI Armoured Personnel Carriers to transport cargo and engage in battles, using the @{AI.AI_Cargo_APC} module.
|
||||
-- * AI Helicopters to transport cargo, using the @{AI.AI_Cargo_Helicopter} module.
|
||||
-- * AI Planes to transport cargo, using the @{AI.AI_Cargo_Airplane} module.
|
||||
-- * AI Ships is planned.
|
||||
--
|
||||
-- The above cargo classes are also used by the TASK_CARGO_ classes to allow human players to transport cargo as part of a tasking:
|
||||
--
|
||||
-- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT} to transport cargo by human players.
|
||||
-- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_CSAR} to transport downed pilots by human players.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- @field #CARGO_GROUP CARGO_GROUP
|
||||
--
|
||||
CARGO_GROUP = {
|
||||
ClassName = "CARGO_GROUP",
|
||||
}
|
||||
|
||||
--- CARGO_GROUP constructor.
|
||||
-- This make a new CARGO_GROUP from a @{Wrapper.Group} object.
|
||||
-- It will "ungroup" the group object within the sim, and will create a @{Core.Set} of individual Unit objects.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param Wrapper.Group#GROUP CargoGroup Group to be transported as cargo.
|
||||
-- @param #string Type Cargo type, e.g. "Infantry". This is the type used in SET_CARGO:New():FilterTypes("Infantry") to define the valid cargo groups of the set.
|
||||
-- @param #string Name A user defined name of the cargo group. This name CAN be the same as the group object but can also have a different name. This name MUST be unique!
|
||||
-- @param #number LoadRadius (optional) Distance in meters until which a cargo is loaded into the carrier. Cargo outside this radius has to be routed by other means to within the radius to be loaded.
|
||||
-- @param #number NearRadius (optional) Once the units are within this radius of the carrier, they are actually loaded, i.e. disappear from the scene.
|
||||
-- @return #CARGO_GROUP Cargo group object.
|
||||
function CARGO_GROUP:New( CargoGroup, Type, Name, LoadRadius, NearRadius )
|
||||
|
||||
-- Inherit CAROG_REPORTABLE
|
||||
local self = BASE:Inherit( self, CARGO_REPORTABLE:New( Type, Name, 0, LoadRadius, NearRadius ) ) -- #CARGO_GROUP
|
||||
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() -- destroy and generate a unit removal event, so that the database gets cleaned, and the linked sets get properly cleaned.
|
||||
|
||||
local GroupName = CargoGroup:GetName()
|
||||
self.CargoName = Name
|
||||
self.CargoTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplate( GroupName ) )
|
||||
|
||||
-- Deactivate late activation.
|
||||
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
|
||||
|
||||
-- And we register the spawned unit as part of the CargoSet.
|
||||
local Unit = UNIT:Register( CargoUnitName )
|
||||
|
||||
end
|
||||
|
||||
-- Then we register the new group in the database
|
||||
self.CargoGroup = GROUP:NewTemplate( self.GroupTemplate, self.GroupTemplate.CoalitionID, self.GroupTemplate.CategoryID, self.GroupTemplate.CountryID )
|
||||
|
||||
-- Now we spawn the new group based on the template created.
|
||||
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 } )
|
||||
|
||||
-- Cargo objects are added to the _DATABASE and SET_CARGO objects.
|
||||
_EVENTDISPATCHER:CreateEventNewCargo( self )
|
||||
|
||||
self:HandleEvent( EVENTS.Dead, self.OnEventCargoDead )
|
||||
self:HandleEvent( EVENTS.Crash, self.OnEventCargoDead )
|
||||
--self:HandleEvent( EVENTS.RemoveUnit, self.OnEventCargoDead )
|
||||
self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventCargoDead )
|
||||
|
||||
self:SetEventPriority( 4 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Respawn the CargoGroup.
|
||||
-- @param #CARGO_GROUP self
|
||||
function CARGO_GROUP:Respawn()
|
||||
|
||||
self:T( { "Respawning" } )
|
||||
|
||||
for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do
|
||||
local Cargo = CargoData -- Cargo.Cargo#CARGO
|
||||
Cargo:Destroy() -- Destroy the cargo and generate a remove unit event to update the sets.
|
||||
Cargo:SetStartState( "UnLoaded" )
|
||||
end
|
||||
|
||||
-- Now we spawn the new group based on the template created.
|
||||
_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
|
||||
|
||||
--- Ungroup the cargo group into individual groups with one unit.
|
||||
-- This is required because by default a group will move in formation and this is really an issue for group control.
|
||||
-- Therefore this method is made to be able to ungroup a group.
|
||||
-- This works for ground only groups.
|
||||
-- @param #CARGO_GROUP self
|
||||
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 -- Cargo.CargoUnit#CARGO_UNIT
|
||||
|
||||
if CargoUnit:IsUnLoaded() then
|
||||
local GroupTemplate = UTILS.DeepCopy( self.CargoTemplate )
|
||||
--local GroupName = env.getValueDictByKey( GroupTemplate.name )
|
||||
|
||||
-- We create a new group object with one unit...
|
||||
-- First we prepare the template...
|
||||
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
|
||||
|
||||
|
||||
-- Then we register the new group in the database
|
||||
local CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID)
|
||||
|
||||
-- Now we spawn the new group based on the template created.
|
||||
_DATABASE:Spawn( GroupTemplate )
|
||||
end
|
||||
end
|
||||
|
||||
self.CargoObject = nil
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Regroup the cargo group into one group with multiple unit.
|
||||
-- This is required because by default a group will move in formation and this is really an issue for group control.
|
||||
-- Therefore this method is made to be able to regroup a group.
|
||||
-- This works for ground only groups.
|
||||
-- @param #CARGO_GROUP self
|
||||
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 -- Cargo.CargoUnit#CARGO_UNIT
|
||||
|
||||
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
|
||||
|
||||
-- Then we register the new group in the database
|
||||
self.CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID )
|
||||
|
||||
self:T( { "Regroup", GroupTemplate } )
|
||||
|
||||
-- Now we spawn the new group based on the template created.
|
||||
self.CargoObject = _DATABASE:Spawn( GroupTemplate )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- @param #CARGO_GROUP self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
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 -- Cargo.Cargo#CARGO
|
||||
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
|
||||
|
||||
--- After Board Event.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Unit#UNIT CargoCarrier
|
||||
-- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier.
|
||||
function CARGO_GROUP:onafterBoard( From, Event, To, CargoCarrier, NearRadius, ... )
|
||||
self:T( { CargoCarrier.UnitName, From, Event, To, NearRadius = NearRadius } )
|
||||
|
||||
NearRadius = NearRadius or self.NearRadius
|
||||
|
||||
-- For each Cargo object within the CARGO_GROUPED, route each object to the CargoLoadPointVec2
|
||||
self.CargoSet:ForEach(
|
||||
function( Cargo, ... )
|
||||
self:T( { "Board Unit", Cargo:GetName( ), Cargo:IsDestroyed(), Cargo.CargoObject:IsAlive() } )
|
||||
local CargoGroup = Cargo.CargoObject --Wrapper.Group#GROUP
|
||||
CargoGroup:OptionAlarmStateGreen()
|
||||
Cargo:__Board( 1, CargoCarrier, NearRadius, ... )
|
||||
end, ...
|
||||
)
|
||||
|
||||
self:__Boarding( -1, CargoCarrier, NearRadius, ... )
|
||||
|
||||
end
|
||||
|
||||
--- Enter Loaded State.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Unit#UNIT CargoCarrier
|
||||
function CARGO_GROUP:onafterLoad( From, Event, To, CargoCarrier, ... )
|
||||
--self:T( { From, Event, To, CargoCarrier, ...} )
|
||||
|
||||
if From == "UnLoaded" then
|
||||
-- For each Cargo object within the CARGO_GROUP, load each cargo to the CargoCarrier.
|
||||
for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do
|
||||
if not Cargo:IsDestroyed() then
|
||||
Cargo:Load( CargoCarrier )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--self.CargoObject:Destroy()
|
||||
self.CargoCarrier = CargoCarrier
|
||||
self.CargoCarrier:AddCargo( self )
|
||||
|
||||
end
|
||||
|
||||
--- Leave Boarding State.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Unit#UNIT CargoCarrier
|
||||
-- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier.
|
||||
function CARGO_GROUP:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... )
|
||||
--self:T( { CargoCarrier.UnitName, From, Event, To } )
|
||||
|
||||
local Boarded = true
|
||||
local Cancelled = false
|
||||
local Dead = true
|
||||
|
||||
self.CargoSet:Flush()
|
||||
|
||||
-- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2
|
||||
for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do
|
||||
--self:T( { Cargo:GetName(), Cargo.current } )
|
||||
|
||||
|
||||
if not Cargo:is( "Loaded" )
|
||||
and (not Cargo:is( "Destroyed" )) then -- If one or more units of a group defined as CARGO_GROUP died, the CARGO_GROUP:Board() command does not trigger the CARGO_GRUOP:OnEnterLoaded() function.
|
||||
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
|
||||
|
||||
--- Enter UnBoarding State.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#COORDINATE ToPointVec2
|
||||
-- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier.
|
||||
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
|
||||
|
||||
-- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2
|
||||
self.CargoSet:ForEach(
|
||||
--- @param Cargo.Cargo#CARGO Cargo
|
||||
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
|
||||
|
||||
--- Leave UnBoarding State.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#COORDINATE ToPointVec2
|
||||
-- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier.
|
||||
function CARGO_GROUP:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... )
|
||||
--self:T( { From, Event, To, ToPointVec2, NearRadius } )
|
||||
|
||||
--local NearRadius = NearRadius or 25
|
||||
|
||||
local Angle = 180
|
||||
local Speed = 10
|
||||
local Distance = 5
|
||||
|
||||
if From == "UnBoarding" then
|
||||
local UnBoarded = true
|
||||
|
||||
-- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2
|
||||
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
|
||||
|
||||
--- Enter UnLoaded State.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#COORDINATE ToPointVec2
|
||||
function CARGO_GROUP:onafterUnLoad( From, Event, To, ToPointVec2, ... )
|
||||
--self:T( { From, Event, To, ToPointVec2 } )
|
||||
|
||||
if From == "Loaded" then
|
||||
|
||||
-- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2
|
||||
self.CargoSet:ForEach(
|
||||
function( Cargo )
|
||||
--Cargo:UnLoad( ToPointVec2 )
|
||||
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
|
||||
|
||||
|
||||
--- Get the current Coordinate of the CargoGroup.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @return Core.Point#COORDINATE The current Coordinate of the first Cargo of the CargoGroup.
|
||||
-- @return #nil There is no valid Cargo in the CargoGroup.
|
||||
function CARGO_GROUP:GetCoordinate()
|
||||
local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO
|
||||
|
||||
if Cargo then
|
||||
return Cargo.CargoObject:GetCoordinate()
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Get the x position of the cargo.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @return #number
|
||||
function CARGO:GetX()
|
||||
|
||||
local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO
|
||||
|
||||
if Cargo then
|
||||
return Cargo:GetCoordinate().x
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Get the y position of the cargo.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @return #number
|
||||
function CARGO:GetY()
|
||||
|
||||
local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO
|
||||
|
||||
if Cargo then
|
||||
return Cargo:GetCoordinate().z
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Check if the CargoGroup is alive.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @return #boolean true if the CargoGroup is alive.
|
||||
-- @return #boolean false if the CargoGroup is dead.
|
||||
function CARGO_GROUP:IsAlive()
|
||||
|
||||
local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO
|
||||
return Cargo ~= nil
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Get the first alive Cargo Unit of the Cargo Group.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @return #CARGO_GROUP
|
||||
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
|
||||
|
||||
|
||||
--- Get the amount of cargo units in the group.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @return #CARGO_GROUP
|
||||
function CARGO_GROUP:GetCount()
|
||||
return self.CargoSet:Count()
|
||||
end
|
||||
|
||||
|
||||
--- Get the underlying GROUP object from the CARGO_GROUP.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @return #CARGO_GROUP
|
||||
function CARGO_GROUP:GetGroup( Cargo )
|
||||
local Cargo = Cargo or self:GetFirstAlive() -- Cargo.Cargo#CARGO
|
||||
return Cargo.CargoObject:GetGroup()
|
||||
end
|
||||
|
||||
|
||||
--- Route Cargo to Coordinate and randomize locations.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
function CARGO_GROUP:RouteTo( Coordinate )
|
||||
--self:T( {Coordinate = Coordinate } )
|
||||
|
||||
-- For each Cargo within the CargoSet, route each object to the Coordinate
|
||||
self.CargoSet:ForEach(
|
||||
function( Cargo )
|
||||
Cargo.CargoObject:RouteGroundTo( Coordinate, 10, "vee", 0 )
|
||||
end
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
--- Check if Cargo is near to the Carrier.
|
||||
-- The Cargo is near to the Carrier if the first unit of the Cargo Group is within NearRadius.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param Wrapper.Group#GROUP CargoCarrier
|
||||
-- @param #number NearRadius
|
||||
-- @return #boolean The Cargo is near to the Carrier or #nil if the Cargo is not near to the Carrier.
|
||||
function CARGO_GROUP:IsNear( CargoCarrier, NearRadius )
|
||||
self:T( {NearRadius = NearRadius } )
|
||||
|
||||
for _, Cargo in pairs( self.CargoSet:GetSet() ) do
|
||||
local Cargo = Cargo -- Cargo.Cargo#CARGO
|
||||
if Cargo:IsAlive() then
|
||||
if Cargo:IsNear( CargoCarrier:GetCoordinate(), NearRadius ) then
|
||||
self:T( "Near" )
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Check if Cargo Group is in the radius for the Cargo to be Boarded.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @return #boolean true if the Cargo Group is within the load radius.
|
||||
function CARGO_GROUP:IsInLoadRadius( Coordinate )
|
||||
--self:T( { Coordinate } )
|
||||
|
||||
local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO
|
||||
|
||||
if Cargo then
|
||||
local Distance = 0
|
||||
local CargoCoordinate
|
||||
if Cargo:IsLoaded() then
|
||||
CargoCoordinate = Cargo.CargoCarrier:GetCoordinate()
|
||||
else
|
||||
CargoCoordinate = Cargo.CargoObject:GetCoordinate()
|
||||
end
|
||||
|
||||
-- FF check if coordinate could be obtained. This was commented out for some (unknown) reason. But the check seems valid!
|
||||
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
|
||||
|
||||
|
||||
--- Check if Cargo Group is in the report radius.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param Core.Point#Coordinate Coordinate
|
||||
-- @return #boolean true if the Cargo Group is within the report radius.
|
||||
function CARGO_GROUP:IsInReportRadius( Coordinate )
|
||||
--self:T( { Coordinate } )
|
||||
|
||||
local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO
|
||||
|
||||
if Cargo then
|
||||
self:T( { Cargo } )
|
||||
local Distance = 0
|
||||
if Cargo:IsUnLoaded() then
|
||||
Distance = Coordinate:Get2DDistance( Cargo.CargoObject:GetCoordinate() )
|
||||
--self:T( Distance )
|
||||
if Distance <= self.LoadRadius then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Signal a flare at the position of the CargoGroup.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param Utilities.Utils#FLARECOLOR FlareColor
|
||||
function CARGO_GROUP:Flare( FlareColor )
|
||||
|
||||
local Cargo = self.CargoSet:GetFirst() -- Cargo.Cargo#CARGO
|
||||
if Cargo then
|
||||
Cargo:Flare( FlareColor )
|
||||
end
|
||||
end
|
||||
|
||||
--- Smoke the CargoGroup.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param Utilities.Utils#SMOKECOLOR SmokeColor The color of the smoke.
|
||||
-- @param #number Radius The radius of randomization around the center of the first element of the CargoGroup.
|
||||
function CARGO_GROUP:Smoke( SmokeColor, Radius )
|
||||
|
||||
local Cargo = self.CargoSet:GetFirst() -- Cargo.Cargo#CARGO
|
||||
|
||||
if Cargo then
|
||||
Cargo:Smoke( SmokeColor, Radius )
|
||||
end
|
||||
end
|
||||
|
||||
--- Check if the first element of the CargoGroup is the given @{Core.Zone}.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param Core.Zone#ZONE_BASE Zone
|
||||
-- @return #boolean **true** if the first element of the CargoGroup is in the Zone
|
||||
-- @return #boolean **false** if there is no element of the CargoGroup in the Zone.
|
||||
function CARGO_GROUP:IsInZone( Zone )
|
||||
--self:T( { Zone } )
|
||||
|
||||
local Cargo = self.CargoSet:GetFirst() -- Cargo.Cargo#CARGO
|
||||
|
||||
if Cargo then
|
||||
return Cargo:IsInZone( Zone )
|
||||
end
|
||||
|
||||
return nil
|
||||
|
||||
end
|
||||
|
||||
--- Get the transportation method of the Cargo.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @return #string The transportation method of the Cargo.
|
||||
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 -- CARGO_GROUP
|
||||
|
||||
@@ -1,277 +0,0 @@
|
||||
--- **Cargo** - Management of single cargo crates, which are based on a STATIC object. The cargo can only be slingloaded.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [Demo Missions]()
|
||||
--
|
||||
-- ### [YouTube Playlist]()
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Cargo.CargoSlingload
|
||||
-- @image Cargo_Slingload.JPG
|
||||
|
||||
|
||||
do -- CARGO_SLINGLOAD
|
||||
|
||||
--- Models the behaviour of cargo crates, which can only be slingloaded.
|
||||
-- @type CARGO_SLINGLOAD
|
||||
-- @extends Cargo.Cargo#CARGO_REPRESENTABLE
|
||||
|
||||
--- Defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier.
|
||||
--
|
||||
-- The above cargo classes are also used by the TASK_CARGO_ classes to allow human players to transport cargo as part of a tasking:
|
||||
--
|
||||
-- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT} to transport cargo by human players.
|
||||
-- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_CSAR} to transport downed pilots by human players.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #CARGO_SLINGLOAD
|
||||
CARGO_SLINGLOAD = {
|
||||
ClassName = "CARGO_SLINGLOAD"
|
||||
}
|
||||
|
||||
--- CARGO_SLINGLOAD Constructor.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
-- @param Wrapper.Static#STATIC CargoStatic
|
||||
-- @param #string Type
|
||||
-- @param #string Name
|
||||
-- @param #number LoadRadius (optional)
|
||||
-- @param #number NearRadius (optional)
|
||||
-- @return #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 ) ) -- #CARGO_SLINGLOAD
|
||||
self:T( { Type, Name, NearRadius } )
|
||||
|
||||
self.CargoObject = CargoStatic
|
||||
|
||||
-- Cargo objects are added to the _DATABASE and SET_CARGO objects.
|
||||
_EVENTDISPATCHER:CreateEventNewCargo( self )
|
||||
|
||||
self:HandleEvent( EVENTS.Dead, self.OnEventCargoDead )
|
||||
self:HandleEvent( EVENTS.Crash, self.OnEventCargoDead )
|
||||
--self:HandleEvent( EVENTS.RemoveUnit, self.OnEventCargoDead )
|
||||
self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventCargoDead )
|
||||
|
||||
self:SetEventPriority( 4 )
|
||||
|
||||
self.NearRadius = NearRadius or 25
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
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
|
||||
|
||||
|
||||
--- Check if the cargo can be Slingloaded.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
function CARGO_SLINGLOAD:CanSlingload()
|
||||
return true
|
||||
end
|
||||
|
||||
--- Check if the cargo can be Boarded.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
function CARGO_SLINGLOAD:CanBoard()
|
||||
return false
|
||||
end
|
||||
|
||||
--- Check if the cargo can be Unboarded.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
function CARGO_SLINGLOAD:CanUnboard()
|
||||
return false
|
||||
end
|
||||
|
||||
--- Check if the cargo can be Loaded.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
function CARGO_SLINGLOAD:CanLoad()
|
||||
return false
|
||||
end
|
||||
|
||||
--- Check if the cargo can be Unloaded.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
function CARGO_SLINGLOAD:CanUnload()
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
--- Check if Cargo Crate is in the radius for the Cargo to be reported.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @return #boolean true if the Cargo Crate is within the report radius.
|
||||
function CARGO_SLINGLOAD:IsInReportRadius( Coordinate )
|
||||
--self:T( { Coordinate, LoadRadius = self.LoadRadius } )
|
||||
|
||||
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
|
||||
|
||||
|
||||
--- Check if Cargo Slingload is in the radius for the Cargo to be Boarded or Loaded.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @return #boolean true if the Cargo Slingload is within the loading radius.
|
||||
function CARGO_SLINGLOAD:IsInLoadRadius( Coordinate )
|
||||
--self:T( { 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
|
||||
|
||||
|
||||
|
||||
--- Get the current Coordinate of the CargoGroup.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
-- @return Core.Point#COORDINATE The current Coordinate of the first Cargo of the CargoGroup.
|
||||
-- @return #nil There is no valid Cargo in the CargoGroup.
|
||||
function CARGO_SLINGLOAD:GetCoordinate()
|
||||
--self:T()
|
||||
|
||||
return self.CargoObject:GetCoordinate()
|
||||
end
|
||||
|
||||
--- Check if the CargoGroup is alive.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
-- @return #boolean true if the CargoGroup is alive.
|
||||
-- @return #boolean false if the CargoGroup is dead.
|
||||
function CARGO_SLINGLOAD:IsAlive()
|
||||
|
||||
local Alive = true
|
||||
|
||||
-- When the Cargo is Loaded, the Cargo is in the CargoCarrier, so we check if the CargoCarrier is alive.
|
||||
-- When the Cargo is not Loaded, the Cargo is the CargoObject, so we check if the CargoObject is alive.
|
||||
if self:IsLoaded() then
|
||||
Alive = Alive == true and self.CargoCarrier:IsAlive()
|
||||
else
|
||||
Alive = Alive == true and self.CargoObject:IsAlive()
|
||||
end
|
||||
|
||||
return Alive
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Route Cargo to Coordinate and randomize locations.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
function CARGO_SLINGLOAD:RouteTo( Coordinate )
|
||||
--self:T( {Coordinate = Coordinate } )
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Check if Cargo is near to the Carrier.
|
||||
-- The Cargo is near to the Carrier within NearRadius.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
-- @param Wrapper.Group#GROUP CargoCarrier
|
||||
-- @param #number NearRadius
|
||||
-- @return #boolean The Cargo is near to the Carrier.
|
||||
-- @return #nil The Cargo is not near to the Carrier.
|
||||
function CARGO_SLINGLOAD:IsNear( CargoCarrier, NearRadius )
|
||||
--self:T( {NearRadius = NearRadius } )
|
||||
|
||||
return self:IsNear( CargoCarrier:GetCoordinate(), NearRadius )
|
||||
end
|
||||
|
||||
|
||||
--- Respawn the CargoGroup.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
function CARGO_SLINGLOAD:Respawn()
|
||||
|
||||
--self:T( { "Respawning slingload " .. self:GetName() } )
|
||||
|
||||
|
||||
-- Respawn the group...
|
||||
if self.CargoObject then
|
||||
self.CargoObject:ReSpawn() -- A cargo destroy crates a DEAD event.
|
||||
self:__Reset( -0.1 )
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Respawn the CargoGroup.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
function CARGO_SLINGLOAD:onafterReset()
|
||||
|
||||
--self:T( { "Reset slingload " .. self:GetName() } )
|
||||
|
||||
|
||||
-- Respawn the group...
|
||||
if self.CargoObject then
|
||||
self:SetDeployed( false )
|
||||
self:SetStartState( "UnLoaded" )
|
||||
self.CargoCarrier = nil
|
||||
-- Cargo objects are added to the _DATABASE and SET_CARGO objects.
|
||||
_EVENTDISPATCHER:CreateEventNewCargo( self )
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Get the transportation method of the Cargo.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
-- @return #string The transportation method of the Cargo.
|
||||
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
|
||||
@@ -1,397 +0,0 @@
|
||||
--- **Cargo** - Management of single cargo logistics, which are based on a UNIT object.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [Demo Missions]()
|
||||
--
|
||||
-- ### [YouTube Playlist]()
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Cargo.CargoUnit
|
||||
-- @image Cargo_Units.JPG
|
||||
|
||||
do -- CARGO_UNIT
|
||||
|
||||
--- Models CARGO in the form of units, which can be boarded, unboarded, loaded, unloaded.
|
||||
-- @type CARGO_UNIT
|
||||
-- @extends Cargo.Cargo#CARGO_REPRESENTABLE
|
||||
|
||||
--- Defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier.
|
||||
-- Use the event functions as described above to Load, UnLoad, Board, UnBoard the CARGO_UNIT objects to and from carriers.
|
||||
-- Note that ground forces behave in a group, and thus, act in formation, regardless if one unit is commanded to move.
|
||||
--
|
||||
-- This class is used in CARGO_GROUP, and is not meant to be used by mission designers individually.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #CARGO_UNIT CARGO_UNIT
|
||||
--
|
||||
CARGO_UNIT = {
|
||||
ClassName = "CARGO_UNIT"
|
||||
}
|
||||
|
||||
--- CARGO_UNIT Constructor.
|
||||
-- @param #CARGO_UNIT self
|
||||
-- @param Wrapper.Unit#UNIT CargoUnit
|
||||
-- @param #string Type
|
||||
-- @param #string Name
|
||||
-- @param #number Weight
|
||||
-- @param #number LoadRadius (optional)
|
||||
-- @param #number NearRadius (optional)
|
||||
-- @return #CARGO_UNIT
|
||||
function CARGO_UNIT:New( CargoUnit, Type, Name, LoadRadius, NearRadius )
|
||||
|
||||
-- Inherit CARGO_REPRESENTABLE.
|
||||
local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoUnit, Type, Name, LoadRadius, NearRadius ) ) -- #CARGO_UNIT
|
||||
|
||||
-- Debug info.
|
||||
self:T({Type=Type, Name=Name, LoadRadius=LoadRadius, NearRadius=NearRadius})
|
||||
|
||||
-- Set cargo object.
|
||||
self.CargoObject = CargoUnit
|
||||
|
||||
-- Set event prio.
|
||||
self:SetEventPriority( 5 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Enter UnBoarding State.
|
||||
-- @param #CARGO_UNIT self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#COORDINATE ToPointVec2
|
||||
-- @param #number NearRadius (optional) Defaut 25 m.
|
||||
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 -- Wrapper.Controllable#CONTROLLABLE
|
||||
|
||||
if CargoCarrier:IsAlive() then
|
||||
|
||||
local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2()
|
||||
local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees.
|
||||
local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle )
|
||||
|
||||
|
||||
local CargoRoutePointVec2 = CargoCarrierPointVec2:Translate( RouteDistance, CargoDeployHeading )
|
||||
|
||||
|
||||
-- if there is no ToPointVec2 given, then use the CargoRoutePointVec2
|
||||
local FromDirectionVec3 = CargoCarrierPointVec2:GetDirectionVec3( ToPointVec2 or CargoRoutePointVec2 )
|
||||
local FromAngle = CargoCarrierPointVec2:GetAngleDegrees(FromDirectionVec3)
|
||||
local FromPointVec2 = CargoCarrierPointVec2:Translate( DeployDistance, FromAngle )
|
||||
--local CargoDeployPointVec2 = CargoCarrierPointVec2:GetRandomCoordinateInRadius( 10, 5 )
|
||||
|
||||
ToPointVec2 = ToPointVec2 or CargoCarrierPointVec2:GetRandomCoordinateInRadius( NearRadius, DeployDistance )
|
||||
|
||||
-- Respawn the group...
|
||||
if self.CargoObject then
|
||||
if CargoCarrier:IsShip() then
|
||||
-- If CargoCarrier is a ship, we don't want to spawn the units in the water next to the boat. Use destination coord instead.
|
||||
self.CargoObject:ReSpawnAt( ToPointVec2, CargoDeployHeading )
|
||||
else
|
||||
self.CargoObject:ReSpawnAt( FromPointVec2, CargoDeployHeading )
|
||||
end
|
||||
self:T( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } )
|
||||
self.CargoCarrier = nil
|
||||
|
||||
local Points = {}
|
||||
|
||||
-- From
|
||||
Points[#Points+1] = FromPointVec2:WaypointGround( Speed, "Vee" )
|
||||
|
||||
-- To
|
||||
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
|
||||
-- the Carrier is dead. This cargo is dead too!
|
||||
self:Destroyed()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Leave UnBoarding State.
|
||||
-- @param #CARGO_UNIT self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#COORDINATE ToPointVec2
|
||||
-- @param #number NearRadius (optional) Defaut 100 m.
|
||||
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
|
||||
--if self:IsNear( ToPointVec2, NearRadius ) then
|
||||
return true
|
||||
--else
|
||||
|
||||
--self:__UnBoarding( 1, ToPointVec2, NearRadius )
|
||||
--end
|
||||
--return false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- UnBoard Event.
|
||||
-- @param #CARGO_UNIT self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#COORDINATE ToPointVec2
|
||||
-- @param #number NearRadius (optional) Defaut 100 m.
|
||||
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 )
|
||||
|
||||
-- Only unboard the cargo when the carrier is not in the air.
|
||||
-- (eg. cargo can be on a oil derrick, moving the cargo on the oil derrick will drop the cargo on the sea).
|
||||
if not self.CargoInAir then
|
||||
|
||||
end
|
||||
|
||||
self:__UnLoad( 1, ToPointVec2, NearRadius )
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Enter UnLoaded State.
|
||||
-- @param #CARGO_UNIT self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#COORDINATE
|
||||
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() -- Get Heading of object in degrees.
|
||||
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 )
|
||||
|
||||
-- Respawn the group...
|
||||
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
|
||||
|
||||
--- Board Event.
|
||||
-- @param #CARGO_UNIT self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Group#GROUP CargoCarrier
|
||||
-- @param #number NearRadius
|
||||
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
|
||||
|
||||
--self:T({Unit=self.CargoObject:GetName()})
|
||||
|
||||
-- A cargo unit can only be boarded if it is not dead
|
||||
|
||||
-- Only move the group to the carrier when the cargo is not in the air
|
||||
-- (eg. cargo can be on a oil derrick, moving the cargo on the oil derrick will drop the cargo on the sea).
|
||||
if not self.CargoInAir then
|
||||
-- If NearRadius is given, then use the given NearRadius, otherwise calculate the NearRadius
|
||||
-- based upon the Carrier bounding radius, which is calculated from the bounding rectangle on the Y axis.
|
||||
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() -- Get Heading of object in degrees.
|
||||
local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle )
|
||||
local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( Distance, CargoDeployHeading )
|
||||
|
||||
-- Set the CargoObject to state Green to ensure it is boarding!
|
||||
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
|
||||
|
||||
|
||||
--- Boarding Event.
|
||||
-- @param #CARGO_UNIT self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Client#CLIENT CargoCarrier
|
||||
-- @param #number NearRadius Default 25 m.
|
||||
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 -- and self.CargoObject and self.CargoObject: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
|
||||
|
||||
--self:T({Unit=self.CargoObject:GetName()})
|
||||
|
||||
local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2()
|
||||
local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees.
|
||||
local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle )
|
||||
local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( Distance, CargoDeployHeading )
|
||||
|
||||
-- Set the CargoObject to state Green to ensure it is boarding!
|
||||
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
|
||||
|
||||
|
||||
--- Loaded State.
|
||||
-- @param #CARGO_UNIT self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Unit#UNIT CargoCarrier
|
||||
function CARGO_UNIT:onenterLoaded( From, Event, To, CargoCarrier )
|
||||
self:T( { From, Event, To, CargoCarrier } )
|
||||
|
||||
self.CargoCarrier = CargoCarrier
|
||||
|
||||
--self:T({Unit=self.CargoObject:GetName()})
|
||||
|
||||
-- Only destroy the CargoObject if there is a CargoObject (packages don't have CargoObjects).
|
||||
if self.CargoObject then
|
||||
self.CargoObject:Destroy( false )
|
||||
--self.CargoObject:ReSpawnAt( COORDINATE:NewFromVec2( {x=0,y=0} ), 0 )
|
||||
end
|
||||
end
|
||||
|
||||
--- Get the transportation method of the Cargo.
|
||||
-- @param #CARGO_UNIT self
|
||||
-- @return #string The transportation method of the Cargo.
|
||||
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 -- CARGO_UNIT
|
||||
@@ -320,10 +320,12 @@ end
|
||||
|
||||
--- Set valid neighbours to be in a certain distance.
|
||||
-- @param #ASTAR self
|
||||
-- @param #number MaxDistance Max distance between nodes in meters. Default is 2000 m.
|
||||
-- @param #number MaxDistance (Optional) Max distance between nodes in meters. Default is 2000 m.
|
||||
-- @return #ASTAR self
|
||||
function ASTAR:SetValidNeighbourDistance(MaxDistance)
|
||||
|
||||
MaxDistance = MaxDistance or 2000
|
||||
|
||||
self:SetValidNeighbourFunction(ASTAR.DistMax, MaxDistance)
|
||||
|
||||
return self
|
||||
@@ -331,10 +333,12 @@ end
|
||||
|
||||
--- Set valid neighbours to be in a certain distance.
|
||||
-- @param #ASTAR self
|
||||
-- @param #number MaxDistance Max distance between nodes in meters. Default is 2000 m.
|
||||
-- @param #number MaxDistance (Optional) Max distance between nodes in meters. Default is 2000 m.
|
||||
-- @return #ASTAR self
|
||||
function ASTAR:SetValidNeighbourRoad(MaxDistance)
|
||||
|
||||
MaxDistance = MaxDistance or 2000
|
||||
|
||||
self:SetValidNeighbourFunction(ASTAR.Road, MaxDistance)
|
||||
|
||||
return self
|
||||
@@ -397,10 +401,10 @@ end
|
||||
-- The coordinate system is oriented along the line between start and end point.
|
||||
-- @param #ASTAR self
|
||||
-- @param #table ValidSurfaceTypes Valid surface types. By default is all surfaces are allowed.
|
||||
-- @param #number BoxHY Box "height" in meters along the y-coordinate. Default 40000 meters (40 km).
|
||||
-- @param #number SpaceX Additional space in meters before start and after end coordinate. Default 10000 meters (10 km).
|
||||
-- @param #number deltaX Increment in the direction of start to end coordinate in meters. Default 2000 meters.
|
||||
-- @param #number deltaY Increment perpendicular to the direction of start to end coordinate in meters. Default is same as deltaX.
|
||||
-- @param #number BoxHY (Optional) Box "height" in meters along the y-coordinate. Default 40000 meters (40 km).
|
||||
-- @param #number SpaceX (Optional) Additional space in meters before start and after end coordinate. Default 10000 meters (10 km).
|
||||
-- @param #number deltaX (Optional) Increment in the direction of start to end coordinate in meters. Default 2000 meters.
|
||||
-- @param #number deltaY (Optional) Increment perpendicular to the direction of start to end coordinate in meters. Default is same as deltaX.
|
||||
-- @param #boolean MarkGrid If true, create F10 map markers at grid nodes.
|
||||
-- @return #ASTAR self
|
||||
function ASTAR:CreateGrid(ValidSurfaceTypes, BoxHY, SpaceX, deltaX, deltaY, MarkGrid)
|
||||
@@ -547,7 +551,7 @@ end
|
||||
--- Function to check if distance between two nodes is less than a threshold distance.
|
||||
-- @param #ASTAR.Node nodeA First node.
|
||||
-- @param #ASTAR.Node nodeB Other node.
|
||||
-- @param #number distmax Max distance in meters. Default is 2000 m.
|
||||
-- @param #number distmax (Optional) Max distance in meters. Default is 2000 m.
|
||||
-- @return #boolean If true, distance between the two nodes is below threshold.
|
||||
function ASTAR.DistMax(nodeA, nodeB, distmax)
|
||||
|
||||
|
||||
@@ -855,7 +855,7 @@ end
|
||||
-- @param DCS#Time EventTime The time stamp of the event.
|
||||
-- @param DCS#Object Initiator The initiating object of the event.
|
||||
function BASE:CreateEventTakeoff( EventTime, Initiator )
|
||||
self:F( { EventTime, Initiator } )
|
||||
--self:F( { EventTime, Initiator } )
|
||||
|
||||
local Event = {
|
||||
id = world.event.S_EVENT_TAKEOFF,
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
--
|
||||
-- @module Core.ClientMenu
|
||||
-- @image Core_Menu.JPG
|
||||
-- last change: Jan 2025
|
||||
-- last change: Sept 2025
|
||||
|
||||
-- TODO
|
||||
----------------------------------------------------------------------------------------------------------------
|
||||
@@ -417,7 +417,7 @@ end
|
||||
CLIENTMENUMANAGER = {
|
||||
ClassName = "CLIENTMENUMANAGER",
|
||||
lid = "",
|
||||
version = "0.1.6",
|
||||
version = "0.1.7",
|
||||
name = nil,
|
||||
clientset = nil,
|
||||
menutree = {},
|
||||
@@ -806,6 +806,16 @@ function CLIENTMENUMANAGER:ResetMenuComplete()
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove the entry and all entries below the given entry from the client's F10 menus.
|
||||
-- @param #CLIENTMENUMANAGER self
|
||||
-- @param #CLIENTMENU Entry The entry to remove
|
||||
-- @param Wrapper.Client#CLIENT Client (optional) If given, make this change only for this client.
|
||||
-- @return #CLIENTMENUMANAGER self
|
||||
function CLIENTMENUMANAGER:DeleteEntry(Entry,Client)
|
||||
self:T(self.lid.."DeleteEntry")
|
||||
return self:DeleteF10Entry(Entry,Client)
|
||||
end
|
||||
|
||||
--- Remove the entry and all entries below the given entry from the client's F10 menus.
|
||||
-- @param #CLIENTMENUMANAGER self
|
||||
-- @param #CLIENTMENU Entry The entry to remove
|
||||
|
||||
@@ -257,7 +257,7 @@ end
|
||||
|
||||
--- Evaluate conditon functions.
|
||||
-- @param #CONDITION self
|
||||
-- @param #boolean AnyTrue If `true`, evaluation return `true` if *any* condition function returns `true`. By default, *all* condition functions must return true.
|
||||
-- @param #boolean AnyTrue (Optional) If `true`, evaluation return `true` if *any* condition function returns `true`. By default, *all* condition functions must return true.
|
||||
-- @return #boolean Result of condition functions.
|
||||
function CONDITION:Evaluate(AnyTrue)
|
||||
|
||||
@@ -401,7 +401,7 @@ end
|
||||
|
||||
--- Condition to check if time is greater than a given threshold time.
|
||||
-- @param #number Time Time in seconds.
|
||||
-- @param #boolean Absolute If `true`, abs. mission time from `timer.getAbsTime()` is checked. Default is relative mission time from `timer.getTime()`.
|
||||
-- @param #boolean Absolute (Optional) If `true`, abs. mission time from `timer.getAbsTime()` is checked. Default is relative mission time from `timer.getTime()`.
|
||||
-- @return #boolean Returns `true` if time is greater than give the time.
|
||||
function CONDITION.IsTimeGreater(Time, Absolute)
|
||||
|
||||
@@ -424,7 +424,7 @@ end
|
||||
|
||||
--- Function that returns `true` (success) with a certain probability. For example, if you specify `Probability=80` there is an 80% chance that `true` is returned.
|
||||
-- Technically, a random number between 0 and 100 is created. If the given success probability is less then this number, `true` is returned.
|
||||
-- @param #number Probability Success probability in percent. Default 50 %.
|
||||
-- @param #number Probability (Optional) Success probability in percent. Default 50 %.
|
||||
-- @return #boolean Returns `true` for success and `false` otherwise.
|
||||
function CONDITION.IsRandomSuccess(Probability)
|
||||
|
||||
|
||||
@@ -54,7 +54,6 @@
|
||||
-- * AIRBASES
|
||||
-- * PLAYERSJOINED
|
||||
-- * PLAYERS
|
||||
-- * CARGOS
|
||||
-- * STORAGES (DCS warehouses)
|
||||
-- * DYNAMICCARGO
|
||||
--
|
||||
@@ -81,7 +80,6 @@ DATABASE = {
|
||||
PLAYERSJOINED = {},
|
||||
PLAYERUNITS = {},
|
||||
CLIENTS = {},
|
||||
CARGOS = {},
|
||||
AIRBASES = {},
|
||||
COUNTRY_ID = {},
|
||||
COUNTRY_NAME = {},
|
||||
@@ -141,8 +139,6 @@ function DATABASE:New()
|
||||
self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash )
|
||||
self:HandleEvent( EVENTS.UnitLost, self._EventOnDeadOrCrash ) -- DCS 2.7.1 for Aerial units no dead event ATM
|
||||
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.PlayerEnterUnit, self._EventOnPlayerEnterUnit ) -- This is not working anymore!, handling this through the birth event.
|
||||
@@ -577,13 +573,19 @@ do -- Zones and Pathlines
|
||||
-- For a rectangular polygon drawing, we have the width (y) and height (x).
|
||||
local w=objectData.width
|
||||
local h=objectData.height
|
||||
local rotation = UTILS.ToRadian(objectData.angle or 0)
|
||||
|
||||
-- Create points from center using with and height (width for y and height for x is a bit confusing, but this is how ED implemented it).
|
||||
local points={}
|
||||
points[1]={x=vec2.x-h/2, y=vec2.y+w/2} --Upper left
|
||||
points[2]={x=vec2.x+h/2, y=vec2.y+w/2} --Upper right
|
||||
points[3]={x=vec2.x+h/2, y=vec2.y-w/2} --Lower right
|
||||
points[4]={x=vec2.x-h/2, y=vec2.y-w/2} --Lower left
|
||||
local sinRot = math.sin(rotation)
|
||||
local cosRot = math.cos(rotation)
|
||||
local dx = h / 2
|
||||
local dy = w / 2
|
||||
|
||||
local points = {
|
||||
{ x = -dx * cosRot - (-dy * sinRot) + vec2.x, y = -dx * sinRot + (-dy * cosRot) + vec2.y },
|
||||
{ x = dx * cosRot - (-dy * sinRot) + vec2.x, y = dx * sinRot + (-dy * cosRot) + vec2.y },
|
||||
{ x = dx * cosRot - (dy * sinRot) + vec2.x, y = dx * sinRot + (dy * cosRot) + vec2.y },
|
||||
{ x = -dx * cosRot - (dy * sinRot) + vec2.x, y = -dx * sinRot + (dy * cosRot) + vec2.y },
|
||||
}
|
||||
|
||||
--local coord=COORDINATE:NewFromVec2(vec2):MarkToAll("MapX, MapY")
|
||||
|
||||
@@ -740,102 +742,6 @@ do -- OpsZone
|
||||
|
||||
end -- OpsZone
|
||||
|
||||
do -- cargo
|
||||
|
||||
--- Adds a Cargo based on the Cargo Name in the DATABASE.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string CargoName The name of the airbase
|
||||
function DATABASE:AddCargo( Cargo )
|
||||
|
||||
if not self.CARGOS[Cargo.Name] then
|
||||
self.CARGOS[Cargo.Name] = Cargo
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Deletes a Cargo from the DATABASE based on the Cargo Name.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string CargoName The name of the airbase
|
||||
function DATABASE:DeleteCargo( CargoName )
|
||||
|
||||
self.CARGOS[CargoName] = nil
|
||||
end
|
||||
|
||||
--- Finds an CARGO based on the CargoName.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string CargoName
|
||||
-- @return Cargo.Cargo#CARGO The found CARGO.
|
||||
function DATABASE:FindCargo( CargoName )
|
||||
|
||||
local CargoFound = self.CARGOS[CargoName]
|
||||
return CargoFound
|
||||
end
|
||||
|
||||
--- Checks if the Template name has a #CARGO tag.
|
||||
-- If yes, the group is a cargo.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string TemplateName
|
||||
-- @return #boolean
|
||||
function DATABASE:IsCargo( TemplateName )
|
||||
|
||||
TemplateName = env.getValueDictByKey( TemplateName )
|
||||
|
||||
local Cargo = TemplateName:match( "#(CARGO)" )
|
||||
|
||||
return Cargo and Cargo == "CARGO"
|
||||
end
|
||||
|
||||
--- Private method that registers new Static Templates within the DATABASE Object.
|
||||
-- @param #DATABASE self
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:_RegisterCargos()
|
||||
|
||||
local Groups = UTILS.DeepCopy( self.GROUPS ) -- This is a very important statement. CARGO_GROUP:New creates a new _DATABASE.GROUP entry, which will confuse the loop. I searched 4 hours on this to find the bug!
|
||||
|
||||
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 -- cargo
|
||||
|
||||
--- Finds a CLIENT based on the ClientName.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string ClientName - Note this is the UNIT name of the client!
|
||||
@@ -1112,7 +1018,7 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category
|
||||
self:E("WARNING: Invalid STN "..tostring(UnitTemplate.AddPropAircraft.STN_L16).." for ".. UnitTemplate.name)
|
||||
else
|
||||
self.STNS[stn] = UnitTemplate.name
|
||||
self:I("Register STN "..tostring(UnitTemplate.AddPropAircraft.STN_L16).." for ".. UnitTemplate.name)
|
||||
self:T("Register STN "..tostring(UnitTemplate.AddPropAircraft.STN_L16).." for ".. UnitTemplate.name)
|
||||
end
|
||||
end
|
||||
if UnitTemplate.AddPropAircraft.SADL_TN then
|
||||
@@ -1121,7 +1027,7 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category
|
||||
self:E("WARNING: Invalid SADL "..tostring(UnitTemplate.AddPropAircraft.SADL_TN).." for ".. UnitTemplate.name)
|
||||
else
|
||||
self.SADL[sadl] = UnitTemplate.name
|
||||
self:I("Register SADL "..tostring(UnitTemplate.AddPropAircraft.SADL_TN).." for ".. UnitTemplate.name)
|
||||
self:T("Register SADL "..tostring(UnitTemplate.AddPropAircraft.SADL_TN).." for ".. UnitTemplate.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1272,10 +1178,10 @@ end
|
||||
--- Get a generic static cargo group template from scratch for dynamic cargo spawns register. Does not register the template!
|
||||
-- @param #DATABASE self
|
||||
-- @param #string Name Name of the static.
|
||||
-- @param #string Typename Typename of the static. Defaults to "container_cargo".
|
||||
-- @param #number Mass Mass of the static. Defaults to 0.
|
||||
-- @param #number Coalition Coalition of the static. Defaults to coalition.side.BLUE.
|
||||
-- @param #number Country Country of the static. Defaults to country.id.GERMANY.
|
||||
-- @param #string Typename (Optional) Typename of the static. Defaults to "container_cargo".
|
||||
-- @param #number Mass (Optional) Mass of the static. Defaults to 0.
|
||||
-- @param #number Coalition (Optional) Coalition of the static. Defaults to coalition.side.BLUE.
|
||||
-- @param #number Country (Optional) Country of the static. Defaults to country.id.GERMANY.
|
||||
-- @return #table Static template table.
|
||||
function DATABASE:_GetGenericStaticCargoGroupTemplate(Name,Typename,Mass,Coalition,Country)
|
||||
local StaticTemplate = {}
|
||||
@@ -1382,7 +1288,7 @@ function DATABASE:GetCoalitionFromClientTemplate( ClientName )
|
||||
if self.Templates.ClientsByName[ClientName] then
|
||||
return self.Templates.ClientsByName[ClientName].CoalitionID
|
||||
end
|
||||
self:E("WARNING: Template does not exist for client "..tostring(ClientName))
|
||||
self:T("WARNING: Template does not exist for client "..tostring(ClientName))
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -1394,7 +1300,7 @@ function DATABASE:GetCategoryFromClientTemplate( ClientName )
|
||||
if self.Templates.ClientsByName[ClientName] then
|
||||
return self.Templates.ClientsByName[ClientName].CategoryID
|
||||
end
|
||||
self:E("WARNING: Template does not exist for client "..tostring(ClientName))
|
||||
self:T("WARNING: Template does not exist for client "..tostring(ClientName))
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -1406,7 +1312,7 @@ function DATABASE:GetCountryFromClientTemplate( ClientName )
|
||||
if self.Templates.ClientsByName[ClientName] then
|
||||
return self.Templates.ClientsByName[ClientName].CountryID
|
||||
end
|
||||
self:E("WARNING: Template does not exist for client "..tostring(ClientName))
|
||||
self:T("WARNING: Template does not exist for client "..tostring(ClientName))
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -1474,12 +1380,12 @@ function DATABASE:_RegisterDynamicGroup(Groupname)
|
||||
local DCSUnitName = DCSUnit:getName()
|
||||
|
||||
-- Add unit.
|
||||
self:I(string.format("Register Unit: %s", tostring(DCSUnitName)))
|
||||
self:T(string.format("Register Unit: %s", tostring(DCSUnitName)))
|
||||
self:AddUnit( tostring(DCSUnitName), true )
|
||||
|
||||
end
|
||||
else
|
||||
self:E({"Group does not exist: ", DCSGroup})
|
||||
self:T({"Group does not exist: ", DCSGroup})
|
||||
end
|
||||
return self
|
||||
end
|
||||
@@ -1582,27 +1488,28 @@ end
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:_RegisterAirbase(airbase)
|
||||
|
||||
local IsSyria = UTILS.GetDCSMap() == "Syria" and true or false
|
||||
local countHSyria = 0
|
||||
--local IsSyria = UTILS.GetDCSMap() == "Syria" and true or false
|
||||
--local countHSyria = 0
|
||||
|
||||
if airbase then
|
||||
|
||||
-- Get the airbase name.
|
||||
local DCSAirbaseName = airbase:getName()
|
||||
|
||||
-- DCS 2.9.8.1107 added 143 helipads all named H with the same object ID ..
|
||||
--[[ DCS 2.9.8.1107 added 143 helipads all named H with the same object ID ..
|
||||
if IsSyria and DCSAirbaseName == "H" and countHSyria > 0 then
|
||||
--[[
|
||||
--
|
||||
local p = airbase:getPosition().p
|
||||
local mgrs = COORDINATE:New(p.x,p.z,p.y):ToStringMGRS()
|
||||
self:I("Airbase on Syria map named H @ "..mgrs)
|
||||
countHSyria = countHSyria + 1
|
||||
if countHSyria > 1 then return self end
|
||||
--]]
|
||||
--
|
||||
return self
|
||||
elseif IsSyria and DCSAirbaseName == "H" and countHSyria == 0 then
|
||||
countHSyria = countHSyria + 1
|
||||
end
|
||||
--]]
|
||||
|
||||
-- This gave the incorrect value to be inserted into the airdromeID for DCS 2.5.6. Is fixed now.
|
||||
local airbaseID=airbase:getID()
|
||||
@@ -1699,7 +1606,7 @@ function DATABASE:_EventOnBirth( Event )
|
||||
if PlayerName then
|
||||
|
||||
-- Debug info.
|
||||
self:I(string.format("Player '%s' joined unit '%s' of group '%s'", tostring(PlayerName), tostring(Event.IniDCSUnitName), tostring(Event.IniDCSGroupName)))
|
||||
self:I(string.format("Player '%s' joined unit '%s' (%s) of group '%s'", tostring(PlayerName), tostring(Event.IniDCSUnitName), tostring(Event.IniTypeName), tostring(Event.IniDCSGroupName)))
|
||||
|
||||
-- Add client in case it does not exist already.
|
||||
if client == nil or (client and client:CountPlayers() == 0) then
|
||||
@@ -1814,7 +1721,7 @@ function DATABASE:_EventOnPlayerEnterUnit( Event )
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
-- Player entering a CA slot
|
||||
if Event.IniObjectCategory == 1 and Event.IniGroup and Event.IniGroup:IsGround() then
|
||||
if Event.IniObjectCategory == 1 and Event.IniUnit and Event.IniUnit:IsGround() then
|
||||
|
||||
local IsPlayer = Event.IniDCSUnit:getPlayerName()
|
||||
if IsPlayer then
|
||||
@@ -2044,19 +1951,6 @@ function DATABASE:ForEachClient( IteratorFunction, FinalizeFunction, ... )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Iterate the DATABASE and call an iterator function for each CARGO, providing the CARGO object to the function and optional parameters.
|
||||
-- @param #DATABASE self
|
||||
-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a CLIENT parameter.
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:ForEachCargo( IteratorFunction, FinalizeFunction, ... )
|
||||
self:F2( arg )
|
||||
|
||||
self:ForEach( IteratorFunction, FinalizeFunction, arg, self.CARGOS )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Handles the OnEventNewCargo event.
|
||||
-- @param #DATABASE self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
|
||||
@@ -184,21 +184,20 @@ EVENT = {
|
||||
ClassName = "EVENT",
|
||||
ClassID = 0,
|
||||
MissionEnd = false,
|
||||
CreateMarkCoordinateOnEvent = 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
|
||||
world.event.S_EVENT_NEW_ZONE = world.event.S_EVENT_MAX + 1000
|
||||
world.event.S_EVENT_DELETE_ZONE = world.event.S_EVENT_MAX + 1001
|
||||
world.event.S_EVENT_NEW_ZONE_GOAL = world.event.S_EVENT_MAX + 1002
|
||||
world.event.S_EVENT_DELETE_ZONE_GOAL = world.event.S_EVENT_MAX + 1003
|
||||
world.event.S_EVENT_REMOVE_UNIT = world.event.S_EVENT_MAX + 1004
|
||||
world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT = world.event.S_EVENT_MAX + 1005
|
||||
-- dynamic cargo
|
||||
world.event.S_EVENT_NEW_DYNAMIC_CARGO = world.event.S_EVENT_MAX + 1008
|
||||
world.event.S_EVENT_DYNAMIC_CARGO_LOADED = world.event.S_EVENT_MAX + 1009
|
||||
world.event.S_EVENT_DYNAMIC_CARGO_UNLOADED = world.event.S_EVENT_MAX + 1010
|
||||
world.event.S_EVENT_DYNAMIC_CARGO_REMOVED = world.event.S_EVENT_MAX + 1011
|
||||
world.event.S_EVENT_NEW_DYNAMIC_CARGO = world.event.S_EVENT_MAX + 1006
|
||||
world.event.S_EVENT_DYNAMIC_CARGO_LOADED = world.event.S_EVENT_MAX + 1007
|
||||
world.event.S_EVENT_DYNAMIC_CARGO_UNLOADED = world.event.S_EVENT_MAX + 1008
|
||||
world.event.S_EVENT_DYNAMIC_CARGO_REMOVED = world.event.S_EVENT_MAX + 1009
|
||||
|
||||
|
||||
--- The different types of events supported by MOOSE.
|
||||
@@ -233,8 +232,6 @@ EVENTS = {
|
||||
MarkChange = world.event.S_EVENT_MARK_CHANGE,
|
||||
MarkRemoved = world.event.S_EVENT_MARK_REMOVED,
|
||||
-- Moose Events
|
||||
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,
|
||||
@@ -339,14 +336,18 @@ EVENTS = {
|
||||
-- @field #string WeaponName Name of the weapon.
|
||||
-- @field DCS#Unit WeaponTgtDCSUnit Target DCS unit of the weapon.
|
||||
--
|
||||
-- @field Cargo.Cargo#CARGO Cargo The cargo object.
|
||||
-- @field #string CargoName The name of the cargo object.
|
||||
--
|
||||
-- @field Core.Zone#ZONE Zone The zone object.
|
||||
-- @field #string ZoneName The name of the zone.
|
||||
--
|
||||
-- @field Wrapper.DynamicCargo#DYNAMICCARGO IniDynamicCargo The dynamic cargo object.
|
||||
-- @field #string IniDynamicCargoName The dynamic cargo unit name.
|
||||
--
|
||||
-- @field #number MarkCoalition Coalition of a marker (if any)
|
||||
-- @field DCS#Vec3 MarkVec3 Position of a marker
|
||||
-- @field #string MarkText Text content of a marker, if any
|
||||
-- @field #number MarkID ID of the marker, for deletion
|
||||
-- @field #number MarkGroupID Group ID of the group that created the marker
|
||||
-- @field Core.Point#COORDINATE Coordinate object of the marker, only filled if EVENT.CreateMarkCoordinateOnEvent is set to true (off by default)
|
||||
|
||||
|
||||
|
||||
@@ -507,16 +508,6 @@ local _EVENTMETA = {
|
||||
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",
|
||||
@@ -1063,36 +1054,6 @@ end
|
||||
|
||||
do -- Event Creation
|
||||
|
||||
--- Creation of a New Cargo Event.
|
||||
-- @param #EVENT self
|
||||
-- @param AI.AI_Cargo#AI_CARGO Cargo The Cargo created.
|
||||
function EVENT:CreateEventNewCargo( Cargo )
|
||||
self:F( { Cargo } )
|
||||
|
||||
local Event = {
|
||||
id = EVENTS.NewCargo,
|
||||
time = timer.getTime(),
|
||||
cargo = Cargo,
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
--- Creation of a Cargo Deletion Event.
|
||||
-- @param #EVENT self
|
||||
-- @param AI.AI_Cargo#AI_CARGO Cargo The Cargo created.
|
||||
function EVENT:CreateEventDeleteCargo( Cargo )
|
||||
self:F( { Cargo } )
|
||||
|
||||
local Event = {
|
||||
id = EVENTS.DeleteCargo,
|
||||
time = timer.getTime(),
|
||||
cargo = Cargo,
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
--- Creation of a New Zone Event.
|
||||
-- @param #EVENT self
|
||||
-- @param Core.Zone#ZONE_BASE Zone The Zone created.
|
||||
@@ -1361,7 +1322,7 @@ function EVENT:onEvent( Event )
|
||||
Event.IniDynamicCargoName = Event.IniUnitName
|
||||
Event.IniPlayerName = string.match(Event.IniUnitName,"^(.+)|%d%d:%d%d|PKG%d+")
|
||||
else
|
||||
Event.IniUnit = CARGO:FindByName( Event.IniDCSUnitName )
|
||||
Event.IniUnit = STATIC:FindByName( Event.IniDCSUnitName, false )
|
||||
end
|
||||
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||
@@ -1374,7 +1335,9 @@ function EVENT:onEvent( Event )
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
Event.IniDCSUnitName = ( Event.IniDCSUnit and Event.IniDCSUnit.getName ) and Event.IniDCSUnit:getName() or "Scenery no name "..math.random(1,20000)
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniUnit = SCENERY:Register( Event.IniDCSUnitName, Event.initiator )
|
||||
local ID = (Event.IniDCSUnit and Event.IniDCSUnit.getID) and Event.IniDCSUnit:getID() or Event.IniDCSUnitName
|
||||
Event.IniUnit = (_SCENERY ~= nil) and _SCENERY[ID] or nil
|
||||
--Event.IniUnit = SCENERY:Register( Event.IniDCSUnitName, Event.initiator )
|
||||
Event.IniCategory = (Event.IniDCSUnit and Event.IniDCSUnit.getDesc ) and Event.IniDCSUnit:getDesc().category
|
||||
Event.IniTypeName = (Event.initiator and Event.initiator.isExist
|
||||
and Event.initiator:isExist() and Event.IniDCSUnit and Event.IniDCSUnit.getTypeName) and Event.IniDCSUnit:getTypeName() or "SCENERY"
|
||||
@@ -1475,10 +1438,12 @@ function EVENT:onEvent( Event )
|
||||
-- SCENERY
|
||||
---
|
||||
Event.TgtDCSUnit = Event.target
|
||||
Event.TgtDCSUnitName = Event.TgtDCSUnit.getName and Event.TgtDCSUnit.getName() or nil
|
||||
Event.TgtDCSUnitName = Event.TgtDCSUnit.getName and Event.TgtDCSUnit:getName() or nil
|
||||
if Event.TgtDCSUnitName~=nil then
|
||||
Event.TgtUnitName = Event.TgtDCSUnitName
|
||||
Event.TgtUnit = SCENERY:Register( Event.TgtDCSUnitName, Event.target )
|
||||
local ID = (Event.TgtDCSUnit and Event.TgtDCSUnit.getID) and Event.TgtDCSUnit:getID() or Event.TgtDCSUnitName
|
||||
--Event.TgtUnit = SCENERY:Register( Event.TgtDCSUnitName, Event.target )
|
||||
Event.TgtUnit = (_SCENERY ~= nil) and _SCENERY[ID] or nil
|
||||
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
|
||||
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
|
||||
end
|
||||
@@ -1488,7 +1453,12 @@ function EVENT:onEvent( Event )
|
||||
-- Weapon.
|
||||
if Event.weapon and type(Event.weapon) == "table" and Event.weapon.isExist and Event.weapon:isExist() then
|
||||
Event.Weapon = Event.weapon
|
||||
Event.WeaponName = Event.weapon:isExist() and Event.weapon:getTypeName() or "Unknown Weapon"
|
||||
if Event.weapon_name == "ZELL Booster" then
|
||||
Event.WeaponName = "ZELL Booster"
|
||||
else
|
||||
Event.WeaponName = Event.weapon:isExist() and Event.weapon.getTypeName and Event.weapon:getTypeName() or "Unknown Weapon"
|
||||
end
|
||||
if Event.weapon_name == "ZELL Booster" then Event.WeaponName = "ZELL Booster" end
|
||||
Event.WeaponUNIT = CLIENT:Find( Event.Weapon, '', true ) -- Sometimes, the weapon is a player unit!
|
||||
Event.WeaponPlayerName = Event.WeaponUNIT and Event.Weapon.getPlayerName and Event.Weapon:getPlayerName()
|
||||
--Event.WeaponPlayerName = Event.WeaponUNIT and Event.Weapon:getPlayerName()
|
||||
@@ -1508,7 +1478,9 @@ function EVENT:onEvent( Event )
|
||||
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()
|
||||
if Event.Place then
|
||||
Event.PlaceName=Event.Place:GetName()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1517,19 +1489,15 @@ function EVENT:onEvent( Event )
|
||||
if Event.idx then
|
||||
Event.MarkID=Event.idx
|
||||
Event.MarkVec3=Event.pos
|
||||
Event.MarkCoordinate=COORDINATE:NewFromVec3(Event.pos)
|
||||
if self.CreateMarkCoordinateOnEvent == true then
|
||||
Event.MarkCoordinate=COORDINATE:NewFromVec3(Event.pos)
|
||||
end
|
||||
Event.MarkText=Event.text
|
||||
Event.MarkCoalition=Event.coalition
|
||||
Event.IniCoalition=Event.coalition
|
||||
Event.MarkGroupID = Event.groupID
|
||||
end
|
||||
|
||||
-- Cargo object.
|
||||
if Event.cargo then
|
||||
Event.Cargo = Event.cargo
|
||||
Event.CargoName = Event.cargo.Name
|
||||
end
|
||||
|
||||
-- Dynamic cargo Object
|
||||
if Event.dynamiccargo then
|
||||
Event.IniDynamicCargo = Event.dynamiccargo
|
||||
@@ -1691,15 +1659,6 @@ function EVENT:onEvent( Event )
|
||||
end
|
||||
end
|
||||
|
||||
-- When cargo was deleted, it may probably be because of an S_EVENT_DEAD.
|
||||
-- However, in the loading logic, an S_EVENT_DEAD is also generated after a Destroy() call.
|
||||
-- And this is a problem because it will remove all entries from the SET_CARGOs.
|
||||
-- To prevent this from happening, the Cargo object has a flag NoDestroy.
|
||||
-- When true, the SET_CARGO won't Remove the Cargo object from the set.
|
||||
-- But we need to switch that flag off after the event handlers have been called.
|
||||
if Event.id == EVENTS.DeleteCargo then
|
||||
Event.Cargo.NoDestroy = nil
|
||||
end
|
||||
else
|
||||
self:T( { EventMeta.Text, Event } )
|
||||
end
|
||||
|
||||
@@ -45,12 +45,6 @@
|
||||
-- By efficiently utilizing the FSM class and derived classes, MOOSE allows mission designers to quickly build processes.
|
||||
-- **Ready made FSM-based implementations classes** exist within the MOOSE framework that **can easily be re-used,
|
||||
-- and tailored** by mission designers through **the implementation of Transition Handlers**.
|
||||
-- Each of these FSM implementation classes start either with:
|
||||
--
|
||||
-- * an acronym **AI\_**, which indicates a FSM implementation directing **AI controlled** @{Wrapper.Group#GROUP} and/or @{Wrapper.Unit#UNIT}. These AI\_ classes derive the @{#FSM_CONTROLLABLE} class.
|
||||
-- * an acronym **TASK\_**, which indicates a FSM implementation executing a @{Tasking.Task#TASK} executed by Groups of players. These TASK\_ classes derive the @{#FSM_TASK} class.
|
||||
-- * an acronym **ACT\_**, which indicates an Sub-FSM implementation, directing **Humans actions** that need to be done in a @{Tasking.Task#TASK}, seated in a @{Wrapper.Client#CLIENT} (slot) or a @{Wrapper.Unit#UNIT} (CA join). These ACT\_ classes derive the @{#FSM_PROCESS} class.
|
||||
--
|
||||
-- Detailed explanations and API specifics are further below clarified and FSM derived class specifics are described in those class documentation sections.
|
||||
--
|
||||
-- ##__Disclaimer:__
|
||||
@@ -61,7 +55,6 @@
|
||||
--
|
||||
-- The following derived classes are available in the MOOSE framework, that implement a specialized form of a FSM:
|
||||
--
|
||||
-- * @{#FSM_TASK}: Models Finite State Machines for @{Tasking.Task}s.
|
||||
-- * @{#FSM_PROCESS}: Models Finite State Machines for @{Tasking.Task} actions, which control @{Wrapper.Client}s.
|
||||
-- * @{#FSM_CONTROLLABLE}: Models Finite State Machines for @{Wrapper.Controllable}s, which are @{Wrapper.Group}s, @{Wrapper.Unit}s, @{Wrapper.Client}s.
|
||||
-- * @{#FSM_SET}: Models Finite State Machines for @{Core.Set}s. Note that these FSMs control multiple objects!!! So State concerns here
|
||||
@@ -79,6 +72,7 @@
|
||||
|
||||
do -- FSM
|
||||
|
||||
--- FSM class
|
||||
-- @type FSM
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field Core.Scheduler#SCHEDULER CallScheduler Call scheduler.
|
||||
@@ -117,11 +111,6 @@ do -- FSM
|
||||
-- By efficiently utilizing the FSM class and derived classes, MOOSE allows mission designers to quickly build processes.
|
||||
-- **Ready made FSM-based implementations classes** exist within the MOOSE framework that **can easily be re-used,
|
||||
-- and tailored** by mission designers through **the implementation of Transition Handlers**.
|
||||
-- Each of these FSM implementation classes start either with:
|
||||
--
|
||||
-- * an acronym **AI\_**, which indicates an FSM implementation directing **AI controlled** @{Wrapper.Group#GROUP} and/or @{Wrapper.Unit#UNIT}. These AI\_ classes derive the @{#FSM_CONTROLLABLE} class.
|
||||
-- * an acronym **TASK\_**, which indicates an FSM implementation executing a @{Tasking.Task#TASK} executed by Groups of players. These TASK\_ classes derive the @{#FSM_TASK} class.
|
||||
-- * an acronym **ACT\_**, which indicates an Sub-FSM implementation, directing **Humans actions** that need to be done in a @{Tasking.Task#TASK}, seated in a @{Wrapper.Client#CLIENT} (slot) or a @{Wrapper.Unit#UNIT} (CA join). These ACT\_ classes derive the @{#FSM_PROCESS} class.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
@@ -832,6 +821,16 @@ do -- FSM
|
||||
return self._handler( self, EventName, ... )
|
||||
end
|
||||
end
|
||||
|
||||
--- Clear scheduled FSM event.
|
||||
-- @param #FSM self
|
||||
-- @param #string EventName Event name.
|
||||
function FSM:_ClearFSMEvent( EventName )
|
||||
if self._EventSchedules[EventName] then
|
||||
self.CallScheduler:Remove( self._EventSchedules[EventName] )
|
||||
self._EventSchedules[EventName]=nil
|
||||
end
|
||||
end
|
||||
|
||||
--- Go sub.
|
||||
-- @param #FSM self
|
||||
|
||||
@@ -50,7 +50,7 @@ MARKEROPS_BASE = {
|
||||
ClassName = "MARKEROPS",
|
||||
Tag = "mytag",
|
||||
Keywords = {},
|
||||
version = "0.1.3",
|
||||
version = "0.1.5",
|
||||
debug = false,
|
||||
Casesensitive = true,
|
||||
}
|
||||
@@ -59,9 +59,8 @@ MARKEROPS_BASE = {
|
||||
-- @param #MARKEROPS_BASE self
|
||||
-- @param #string Tagname Name to identify us from the event text.
|
||||
-- @param #table Keywords Table of keywords recognized from the event text.
|
||||
-- @param #boolean Casesensitive (Optional) Switch case sensitive identification of Tagname. Defaults to true.
|
||||
-- @return #MARKEROPS_BASE self
|
||||
function MARKEROPS_BASE:New(Tagname,Keywords,Casesensitive)
|
||||
function MARKEROPS_BASE:New(Tagname,Keywords)
|
||||
-- Inherit FSM
|
||||
local self=BASE:Inherit(self, FSM:New()) -- #MARKEROPS_BASE
|
||||
|
||||
@@ -72,11 +71,7 @@ function MARKEROPS_BASE:New(Tagname,Keywords,Casesensitive)
|
||||
self.Keywords = Keywords or {} -- #table - might want to use lua regex here, too
|
||||
self.debug = false
|
||||
self.Casesensitive = true
|
||||
|
||||
if Casesensitive and Casesensitive == false then
|
||||
self.Casesensitive = false
|
||||
end
|
||||
|
||||
|
||||
-----------------------
|
||||
--- FSM Transitions ---
|
||||
-----------------------
|
||||
@@ -145,7 +140,7 @@ function MARKEROPS_BASE:New(Tagname,Keywords,Casesensitive)
|
||||
|
||||
end
|
||||
|
||||
--- (internal) Handle events.
|
||||
--- (Internal) Handle events.
|
||||
-- @param #MARKEROPS_BASE self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
function MARKEROPS_BASE:OnEventMark(Event)
|
||||
@@ -154,14 +149,7 @@ function MARKEROPS_BASE:OnEventMark(Event)
|
||||
self:E("Skipping onEvent. Event or Event.idx unknown.")
|
||||
return true
|
||||
end
|
||||
--position
|
||||
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
|
||||
|
||||
local coalition = Event.MarkCoalition
|
||||
-- decision
|
||||
if Event.id==world.event.S_EVENT_MARK_ADDED then
|
||||
@@ -170,8 +158,14 @@ function MARKEROPS_BASE:OnEventMark(Event)
|
||||
local Eventtext = tostring(Event.text)
|
||||
if Eventtext~=nil then
|
||||
if self:_MatchTag(Eventtext) then
|
||||
local matchtable = self:_MatchKeywords(Eventtext)
|
||||
self:MarkAdded(Eventtext,matchtable,coord,Event.idx,coalition,Event.PlayerName,Event)
|
||||
local coord=COORDINATE:NewFromVec3({y=Event.pos.y, x=Event.pos.x, z=Event.pos.z})
|
||||
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
|
||||
local matchtable = self:_MatchKeywords(Eventtext)
|
||||
self:MarkAdded(Eventtext,matchtable,coord,Event.idx,coalition,Event.PlayerName,Event)
|
||||
end
|
||||
end
|
||||
elseif Event.id==world.event.S_EVENT_MARK_CHANGE then
|
||||
@@ -180,8 +174,14 @@ function MARKEROPS_BASE:OnEventMark(Event)
|
||||
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,coalition,Event.PlayerName,Event)
|
||||
local coord=COORDINATE:NewFromVec3({y=Event.pos.y, x=Event.pos.x, z=Event.pos.z})
|
||||
if self.debug then
|
||||
local coordtext = coord:ToStringLLDDM()
|
||||
local text = tostring(Event.text)
|
||||
local m = MESSAGE:New(string.format("Mark changed at %s with text: %s",coordtext,text),10,"Info",false):ToAll()
|
||||
end
|
||||
local matchtable = self:_MatchKeywords(Eventtext)
|
||||
self:MarkChanged(Eventtext,matchtable,coord,Event.idx,coalition,Event.PlayerName,Event)
|
||||
end
|
||||
end
|
||||
elseif Event.id==world.event.S_EVENT_MARK_REMOVED then
|
||||
@@ -196,15 +196,17 @@ function MARKEROPS_BASE:OnEventMark(Event)
|
||||
end
|
||||
end
|
||||
|
||||
--- (internal) Match tag.
|
||||
--- (Internal) Match tag.
|
||||
-- @param #MARKEROPS_BASE self
|
||||
-- @param #string Eventtext Text added to the marker.
|
||||
-- @return #boolean
|
||||
function MARKEROPS_BASE:_MatchTag(Eventtext)
|
||||
local matches = false
|
||||
if not self.Casesensitive then
|
||||
--self:I(self.lid .. "Casesensitive "..tostring(self.Casesensitive))
|
||||
if self.Casesensitive == false then
|
||||
--self:I(self.lid .. "Marker non-casesensitive "..Eventtext)
|
||||
local type = string.lower(self.Tag) -- #string
|
||||
if string.find(string.lower(Eventtext),type) then
|
||||
if string.find(string.lower(Eventtext),type,1,true) then
|
||||
matches = true --event text contains tag
|
||||
end
|
||||
else
|
||||
@@ -216,7 +218,7 @@ function MARKEROPS_BASE:_MatchTag(Eventtext)
|
||||
return matches
|
||||
end
|
||||
|
||||
--- (internal) Match keywords table.
|
||||
--- (Internal) Match keywords table.
|
||||
-- @param #MARKEROPS_BASE self
|
||||
-- @param #string Eventtext Text added to the marker.
|
||||
-- @return #table
|
||||
@@ -281,6 +283,22 @@ function MARKEROPS_BASE:onenterStopped(From,Event,To)
|
||||
self:UnHandleEvent(EVENTS.MarkRemoved)
|
||||
end
|
||||
|
||||
--- Switch off case sensitive matching
|
||||
-- @param #MARKEROPS_BASE self
|
||||
-- @return self
|
||||
function MARKEROPS_BASE:SwitchCaseSensitiveOff()
|
||||
self.Casesensitive = false
|
||||
return self
|
||||
end
|
||||
|
||||
--- Switch on case sensitive matching
|
||||
-- @param #MARKEROPS_BASE self
|
||||
-- @return self
|
||||
function MARKEROPS_BASE:SwitchCaseSensitiveOn()
|
||||
self.Casesensitive = true
|
||||
return self
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
-- MARKEROPS_BASE Class Definition End.
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
@@ -869,10 +869,21 @@ do
|
||||
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}
|
||||
MenuTable[#MenuTable+1] = {Tag=tag, Entry=Menu}
|
||||
end
|
||||
table.sort(MenuTable, function (k1, k2) return k1.tag < k2.tag end )
|
||||
for _, Menu in pairs( MenuTable ) do
|
||||
local function SortTable(k1,k2)
|
||||
if not k1 then
|
||||
if not k2 then return true else return false end
|
||||
elseif not k2 then
|
||||
if not k1 then return true else return false end
|
||||
else
|
||||
return (k1.Tag or 15) <= (k2.Tag or 15)
|
||||
end
|
||||
return false
|
||||
end
|
||||
table.sort(MenuTable, SortTable)
|
||||
--table.sort(MenuTable, function (k1, k2) return (k1.tag or 15) <= (k2.tag or 15) end )
|
||||
for _, Menu in ipairs( MenuTable ) do
|
||||
Menu.Entry:Refresh()
|
||||
end
|
||||
end
|
||||
|
||||
@@ -198,10 +198,41 @@ function MESSAGE:ToClient( Client, Settings )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Sends a MESSAGE to a SET_GROUP, SET_UNIT, or SET_CLIENT.
|
||||
-- @param #MESSAGE self
|
||||
-- @param Core.Set#SET_GROUP Set The set to send to.
|
||||
-- @param Core.Settings#SETTINGS Settings (Optional) Settings for message display.
|
||||
-- @return self
|
||||
function MESSAGE:ToSet(Set, Settings)
|
||||
for _,_obj in pairs (Set:GetSetObjects() or {}) do
|
||||
if _obj and _obj:IsAlive() then
|
||||
if _obj:IsInstanceOf("SET_GROUP") then
|
||||
self:ToGroup(_obj, Settings)
|
||||
elseif _obj:IsInstanceOf("SET_CLIENT") or _obj:IsInstanceOf("SET_UNIT") then
|
||||
self:ToUnit(_obj, Settings)
|
||||
end
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Sends a MESSAGE to a SET_GROUP, SET_UNIT, or SET_CLIENT if a condition is true.
|
||||
-- @param #MESSAGE self
|
||||
-- @param Core.Set#SET_GROUP Set The set to send to.
|
||||
-- @param #boolean Condition The condition which needs to be true.
|
||||
-- @param Core.Settings#SETTINGS Settings (Optional) Settings for message display.
|
||||
-- @return self
|
||||
function MESSAGE:ToSetIf(Set, Condition, Settings)
|
||||
if Set and Condition == true then
|
||||
self:ToSet(Set, Settings)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Sends a MESSAGE to a Group.
|
||||
-- @param #MESSAGE self
|
||||
-- @param Wrapper.Group#GROUP Group to which the message is displayed.
|
||||
-- @param Core.Settings#Settings Settings (Optional) Settings for message display.
|
||||
-- @param Core.Settings#SETTINGS Settings (Optional) Settings for message display.
|
||||
-- @return #MESSAGE Message object.
|
||||
function MESSAGE:ToGroup( Group, Settings )
|
||||
self:F( Group.GroupName )
|
||||
@@ -226,7 +257,7 @@ end
|
||||
--- Sends a MESSAGE to a Unit.
|
||||
-- @param #MESSAGE self
|
||||
-- @param Wrapper.Unit#UNIT Unit to which the message is displayed.
|
||||
-- @param Core.Settings#Settings Settings (Optional) Settings for message display.
|
||||
-- @param Core.Settings#SETTINGS Settings (Optional) Settings for message display.
|
||||
-- @return #MESSAGE Message object.
|
||||
function MESSAGE:ToUnit( Unit, Settings )
|
||||
self:F( Unit.IdentifiableName )
|
||||
@@ -252,7 +283,7 @@ end
|
||||
--- Sends a MESSAGE to a Country.
|
||||
-- @param #MESSAGE self
|
||||
-- @param #number Country to which the message is displayed, e.g. country.id.GERMANY. For all country numbers see here: [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_enum_country)
|
||||
-- @param Core.Settings#Settings Settings (Optional) Settings for message display.
|
||||
-- @param Core.Settings#SETTINGS Settings (Optional) Settings for message display.
|
||||
-- @return #MESSAGE Message object.
|
||||
function MESSAGE:ToCountry( Country, Settings )
|
||||
self:F(Country )
|
||||
@@ -274,7 +305,7 @@ end
|
||||
-- @param #MESSAGE self
|
||||
-- @param #number Country to which the message is displayed, , e.g. country.id.GERMANY. For all country numbers see here: [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_enum_country)
|
||||
-- @param #boolean Condition Sends the message only if the condition is true.
|
||||
-- @param Core.Settings#Settings Settings (Optional) Settings for message display.
|
||||
-- @param Core.Settings#SETTINGS Settings (Optional) Settings for message display.
|
||||
-- @return #MESSAGE Message object.
|
||||
function MESSAGE:ToCountryIf( Country, Condition, Settings )
|
||||
self:F(Country )
|
||||
@@ -379,7 +410,7 @@ end
|
||||
|
||||
--- Sends a MESSAGE to all players.
|
||||
-- @param #MESSAGE self
|
||||
-- @param Core.Settings#Settings Settings (Optional) Settings for message display.
|
||||
-- @param Core.Settings#SETTINGS Settings (Optional) Settings for message display.
|
||||
-- @param #number Delay (Optional) Delay in seconds before the message is send. Default instantly (`nil`).
|
||||
-- @return #MESSAGE self
|
||||
-- @usage
|
||||
@@ -452,7 +483,7 @@ end
|
||||
_MESSAGESRS = {}
|
||||
|
||||
--- Set up MESSAGE generally to allow Text-To-Speech via SRS and TTS functions. `SetMSRS()` will try to use as many attributes configured with @{Sound.SRS#MSRS.LoadConfigFile}() as possible.
|
||||
-- @param #string PathToSRS (optional) Path to SRS Folder, defaults to "C:\\\\Program Files\\\\DCS-SimpleRadio-Standalone" or your configuration file setting.
|
||||
-- @param #string PathToSRS (optional) Path to SRS TTS Folder, defaults to "C:\\\\Program Files\\\\DCS-SimpleRadio-Standalone\\ExternalAudio" or your configuration file setting.
|
||||
-- @param #number Port Port (optional) number of SRS, defaults to 5002 or your configuration file setting.
|
||||
-- @param #string PathToCredentials (optional) Path to credentials file for Google.
|
||||
-- @param #number Frequency Frequency in MHz. Can also be given as a #table of frequencies.
|
||||
@@ -464,17 +495,19 @@ _MESSAGESRS = {}
|
||||
-- @param #number Volume (optional) Volume, can be between 0.0 and 1.0 (loudest).
|
||||
-- @param #string Label (optional) Label, defaults to "MESSAGE" or the Message Category set.
|
||||
-- @param Core.Point#COORDINATE Coordinate (optional) Coordinate this messages originates from.
|
||||
-- @param #string Backend (optional) Backend to be used, can be MSRS.Backend.SRSEXE or MSRS.Backend.GRPC
|
||||
-- @param #string Backend (optional) Backend to be used, can be MSRS.Backend.SRSEXE or MSRS.Backend.GRPC or MSRS.Backend.HOUND etc
|
||||
-- @param #string Provider (optional) Provider to be used, can be MSRS.Provider.WINDOWS or MSRS.Provider.GOOGLE or MSRS.Provider.PIPER etc
|
||||
-- @param #string Speaker (optional) Speaker to be used. Only for select provider PIPER TTS Voices, requires HOUND backend.
|
||||
-- @usage
|
||||
-- -- Mind the dot here, not using the colon this time around!
|
||||
-- -- Needed once only
|
||||
-- MESSAGE.SetMSRS("D:\\Program Files\\DCS-SimpleRadio-Standalone",5012,nil,127,radio.modulation.FM,"female","en-US",nil,coalition.side.BLUE)
|
||||
-- MESSAGE.SetMSRS("D:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio",5012,nil,127,radio.modulation.FM,"female","en-US",nil,coalition.side.BLUE)
|
||||
-- -- later on in your code
|
||||
-- MESSAGE:New("Test message!",15,"SPAWN"):ToSRS()
|
||||
--
|
||||
function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,Gender,Culture,Voice,Coalition,Volume,Label,Coordinate,Backend)
|
||||
function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,Gender,Culture,Voice,Coalition,Volume,Label,Coordinate,Backend,Provider,Speaker)
|
||||
|
||||
_MESSAGESRS.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone"
|
||||
_MESSAGESRS.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio"
|
||||
|
||||
_MESSAGESRS.frequency = Frequency or MSRS.frequencies or 243
|
||||
_MESSAGESRS.modulation = Modulation or MSRS.modulations or radio.modulation.AM
|
||||
@@ -505,6 +538,10 @@ function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,G
|
||||
_MESSAGESRS.MSRS:SetProvider(MSRS.Provider.GOOGLE)
|
||||
end
|
||||
|
||||
if Provider then
|
||||
_MESSAGESRS.MSRS:SetProvider(Provider)
|
||||
end
|
||||
|
||||
_MESSAGESRS.label = Label or MSRS.Label or "MESSAGE"
|
||||
_MESSAGESRS.MSRS:SetLabel(_MESSAGESRS.label)
|
||||
|
||||
@@ -515,6 +552,7 @@ function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,G
|
||||
_MESSAGESRS.MSRS:SetVolume(_MESSAGESRS.volume)
|
||||
|
||||
if Voice then _MESSAGESRS.MSRS:SetVoice(Voice) end
|
||||
if Speaker then _MESSAGESRS.MSRS:SetSpeakerPiper(Speaker) end
|
||||
|
||||
_MESSAGESRS.voice = Voice or MSRS.voice --or MSRS.Voices.Microsoft.Hedda
|
||||
|
||||
@@ -535,7 +573,7 @@ end
|
||||
-- @usage
|
||||
-- -- Mind the dot here, not using the colon this time around!
|
||||
-- -- Needed once only
|
||||
-- MESSAGE.SetMSRS("D:\\Program Files\\DCS-SimpleRadio-Standalone",5012,nil,127,radio.modulation.FM,"female","en-US",nil,coalition.side.BLUE)
|
||||
-- MESSAGE.SetMSRS("D:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio",5012,nil,127,radio.modulation.FM,"female","en-US",nil,coalition.side.BLUE)
|
||||
-- -- later on in your code
|
||||
-- MESSAGE:New("Test message!",15,"SPAWN"):ToSRS()
|
||||
--
|
||||
@@ -567,7 +605,7 @@ end
|
||||
-- @usage
|
||||
-- -- Mind the dot here, not using the colon this time around!
|
||||
-- -- Needed once only
|
||||
-- MESSAGE.SetMSRS("D:\\Program Files\\DCS-SimpleRadio-Standalone",5012,nil,127,radio.modulation.FM,"female","en-US",nil,coalition.side.BLUE)
|
||||
-- MESSAGE.SetMSRS("D:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio",5012,nil,127,radio.modulation.FM,"female","en-US",nil,coalition.side.BLUE)
|
||||
-- -- later on in your code
|
||||
-- MESSAGE:New("Test message!",15,"SPAWN"):ToSRSBlue()
|
||||
--
|
||||
@@ -589,7 +627,7 @@ end
|
||||
-- @usage
|
||||
-- -- Mind the dot here, not using the colon this time around!
|
||||
-- -- Needed once only
|
||||
-- MESSAGE.SetMSRS("D:\\Program Files\\DCS-SimpleRadio-Standalone",5012,nil,127,radio.modulation.FM,"female","en-US",nil,coalition.side.RED)
|
||||
-- MESSAGE.SetMSRS("D:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio",5012,nil,127,radio.modulation.FM,"female","en-US",nil,coalition.side.RED)
|
||||
-- -- later on in your code
|
||||
-- MESSAGE:New("Test message!",15,"SPAWN"):ToSRSRed()
|
||||
--
|
||||
@@ -611,7 +649,7 @@ end
|
||||
-- @usage
|
||||
-- -- Mind the dot here, not using the colon this time around!
|
||||
-- -- Needed once only
|
||||
-- MESSAGE.SetMSRS("D:\\Program Files\\DCS-SimpleRadio-Standalone",5012,nil,127,radio.modulation.FM,"female","en-US",nil,coalition.side.NEUTRAL)
|
||||
-- MESSAGE.SetMSRS("D:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio",5012,nil,127,radio.modulation.FM,"female","en-US",nil,coalition.side.NEUTRAL)
|
||||
-- -- later on in your code
|
||||
-- MESSAGE:New("Test message!",15,"SPAWN"):ToSRSAll()
|
||||
--
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
--
|
||||
-- # Mark on F10 map
|
||||
--
|
||||
-- The ponints of the PATHLINE can be marked on the F10 map with the @{#PATHLINE.MarkPoints}(`true`) function. The mark points contain information of the surface type, land height and
|
||||
-- The points of the PATHLINE can be marked on the F10 map with the @{#PATHLINE.MarkPoints}(`true`) function. The mark points contain information of the surface type, land height and
|
||||
-- water depth.
|
||||
--
|
||||
-- To remove the marks, use @{#PATHLINE.MarkPoints}(`false`).
|
||||
@@ -69,11 +69,12 @@ PATHLINE = {
|
||||
-- @field #number landHeight Land height in meters.
|
||||
-- @field #number depth Water depth in meters.
|
||||
-- @field #number markerID Marker ID.
|
||||
-- @field #number lineID Line marker ID.
|
||||
|
||||
|
||||
--- PATHLINE class version.
|
||||
-- @field #string version
|
||||
PATHLINE.version="0.1.1"
|
||||
PATHLINE.version="0.2.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -252,7 +253,7 @@ end
|
||||
|
||||
--- Get the n-th point of the pathline.
|
||||
-- @param #PATHLINE self
|
||||
-- @param #number n The index of the point. Default is the first point.
|
||||
-- @param #number n (optional) The index of the point. Default is the first point.
|
||||
-- @return #PATHLINE.Point Point.
|
||||
function PATHLINE:GetPointFromIndex(n)
|
||||
|
||||
@@ -274,7 +275,7 @@ end
|
||||
--- Get the 3D position of the n-th point.
|
||||
-- @param #PATHLINE self
|
||||
-- @param #number n The n-th point.
|
||||
-- @return DCS#VEC3 Position in 3D.
|
||||
-- @return DCS#Vec3 Position in 3D.
|
||||
function PATHLINE:GetPoint3DFromIndex(n)
|
||||
|
||||
local point=self:GetPointFromIndex(n)
|
||||
@@ -289,7 +290,7 @@ end
|
||||
--- Get the 2D position of the n-th point.
|
||||
-- @param #PATHLINE self
|
||||
-- @param #number n The n-th point.
|
||||
-- @return DCS#VEC2 Position in 3D.
|
||||
-- @return DCS#Vec2 Position in 3D.
|
||||
function PATHLINE:GetPoint2DFromIndex(n)
|
||||
|
||||
local point=self:GetPointFromIndex(n)
|
||||
@@ -301,18 +302,42 @@ function PATHLINE:GetPoint2DFromIndex(n)
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Calculate the length of the line.
|
||||
-- @param #PATHLINE self
|
||||
-- @param #boolean Project2D Calculate 2D distance between points.
|
||||
-- @return #number Length in meters.
|
||||
function PATHLINE:GetLength(Project2D)
|
||||
local l=0
|
||||
|
||||
local np=#self.points
|
||||
|
||||
for i=1,np-1 do
|
||||
local p1=self.points[i] --#PATHLINE.Point
|
||||
local p2=self.points[i+1] --#PATHLINE.Point
|
||||
|
||||
if Project2D then
|
||||
l=l+UTILS.VecDist2D(p1.vec2, p2.vec2)
|
||||
else
|
||||
l=l+UTILS.VecDist3D(p1.vec3, p2.vec3)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return l
|
||||
end
|
||||
|
||||
|
||||
--- Mark points on F10 map.
|
||||
-- @param #PATHLINE self
|
||||
-- @param #boolean Switch If `true` or nil, set marks. If `false`, remove marks.
|
||||
-- @return <DCS#Vec3> List of DCS#Vec3 points.
|
||||
-- @return #PATHLINE self
|
||||
function PATHLINE:MarkPoints(Switch)
|
||||
for i,_point in pairs(self.points) do
|
||||
local point=_point --#PATHLINE.Point
|
||||
if Switch==false then
|
||||
|
||||
if point.markerID then
|
||||
UTILS.RemoveMark(point.markerID, Delay)
|
||||
UTILS.RemoveMark(point.markerID)
|
||||
end
|
||||
|
||||
else
|
||||
@@ -329,6 +354,56 @@ function PATHLINE:MarkPoints(Switch)
|
||||
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Draw line on F10 map.
|
||||
-- @param #PATHLINE self
|
||||
-- @param #number Recipient Recipent of the line: -1=All.
|
||||
-- @param #table Color (optional) Color as RGB table plus alpha value. Default {1, 0, 0, 1.0}.
|
||||
-- @param #number LineType (optional) Line type: 1=Solid (default).
|
||||
-- @return #PATHLINE self
|
||||
function PATHLINE:DrawLine(Recipient, Color, LineType)
|
||||
|
||||
-- Input
|
||||
Recipient= Recipient or -1
|
||||
Color= Color or {1,0,0, 1.0}
|
||||
LineType=LineType or 1
|
||||
local ReadOnly=false
|
||||
|
||||
|
||||
local np=#self.points
|
||||
|
||||
for i=1,np-1 do
|
||||
local p1=self.points[i] --#PATHLINE.Point
|
||||
local p2=self.points[i+1] --#PATHLINE.Point
|
||||
|
||||
p1.lineID = UTILS.GetMarkID()
|
||||
|
||||
trigger.action.lineToAll(Recipient, p1.lineID, p1.vec3, p2.vec3, Color, LineType, ReadOnly, "")
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove line on F10 map.
|
||||
-- @param #PATHLINE self
|
||||
-- @param #number Delay Delay in seconds before line is removed.
|
||||
-- @return #PATHLINE self
|
||||
function PATHLINE:UnDrawLine(Delay)
|
||||
|
||||
|
||||
local np=#self.points
|
||||
|
||||
for _,_point in pairs(self.points) do
|
||||
local p=_point --#PATHLINE.Point
|
||||
if p.lineID then
|
||||
UTILS.RemoveMark(p.lineID, Delay)
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Provides a COORDINATE class, which allows to manage points in 3D space and perform various operations on it.
|
||||
-- * Provides a POINT\_VEC2 class, which is derived from COORDINATE, and allows to manage points in 3D space, but from a Lat/Lon and Altitude perspective.
|
||||
-- * Provides a POINT\_VEC3 class, which is derived from COORDINATE, and allows to manage points in 3D space, but from a X, Z and Y vector perspective.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -59,6 +57,10 @@ do -- COORDINATE
|
||||
-- * @{#COORDINATE.SmokeOrange}(): To smoke the point in orange.
|
||||
-- * @{#COORDINATE.SmokeWhite}(): To smoke the point in white.
|
||||
-- * @{#COORDINATE.SmokeGreen}(): To smoke the point in green.
|
||||
-- * @{#COORDINATE.SetSmokeOffsetDirection}(): To set an offset point direction for smoke.
|
||||
-- * @{#COORDINATE.SetSmokeOffsetDistance}(): To set an offset point distance for smoke.
|
||||
-- * @{#COORDINATE.SwitchSmokeOffsetOn}(): To set an offset point for smoke to on.
|
||||
-- * @{#COORDINATE.SwitchSmokeOffsetOff}(): To set an offset point for smoke to off.
|
||||
--
|
||||
-- ## 2.2) Flare
|
||||
--
|
||||
@@ -469,6 +471,121 @@ do -- COORDINATE
|
||||
return x - Precision <= self.x and x + Precision >= self.x and z - Precision <= self.z and z + Precision >= self.z
|
||||
end
|
||||
|
||||
---
|
||||
--Box volume scanning function matching MOOSE COORDINATE:ScanObjects() structure
|
||||
--For use as COORDINATE:ScanObjectsSquare(sideLength, scanunits, scanstatics, scanscenery)
|
||||
--Creates a cubic search volume with the COORDINATE as the lower-left (southwest) corner.
|
||||
--Perfect for grid-based map scanning: increment X and Z by sideLength for next grid cell.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number radius (Optional) Scan radius in meters. Default 100 m.
|
||||
-- @param #boolean scanunits (Optional) If true scan for units. Default true.
|
||||
-- @param #boolean scanstatics (Optional) If true scan for static objects. Default true.
|
||||
-- @param #boolean scanscenery (Optional) If true scan for scenery objects. Default false.
|
||||
-- @return #boolean True if units were found.
|
||||
-- @return #boolean True if statics were found.
|
||||
-- @return #boolean True if scenery objects were found.
|
||||
-- @return #table Table of MOOSE @{Wrapper.Unit#UNIT} objects found.
|
||||
-- @return #table Table of DCS static objects found.
|
||||
-- @return #table Table of DCS scenery objects found.
|
||||
function COORDINATE:ScanObjectsSquare(sideLength, scanunits, scanstatics, scanscenery)
|
||||
self:F(string.format("Scanning cube volume (lower-left corner) with side length %.1f m.", sideLength))
|
||||
|
||||
local CornerVec3 = self:GetVec3()
|
||||
local CenterY = CornerVec3.y
|
||||
|
||||
local MinVec3 = {
|
||||
x = CornerVec3.x,
|
||||
y = CenterY - (sideLength / 2),
|
||||
z = CornerVec3.z
|
||||
}
|
||||
local MaxVec3 = {
|
||||
x = CornerVec3.x + sideLength,
|
||||
y = CenterY + (sideLength / 2),
|
||||
z = CornerVec3.z + sideLength
|
||||
}
|
||||
|
||||
local BoxSearch = {
|
||||
id = world.VolumeType.BOX,
|
||||
params = {
|
||||
min = MinVec3,
|
||||
max = MaxVec3,
|
||||
}
|
||||
}
|
||||
|
||||
-- Defaults
|
||||
if scanunits==nil then
|
||||
scanunits=true
|
||||
end
|
||||
if scanstatics==nil then
|
||||
scanstatics=true
|
||||
end
|
||||
if scanscenery==nil then
|
||||
scanscenery=false
|
||||
end
|
||||
|
||||
-- {Object.Category.UNIT, Object.Category.STATIC, Object.Category.SCENERY}
|
||||
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
|
||||
|
||||
-- Found stuff.
|
||||
local Units = {}
|
||||
local Statics = {}
|
||||
local Scenery = {}
|
||||
local gotstatics=false
|
||||
local gotunits=false
|
||||
local gotscenery=false
|
||||
|
||||
local function EvaluateZone( ZoneObject )
|
||||
|
||||
if ZoneObject then
|
||||
|
||||
-- Get category of scanned object.
|
||||
local ObjectCategory = ZoneObject:getCategory()
|
||||
|
||||
-- Check for unit or static objects
|
||||
if (ObjectCategory == Object.Category.UNIT and ZoneObject:isExist()) then
|
||||
|
||||
table.insert(Units, 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
|
||||
|
||||
-- Search the world.
|
||||
world.searchObjects(scanobjects, BoxSearch, EvaluateZone)
|
||||
|
||||
for _,unit in pairs(Units) do
|
||||
if not unit:isExist() then
|
||||
gotunits=false
|
||||
end
|
||||
end
|
||||
|
||||
return gotunits, gotstatics, gotscenery, Units, Statics, Scenery
|
||||
end
|
||||
|
||||
|
||||
--- Scan/find objects (units, statics, scenery) within a certain radius around the coordinate using the world.searchObjects() DCS API function.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number radius (Optional) Scan radius in meters. Default 100 m.
|
||||
@@ -608,7 +725,7 @@ do -- COORDINATE
|
||||
|
||||
--- Find the closest static to the COORDINATE within a certain radius.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number radius Scan radius in meters. Default 100 m.
|
||||
-- @param #number radius (Optional) Scan radius in meters. Default 100 m.
|
||||
-- @return Wrapper.Static#STATIC The closest static or #nil if no unit is inside the given radius.
|
||||
function COORDINATE:FindClosestStatic(radius)
|
||||
|
||||
@@ -631,7 +748,7 @@ do -- COORDINATE
|
||||
|
||||
--- Find the closest unit to the COORDINATE within a certain radius.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number radius Scan radius in meters. Default 100 m.
|
||||
-- @param #number radius (Optional) Scan radius in meters. Default 100 m.
|
||||
-- @return Wrapper.Unit#UNIT The closest unit or #nil if no unit is inside the given radius.
|
||||
function COORDINATE:FindClosestUnit(radius)
|
||||
|
||||
@@ -676,7 +793,7 @@ do -- COORDINATE
|
||||
|
||||
--- Find the closest scenery to the COORDINATE within a certain radius.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number radius Scan radius in meters. Default 100 m.
|
||||
-- @param #number radius (Optional) Scan radius in meters. Default 100 m.
|
||||
-- @return Wrapper.Scenery#SCENERY The closest scenery or #nil if no object is inside the given radius.
|
||||
function COORDINATE:FindClosestScenery(radius)
|
||||
|
||||
@@ -714,8 +831,8 @@ do -- COORDINATE
|
||||
--- Add a Distance in meters from the COORDINATE orthonormal plane, with the given angle, and calculate the new COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @param DCS#Distance Distance The Distance to be added in meters.
|
||||
-- @param DCS#Angle Angle The Angle in degrees. Defaults to 0 if not specified (nil).
|
||||
-- @param #boolean Keepalt If true, keep altitude of original coordinate. Default is that the new coordinate is created at the translated land height.
|
||||
-- @param DCS#Angle Angle (Optional) The Angle in degrees. Defaults to 0 if not specified (nil).
|
||||
-- @param #boolean Keepalt (Optional) If true, keep altitude of original coordinate. Default is that the new coordinate is created at the translated land height.
|
||||
-- @param #boolean Overwrite If true, overwrite the original COORDINATE with the translated one. Otherwise, create a new COORDINATE.
|
||||
-- @return #COORDINATE The new calculated COORDINATE.
|
||||
function COORDINATE:Translate( Distance, Angle, Keepalt, Overwrite )
|
||||
@@ -773,7 +890,9 @@ do -- COORDINATE
|
||||
-- @return DCS#Vec2 Vec2
|
||||
function COORDINATE:GetRandomVec2InRadius( OuterRadius, InnerRadius )
|
||||
self:F2( { OuterRadius, InnerRadius } )
|
||||
|
||||
math.random()
|
||||
math.random()
|
||||
math.random()
|
||||
local Theta = 2 * math.pi * math.random()
|
||||
local Radials = math.random() + math.random()
|
||||
if Radials > 1 then
|
||||
@@ -833,6 +952,26 @@ do -- COORDINATE
|
||||
return land.getHeight( Vec2 )
|
||||
end
|
||||
|
||||
--- Returns a table of DCS#Vec3 points representing the terrain profile between two points.
|
||||
-- @param #COORDINATE self
|
||||
-- @param Destination DCS#Vec3 Ending point of the profile.
|
||||
-- @return #table DCS#Vec3 table of the profile
|
||||
function COORDINATE:GetLandProfileVec3(Destination)
|
||||
return land.profile(self:GetVec3(), Destination)
|
||||
end
|
||||
|
||||
--- Returns a table of #COORDINATE representing the terrain profile between two points.
|
||||
-- @param #COORDINATE self
|
||||
-- @param Destination #COORDINATE Ending coordinate of the profile.
|
||||
-- @return #table #COORDINATE table of the profile
|
||||
function COORDINATE:GetLandProfileCoordinates(Destination)
|
||||
local points = self:GetLandProfileVec3(Destination:GetVec3())
|
||||
local coords = {}
|
||||
for _, point in ipairs(points) do
|
||||
table.insert(coords, COORDINATE:NewFromVec3(point))
|
||||
end
|
||||
return coords
|
||||
end
|
||||
|
||||
--- Set the heading of the coordinate, if applicable.
|
||||
-- @param #COORDINATE self
|
||||
@@ -934,7 +1073,7 @@ do -- COORDINATE
|
||||
--- Return an intermediate COORDINATE between this an another coordinate.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE ToCoordinate The other coordinate.
|
||||
-- @param #number Fraction The fraction (0,1) where the new coordinate is created. Default 0.5, i.e. in the middle.
|
||||
-- @param #number Fraction (Optional) The fraction (0,1) where the new coordinate is created. Default 0.5, i.e. in the middle.
|
||||
-- @return #COORDINATE Coordinate between this and the other coordinate.
|
||||
function COORDINATE:GetIntermediateCoordinate( ToCoordinate, Fraction )
|
||||
|
||||
@@ -1490,7 +1629,7 @@ do -- COORDINATE
|
||||
-- @param Core.Settings#SETTINGS Settings
|
||||
-- @param #string Language (Optional) Language "en" or "ru"
|
||||
-- @param #boolean MagVar If true, also state angle in magnetic
|
||||
-- @param #number Precision Rounding precision, defaults to 0
|
||||
-- @param #number Precision (Optional) Rounding precision, defaults to 0
|
||||
-- @return #string The BR Text
|
||||
function COORDINATE:GetBRText( AngleRadians, Distance, Settings, Language, MagVar, Precision )
|
||||
|
||||
@@ -1531,7 +1670,7 @@ do -- COORDINATE
|
||||
--- Set altitude.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number altitude New altitude in meters.
|
||||
-- @param #boolean asl Altitude above sea level. Default is above ground level.
|
||||
-- @param #boolean asl (Optional) Altitude above sea level. Default is above ground level.
|
||||
-- @return #COORDINATE The COORDINATE with adjusted altitude.
|
||||
function COORDINATE:SetAltitude(altitude, asl)
|
||||
local alt=altitude
|
||||
@@ -1557,7 +1696,7 @@ do -- COORDINATE
|
||||
-- @param #COORDINATE.WaypointAltType AltType The altitude type.
|
||||
-- @param #COORDINATE.WaypointType Type The route point type.
|
||||
-- @param #COORDINATE.WaypointAction Action The route point action.
|
||||
-- @param DCS#Speed Speed Airspeed in km/h. Default is 500 km/h.
|
||||
-- @param DCS#Speed Speed (Optional) Airspeed in km/h. Default is 500 km/h.
|
||||
-- @param #boolean SpeedLocked true means the speed is locked.
|
||||
-- @param Wrapper.Airbase#AIRBASE airbase The airbase for takeoff and landing points.
|
||||
-- @param #table DCSTasks A table of @{DCS#Task} items which are executed at the waypoint.
|
||||
@@ -1611,6 +1750,7 @@ do -- COORDINATE
|
||||
if AirbaseCategory == Airbase.Category.SHIP or AirbaseCategory == Airbase.Category.HELIPAD then
|
||||
RoutePoint.linkUnit = AirbaseID
|
||||
RoutePoint.helipadId = AirbaseID
|
||||
RoutePoint.airdromeId = airbase:IsAirdrome() and AirbaseID or nil
|
||||
elseif AirbaseCategory == Airbase.Category.AIRDROME then
|
||||
RoutePoint.airdromeId = AirbaseID
|
||||
else
|
||||
@@ -1716,7 +1856,7 @@ do -- COORDINATE
|
||||
-- @param #COORDINATE self
|
||||
-- @param DCS#Speed Speed Airspeed in km/h.
|
||||
-- @param Wrapper.Airbase#AIRBASE airbase The airbase for takeoff and landing points.
|
||||
-- @param #number timeReFuAr Time in minutes, the aircraft stays at the airbase. Default 10 min.
|
||||
-- @param #number timeReFuAr (Optional) Time in minutes, the aircraft stays at the airbase. Default 10 min.
|
||||
-- @param #table DCSTasks A table of @{DCS#Task} items which are executed at the waypoint.
|
||||
-- @param #string description A text description of the waypoint, which will be shown on the F10 map.
|
||||
-- @return #table The route point.
|
||||
@@ -2033,6 +2173,40 @@ do -- COORDINATE
|
||||
|
||||
return Path, Way, GotPath
|
||||
end
|
||||
|
||||
--- Returns a table of coordinates to a destination using only roads or railroads.
|
||||
-- The first point is the closest point on road of the given coordinate.
|
||||
-- By default, the last point is the closest point on road of the ToCoord. Hence, the coordinate itself and the final ToCoord are not necessarily included in the path.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE ToCoord Coordinate of destination.
|
||||
-- @param #boolean IncludeEndpoints (Optional) Include the coordinate itself and the ToCoordinate in the path.
|
||||
-- @param #boolean Railroad (Optional) If true, path on railroad is returned. Default false.
|
||||
-- @return Core.Pathline#PATHLINE Pathline containing the points on road. If no path on road can be found, nil is returned or just the endpoints.
|
||||
function COORDINATE:GetPathlineOnRoad(ToCoord, IncludeEndpoints, Railroad)
|
||||
|
||||
-- Set road type.
|
||||
local RoadType="roads"
|
||||
if Railroad==true then
|
||||
RoadType="railroads"
|
||||
end
|
||||
|
||||
-- DCS API function returning a table of vec2.
|
||||
local path = land.findPathOnRoads(RoadType, self.x, self.z, ToCoord.x, ToCoord.z)
|
||||
|
||||
if IncludeEndpoints then
|
||||
path=path or {}
|
||||
table.insert(path, 1, self:GetVec2())
|
||||
table.insert(path, ToCoord:GetVec2())
|
||||
end
|
||||
|
||||
local pathline=nil
|
||||
if path then
|
||||
pathline=PATHLINE:NewFromVec2Array(RoadType, path)
|
||||
end
|
||||
|
||||
return pathline
|
||||
end
|
||||
|
||||
|
||||
--- Gets the surface type at the coordinate.
|
||||
-- @param #COORDINATE self
|
||||
@@ -2089,7 +2263,7 @@ do -- COORDINATE
|
||||
|
||||
--- Creates an explosion at the point of a certain intensity.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number ExplosionIntensity Intensity of the explosion in kg TNT. Default 100 kg.
|
||||
-- @param #number ExplosionIntensity (Optional) Intensity of the explosion in kg TNT. Default 100 kg.
|
||||
-- @param #number Delay (Optional) Delay before explosion is triggered in seconds.
|
||||
-- @return #COORDINATE self
|
||||
function COORDINATE:Explosion( ExplosionIntensity, Delay )
|
||||
@@ -2104,7 +2278,7 @@ do -- COORDINATE
|
||||
|
||||
--- Creates an illumination bomb at the point.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number Power Power of illumination bomb in Candela. Default 1000 cd.
|
||||
-- @param #number Power (Optional) Power of illumination bomb in Candela. Default 1000 cd.
|
||||
-- @param #number Delay (Optional) Delay before bomb is ignited in seconds.
|
||||
-- @return #COORDINATE self
|
||||
function COORDINATE:IlluminationBomb(Power, Delay)
|
||||
@@ -2124,21 +2298,32 @@ do -- COORDINATE
|
||||
-- @param #number Duration (Optional) Duration of the smoke in seconds. DCS stopps the smoke automatically after 5 min.
|
||||
-- @param #number Delay (Optional) Delay before the smoke is started in seconds.
|
||||
-- @param #string Name (Optional) Name if you want to stop the smoke early (normal duration: 5mins)
|
||||
-- @param #boolean Offset (Optional) If true, offset the smokle a bit.
|
||||
-- @param #number Direction (Optional) If Offset is true this is the direction of the offset, 1-359 (degrees). Default random.
|
||||
-- @param #number Distance (Optional) If Offset is true this is the distance of the offset in meters. Default random 10-20.
|
||||
-- @return #COORDINATE self
|
||||
function COORDINATE:Smoke( SmokeColor, Duration, Delay, Name)
|
||||
self:F2( { SmokeColor, Name, Duration, Delay } )
|
||||
function COORDINATE:Smoke( SmokeColor, Duration, Delay, Name, Offset,Direction,Distance)
|
||||
self:F2( { SmokeColor, Name, Duration, Delay, Offset } )
|
||||
|
||||
SmokeColor=SmokeColor or SMOKECOLOR.Green
|
||||
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, COORDINATE.Smoke, self, SmokeColor, Duration, 0, Name)
|
||||
self:ScheduleOnce(Delay, COORDINATE.Smoke, self, SmokeColor, Duration, 0, Name, Direction,Distance)
|
||||
else
|
||||
|
||||
-- Create a name which is used to stop the smoke manually
|
||||
self.firename = Name or "Smoke-"..math.random(1,100000)
|
||||
|
||||
-- Create smoke
|
||||
trigger.action.smoke( self:GetVec3(), SmokeColor, self.firename )
|
||||
if Offset or self.SmokeOffset then
|
||||
local Angle = Direction or self:GetSmokeOffsetDirection()
|
||||
local Distance = Distance or self:GetSmokeOffsetDistance()
|
||||
local newpos = self:Translate(Distance,Angle,true,false)
|
||||
local newvec3 = newpos:GetVec3()
|
||||
trigger.action.smoke( newvec3, SmokeColor, self.firename )
|
||||
else
|
||||
trigger.action.smoke( self:GetVec3(), SmokeColor, self.firename )
|
||||
end
|
||||
|
||||
-- Stop smoke
|
||||
if Duration and Duration>0 then
|
||||
@@ -2148,6 +2333,72 @@ do -- COORDINATE
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the offset direction when using `COORDINATE:Smoke()`.
|
||||
-- @param #COORDINATE self
|
||||
-- @return #number Direction in degrees.
|
||||
function COORDINATE:GetSmokeOffsetDirection()
|
||||
local direction = self.SmokeOffsetDirection or math.random(1,359)
|
||||
return direction
|
||||
end
|
||||
|
||||
--- Set the offset direction when using `COORDINATE:Smoke()`.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number Direction (Optional) This is the direction of the offset, 1-359 (degrees). Default random.
|
||||
-- @return #COORDINATE self
|
||||
function COORDINATE:SetSmokeOffsetDirection(Direction)
|
||||
if self then
|
||||
self.SmokeOffsetDirection = Direction or math.random(1,359)
|
||||
return self
|
||||
else
|
||||
COORDINATE.SmokeOffsetDirection = Direction or math.random(1,359)
|
||||
end
|
||||
end
|
||||
|
||||
--- Get the offset distance when using `COORDINATE:Smoke()`.
|
||||
-- @param #COORDINATE self
|
||||
-- @return #number Distance Distance in meters.
|
||||
function COORDINATE:GetSmokeOffsetDistance()
|
||||
local distance = self.SmokeOffsetDistance or math.random(10,20)
|
||||
return distance
|
||||
end
|
||||
|
||||
--- Set the offset distance when using `COORDINATE:Smoke()`.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number Distance (Optional) This is the distance of the offset in meters. Default random 10-20.
|
||||
-- @return #COORDINATE self
|
||||
function COORDINATE:SetSmokeOffsetDistance(Distance)
|
||||
if self then
|
||||
self.SmokeOffsetDistance = Distance or math.random(10,20)
|
||||
return self
|
||||
else
|
||||
COORDINATE.SmokeOffsetDistance = Distance or math.random(10,20)
|
||||
end
|
||||
end
|
||||
|
||||
--- Set the offset on when using `COORDINATE:Smoke()`.
|
||||
-- @param #COORDINATE self
|
||||
-- @return #COORDINATE self
|
||||
function COORDINATE:SwitchSmokeOffsetOn()
|
||||
if self then
|
||||
self.SmokeOffset = true
|
||||
return self
|
||||
else
|
||||
COORDINATE.SmokeOffset = true
|
||||
end
|
||||
end
|
||||
|
||||
--- Set the offset off when using `COORDINATE:Smoke()`.
|
||||
-- @param #COORDINATE self
|
||||
-- @return #COORDINATE self
|
||||
function COORDINATE:SwitchSmokeOffsetOff()
|
||||
if self then
|
||||
self.SmokeOffset = false
|
||||
return self
|
||||
else
|
||||
COORDINATE.SmokeOffset = false
|
||||
end
|
||||
end
|
||||
|
||||
--- Stops smoking the point in a color.
|
||||
-- @param #COORDINATE self
|
||||
@@ -2486,10 +2737,10 @@ do -- COORDINATE
|
||||
-- Creates a line on the F10 map from one point to another.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE Endpoint COORDINATE to where the line is drawn.
|
||||
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
|
||||
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||
-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||
-- @param #number Coalition (Optional) Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||
-- @param #table Color (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
|
||||
-- @param #number Alpha (Optional) Transparency [0,1]. Default 1.
|
||||
-- @param #number LineType (Optional) Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.
|
||||
-- @param #string Text (Optional) Text displayed when mark is added. Default none.
|
||||
-- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again.
|
||||
@@ -2510,13 +2761,13 @@ do -- COORDINATE
|
||||
--- Circle to all.
|
||||
-- Creates a circle on the map with a given radius, color, fill color, and outline.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number Radius Radius in meters. Default 1000 m.
|
||||
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
|
||||
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||
-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value.
|
||||
-- @param #number FillAlpha Transparency [0,1]. Default 0.15.
|
||||
-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||
-- @param #number Radius (Optional) Radius in meters. Default 1000 m.
|
||||
-- @param #number Coalition (Optional) Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||
-- @param #table Color (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
|
||||
-- @param #number Alpha (Optional) Transparency [0,1]. Default 1.
|
||||
-- @param #table FillColor (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value.
|
||||
-- @param #number FillAlpha (Optional) Transparency [0,1]. Default 0.15.
|
||||
-- @param #number LineType (Optional) Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.
|
||||
-- @param #string Text (Optional) Text displayed when mark is added. Default none.
|
||||
-- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again.
|
||||
@@ -2550,12 +2801,12 @@ do -- COORDINATE
|
||||
-- Creates a line on the F10 map from one point to another.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE Endpoint COORDINATE in the opposite corner.
|
||||
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
|
||||
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||
-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value.
|
||||
-- @param #number FillAlpha Transparency [0,1]. Default 0.15.
|
||||
-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||
-- @param #number Coalition (Optional) Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||
-- @param #table Color (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
|
||||
-- @param #number Alpha (Optional) Transparency [0,1]. Default 1.
|
||||
-- @param #table FillColor (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value.
|
||||
-- @param #number FillAlpha (Optional) Transparency [0,1]. Default 0.15.
|
||||
-- @param #number LineType (Optional) Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.
|
||||
-- @param #string Text (Optional) Text displayed when mark is added. Default none.
|
||||
-- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again.
|
||||
@@ -2586,12 +2837,12 @@ do -- COORDINATE
|
||||
-- @param #COORDINATE Coord2 Second COORDINATE of the quad shape.
|
||||
-- @param #COORDINATE Coord3 Third COORDINATE of the quad shape.
|
||||
-- @param #COORDINATE Coord4 Fourth COORDINATE of the quad shape.
|
||||
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
|
||||
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||
-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value.
|
||||
-- @param #number FillAlpha Transparency [0,1]. Default 0.15.
|
||||
-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||
-- @param #number Coalition (Optional) Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||
-- @param #table Color (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
|
||||
-- @param #number Alpha (Optional) Transparency [0,1]. Default 1.
|
||||
-- @param #table FillColor (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value.
|
||||
-- @param #number FillAlpha (Optional) Transparency [0,1]. Default 0.15.
|
||||
-- @param #number LineType (Optional) Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.
|
||||
-- @param #string Text (Optional) Text displayed when mark is added. Default none.
|
||||
-- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again.
|
||||
@@ -2623,12 +2874,12 @@ do -- COORDINATE
|
||||
--- Creates a free form shape on the F10 map. The first point is the current COORDINATE. The remaining points need to be specified.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #table Coordinates Table of coordinates of the remaining points of the shape.
|
||||
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
|
||||
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||
-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value.
|
||||
-- @param #number FillAlpha Transparency [0,1]. Default 0.15.
|
||||
-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||
-- @param #number Coalition (Optional) Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||
-- @param #table Color (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
|
||||
-- @param #number Alpha (Optional) Transparency [0,1]. Default 1.
|
||||
-- @param #table FillColor (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value.
|
||||
-- @param #number FillAlpha (Optional) Transparency [0,1]. Default 0.15.
|
||||
-- @param #number LineType (Optional) Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.
|
||||
-- @param #string Text (Optional) Text displayed when mark is added. Default none.
|
||||
-- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again.
|
||||
@@ -2729,12 +2980,12 @@ do -- COORDINATE
|
||||
--- Text to all. Creates a text imposed on the map at the COORDINATE. Text scales with the map.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #string Text Text displayed on the F10 map.
|
||||
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
|
||||
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||
-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value.
|
||||
-- @param #number FillAlpha Transparency [0,1]. Default 0.3.
|
||||
-- @param #number FontSize Font size. Default 14.
|
||||
-- @param #number Coalition (Optional) Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||
-- @param #table Color (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
|
||||
-- @param #number Alpha (Optional) Transparency [0,1]. Default 1.
|
||||
-- @param #table FillColor (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value.
|
||||
-- @param #number FillAlpha (Optional) Transparency [0,1]. Default 0.3.
|
||||
-- @param #number FontSize (Optional) Font size. Default 14.
|
||||
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.
|
||||
-- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again.
|
||||
function COORDINATE:TextToAll(Text, Coalition, Color, Alpha, FillColor, FillAlpha, FontSize, ReadOnly)
|
||||
@@ -2759,12 +3010,12 @@ do -- COORDINATE
|
||||
--- Arrow to all. Creates an arrow from the COORDINATE to the endpoint COORDINATE on the F10 map. There is no control over other dimensions of the arrow.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE Endpoint COORDINATE where the tip of the arrow is pointing at.
|
||||
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
|
||||
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||
-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value.
|
||||
-- @param #number FillAlpha Transparency [0,1]. Default 0.15.
|
||||
-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||
-- @param #number Coalition (Optional) Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||
-- @param #table Color (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
|
||||
-- @param #number Alpha (Optional) Transparency [0,1]. Default 1.
|
||||
-- @param #table FillColor (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value.
|
||||
-- @param #number FillAlpha (Optional) Transparency [0,1]. Default 0.15.
|
||||
-- @param #number LineType (Optional) Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.
|
||||
-- @param #string Text (Optional) Text displayed when mark is added. Default none.
|
||||
-- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again.
|
||||
@@ -2794,7 +3045,7 @@ do -- COORDINATE
|
||||
--- Returns if a Coordinate has Line of Sight (LOS) with the ToCoordinate.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE ToCoordinate
|
||||
-- @param #number Offset Height offset in meters. Default 2 m.
|
||||
-- @param #number Offset (Optional) Height offset in meters. Default 2 m.
|
||||
-- @return #boolean true If the ToCoordinate has LOS with the Coordinate, otherwise false.
|
||||
function COORDINATE:IsLOS( ToCoordinate, Offset )
|
||||
|
||||
@@ -3145,19 +3396,32 @@ do -- COORDINATE
|
||||
return delta/60
|
||||
end
|
||||
|
||||
|
||||
--- Return a BR string from a COORDINATE to the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE FromCoordinate The coordinate to measure the distance and the bearing from.
|
||||
-- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object.
|
||||
-- @param #boolean MagVar If true, also get angle in MagVar for BR/BRA
|
||||
-- @param #number Precision Rounding precision, currently full km as default (=0)
|
||||
-- @param #number Precision (Optional) Rounding precision, currently full km as default (=0)
|
||||
-- @return #string The BR text.
|
||||
function COORDINATE:ToStringBR( FromCoordinate, Settings, MagVar, Precision )
|
||||
local DirectionVec3 = FromCoordinate:GetDirectionVec3( self )
|
||||
local AngleRadians = self:GetAngleRadians( DirectionVec3 )
|
||||
local Distance = self:Get2DDistance( FromCoordinate )
|
||||
return "BR, " .. self:GetBRText( AngleRadians, Distance, Settings, nil, MagVar, Precision )
|
||||
return "BR " .. self:GetBRText( AngleRadians, Distance, Settings, nil, MagVar, Precision )
|
||||
end
|
||||
|
||||
--- Return a Bearing string from a COORDINATE to the (self) COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE FromCoordinate The coordinate to measure the distance and the bearing from.
|
||||
-- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object.
|
||||
-- @param #boolean MagVar If true, also get angle in MagVar for BR/BRA
|
||||
-- @param #number Precision (Optional) Rounding precision, currently full km as default (=0)
|
||||
-- @return #string The BR text.
|
||||
function COORDINATE:ToStringBearing( FromCoordinate, Settings, MagVar, Precision )
|
||||
local DirectionVec3 = FromCoordinate:GetDirectionVec3( self )
|
||||
local AngleRadians = self:GetAngleRadians( DirectionVec3 )
|
||||
--local Distance = self:Get2DDistance( FromCoordinate )
|
||||
return self:GetBearingText(AngleRadians,Precision,Settings,MagVar)
|
||||
end
|
||||
|
||||
--- Return a BRA string from a COORDINATE to the COORDINATE.
|
||||
@@ -3565,8 +3829,8 @@ do -- COORDINATE
|
||||
-- * Uses default settings in COORDINATE.
|
||||
-- * Can be overridden if for a GROUP containing x clients, a menu was selected to override the default.
|
||||
-- @param #COORDINATE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The controllable to retrieve the settings from, otherwise the default settings will be chosen.
|
||||
-- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object.
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable (Optional) The controllable to retrieve the settings from, otherwise the default settings will be chosen.
|
||||
-- @param Core.Settings#SETTINGS Settings (Optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object.
|
||||
-- @return #string The coordinate Text in the configured coordinate system.
|
||||
function COORDINATE:ToString( Controllable, Settings )
|
||||
|
||||
@@ -3576,25 +3840,6 @@ do -- COORDINATE
|
||||
|
||||
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
|
||||
@@ -3620,7 +3865,7 @@ do -- COORDINATE
|
||||
-- * Can be overridden if for a GROUP containing x clients, a menu was selected to override the default.
|
||||
-- @param #COORDINATE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable
|
||||
-- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object.
|
||||
-- @param Core.Settings#SETTINGS Settings (Optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object.
|
||||
-- @return #string The pressure text in the configured measurement system.
|
||||
function COORDINATE:ToStringPressure( Controllable, Settings )
|
||||
|
||||
@@ -3716,173 +3961,24 @@ do -- COORDINATE
|
||||
function COORDINATE:GetRandomPointVec3InRadius( OuterRadius, InnerRadius )
|
||||
return COORDINATE:NewFromVec3( self:GetRandomVec3InRadius( OuterRadius, InnerRadius ) )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
do
|
||||
|
||||
--- The POINT_VEC3 class
|
||||
-- @type POINT_VEC3
|
||||
-- @field #number x The x coordinate in 3D space.
|
||||
-- @field #number y The y coordinate in 3D space.
|
||||
-- @field #number z The z COORDINATE in 3D space.
|
||||
-- @field Utilities.Utils#SMOKECOLOR SmokeColor
|
||||
-- @field Utilities.Utils#FLARECOLOR FlareColor
|
||||
-- @field #POINT_VEC3.RoutePointAltType RoutePointAltType
|
||||
-- @field #POINT_VEC3.RoutePointType RoutePointType
|
||||
-- @field #POINT_VEC3.RoutePointAction RoutePointAction
|
||||
-- @extends #COORDINATE
|
||||
|
||||
|
||||
--- Defines a 3D point in the simulator and with its methods, you can use or manipulate the point in 3D space.
|
||||
--
|
||||
-- **DEPRECATED - PLEASE USE COORDINATE!**
|
||||
--
|
||||
-- **Important Note:** Most of the functions in this section were taken from MIST, and reworked to OO concepts.
|
||||
-- In order to keep the credibility of the the author,
|
||||
-- I want to emphasize that the formulas embedded in the MIST framework were created by Grimes or previous authors,
|
||||
-- who you can find on the Eagle Dynamics Forums.
|
||||
--
|
||||
--
|
||||
-- ## POINT_VEC3 constructor
|
||||
--
|
||||
-- A new POINT_VEC3 object can be created with:
|
||||
--
|
||||
-- * @{#POINT_VEC3.New}(): a 3D point.
|
||||
-- * @{#POINT_VEC3.NewFromVec3}(): a 3D point created from a @{DCS#Vec3}.
|
||||
--
|
||||
--
|
||||
-- ## Manupulate the X, Y, Z coordinates of the POINT_VEC3
|
||||
--
|
||||
-- A POINT_VEC3 class works in 3D space. It contains internally an X, Y, Z coordinate.
|
||||
-- Methods exist to manupulate these coordinates.
|
||||
--
|
||||
-- The current X, Y, Z axis can be retrieved with the methods @{#POINT_VEC3.GetX}(), @{#POINT_VEC3.GetY}(), @{#POINT_VEC3.GetZ}() respectively.
|
||||
-- The methods @{#POINT_VEC3.SetX}(), @{#POINT_VEC3.SetY}(), @{#POINT_VEC3.SetZ}() change the respective axis with a new value.
|
||||
-- The current axis values can be changed by using the methods @{#POINT_VEC3.AddX}(), @{#POINT_VEC3.AddY}(), @{#POINT_VEC3.AddZ}()
|
||||
-- to add or substract a value from the current respective axis value.
|
||||
-- Note that the Set and Add methods return the current POINT_VEC3 object, so these manipulation methods can be chained... For example:
|
||||
--
|
||||
-- local Vec3 = PointVec3:AddX( 100 ):AddZ( 150 ):GetVec3()
|
||||
--
|
||||
--
|
||||
-- ## 3D calculation methods
|
||||
--
|
||||
-- Various calculation methods exist to use or manipulate 3D space. Find below a short description of each method:
|
||||
--
|
||||
--
|
||||
-- ## Point Randomization
|
||||
--
|
||||
-- Various methods exist to calculate random locations around a given 3D point.
|
||||
--
|
||||
-- * @{#POINT_VEC3.GetRandomPointVec3InRadius}(): Provides a random 3D point around the current 3D point, in the given inner to outer band.
|
||||
--
|
||||
--
|
||||
-- @field #POINT_VEC3
|
||||
POINT_VEC3 = {
|
||||
ClassName = "POINT_VEC3",
|
||||
Metric = true,
|
||||
RoutePointAltType = {
|
||||
BARO = "BARO",
|
||||
},
|
||||
RoutePointType = {
|
||||
TakeOffParking = "TakeOffParking",
|
||||
TurningPoint = "Turning Point",
|
||||
},
|
||||
RoutePointAction = {
|
||||
FromParkingArea = "From Parking Area",
|
||||
TurningPoint = "Turning Point",
|
||||
},
|
||||
}
|
||||
|
||||
--- RoutePoint AltTypes
|
||||
-- @type POINT_VEC3.RoutePointAltType
|
||||
-- @field BARO "BARO"
|
||||
|
||||
--- RoutePoint Types
|
||||
-- @type POINT_VEC3.RoutePointType
|
||||
-- @field TakeOffParking "TakeOffParking"
|
||||
-- @field TurningPoint "Turning Point"
|
||||
|
||||
--- RoutePoint Actions
|
||||
-- @type POINT_VEC3.RoutePointAction
|
||||
-- @field FromParkingArea "From Parking Area"
|
||||
-- @field TurningPoint "Turning Point"
|
||||
|
||||
-- Constructor.
|
||||
|
||||
--- Create a new POINT_VEC3 object.
|
||||
-- @param #POINT_VEC3 self
|
||||
-- @param DCS#Distance x The x coordinate of the Vec3 point, pointing to the North.
|
||||
-- @param DCS#Distance y The y coordinate of the Vec3 point, pointing Upwards.
|
||||
-- @param DCS#Distance z The z coordinate of the Vec3 point, pointing to the Right.
|
||||
-- @return Core.Point#POINT_VEC3
|
||||
function POINT_VEC3:New( x, y, z )
|
||||
|
||||
local self = BASE:Inherit( self, COORDINATE:New( x, y, z ) ) -- Core.Point#POINT_VEC3
|
||||
self:F2( self )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
do
|
||||
|
||||
--- @type POINT_VEC2
|
||||
-- @field DCS#Distance x The x coordinate in meters.
|
||||
-- @field DCS#Distance y the y coordinate in meters.
|
||||
-- @extends Core.Point#COORDINATE
|
||||
|
||||
--- Defines a 2D point in the simulator. The height coordinate (if needed) will be the land height + an optional added height specified.
|
||||
--
|
||||
-- **DEPRECATED - PLEASE USE COORDINATE!**
|
||||
--
|
||||
-- ## POINT_VEC2 constructor
|
||||
--
|
||||
-- A new POINT_VEC2 instance can be created with:
|
||||
--
|
||||
-- * @{Core.Point#POINT_VEC2.New}(): a 2D point, taking an additional height parameter.
|
||||
-- * @{Core.Point#POINT_VEC2.NewFromVec2}(): a 2D point created from a @{DCS#Vec2}.
|
||||
--
|
||||
-- ## Manupulate the X, Altitude, Y coordinates of the 2D point
|
||||
--
|
||||
-- A POINT_VEC2 class works in 2D space, with an altitude setting. It contains internally an X, Altitude, Y coordinate.
|
||||
-- Methods exist to manupulate these coordinates.
|
||||
--
|
||||
-- The current X, Altitude, Y axis can be retrieved with the methods @{#POINT_VEC2.GetX}(), @{#POINT_VEC2.GetAlt}(), @{#POINT_VEC2.GetY}() respectively.
|
||||
-- The methods @{#POINT_VEC2.SetX}(), @{#POINT_VEC2.SetAlt}(), @{#POINT_VEC2.SetY}() change the respective axis with a new value.
|
||||
-- The current Lat(itude), Alt(itude), Lon(gitude) values can also be retrieved with the methods @{#POINT_VEC2.GetLat}(), @{#POINT_VEC2.GetAlt}(), @{#POINT_VEC2.GetLon}() respectively.
|
||||
-- The current axis values can be changed by using the methods @{#POINT_VEC2.AddX}(), @{#POINT_VEC2.AddAlt}(), @{#POINT_VEC2.AddY}()
|
||||
-- to add or substract a value from the current respective axis value.
|
||||
-- Note that the Set and Add methods return the current POINT_VEC2 object, so these manipulation methods can be chained... For example:
|
||||
--
|
||||
-- local Vec2 = PointVec2:AddX( 100 ):AddY( 2000 ):GetVec2()
|
||||
--
|
||||
-- @field #POINT_VEC2
|
||||
POINT_VEC2 = {
|
||||
ClassName = "POINT_VEC2",
|
||||
}
|
||||
|
||||
|
||||
|
||||
--- POINT_VEC2 constructor.
|
||||
-- @param #POINT_VEC2 self
|
||||
-- @param DCS#Distance x The x coordinate of the Vec3 point, pointing to the North.
|
||||
-- @param DCS#Distance y The y coordinate of the Vec3 point, pointing to the Right.
|
||||
-- @param DCS#Distance LandHeightAdd (optional) The default height if required to be evaluated will be the land height of the x, y coordinate. You can specify an extra height to be added to the land height.
|
||||
-- @return Core.Point#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 ) ) -- Core.Point#POINT_VEC2
|
||||
self:F2( self )
|
||||
|
||||
return self
|
||||
--- Search for clear zones in a given area. A powerful and efficient function using Disposition to find clear areas for spawning ground units avoiding trees, water and map scenery.
|
||||
-- @param #number SearchRadius Radius of the search area.
|
||||
-- @param #number PosRadius Required clear radius around each position.
|
||||
-- @param #number NumPositions Number of positions to find.
|
||||
-- @return #table A table of Core.Point#COORDINATE that are clear of map objects within the given PosRadius. nil if no positions are found.
|
||||
function COORDINATE:GetSimpleZones(SearchRadius, PosRadius, NumPositions)
|
||||
local clearPositions = UTILS.GetSimpleZones(self:GetVec3(), SearchRadius, PosRadius, NumPositions)
|
||||
if clearPositions and #clearPositions > 0 then
|
||||
local coords = {}
|
||||
for _, pos in pairs(clearPositions) do
|
||||
local coord = COORDINATE:NewFromVec2(pos)
|
||||
table.insert(coords, coord)
|
||||
end
|
||||
return coords
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -175,7 +175,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
local Name = Info.name or "?"
|
||||
|
||||
local ErrorHandler = function( errmsg )
|
||||
env.info( "Error in timer function: " .. errmsg )
|
||||
env.info( "Error in timer function: " .. errmsg or "" )
|
||||
if BASE.Debug ~= nil then
|
||||
env.info( BASE.Debug.traceback() )
|
||||
end
|
||||
@@ -326,7 +326,7 @@ function SCHEDULEDISPATCHER:Stop( Scheduler, CallID )
|
||||
local Schedule = self.Schedule[Scheduler][CallID] -- #SCHEDULEDISPATCHER.ScheduleData
|
||||
|
||||
-- Only stop when there is a ScheduleID defined for the CallID. So, when the scheduler was stopped before, do nothing.
|
||||
if Schedule.ScheduleID then
|
||||
if Schedule and Schedule.ScheduleID then
|
||||
|
||||
self:T( string.format( "SCHEDULEDISPATCHER stopping scheduler CallID=%s, ScheduleID=%s", tostring( CallID ), tostring( Schedule.ScheduleID ) ) )
|
||||
|
||||
@@ -375,3 +375,13 @@ function SCHEDULEDISPATCHER:NoTrace( Scheduler )
|
||||
Scheduler.ShowTrace = false
|
||||
end
|
||||
|
||||
--- Helper for memory cleanup for self stopping schedulers
|
||||
-- @param #SCHEDULEDISPATCHER self
|
||||
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
||||
-- @param #string CallID (Optional) Scheduler Call ID.
|
||||
function SCHEDULEDISPATCHER:_Reclaim( Scheduler, CallID )
|
||||
self:Stop( Scheduler, CallID ) -- remove DCS timer, nil ScheduleID
|
||||
if self.Schedule[Scheduler] then self.Schedule[Scheduler][CallID] = nil end
|
||||
self.ObjectSchedulers[CallID] = nil
|
||||
self.PersistentSchedulers[CallID] = nil
|
||||
end
|
||||
|
||||
@@ -231,7 +231,7 @@ end
|
||||
-- @param #number Repeat Specifies the time interval in seconds when the scheduler will call the event function.
|
||||
-- @param #number RandomizeFactor Specifies a randomization factor between 0 and 1 to randomize the Repeat.
|
||||
-- @param #number Stop Time interval in seconds after which the scheduler will be stopped.
|
||||
-- @param #number TraceLevel Trace level [0,3]. Default 3.
|
||||
-- @param #number TraceLevel (Optional) Trace level [0,3]. Default 3.
|
||||
-- @param Core.Fsm#FSM Fsm Finite state model.
|
||||
-- @return #string The Schedule ID of the planned schedule.
|
||||
function SCHEDULER:Schedule( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop, TraceLevel, Fsm )
|
||||
|
||||
+1402
-1791
File diff suppressed because it is too large
Load Diff
@@ -802,7 +802,7 @@ end
|
||||
|
||||
--- [Airplane - F15/16/18/AWACS/B1B/Tanker only] Set the STN Link16 starting number of the Group; each unit of the spawned group will have a consecutive STN set.
|
||||
-- @param #SPAWN self
|
||||
-- @param #number Octal The octal number (digits 1..7, max 5 digits, i.e. 1..77777) to set the STN to. Every STN needs to be unique!
|
||||
-- @param #number Octal The octal number (digits 0..7, max 5 digits, i.e. 1..77777, cannot be zero) to set the STN to. Every STN needs to be unique!
|
||||
-- @return #SPAWN self
|
||||
function SPAWN:InitSTN(Octal)
|
||||
--self:F( { Octal = Octal } )
|
||||
@@ -820,7 +820,7 @@ end
|
||||
|
||||
--- [Airplane - A10-C II only] Set the SADL TN starting number of the Group; each unit of the spawned group will have a consecutive SADL set.
|
||||
-- @param #SPAWN self
|
||||
-- @param #number Octal The octal number (digits 1..7, max 4 digits, i.e. 1..7777) to set the SADL to. Every SADL needs to be unique!
|
||||
-- @param #number Octal The octal number (digits 0..7, max 4 digits, i.e. 1..7777, cannot be zero) to set the SADL to. Every SADL needs to be unique!
|
||||
-- @return #SPAWN self
|
||||
function SPAWN:InitSADL(Octal)
|
||||
--self:F( { Octal = Octal } )
|
||||
@@ -1049,6 +1049,23 @@ function SPAWN:InitSetUnitAbsolutePositions(Positions)
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Uses Disposition and other fallback logic to find better ground positions for ground units.
|
||||
--- NOTE: This is not a spawn randomizer.
|
||||
--- It will try to find clear ground locations avoiding trees, water, roads, runways, map scenery, statics and other units in the area.
|
||||
--- Maintains the original layout and unit positions as close as possible by searching for the next closest valid position to each unit.
|
||||
-- @param #SPAWN self
|
||||
-- @param #boolean OnOff Enable/disable the feature.
|
||||
-- @param #number MaxRadius (Optional) Max radius to search for valid ground locations in meters. Default is double the max radius of the units.
|
||||
-- @param #number Spacing (Optional) Minimum spacing between units in meters. Default is 5% of the search radius or 5 meters, whichever is larger.
|
||||
-- @return #SPAWN
|
||||
function SPAWN:InitValidateAndRepositionGroundUnits(OnOff, MaxRadius, Spacing)
|
||||
self.SpawnValidateAndRepositionGroundUnits = OnOff
|
||||
self.SpawnValidateAndRepositionGroundUnitsRadius = MaxRadius
|
||||
self.SpawnValidateAndRepositionGroundUnitsSpacing = Spacing
|
||||
return self
|
||||
end
|
||||
|
||||
--- This method is rather complicated to understand. But I'll try to explain.
|
||||
-- This method becomes useful when you need to spawn groups with random templates of groups defined within the mission editor,
|
||||
-- but they will all follow the same Template route and have the same prefix name.
|
||||
@@ -1167,6 +1184,7 @@ end
|
||||
|
||||
--- This method provides the functionality to randomize the spawning of the Groups at a given list of zones of different types.
|
||||
-- @param #SPAWN self
|
||||
-- @param #table SpawnZoneTable A table with @{Core.Zone} objects. If nil or empty, the method returns self without effect.
|
||||
-- @param #table SpawnZoneTable A table with @{Core.Zone} objects. If this table is given, then each spawn will be executed within the given list of @{Core.Zone}s objects.
|
||||
-- @param #boolean RandomizePositionInZone If nil or true, also the position inside the selected random zone will be randomized. Set to false to use the center of the zone.
|
||||
-- @return #SPAWN self
|
||||
@@ -1184,6 +1202,9 @@ end
|
||||
function SPAWN:InitRandomizeZones( SpawnZoneTable, RandomizePositionInZone )
|
||||
--self:F( { self.SpawnTemplatePrefix, SpawnZoneTable } )
|
||||
|
||||
if not SpawnZoneTable then
|
||||
return self
|
||||
end
|
||||
local temptable = {}
|
||||
for _,_temp in pairs(SpawnZoneTable) do
|
||||
temptable[#temptable+1] = _temp
|
||||
@@ -1224,6 +1245,16 @@ function SPAWN:InitCallSign(ID,Name,Minor,Major)
|
||||
return self
|
||||
end
|
||||
|
||||
--- [RED AIR only!] This method sets a specific callsign for a spawned group.
|
||||
-- @param #SPAWN self
|
||||
-- @param #number ID The number with which to start for the first unit, e.g. 100, further units would then be 101, 102 .. etc.
|
||||
-- @return #SPAWN self
|
||||
function SPAWN:InitCallSignRed(ID)
|
||||
self.SpawnInitCallSign = true
|
||||
self.SpawnInitCallSignID = ID or 100
|
||||
self.SpawnInitCallSignRED = true
|
||||
end
|
||||
|
||||
--- This method sets a spawn position for the group that is different from the location of the template.
|
||||
-- @param #SPAWN self
|
||||
-- @param Core.Point#COORDINATE Coordinate The position to spawn from
|
||||
@@ -1278,7 +1309,7 @@ end
|
||||
|
||||
--- Respawn group after landing.
|
||||
-- @param #SPAWN self
|
||||
-- @param #number WaitingTime Wait this many seconds before despawning the alive group after landing. Defaults to 3 .
|
||||
-- @param #number WaitingTime (Optional) Wait this many seconds before despawning the alive group after landing. Defaults to 3 .
|
||||
-- @return #SPAWN self
|
||||
-- @usage
|
||||
--
|
||||
@@ -1584,7 +1615,7 @@ end
|
||||
-- This method can be used to "reset" the spawn counter to a specific index number.
|
||||
-- This will actually enable a respawn of groups from the specific index.
|
||||
-- @param #SPAWN self
|
||||
-- @param #string SpawnIndex The index of the group from where the spawning will start again. The default value would be 0, which means a complete reset of the spawnindex.
|
||||
-- @param #string SpawnIndex (Optional) The index of the group from where the spawning will start again. The default value would be 0, which means a complete reset of the spawnindex.
|
||||
-- @return #SPAWN self
|
||||
function SPAWN:SetSpawnIndex( SpawnIndex )
|
||||
self.SpawnIndex = SpawnIndex or 0
|
||||
@@ -1650,6 +1681,9 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
|
||||
|
||||
-- If RandomizeUnits, then Randomize the formation at the start point.
|
||||
if self.SpawnRandomizeUnits then
|
||||
if self.SpawnRandomizePosition then
|
||||
PointVec3 = COORDINATE:New( SpawnTemplate.x, SpawnTemplate.route.points[1].alt, SpawnTemplate.y )
|
||||
end
|
||||
for UnitID = 1, #SpawnTemplate.units do
|
||||
local RandomVec2 = PointVec3:GetRandomVec2InRadius( self.SpawnOuterRadius, self.SpawnInnerRadius )
|
||||
if (SpawnZone) then
|
||||
@@ -1829,7 +1863,13 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
|
||||
if self.SpawnHiddenOnMap then
|
||||
SpawnTemplate.hidden=self.SpawnHiddenOnMap
|
||||
end
|
||||
|
||||
|
||||
if self.SpawnValidateAndRepositionGroundUnits then
|
||||
local units = SpawnTemplate.units
|
||||
local gPos = { x = SpawnTemplate.x, y = SpawnTemplate.y }
|
||||
UTILS.ValidateAndRepositionGroundUnits(units, gPos, self.SpawnValidateAndRepositionGroundUnitsRadius, self.SpawnValidateAndRepositionGroundUnitsSpacing)
|
||||
end
|
||||
|
||||
-- Set country, coalition and category.
|
||||
SpawnTemplate.CategoryID = self.SpawnInitCategory or SpawnTemplate.CategoryID
|
||||
SpawnTemplate.CountryID = self.SpawnInitCountry or SpawnTemplate.CountryID
|
||||
@@ -2113,6 +2153,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
local parkingspots = {}
|
||||
local parkingindex = {}
|
||||
local spots
|
||||
local useexplicitspots = false
|
||||
|
||||
-- Spawn happens on ground, i.e. at an airbase, a FARP or a ship.
|
||||
if spawnonground and not SpawnTemplate.parked then
|
||||
@@ -2142,15 +2183,41 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
local scanscenery = false
|
||||
local verysafe = false
|
||||
|
||||
-- Use exact parking data when provided, otherwise let helicopters on ships/FARPs
|
||||
-- use the smarter parking search before falling back to the procedural queue path.
|
||||
-- Number of free parking spots at the airbase.
|
||||
if autoparking then
|
||||
if Parkingdata~=nil then
|
||||
-- Parking data explicitly set by user as input parameter.
|
||||
nfree = #Parkingdata
|
||||
spots = Parkingdata
|
||||
useexplicitspots = true
|
||||
elseif autoparking and AirbaseCategory == Airbase.Category.HELIPAD and ishelo then
|
||||
if termtype == nil then
|
||||
-- Helo is spawned. Try exclusive helo spots first.
|
||||
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( group, AIRBASE.TerminalType.HelicopterOnly, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, nil )
|
||||
nfree = #spots
|
||||
if nfree < nunits then
|
||||
-- Not enough helo ports. Let's try also other terminal types.
|
||||
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( group, AIRBASE.TerminalType.HelicopterUsable, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, nil )
|
||||
nfree = #spots
|
||||
end
|
||||
else
|
||||
-- Terminal type explicitly given.
|
||||
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( group, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, nil )
|
||||
nfree = #spots
|
||||
end
|
||||
|
||||
if nfree >= nunits then
|
||||
useexplicitspots = true
|
||||
else
|
||||
-- These places work procedural and have some kind of build in queue ==> Less effort.
|
||||
nfree = SpawnAirbase:GetFreeParkingSpotsNumber( termtype, true )
|
||||
spots = SpawnAirbase:GetFreeParkingSpotsTable( termtype, true )
|
||||
end
|
||||
elseif autoparking then
|
||||
-- These places work procedural and have some kind of build in queue ==> Less effort.
|
||||
nfree = SpawnAirbase:GetFreeParkingSpotsNumber( termtype, true )
|
||||
spots = SpawnAirbase:GetFreeParkingSpotsTable( termtype, true )
|
||||
elseif Parkingdata~=nil then
|
||||
-- Parking data explicitly set by user as input parameter. (This was commented out for some unknown reason. But I need it this way.)
|
||||
nfree=#Parkingdata
|
||||
spots=Parkingdata
|
||||
else
|
||||
if ishelo then
|
||||
if termtype == nil then
|
||||
@@ -2202,7 +2269,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
local _notenough = false
|
||||
|
||||
-- Need to differentiate some cases again.
|
||||
if autoparking then
|
||||
if autoparking and not useexplicitspots then
|
||||
|
||||
-- On free spot required in these cases.
|
||||
if nfree >= 1 then
|
||||
@@ -2235,6 +2302,10 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
end
|
||||
end
|
||||
|
||||
if useexplicitspots and parkingspots[1] then
|
||||
PointVec3 = parkingspots[1]
|
||||
end
|
||||
|
||||
-- Not enough spots ==> Prepare airstart.
|
||||
if _notenough then
|
||||
|
||||
@@ -2259,9 +2330,16 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
end
|
||||
|
||||
Takeoff = GROUP.Takeoff.Air
|
||||
else
|
||||
spawnonground = false
|
||||
else
|
||||
if not Takeoff == GROUP.Takeoff.Runway then
|
||||
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
|
||||
else
|
||||
Takeoff = GROUP.Takeoff.Runway
|
||||
spawnonground = false
|
||||
self:E( string.format( "WARNING: Group %s set to runway spawning at %s, this only works in Single Player!", self.SpawnTemplatePrefix, SpawnAirbase:GetName() ) )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2302,7 +2380,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
if spawnonground then
|
||||
|
||||
-- Ships and FARPS seem to have a build in queue.
|
||||
if autoparking then
|
||||
if autoparking and not useexplicitspots then
|
||||
|
||||
-- Spawn on ship. We take only the position of the ship.
|
||||
SpawnTemplate.units[UnitID].x = PointVec3.x -- TX
|
||||
@@ -2320,7 +2398,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
|
||||
else
|
||||
|
||||
-- Spawn in air as requested initially. Original template orientation is perserved, altitude is already correctly set.
|
||||
-- Spawn in air as requested initially. Original template orientation is preserved, altitude is already correctly set.
|
||||
SpawnTemplate.units[UnitID].x = TX
|
||||
SpawnTemplate.units[UnitID].y = TY
|
||||
SpawnTemplate.units[UnitID].alt = PointVec3.y
|
||||
@@ -2353,7 +2431,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
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 ) --No need to create a new SCHEDULER instance every time!
|
||||
self:ScheduleOnce(5, BASE.CreateEventTakeoff, {GroupSpawned, timer.getTime(), UnitSpawned:GetDCSObject()})
|
||||
self:ScheduleOnce(5, BASE.CreateEventTakeoff, GroupSpawned, timer.getTime(), UnitSpawned:GetDCSObject())
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2545,21 +2623,51 @@ function SPAWN:ParkAircraft( SpawnAirbase, TerminalType, Parkingdata, SpawnIndex
|
||||
local scanscenery = false
|
||||
local verysafe = false
|
||||
|
||||
-- Number of free parking spots at the airbase.
|
||||
if spawnonship or spawnonfarp or spawnonrunway then
|
||||
-- These places work procedural and have some kind of build in queue ==> Less effort.
|
||||
--self:T2( 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 )
|
||||
--[[
|
||||
elseif Parkingdata~=nil then
|
||||
-- Parking data explicitly set by user as input parameter.
|
||||
nfree=#Parkingdata
|
||||
spots=Parkingdata
|
||||
]]
|
||||
else
|
||||
if ishelo then
|
||||
-- Use exact parking data when provided, otherwise let helicopters on FARPs
|
||||
-- use the smarter parking search before falling back to the procedural queue path.
|
||||
local useexplicitspots = false
|
||||
|
||||
-- Number of free parking spots at the airbase.
|
||||
if Parkingdata~=nil then
|
||||
-- Parking data explicitly set by user as input parameter.
|
||||
nfree = #Parkingdata
|
||||
spots = Parkingdata
|
||||
useexplicitspots = true
|
||||
elseif spawnonfarp and ishelo then
|
||||
if termtype == nil then
|
||||
-- Helo is spawned. Try exclusive helo spots first.
|
||||
--self:T2( 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, nil )
|
||||
nfree = #spots
|
||||
if nfree < nunits then
|
||||
-- Not enough helo ports. Let's try also other terminal types.
|
||||
--self:T2( string.format( "Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.HelicopterUsable ) )
|
||||
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( TemplateGroup, AIRBASE.TerminalType.HelicopterUsable, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, nil )
|
||||
nfree = #spots
|
||||
end
|
||||
else
|
||||
-- Terminal type explicitly given.
|
||||
--self:T2( string.format( "Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), termtype ) )
|
||||
spots = SpawnAirbase:FindFreeParkingSpotForAircraft( TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, nil )
|
||||
nfree = #spots
|
||||
end
|
||||
|
||||
if nfree >= nunits then
|
||||
useexplicitspots = true
|
||||
else
|
||||
-- These places work procedural and have some kind of build in queue ==> Less effort.
|
||||
--self:T2( string.format( "Group %s is spawned on farp %s using procedural spots.", self.SpawnTemplatePrefix, SpawnAirbase:GetName() ) )
|
||||
nfree = SpawnAirbase:GetFreeParkingSpotsNumber( termtype, true )
|
||||
spots = SpawnAirbase:GetFreeParkingSpotsTable( termtype, true )
|
||||
end
|
||||
elseif spawnonship or spawnonfarp or spawnonrunway then
|
||||
-- These places work procedural and have some kind of build in queue ==> Less effort.
|
||||
--self:T2( 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
|
||||
-- Helo is spawned. Try exclusive helo spots first.
|
||||
--self:T2( 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 )
|
||||
@@ -2622,7 +2730,7 @@ function SPAWN:ParkAircraft( SpawnAirbase, TerminalType, Parkingdata, SpawnIndex
|
||||
local _notenough = false
|
||||
|
||||
-- Need to differentiate some cases again.
|
||||
if spawnonship or spawnonfarp or spawnonrunway then
|
||||
if (spawnonship or spawnonfarp or spawnonrunway) and not useexplicitspots then
|
||||
|
||||
-- On free spot required in these cases.
|
||||
if nfree >= 1 then
|
||||
@@ -3495,6 +3603,11 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2
|
||||
max = 18
|
||||
ctable = CALLSIGN.F15E
|
||||
end
|
||||
if SpawnTemplate.units[1].type == "A6E" then
|
||||
min = 4
|
||||
max = 18
|
||||
ctable = CALLSIGN.Intruder
|
||||
end
|
||||
local callsignnr = math.random(min,max)
|
||||
local callsignname = "Enfield"
|
||||
for name, value in pairs(ctable) do
|
||||
@@ -3518,14 +3631,21 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2
|
||||
end
|
||||
|
||||
if self.SpawnInitCallSign then
|
||||
for UnitID = 1, #SpawnTemplate.units do
|
||||
local Callsign = SpawnTemplate.units[UnitID].callsign
|
||||
if Callsign and type( Callsign ) ~= "number" then
|
||||
SpawnTemplate.units[UnitID].callsign[1] = self.SpawnInitCallSignID
|
||||
SpawnTemplate.units[UnitID].callsign[2] = self.SpawnInitCallSignMinor
|
||||
SpawnTemplate.units[UnitID].callsign[3] = self.SpawnInitCallSignMajor
|
||||
SpawnTemplate.units[UnitID].callsign["name"] = string.format("%s%d%d",self.SpawnInitCallSignName,self.SpawnInitCallSignMinor,self.SpawnInitCallSignMajor)
|
||||
--UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].callsign,1)
|
||||
if self.SpawnInitCallSignRED == true then
|
||||
for UnitID = 1, #SpawnTemplate.units do
|
||||
SpawnTemplate.units[UnitID].callsign = self.SpawnInitCallSignID
|
||||
self.SpawnInitCallSignID = self.SpawnInitCallSignID + 1
|
||||
end
|
||||
else
|
||||
for UnitID = 1, #SpawnTemplate.units do
|
||||
local Callsign = SpawnTemplate.units[UnitID].callsign
|
||||
if Callsign and type( Callsign ) ~= "number" then
|
||||
SpawnTemplate.units[UnitID].callsign[1] = self.SpawnInitCallSignID
|
||||
SpawnTemplate.units[UnitID].callsign[2] = self.SpawnInitCallSignMinor
|
||||
SpawnTemplate.units[UnitID].callsign[3] = self.SpawnInitCallSignMajor
|
||||
SpawnTemplate.units[UnitID].callsign["name"] = string.format("%s%d%d",self.SpawnInitCallSignName,self.SpawnInitCallSignMinor,self.SpawnInitCallSignMajor)
|
||||
--UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].callsign,1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -3541,7 +3661,7 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2
|
||||
local CallsignLen = CallsignName:len()
|
||||
SpawnTemplate.units[UnitID].callsign[2] = UnitID
|
||||
SpawnTemplate.units[UnitID].callsign["name"] = CallsignName:sub( 1, CallsignLen ) .. SpawnTemplate.units[UnitID].callsign[2] .. SpawnTemplate.units[UnitID].callsign[3]
|
||||
elseif type( Callsign ) == "number" then
|
||||
elseif type( Callsign ) == "number" and self.SpawnInitCallSignRED ~= true then
|
||||
SpawnTemplate.units[UnitID].callsign = Callsign + SpawnIndex
|
||||
end
|
||||
end
|
||||
|
||||
@@ -149,6 +149,7 @@ function SPAWNSTATIC:NewFromStatic(SpawnTemplateName, SpawnCountryID)
|
||||
self.CategoryID = CategoryID
|
||||
self.CoalitionID = CoalitionID
|
||||
self.SpawnIndex = 0
|
||||
self.StaticCopyFrom = SpawnTemplateName
|
||||
else
|
||||
error( "SPAWNSTATIC:New: There is no static declared in the mission editor with SpawnTemplatePrefix = '" .. tostring(SpawnTemplateName) .. "'" )
|
||||
end
|
||||
@@ -161,7 +162,7 @@ end
|
||||
--- Creates the main object to spawn a @{Wrapper.Static} given a template table.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #table SpawnTemplate Template used for spawning.
|
||||
-- @param DCS#country.id CountryID The ID of the country. Default `country.id.USA`.
|
||||
-- @param DCS#country.id CountryID (Optional) The ID of the country. Default `country.id.USA`.
|
||||
-- @return #SPAWNSTATIC self
|
||||
function SPAWNSTATIC:NewFromTemplate(SpawnTemplate, CountryID)
|
||||
|
||||
@@ -179,7 +180,7 @@ end
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #string StaticType Type of the static.
|
||||
-- @param #string StaticCategory Category of the static, e.g. "Planes".
|
||||
-- @param DCS#country.id CountryID The ID of the country. Default `country.id.USA`.
|
||||
-- @param DCS#country.id CountryID (Optional) The ID of the country. Default `country.id.USA`.
|
||||
-- @return #SPAWNSTATIC self
|
||||
function SPAWNSTATIC:NewFromType(StaticType, StaticCategory, CountryID)
|
||||
|
||||
@@ -200,7 +201,7 @@ end
|
||||
--- (Internal/Cargo) Init the resource table for STATIC object that should be spawned containing storage objects.
|
||||
-- NOTE that you have to init many other parameters as the resources.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #number CombinedWeight The weight this cargo object should have (some have fixed weights!), defaults to 1kg.
|
||||
-- @param #number CombinedWeight (Optional) The weight this cargo object should have (some have fixed weights!), defaults to 1kg.
|
||||
-- @return #SPAWNSTATIC self
|
||||
function SPAWNSTATIC:_InitResourceTable(CombinedWeight)
|
||||
if not self.TemplateStaticUnit.resourcePayload then
|
||||
@@ -299,15 +300,19 @@ end
|
||||
|
||||
--- Initialize parameters for spawning FARPs.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #number CallsignID Callsign ID. Default 1 (="London").
|
||||
-- @param #number Frequency Frequency in MHz. Default 127.5 MHz.
|
||||
-- @param #number Modulation Modulation 0=AM, 1=FM.
|
||||
-- @param #number CallsignID (Optional) Callsign ID. Default 1 (="London").
|
||||
-- @param #number Frequency (Optional) Frequency in MHz. Default 127.5 MHz.
|
||||
-- @param #number Modulation (Optional) Modulation 0=AM, 1=FM. Defaults to 0
|
||||
-- @param #boolean DynamicSpawns If true, allow Dynamic Spawns
|
||||
-- @param #boolean DynamicHotStarts If true, and DynamicSpawns is true, then allow Dynamic Spawns with hot starts.
|
||||
-- @return #SPAWNSTATIC self
|
||||
function SPAWNSTATIC:InitFARP(CallsignID, Frequency, Modulation)
|
||||
function SPAWNSTATIC:InitFARP(CallsignID, Frequency, Modulation, DynamicSpawns,DynamicHotStarts)
|
||||
self.InitFarp=true
|
||||
self.InitFarpCallsignID=CallsignID or 1
|
||||
self.InitFarpFreq=Frequency or 127.5
|
||||
self.InitFarpModu=Modulation or 0
|
||||
self.InitFarpDynamicSpawns = DynamicSpawns
|
||||
self.InitFarpDynamicHotStarts = (DynamicSpawns == true and DynamicHotStarts == true) and true or nil
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -329,6 +334,31 @@ function SPAWNSTATIC:InitCargo(IsCargo)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Hide the static on the map view (visible to game master slots!).
|
||||
-- @param #SPAWN self
|
||||
-- @param #boolean OnOff Defaults to true
|
||||
-- @return #SPAWN The SPAWN object
|
||||
function SPAWNSTATIC:InitHiddenOnMap(OnOff)
|
||||
self.SpawnHiddenOnMap = OnOff == false and false or true
|
||||
return self
|
||||
end
|
||||
|
||||
--- Hide the static on MFDs (visible to game master slots!).
|
||||
-- @param #SPAWN self
|
||||
-- @return #SPAWN The SPAWN object
|
||||
function SPAWNSTATIC:InitHiddenOnMFD()
|
||||
self.SpawnHiddenOnMFD = true
|
||||
return self
|
||||
end
|
||||
|
||||
--- Hide the static on planner (visible to game master slots!).
|
||||
-- @param #SPAWN self
|
||||
-- @return #SPAWN The SPAWN object
|
||||
function SPAWNSTATIC:InitHiddenOnPlanner()
|
||||
self.SpawnHiddenOnPlanner = true
|
||||
return self
|
||||
end
|
||||
|
||||
--- Initialize as dead.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #boolean IsDead If true, this static is dead.
|
||||
@@ -373,6 +403,20 @@ function SPAWNSTATIC:InitLinkToUnit(Unit, OffsetX, OffsetY, OffsetAngle)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Uses Disposition and other fallback logic to find a better and valid ground spawn position.
|
||||
--- NOTE: This is not a spawn randomizer.
|
||||
--- It will try to a find clear ground location avoiding trees, water, roads, runways, map scenery, other statics and other units in the area.
|
||||
--- Uses the initial position if it's a valid location.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #boolean OnOff Enable/disable the feature.
|
||||
-- @param #number MaxRadius (Optional) Max radius to search for a valid ground location in meters. Default is 10 times the max radius of the static.
|
||||
-- @return #SPAWNSTATIC self
|
||||
function SPAWNSTATIC:InitValidateAndRepositionStatic(OnOff, MaxRadius)
|
||||
self.ValidateAndRepositionStatic = OnOff
|
||||
self.ValidateAndRepositionStaticMaxRadius = MaxRadius
|
||||
return self
|
||||
end
|
||||
|
||||
--- Allows to place a CallFunction hook when a new static spawns.
|
||||
-- The provided method will be called when a new group is spawned, including its given parameters.
|
||||
-- The first parameter of the SpawnFunction is the @{Wrapper.Static#STATIC} that was spawned.
|
||||
@@ -459,8 +503,9 @@ end
|
||||
function SPAWNSTATIC:SpawnFromZone(Zone, Heading, NewName)
|
||||
|
||||
-- Spawn the new static at the center of the zone.
|
||||
local Static = self:SpawnFromPointVec2( Zone:GetPointVec2(), Heading, NewName )
|
||||
|
||||
--local Static = self:SpawnFromPointVec2( Zone:GetPointVec2(), Heading, NewName )
|
||||
local Static = self:SpawnFromCoordinate(Zone:GetCoordinate(), Heading, NewName)
|
||||
|
||||
return Static
|
||||
end
|
||||
|
||||
@@ -513,6 +558,19 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
|
||||
Template.mass=self.InitStaticCargoMass
|
||||
end
|
||||
|
||||
-- hiding options
|
||||
if self.SpawnHiddenOnPlanner then
|
||||
Template.hiddenOnPlanner=true
|
||||
end
|
||||
|
||||
if self.SpawnHiddenOnMFD then
|
||||
Template.hiddenOnMFD=true
|
||||
end
|
||||
|
||||
if self.SpawnHiddenOnMap then
|
||||
Template.hidden=self.SpawnHiddenOnMap
|
||||
end
|
||||
|
||||
if self.InitLinkUnit then
|
||||
Template.linkUnit=self.InitLinkUnit:GetID()
|
||||
Template.linkOffset=true
|
||||
@@ -538,6 +596,14 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
|
||||
-- Add static to the game.
|
||||
local Static=nil --DCS#StaticObject
|
||||
|
||||
if self.ValidateAndRepositionStatic then
|
||||
local validPos = UTILS.ValidateAndRepositionStatic(CountryID, Template.category, Template.type, Template, Template.shape_name, self.ValidateAndRepositionStaticMaxRadius)
|
||||
if validPos then
|
||||
Template.x = validPos.x
|
||||
Template.y = validPos.y
|
||||
end
|
||||
end
|
||||
|
||||
if self.InitFarp then
|
||||
|
||||
local TemplateGroup={}
|
||||
@@ -549,6 +615,13 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
|
||||
TemplateGroup.x=Template.x
|
||||
TemplateGroup.y=Template.y
|
||||
TemplateGroup.name=Template.name
|
||||
|
||||
if self.InitFarpDynamicSpawns == true then
|
||||
TemplateGroup.units[1].dynamicSpawn = true
|
||||
if self.InitFarpDynamicHotStarts == true then
|
||||
TemplateGroup.units[1].allowHotStart = true
|
||||
end
|
||||
end
|
||||
|
||||
self:T("Spawning FARP")
|
||||
self:T({Template=Template})
|
||||
@@ -556,7 +629,8 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
|
||||
|
||||
-- ED's dirty way to spawn FARPS.
|
||||
Static=coalition.addGroup(CountryID, -1, TemplateGroup)
|
||||
|
||||
--Static=coalition.addStaticObject(CountryID, Template)
|
||||
|
||||
-- Currently DCS 2.8 does not trigger birth events if FARPS are spawned!
|
||||
-- We create such an event. The airbase is registered in Core.Event
|
||||
local Event = {
|
||||
@@ -594,6 +668,18 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
|
||||
-- delay calling this for .3 seconds so that it hopefully comes after the BIRTH event of the group.
|
||||
self:ScheduleOnce(0.3, self.SpawnFunctionHook, mystatic, unpack(self.SpawnFunctionArguments))
|
||||
end
|
||||
|
||||
if self.StaticCopyFrom ~= nil then
|
||||
mystatic.StaticCopyFrom = self.StaticCopyFrom
|
||||
end
|
||||
|
||||
local TemplateGroup={}
|
||||
TemplateGroup.units={}
|
||||
TemplateGroup.units[1]=Template
|
||||
TemplateGroup.x=Template.x
|
||||
TemplateGroup.y=Template.y
|
||||
TemplateGroup.name=Template.name
|
||||
_DATABASE:_RegisterStaticTemplate( TemplateGroup, self.CoalitionID, self.CategoryID, CountryID )
|
||||
|
||||
return mystatic
|
||||
end
|
||||
|
||||
@@ -388,7 +388,7 @@ do
|
||||
|
||||
--- Set laser start position relative to the lasing unit.
|
||||
-- @param #SPOT self
|
||||
-- @param #table position Start position of the laser relative to the lasing unit. Default is { x = 0, y = 2, z = 0 }
|
||||
-- @param #table position (Optional) Start position of the laser relative to the lasing unit. Default is { x = 0, y = 2, z = 0 }
|
||||
-- @return #SPOT self
|
||||
-- @usage
|
||||
-- -- Set lasing position to be the position of the optics of the Gazelle M:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -70,6 +70,7 @@
|
||||
-- @field #table Table of any trigger zone properties from the ME. The key is the Name of the property, and the value is the property's Value.
|
||||
-- @field #number Surface Type of surface. Only determined at the center of the zone!
|
||||
-- @field #number Checktime Check every Checktime seconds, used for ZONE:Trigger()
|
||||
-- @field #boolean PartlyInside When called, a GROUP is considered inside as soon as any of its units enters the zone even if they are far apart.
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
|
||||
@@ -368,7 +369,7 @@ end
|
||||
|
||||
--- Set draw coalition of zone.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param #number Coalition Coalition. Default -1.
|
||||
-- @param #number Coalition (Optional) Coalition. Default -1.
|
||||
-- @return #ZONE_BASE self
|
||||
function ZONE_BASE:SetDrawCoalition(Coalition)
|
||||
self.drawCoalition=Coalition or -1
|
||||
@@ -384,8 +385,8 @@ end
|
||||
|
||||
--- Set color of zone.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param #table RGBcolor RGB color table. Default `{1, 0, 0}`.
|
||||
-- @param #number Alpha Transparency between 0 and 1. Default 0.15.
|
||||
-- @param #table RGBcolor (Optional) RGB color table. Default `{1, 0, 0}`.
|
||||
-- @param #number Alpha (Optional) Transparency between 0 and 1. Default 0.15.
|
||||
-- @return #ZONE_BASE self
|
||||
function ZONE_BASE:SetColor(RGBcolor, Alpha)
|
||||
|
||||
@@ -431,8 +432,8 @@ end
|
||||
|
||||
--- Set fill color of zone.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param #table RGBcolor RGB color table. Default `{1, 0, 0}`.
|
||||
-- @param #number Alpha Transparacy between 0 and 1. Default 0.15.
|
||||
-- @param #table RGBcolor (Optional) RGB color table. Default `{1, 0, 0}`.
|
||||
-- @param #number Alpha (Optional) Transparacy between 0 and 1. Default 0.15.
|
||||
-- @return #ZONE_BASE self
|
||||
function ZONE_BASE:SetFillColor(RGBcolor, Alpha)
|
||||
|
||||
@@ -534,6 +535,19 @@ function ZONE_BASE:GetZoneProbability()
|
||||
return self.ZoneProbability
|
||||
end
|
||||
|
||||
--- Get the coordinate on the radius of the zone nearest to Outsidecoordinate. Useto e.g. find an ingress point.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param Core.Point#COORDINATE Outsidecoordinate The coordinate outside of the zone from where to look.
|
||||
-- @return Core.Point#COORDINATE CoordinateOnRadius
|
||||
function ZONE_BASE:FindNearestCoordinateOnRadius(Outsidecoordinate)
|
||||
local Vec1 = self:GetVec2()
|
||||
local Radius = self:GetRadius()
|
||||
local Vec2 = Outsidecoordinate:GetVec2()
|
||||
local Point = UTILS.FindNearestPointOnCircle(Vec1,Radius,Vec2)
|
||||
local rc = COORDINATE:NewFromVec2(Point)
|
||||
return rc
|
||||
end
|
||||
|
||||
--- Get the zone taking into account the randomization probability of a zone to be selected.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @return #ZONE_BASE The zone is selected taking into account the randomization probability factor.
|
||||
@@ -572,7 +586,7 @@ end
|
||||
|
||||
--- Set the check time for ZONE:Trigger()
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param #number seconds Check every seconds for objects entering or leaving the zone. Defaults to 5 secs.
|
||||
-- @param #number seconds (Optional) Check every seconds for objects entering or leaving the zone. Defaults to 5 secs.
|
||||
-- @return #ZONE_BASE self
|
||||
function ZONE_BASE:SetCheckTime(seconds)
|
||||
self.Checktime = seconds or 5
|
||||
@@ -599,6 +613,8 @@ end
|
||||
--
|
||||
-- -- Stop watching the zone after 1 hour
|
||||
-- triggerzone:__TriggerStop(3600)
|
||||
-- -- Call :SetPartlyInside() if you use SET_GROUP to count as inside when any of their units enters even when they are far apart.
|
||||
-- -- Make sure to call :SetPartlyInside() before :Trigger()!
|
||||
function ZONE_BASE:Trigger(Objects)
|
||||
--self:I("Added Zone Trigger")
|
||||
self:SetStartState("TriggerStopped")
|
||||
@@ -667,6 +683,16 @@ function ZONE_BASE:Trigger(Objects)
|
||||
|
||||
end
|
||||
|
||||
--- Toggle “partly-inside” handling for this zone. To be used before :Trigger().
|
||||
-- * Default:* flag is **false** until you call the method.
|
||||
-- * Call with no argument or with **true** → enable.
|
||||
-- * Call with **false** → disable again (handy if it was enabled before).
|
||||
-- @param #ZONE_BASE self
|
||||
-- @return #ZONE_BASE self
|
||||
function ZONE_BASE:SetPartlyInside(state)
|
||||
self.PartlyInside = state or not ( state == false )
|
||||
return self
|
||||
end
|
||||
--- (Internal) Check the assigned objects for being in/out of the zone
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param #boolean fromstart If true, do the init of the objects
|
||||
@@ -705,7 +731,12 @@ function ZONE_BASE:_TriggerCheck(fromstart)
|
||||
obj.TriggerInZone[self.ZoneName] = false
|
||||
end
|
||||
-- is obj in zone?
|
||||
local inzone = self:IsCoordinateInZone(obj:GetCoordinate())
|
||||
local inzone
|
||||
if self.PartlyInside and obj.ClassName == "GROUP" then
|
||||
inzone = obj:IsAnyInZone(self) -- TRUE if any unit is inside
|
||||
else
|
||||
inzone = self:IsCoordinateInZone(obj:GetCoordinate()) -- original barycentre test
|
||||
end
|
||||
--self:I("Object "..obj:GetName().." is in zone: "..tostring(inzone))
|
||||
if inzone and obj.TriggerInZone[self.ZoneName] then
|
||||
-- just count
|
||||
@@ -752,7 +783,7 @@ end
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string to
|
||||
-- @param #string To
|
||||
-- @return #ZONE_BASE self
|
||||
function ZONE_BASE:onafterTriggerRunCheck(From,Event,To)
|
||||
if self:GetState() ~= "TriggerStopped" then
|
||||
@@ -831,7 +862,7 @@ ZONE_RADIUS = {
|
||||
-- @param #string ZoneName Name of the zone.
|
||||
-- @param DCS#Vec2 Vec2 The location of the zone.
|
||||
-- @param DCS#Distance Radius The radius of the zone.
|
||||
-- @param DCS#Boolean DoNotRegisterZone Determines if the Zone should not be registered in the _Database Table. Default=false
|
||||
-- @param DCS#Boolean DoNotRegisterZone (Optional) Determines if the Zone should not be registered in the _Database Table. Default=false
|
||||
-- @return #ZONE_RADIUS self
|
||||
function ZONE_RADIUS:New( ZoneName, Vec2, Radius, DoNotRegisterZone )
|
||||
|
||||
@@ -915,12 +946,12 @@ end
|
||||
|
||||
--- Draw the zone circle on the F10 map.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red.
|
||||
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||
-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value.
|
||||
-- @param #number FillAlpha Transparency [0,1]. Default 0.15.
|
||||
-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||
-- @param #number Coalition (Optional) Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||
-- @param #table Color (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red.
|
||||
-- @param #number Alpha (Optional) Transparency [0,1]. Default 1.
|
||||
-- @param #table FillColor (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value.
|
||||
-- @param #number FillAlpha (Optional) Transparency [0,1]. Default 0.15.
|
||||
-- @param #number LineType (Optional) Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.
|
||||
-- @return #ZONE_RADIUS self
|
||||
function ZONE_RADIUS:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
|
||||
@@ -1127,17 +1158,17 @@ end
|
||||
-- myzone:Scan({Object.Category.UNIT},{Unit.Category.GROUND_UNIT})
|
||||
-- local IsAttacked = myzone:IsSomeInZoneOfCoalition( self.Coalition )
|
||||
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 ZoneCoord = self:GetCoordinate():SetAlt()
|
||||
local ZoneRadius = self:GetRadius()
|
||||
|
||||
--self:F({ZoneCoord = ZoneCoord, ZoneRadius = ZoneRadius, ZoneCoordLL = ZoneCoord:ToStringLLDMS()})
|
||||
--self:I({x = ZoneCoord.x, y=ZoneCoord.y, z=ZoneCoord.z, ZoneRadius = ZoneRadius})
|
||||
|
||||
local SphereSearch = {
|
||||
id = world.VolumeType.SPHERE,
|
||||
@@ -1149,15 +1180,14 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories )
|
||||
|
||||
local function EvaluateZone( ZoneObject )
|
||||
--if ZoneObject:isExist() then --FF: isExist always returns false for SCENERY objects since DCS 2.2 and still in DCS 2.5
|
||||
if ZoneObject then
|
||||
|
||||
if ZoneObject and self:IsVec3InZone(ZoneObject:getPoint()) then
|
||||
|
||||
-- Get object category.
|
||||
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
|
||||
-- Anything found is included.
|
||||
@@ -1236,38 +1266,48 @@ end
|
||||
|
||||
--- Get a set of scanned units.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param #number Coalition (optional) Filter for this coalition only.
|
||||
-- @return Core.Set#SET_UNIT Set of units and statics inside the zone.
|
||||
function ZONE_RADIUS:GetScannedSetUnit()
|
||||
|
||||
local SetUnit = SET_UNIT:New()
|
||||
function ZONE_RADIUS:GetScannedSetUnit(Coalition)
|
||||
|
||||
self.SetUnit = self.SetUnit or SET_UNIT:New()
|
||||
self.SetUnit:Clear(false)
|
||||
self.SetUnit.Set={}
|
||||
|
||||
if self.ScanData then
|
||||
for ObjectID, UnitObject in pairs( self.ScanData.Units ) do
|
||||
local UnitObject = UnitObject -- DCS#Unit
|
||||
if UnitObject:isExist() then
|
||||
local FoundUnit = UNIT:FindByName( UnitObject:getName() )
|
||||
if FoundUnit then
|
||||
SetUnit:AddUnit( FoundUnit )
|
||||
local FoundCoalition = FoundUnit and FoundUnit:GetCoalition() or nil
|
||||
local includeoncoalition = true
|
||||
if Coalition ~= nil and FoundCoalition==Coalition then includeoncoalition = true else includeoncoalition = false end
|
||||
if Coalition == nil then includeoncoalition = true end
|
||||
--self:I(string.format("Unit name %s coalition %s filter coalition = %s include = %s",FoundUnit:GetName(),tostring(FoundCoalition),tostring(Coalition),tostring(includeoncoalition)))
|
||||
if FoundUnit and includeoncoalition then
|
||||
self.SetUnit:AddUnit( FoundUnit )
|
||||
else
|
||||
local FoundStatic = STATIC:FindByName( UnitObject:getName(), false )
|
||||
if FoundStatic then
|
||||
SetUnit:AddUnit( FoundStatic )
|
||||
self.SetUnit:AddUnit( FoundStatic )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return SetUnit
|
||||
return self.SetUnit
|
||||
end
|
||||
|
||||
--- Get a set of scanned groups.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param #number Coalition (optional) Filter for this coalition only.
|
||||
-- @return Core.Set#SET_GROUP Set of groups.
|
||||
function ZONE_RADIUS:GetScannedSetGroup()
|
||||
function ZONE_RADIUS:GetScannedSetGroup(Coalition)
|
||||
|
||||
self.ScanSetGroup=self.ScanSetGroup or SET_GROUP:New() --Core.Set#SET_GROUP
|
||||
|
||||
|
||||
self.ScanSetGroup:Clear(false)
|
||||
self.ScanSetGroup.Set={}
|
||||
|
||||
if self.ScanData then
|
||||
@@ -1276,7 +1316,12 @@ function ZONE_RADIUS:GetScannedSetGroup()
|
||||
if UnitObject:isExist() then
|
||||
|
||||
local FoundUnit=UNIT:FindByName(UnitObject:getName())
|
||||
if FoundUnit then
|
||||
local FoundCoalition = FoundUnit and FoundUnit:GetCoalition() or nil
|
||||
local includeoncoalition = true
|
||||
if Coalition ~= nil and FoundCoalition==Coalition then includeoncoalition = true else includeoncoalition = false end
|
||||
if Coalition == nil then includeoncoalition = true end
|
||||
--self:I(string.format("Unit name %s coalition %s filter coalition = %s include = %s",FoundUnit:GetName(),tostring(FoundCoalition),tostring(Coalition),tostring(includeoncoalition)))
|
||||
if FoundUnit and includeoncoalition then
|
||||
local group=FoundUnit:GetGroup()
|
||||
self.ScanSetGroup:AddGroup(group)
|
||||
end
|
||||
@@ -1509,6 +1554,26 @@ function ZONE_RADIUS:IsVec3InZone( Vec3 )
|
||||
return InZone
|
||||
end
|
||||
|
||||
--- Search for clear ground spawn zones within this zone. A powerful and efficient function using Disposition to find clear areas for spawning ground units avoiding trees, water and map scenery.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param #number PosRadius Required clear radius around each position.
|
||||
-- @param #number NumPositions Number of positions to find.
|
||||
-- @return #table A table of DCS#Vec2 positions that are clear of map objects within the given PosRadius. nil if no clear positions are found.
|
||||
function ZONE_RADIUS:GetClearZonePositions(PosRadius, NumPositions)
|
||||
return UTILS.GetClearZonePositions(self, PosRadius, NumPositions)
|
||||
end
|
||||
|
||||
|
||||
--- Search for a random clear ground spawn coordinate within this zone. A powerful and efficient function using Disposition to find clear areas for spawning ground units avoiding trees, water and map scenery.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param #number PosRadius (Optional) Required clear radius around each position. (Default is math.min(Radius/10, 200))
|
||||
-- @param #number NumPositions (Optional) Number of positions to find. (Default 50)
|
||||
-- @return Core.Point#COORDINATE A random coordinate for a clear zone. nil if no clear positions are found.
|
||||
-- @return #number Assigned radius for the found zones. nil if no clear positions are found.
|
||||
function ZONE_RADIUS:GetRandomClearZoneCoordinate(PosRadius, NumPositions)
|
||||
return UTILS.GetRandomClearZoneCoordinate(self, PosRadius, NumPositions)
|
||||
end
|
||||
|
||||
--- Returns a random Vec2 location within the zone.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param #number inner (Optional) Minimal distance from the center of the zone. Default is 0.
|
||||
@@ -1520,6 +1585,10 @@ function ZONE_RADIUS:GetRandomVec2(inner, outer, surfacetypes)
|
||||
local Vec2 = self:GetVec2()
|
||||
local _inner = inner or 0
|
||||
local _outer = outer or self:GetRadius()
|
||||
|
||||
math.random()
|
||||
math.random()
|
||||
math.random()
|
||||
|
||||
if surfacetypes and type(surfacetypes)~="table" then
|
||||
surfacetypes={surfacetypes}
|
||||
@@ -1881,6 +1950,21 @@ function ZONE_UNIT:New( ZoneName, ZoneUNIT, Radius, Offset)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Updates the current location from a @{Wrapper.Group}.
|
||||
-- @param #ZONE_UNIT self
|
||||
-- @param Wrapper.Group#GROUP Group (optional) Update from this Unit, if nil, update from the UNIT this zone is based on.
|
||||
-- @return self
|
||||
function ZONE_UNIT:UpdateFromUnit(Unit)
|
||||
if Unit and Unit:IsAlive() then
|
||||
local vec2 = Unit:GetVec2()
|
||||
self.LastVec2 = vec2
|
||||
elseif self.ZoneUNIT and self.ZoneUNIT:IsAlive() then
|
||||
local ZoneVec2 = self.ZoneUNIT:GetVec2()
|
||||
self.LastVec2 = ZoneVec2
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Returns the current location of the @{Wrapper.Unit#UNIT}.
|
||||
-- @param #ZONE_UNIT self
|
||||
@@ -2018,6 +2102,22 @@ function ZONE_GROUP:GetVec2()
|
||||
return ZoneVec2
|
||||
end
|
||||
|
||||
--- Updates the current location from a @{Wrapper.Group}.
|
||||
-- @param #ZONE_GROUP self
|
||||
-- @param Wrapper.Group#GROUP Group (optional) Update from this Group, if nil, update from the GROUP this zone is based on.
|
||||
-- @return self
|
||||
function ZONE_GROUP:UpdateFromGroup(Group)
|
||||
if Group and Group:IsAlive() then
|
||||
local vec2 = Group:GetVec2()
|
||||
self.Vec2 = vec2
|
||||
elseif self._.ZoneGROUP and self._.ZoneGROUP:IsAlive() then
|
||||
local ZoneVec2 = self._.ZoneGROUP:GetVec2()
|
||||
self.Vec2 = ZoneVec2
|
||||
self._.ZoneVec2Cache = ZoneVec2
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Returns a random location within the zone of the @{Wrapper.Group}.
|
||||
-- @param #ZONE_GROUP self
|
||||
-- @return DCS#Vec2 The random location of the zone based on the @{Wrapper.Group} location.
|
||||
@@ -2406,7 +2506,7 @@ end
|
||||
|
||||
--- Get a vertex of the polygon.
|
||||
-- @param #ZONE_POLYGON_BASE self
|
||||
-- @param #number Index Index of the vertex. Default 1.
|
||||
-- @param #number Index (Optional) Index of the vertex. Default 1.
|
||||
-- @return DCS#Vec2 Vertex of the polygon.
|
||||
function ZONE_POLYGON_BASE:GetVertexVec2(Index)
|
||||
return self._.Polygon[Index or 1]
|
||||
@@ -2414,7 +2514,7 @@ end
|
||||
|
||||
--- Get a vertex of the polygon.
|
||||
-- @param #ZONE_POLYGON_BASE self
|
||||
-- @param #number Index Index of the vertex. Default 1.
|
||||
-- @param #number Index (Optional) Index of the vertex. Default 1.
|
||||
-- @return DCS#Vec3 Vertex of the polygon.
|
||||
function ZONE_POLYGON_BASE:GetVertexVec3(Index)
|
||||
local vec2=self:GetVertexVec2(Index)
|
||||
@@ -2427,7 +2527,7 @@ end
|
||||
|
||||
--- Get a vertex of the polygon.
|
||||
-- @param #ZONE_POLYGON_BASE self
|
||||
-- @param #number Index Index of the vertex. Default 1.
|
||||
-- @param #number Index (Optional) Index of the vertex. Default 1.
|
||||
-- @return Core.Point#COORDINATE Vertex of the polygon.
|
||||
function ZONE_POLYGON_BASE:GetVertexCoordinate(Index)
|
||||
local vec2=self:GetVertexVec2(Index)
|
||||
@@ -2487,6 +2587,26 @@ function ZONE_POLYGON_BASE:Flush()
|
||||
return self
|
||||
end
|
||||
|
||||
--- Search for clear ground spawn zones within this zone. A powerful and efficient function using Disposition to find clear areas for spawning ground units avoiding trees, water and map scenery.
|
||||
-- @param #ZONE_POLYGON_BASE self
|
||||
-- @param #number PosRadius Required clear radius around each position.
|
||||
-- @param #number NumPositions Number of positions to find.
|
||||
-- @return #table A table of DCS#Vec2 positions that are clear of map objects within the given PosRadius. nil if no clear positions are found.
|
||||
function ZONE_POLYGON_BASE:GetClearZonePositions(PosRadius, NumPositions)
|
||||
return UTILS.GetClearZonePositions(self, PosRadius, NumPositions)
|
||||
end
|
||||
|
||||
|
||||
--- Search for a random clear ground spawn coordinate within this zone. A powerful and efficient function using Disposition to find clear areas for spawning ground units avoiding trees, water and map scenery.
|
||||
-- @param #ZONE_POLYGON_BASE self
|
||||
-- @param #number PosRadius (Optional) Required clear radius around each position. (Default is math.min(Radius/10, 200))
|
||||
-- @param #number NumPositions (Optional) Number of positions to find. (Default 50)
|
||||
-- @return Core.Point#COORDINATE A random coordinate for a clear zone. nil if no clear positions are found.
|
||||
-- @return #number Assigned radius for the found zones. nil if no clear positions are found.
|
||||
function ZONE_POLYGON_BASE:GetRandomClearZoneCoordinate(PosRadius, NumPositions)
|
||||
return UTILS.GetRandomClearZoneCoordinate(self, PosRadius, NumPositions)
|
||||
end
|
||||
|
||||
--- Smokes the zone boundaries in a color.
|
||||
-- @param #ZONE_POLYGON_BASE self
|
||||
-- @param #boolean UnBound If true, the tyres will be destroyed.
|
||||
@@ -2537,12 +2657,12 @@ end
|
||||
--- Draw the zone on the F10 map. Infinite number of points supported
|
||||
--- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Polygon.lua
|
||||
-- @param #ZONE_POLYGON_BASE self
|
||||
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red.
|
||||
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||
-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value. -- doesn't seem to work
|
||||
-- @param #number FillAlpha Transparency [0,1]. Default 0.15. -- doesn't seem to work
|
||||
-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||
-- @param #number Coalition (Optional) Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||
-- @param #table Color (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red.
|
||||
-- @param #number Alpha (Optional) Transparency [0,1]. Default 1.
|
||||
-- @param #table FillColor (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value. -- doesn't seem to work
|
||||
-- @param #number FillAlpha (Optional) Transparency [0,1]. Default 0.15. -- doesn't seem to work
|
||||
-- @param #number LineType (Optional) Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.s
|
||||
-- @return #ZONE_POLYGON_BASE self
|
||||
function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly, IncludeTriangles)
|
||||
@@ -2590,7 +2710,7 @@ end
|
||||
--- Change/Re-fill a Polygon Zone
|
||||
-- @param #ZONE_POLYGON_BASE self
|
||||
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red.
|
||||
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||
-- @param #number Alpha (Optional) Transparency [0,1]. Default 1.
|
||||
-- @return #ZONE_POLYGON_BASE self
|
||||
function ZONE_POLYGON_BASE:ReFill(Color,Alpha)
|
||||
local color = Color or self:GetFillColorRGB() or {1,0,0}
|
||||
@@ -2620,8 +2740,8 @@ end
|
||||
--- Change/Re-draw the border of a Polygon Zone
|
||||
-- @param #ZONE_POLYGON_BASE self
|
||||
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red.
|
||||
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||
-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||
-- @param #number Alpha (Optional) Transparency [0,1]. Default 1.
|
||||
-- @param #number LineType (Optional) Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||
-- @return #ZONE_POLYGON_BASE
|
||||
function ZONE_POLYGON_BASE:ReDrawBorderline(Color, Alpha, LineType)
|
||||
local color = Color or self:GetFillColorRGB() or {1,0,0}
|
||||
@@ -2720,7 +2840,7 @@ end
|
||||
|
||||
--- Remove junk inside the zone. Due to DCS limitations, this works only for rectangular zones. So we get the smallest rectangular zone encompassing all points points of the polygon zone.
|
||||
-- @param #ZONE_POLYGON_BASE self
|
||||
-- @param #number Height Height of the box in meters. Default 1000.
|
||||
-- @param #number Height (Optional) Height of the box in meters. Default 1000.
|
||||
-- @return #number Number of removed objects.
|
||||
function ZONE_POLYGON_BASE:RemoveJunk(Height)
|
||||
|
||||
@@ -2865,6 +2985,11 @@ end
|
||||
function ZONE_POLYGON_BASE:GetRandomVec2()
|
||||
-- make sure we assign weights to the triangles based on their surface area, otherwise
|
||||
-- we'll be more likely to generate random points in smaller triangles
|
||||
|
||||
math.random()
|
||||
math.random()
|
||||
math.random()
|
||||
|
||||
local weights = {}
|
||||
for _, triangle in pairs(self._Triangles) do
|
||||
weights[triangle] = triangle.SurfaceArea / self.SurfaceArea
|
||||
@@ -3204,12 +3329,7 @@ function ZONE_POLYGON:Scan( ObjectCategories, UnitCategories )
|
||||
|
||||
local vectors = self:GetBoundingSquare()
|
||||
|
||||
local minVec3 = {x=vectors.x1, y=0, z=vectors.y1}
|
||||
local maxVec3 = {x=vectors.x2, y=0, z=vectors.y2}
|
||||
|
||||
local minmarkcoord = COORDINATE:NewFromVec3(minVec3)
|
||||
local maxmarkcoord = COORDINATE:NewFromVec3(maxVec3)
|
||||
local ZoneRadius = minmarkcoord:Get2DDistance(maxmarkcoord)/2
|
||||
local ZoneRadius = UTILS.VecDist2D({x=vectors.x1, y=vectors.y1}, {x=vectors.x2, y=vectors.y2})/2
|
||||
-- self:I("Scan Radius:" ..ZoneRadius)
|
||||
local CenterVec3 = self:GetCoordinate():GetVec3()
|
||||
|
||||
@@ -3233,14 +3353,12 @@ function ZONE_POLYGON:Scan( ObjectCategories, UnitCategories )
|
||||
|
||||
local function EvaluateZone( ZoneObject )
|
||||
|
||||
if ZoneObject then
|
||||
if ZoneObject and self:IsVec3InZone(ZoneObject:getPoint()) 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
|
||||
-- Anything found is included.
|
||||
@@ -3272,13 +3390,13 @@ function ZONE_POLYGON:Scan( ObjectCategories, UnitCategories )
|
||||
end
|
||||
|
||||
-- trying with box search
|
||||
if ObjectCategory == Object.Category.SCENERY and self:IsVec3InZone(ZoneObject:getPoint()) then
|
||||
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( SceneryName, ZoneObject )
|
||||
table.insert(self.ScanData.SceneryTable,self.ScanData.Scenery[SceneryType][SceneryName])
|
||||
--self:T( { SCENERY = self.ScanData.Scenery[SceneryType][SceneryName] } )
|
||||
--self:I( { SCENERY = self.ScanData.Scenery[SceneryType][SceneryName] } )
|
||||
end
|
||||
|
||||
end
|
||||
@@ -3643,8 +3761,8 @@ do -- ZONE_ELASTIC
|
||||
--- Update the convex hull of the polygon.
|
||||
-- This uses the [Graham scan](https://en.wikipedia.org/wiki/Graham_scan).
|
||||
-- @param #ZONE_ELASTIC self
|
||||
-- @param #number Delay Delay in seconds before the zone is updated. Default 0.
|
||||
-- @param #boolean Draw Draw the zone. Default `nil`.
|
||||
-- @param #number Delay (Optional) Delay in seconds before the zone is updated. Default 0.
|
||||
-- @param #boolean Draw (Optional) Draw the zone. Default `nil`.
|
||||
-- @return #ZONE_ELASTIC self
|
||||
function ZONE_ELASTIC:Update(Delay, Draw)
|
||||
|
||||
@@ -3685,9 +3803,9 @@ do -- ZONE_ELASTIC
|
||||
--- Start the updating scheduler.
|
||||
-- @param #ZONE_ELASTIC self
|
||||
-- @param #number Tstart Time in seconds before the updating starts.
|
||||
-- @param #number dT Time interval in seconds between updates. Default 60 sec.
|
||||
-- @param #number Tstop Time in seconds after which the updating stops. Default `nil`.
|
||||
-- @param #boolean Draw Draw the zone. Default `nil`.
|
||||
-- @param #number dT (Optional) Time interval in seconds between updates. Default 60 sec.
|
||||
-- @param #number Tstop (Optional) Time in seconds after which the updating stops. Default `nil`.
|
||||
-- @param #boolean Draw (Optional) Draw the zone. Default `nil`.
|
||||
-- @return #ZONE_ELASTIC self
|
||||
function ZONE_ELASTIC:StartUpdate(Tstart, dT, Tstop, Draw)
|
||||
|
||||
@@ -3698,7 +3816,7 @@ do -- ZONE_ELASTIC
|
||||
|
||||
--- Stop the updating scheduler.
|
||||
-- @param #ZONE_ELASTIC self
|
||||
-- @param #number Delay Delay in seconds before the scheduler will be stopped. Default 0.
|
||||
-- @param #number Delay (Optional) Delay in seconds before the scheduler will be stopped. Default 0.
|
||||
-- @return #ZONE_ELASTIC self
|
||||
function ZONE_ELASTIC:StopUpdate(Delay)
|
||||
|
||||
@@ -3948,12 +4066,12 @@ end
|
||||
--- Draw the zone on the F10 map.
|
||||
--- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua
|
||||
-- @param #ZONE_OVAL self
|
||||
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red.
|
||||
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||
-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value. -- doesn't seem to work
|
||||
-- @param #number FillAlpha Transparency [0,1]. Default 0.15. -- doesn't seem to work
|
||||
-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||
-- @param #number Coalition (Optional) Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||
-- @param #table Color (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red.
|
||||
-- @param #number Alpha (Optional) Transparency [0,1]. Default 1.
|
||||
-- @param #table FillColor (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value. -- doesn't seem to work
|
||||
-- @param #number FillAlpha (Optional) Transparency [0,1]. Default 0.15. -- doesn't seem to work
|
||||
-- @param #number LineType (Optional) Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.
|
||||
-- @return #ZONE_OVAL self
|
||||
function ZONE_OVAL:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType)
|
||||
|
||||
@@ -131,7 +131,7 @@ do -- world
|
||||
|
||||
--- Returns a table of DCS airbase objects.
|
||||
-- @function [parent=#world] getAirbases
|
||||
-- @param #number coalitionId The coalition side number ID. Default is all airbases are returned.
|
||||
-- @param #number coalitionId (Optional) The coalition side number ID. Default is all airbases are returned.
|
||||
-- @return #table Table of DCS airbase objects.
|
||||
|
||||
|
||||
@@ -614,6 +614,7 @@ do -- Object
|
||||
-- @field SCENERY
|
||||
-- @field CARGO
|
||||
|
||||
---
|
||||
-- @type Object.Desc
|
||||
-- @extends #Desc
|
||||
-- @field #number life initial life level
|
||||
@@ -1033,7 +1034,7 @@ do -- Spot
|
||||
--- Sets the number that is used to define the laser code for which laser designation can track.
|
||||
-- @function [parent=#Spot] setCode
|
||||
-- @param #Spot self
|
||||
-- @param #number Code The laser code. Default value is 1688.
|
||||
-- @param #number Code (Optional) The laser code. Default value is 1688.
|
||||
|
||||
--- Destroys the spot.
|
||||
-- @function [parent=#Spot] destroy
|
||||
@@ -1672,6 +1673,9 @@ do -- AI
|
||||
-- @field OPTION_RADIO_USAGE_KILL
|
||||
-- @field JETT_TANKS_IF_EMPTY
|
||||
-- @field FORCED_ATTACK
|
||||
-- @field PREFER_VERTICAL
|
||||
-- @field ALLOW_FORMATION_SIDE_SWAP
|
||||
-- @field AI_RUNWAY_LINE_UP
|
||||
|
||||
---
|
||||
-- @type AI.Option.Air.val
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **Applevangelist**
|
||||
-- Last Update Sept 2023
|
||||
-- Last Update July 2025
|
||||
--
|
||||
-- ===
|
||||
-- @module Functional.AICSAR
|
||||
@@ -57,6 +57,8 @@
|
||||
-- @field #number Speed Default speed setting for the helicopter FLIGHTGROUP is 100kn.
|
||||
-- @field #boolean UseEventEject In case Event LandingAfterEjection isn't working, use set this to true.
|
||||
-- @field #number Delay In case of UseEventEject wait this long until we spawn a landed pilot.
|
||||
-- @field #boolean UseRescueZone If true, use a rescue zone and not the max distance to FARP/MASH
|
||||
-- @field Core.Zone#ZONE_RADIUS RescueZone Use this zone as operational area for the AICSAR instance.
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
|
||||
@@ -153,10 +155,10 @@
|
||||
-- To set up AICSAR for SRS TTS output, add e.g. the following to your script:
|
||||
--
|
||||
-- -- setup for google TTS, radio 243 AM, SRS server port 5002 with a google standard-quality voice (google cloud account required)
|
||||
-- my_aicsar:SetSRSTTSRadio(true,"C:\\Program Files\\DCS-SimpleRadio-Standalone",243,radio.modulation.AM,5002,MSRS.Voices.Google.Standard.en_US_Standard_D,"en-US","female","C:\\Program Files\\DCS-SimpleRadio-Standalone\\google.json")
|
||||
-- my_aicsar:SetSRSTTSRadio(true,"C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio",243,radio.modulation.AM,5002,MSRS.Voices.Google.Standard.en_US_Standard_D,"en-US","female","C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio\\google.json")
|
||||
--
|
||||
-- -- alternatively for MS Desktop TTS (voices need to be installed locally first!)
|
||||
-- my_aicsar:SetSRSTTSRadio(true,"C:\\Program Files\\DCS-SimpleRadio-Standalone",243,radio.modulation.AM,5002,MSRS.Voices.Microsoft.Hazel,"en-GB","female")
|
||||
-- my_aicsar:SetSRSTTSRadio(true,"C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio",243,radio.modulation.AM,5002,MSRS.Voices.Microsoft.Hazel,"en-GB","female")
|
||||
--
|
||||
-- -- define a different voice for the downed pilot(s)
|
||||
-- my_aicsar:SetPilotTTSVoice(MSRS.Voices.Google.Standard.en_AU_Standard_D,"en-AU","male")
|
||||
@@ -177,7 +179,7 @@
|
||||
--
|
||||
-- Switch on radio transmissions via **either** SRS **or** "normal" DCS radio e.g. like so:
|
||||
--
|
||||
-- my_aicsar:SetSRSRadio(true,"C:\\Program Files\\DCS-SimpleRadio-Standalone",270,radio.modulation.AM,nil,5002)
|
||||
-- my_aicsar:SetSRSRadio(true,"C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio",270,radio.modulation.AM,nil,5002)
|
||||
--
|
||||
-- or
|
||||
--
|
||||
@@ -191,7 +193,7 @@
|
||||
-- @field #AICSAR
|
||||
AICSAR = {
|
||||
ClassName = "AICSAR",
|
||||
version = "0.1.16",
|
||||
version = "0.1.18",
|
||||
lid = "",
|
||||
coalition = coalition.side.BLUE,
|
||||
template = "",
|
||||
@@ -236,6 +238,8 @@ AICSAR = {
|
||||
Altitude = 1500,
|
||||
UseEventEject = false,
|
||||
Delay = 100,
|
||||
UseRescueZone = false,
|
||||
RescueZone = nil,
|
||||
}
|
||||
|
||||
-- TODO Messages
|
||||
@@ -304,8 +308,9 @@ AICSAR.RadioLength = {
|
||||
-- @param #string Helotemplate Helicopter template name.
|
||||
-- @param Wrapper.Airbase#AIRBASE FARP FARP object or Airbase from where to start.
|
||||
-- @param Core.Zone#ZONE MASHZone Zone where to drop pilots after rescue.
|
||||
-- @param #number Helonumber (Optional) Max number of alive Ai Helos at the same time. Defaults to 3.
|
||||
-- @return #AICSAR self
|
||||
function AICSAR:New(Alias,Coalition,Pilottemplate,Helotemplate,FARP,MASHZone)
|
||||
function AICSAR:New(Alias,Coalition,Pilottemplate,Helotemplate,FARP,MASHZone,Helonumber)
|
||||
-- Inherit everything from FSM class.
|
||||
local self=BASE:Inherit(self, FSM:New())
|
||||
|
||||
@@ -373,7 +378,7 @@ function AICSAR:New(Alias,Coalition,Pilottemplate,Helotemplate,FARP,MASHZone)
|
||||
|
||||
-- limit number of available helos at the same time
|
||||
self.limithelos = true
|
||||
self.helonumber = 3
|
||||
self.helonumber = Helonumber or 3
|
||||
|
||||
-- localization
|
||||
self:InitLocalization()
|
||||
@@ -524,21 +529,31 @@ function AICSAR:InitLocalization()
|
||||
return self
|
||||
end
|
||||
|
||||
--- [User] Use a defined zone as area of operation and not the distance to FARP.
|
||||
-- @param #AICSAR self
|
||||
-- @param Core.Zone#ZONE Zone The operational zone to use. Downed pilots in this area will be rescued. Can be any known #ZONE type.
|
||||
-- @return #AICSAR self
|
||||
function AICSAR:SetUsingRescueZone(Zone)
|
||||
self.UseRescueZone = true
|
||||
self.RescueZone = Zone
|
||||
return self
|
||||
end
|
||||
|
||||
--- [User] Switch sound output on and use SRS output for sound files.
|
||||
-- @param #AICSAR self
|
||||
-- @param #boolean OnOff Switch on (true) or off (false).
|
||||
-- @param #string Path Path to your SRS Server Component, e.g. "C:\\\\Program Files\\\\DCS-SimpleRadio-Standalone"
|
||||
-- @param #number Frequency Defaults to 243 (guard)
|
||||
-- @param #number Modulation Radio modulation. Defaults to radio.modulation.AM
|
||||
-- @param #string SoundPath Where to find the audio files. Defaults to nil, i.e. add messages via "Sound to..." in the Mission Editor.
|
||||
-- @param #number Port Port of the SRS, defaults to 5002.
|
||||
-- @param #string Path Path to your SRS Server External Audio Component, e.g. "C:\\\\Program Files\\\\DCS-SimpleRadio-Standalone\\\\ExternalAudio"
|
||||
-- @param #number Frequency (Optional) Defaults to 243 (guard)
|
||||
-- @param #number Modulation (Optional) Radio modulation. Defaults to radio.modulation.AM
|
||||
-- @param #string SoundPath (Optional) Where to find the audio files. Defaults to nil, i.e. add messages via "Sound to..." in the Mission Editor.
|
||||
-- @param #number Port (Optional) Port of the SRS, defaults to 5002.
|
||||
-- @return #AICSAR self
|
||||
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 MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone"
|
||||
self.SRSPath = Path or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio"
|
||||
self.SRS:SetLabel("ACSR")
|
||||
self.SRS:SetCoalition(self.coalition)
|
||||
self.SRSModulation = Modulation or radio.modulation.AM
|
||||
@@ -556,7 +571,7 @@ end
|
||||
-- See `AICSAR:SetPilotTTSVoice()` and `AICSAR:SetOperatorTTSVoice()`
|
||||
-- @param #AICSAR self
|
||||
-- @param #boolean OnOff Switch on (true) or off (false).
|
||||
-- @param #string Path Path to your SRS Server Component, e.g. "E:\\\\Program Files\\\\DCS-SimpleRadio-Standalone"
|
||||
-- @param #string Path Path to your SRS Server Component, e.g. "E:\\\\Program Files\\\\DCS-SimpleRadio-Standalone\\ExternalAudio"
|
||||
-- @param #number Frequency (Optional) Defaults to 243 (guard)
|
||||
-- @param #number Modulation (Optional) Radio modulation. Defaults to radio.modulation.AM
|
||||
-- @param #number Port (Optional) Port of the SRS, defaults to 5002.
|
||||
@@ -564,13 +579,15 @@ end
|
||||
-- @param #string Culture (Optional) The culture to be used, defaults to "en-GB"
|
||||
-- @param #string Gender (Optional) The gender to be used, defaults to "male"
|
||||
-- @param #string GoogleCredentials (Optional) Path to google credentials
|
||||
-- @param #string Provider (Optional) TTS Provider to be used.
|
||||
-- @param #string Speaker (Optional) Use a specific speaker for a voice if Piper is used as provider (only Hound-TTS backend).
|
||||
-- @return #AICSAR self
|
||||
function AICSAR:SetSRSTTSRadio(OnOff,Path,Frequency,Modulation,Port,Voice,Culture,Gender,GoogleCredentials)
|
||||
function AICSAR:SetSRSTTSRadio(OnOff,Path,Frequency,Modulation,Port,Voice,Culture,Gender,GoogleCredentials,Provider,Speaker)
|
||||
self:T(self.lid .. "SetSRSTTSRadio")
|
||||
self.SRSTTSRadio = OnOff and true
|
||||
self.SRSRadio = false
|
||||
self.SRSFrequency = Frequency or 243
|
||||
self.SRSPath = Path or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone"
|
||||
self.SRSPath = Path or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio"
|
||||
self.SRSModulation = Modulation or radio.modulation.AM
|
||||
self.SRSPort = Port or MSRS.port or 5002
|
||||
if OnOff then
|
||||
@@ -579,13 +596,19 @@ function AICSAR:SetSRSTTSRadio(OnOff,Path,Frequency,Modulation,Port,Voice,Cultur
|
||||
self.SRS:SetCoalition(self.coalition)
|
||||
self.SRS:SetLabel("ACSR")
|
||||
self.SRS:SetVoice(Voice)
|
||||
if Speaker then
|
||||
self.SRS:SetSpeakerPiper(Speaker)
|
||||
end
|
||||
self.SRS:SetCulture(Culture)
|
||||
self.SRS:SetGender(Gender)
|
||||
if GoogleCredentials then
|
||||
if GoogleCredentials and not Provider then
|
||||
self.SRS:SetProviderOptionsGoogle(GoogleCredentials,GoogleCredentials)
|
||||
self.SRS:SetProvider(MSRS.Provider.GOOGLE)
|
||||
self.SRSGoogle = true
|
||||
end
|
||||
if Provider then
|
||||
self.SRS:SetProvider(Provider)
|
||||
end
|
||||
self.SRSQ = MSRSQUEUE:New(self.alias)
|
||||
end
|
||||
return self
|
||||
@@ -597,13 +620,17 @@ end
|
||||
-- Specific voices override culture and gender!
|
||||
-- @param #string Culture (Optional) The culture to be used, defaults to "en-US"
|
||||
-- @param #string Gender (Optional) The gender to be used, defaults to "male"
|
||||
-- @param #string Speaker (Optional) Use a specific speaker for a voice if Piper is used as provider (only Hound-TTS backend).
|
||||
-- @return #AICSAR self
|
||||
function AICSAR:SetPilotTTSVoice(Voice,Culture,Gender)
|
||||
function AICSAR:SetPilotTTSVoice(Voice,Culture,Gender,Speaker)
|
||||
self:T(self.lid .. "SetPilotTTSVoice")
|
||||
self.SRSPilotVoice = true
|
||||
self.SRSPilot = MSRS:New(self.SRSPath,self.SRSFrequency,self.SRSModulation)
|
||||
self.SRSPilot:SetCoalition(self.coalition)
|
||||
self.SRSPilot:SetVoice(Voice)
|
||||
if Speaker then
|
||||
self.SRSPilot:SetSpeakerPiper(Speaker)
|
||||
end
|
||||
self.SRSPilot:SetCulture(Culture or "en-US")
|
||||
self.SRSPilot:SetGender(Gender or "male")
|
||||
self.SRSPilot:SetLabel("PILOT")
|
||||
@@ -621,13 +648,17 @@ end
|
||||
-- Specific voices override culture and gender!
|
||||
-- @param #string Culture (Optional) The culture to be used, defaults to "en-GB"
|
||||
-- @param #string Gender (Optional) The gender to be used, defaults to "female"
|
||||
-- @param #string Speaker (Optional) Use a specific speaker for a voice if Piper is used as provider (only Hound-TTS backend).
|
||||
-- @return #AICSAR self
|
||||
function AICSAR:SetOperatorTTSVoice(Voice,Culture,Gender)
|
||||
function AICSAR:SetOperatorTTSVoice(Voice,Culture,Gender,Speaker)
|
||||
self:T(self.lid .. "SetOperatorTTSVoice")
|
||||
self.SRSOperatorVoice = true
|
||||
self.SRSOperator = MSRS:New(self.SRSPath,self.SRSFrequency,self.SRSModulation)
|
||||
self.SRSOperator:SetCoalition(self.coalition)
|
||||
self.SRSOperator:SetVoice(Voice)
|
||||
if Speaker then
|
||||
self.SRSOperator:SetSpeakerPiper(Speaker)
|
||||
end
|
||||
self.SRSOperator:SetCulture(Culture or "en-GB")
|
||||
self.SRSOperator:SetGender(Gender or "female")
|
||||
self.SRSOperator:SetLabel("RESCUE")
|
||||
@@ -642,8 +673,8 @@ end
|
||||
--- [User] Switch sound output on and use normale (DCS) radio
|
||||
-- @param #AICSAR self
|
||||
-- @param #boolean OnOff Switch on (true) or off (false).
|
||||
-- @param #number Frequency Defaults to 243 (guard).
|
||||
-- @param #number Modulation Radio modulation. Defaults to radio.modulation.AM.
|
||||
-- @param #number Frequency(Optional) Defaults to 243 (guard).
|
||||
-- @param #number Modulation (Optional) Radio modulation. Defaults to radio.modulation.AM.
|
||||
-- @param Wrapper.Group#GROUP Group The group to use as sending station.
|
||||
-- @return #AICSAR self
|
||||
function AICSAR:SetDCSRadio(OnOff,Frequency,Modulation,Group)
|
||||
@@ -693,7 +724,7 @@ function AICSAR:_EjectEventHandler(EventData)
|
||||
local _LandingPos = COORDINATE:NewFromVec3(_event.initiator:getPosition().p)
|
||||
local _country = _event.initiator:getCountry()
|
||||
local _coalition = coalition.getCountryCoalition( _country )
|
||||
local data = UTILS.DeepCopy(EventData)
|
||||
--local data = UTILS.DeepCopy(EventData)
|
||||
Unit.destroy(_event.initiator) -- shagrat remove static Pilot model
|
||||
self:ScheduleOnce(self.Delay,self._DelayedSpawnPilot,self,_LandingPos,_coalition)
|
||||
end
|
||||
@@ -708,7 +739,14 @@ end
|
||||
-- @return #AICSAR self
|
||||
function AICSAR:_DelayedSpawnPilot(_LandingPos,_coalition)
|
||||
|
||||
local distancetofarp = _LandingPos:Get2DDistance(self.farp:GetCoordinate())
|
||||
local distancetofarp = _LandingPos:Get2DDistance(self.farp:GetCoordinate())
|
||||
if self.UseRescueZone == true and self.RescueZone ~= nil then
|
||||
if self.RescueZone:IsCoordinateInZone(_LandingPos) then
|
||||
distancetofarp = self.maxdistance - 10
|
||||
else
|
||||
distancetofarp = self.maxdistance + 10
|
||||
end
|
||||
end
|
||||
-- Mayday Message
|
||||
local Text,Soundfile,Soundlength,Subtitle = self.gettext:GetEntry("PILOTDOWN",self.locale)
|
||||
local text = ""
|
||||
@@ -795,7 +833,13 @@ function AICSAR:_EventHandler(EventData, FromEject)
|
||||
|
||||
-- DONE: add distance check
|
||||
local distancetofarp = _LandingPos:Get2DDistance(self.farp:GetCoordinate())
|
||||
|
||||
if self.UseRescueZone == true and self.RescueZone ~= nil then
|
||||
if self.RescueZone:IsCoordinateInZone(_LandingPos) then
|
||||
distancetofarp = self.maxdistance - 10
|
||||
else
|
||||
distancetofarp = self.maxdistance + 10
|
||||
end
|
||||
end
|
||||
-- Mayday Message
|
||||
local Text,Soundfile,Soundlength,Subtitle = self.gettext:GetEntry("PILOTDOWN",self.locale)
|
||||
local text = ""
|
||||
@@ -817,7 +861,6 @@ function AICSAR:_EventHandler(EventData, FromEject)
|
||||
if _coalition == self.coalition then
|
||||
if self.verbose then
|
||||
MESSAGE:New(msgtxt,15,"AICSAR"):ToCoalition(self.coalition)
|
||||
-- MESSAGE:New(msgtxt,15,"AICSAR"):ToLog()
|
||||
end
|
||||
if self.SRSRadio then
|
||||
local sound = SOUNDFILE:New(Soundfile,self.SRSSoundPath,Soundlength)
|
||||
@@ -869,6 +912,7 @@ function AICSAR:_GetFlight()
|
||||
:InitUnControlled(true)
|
||||
:OnSpawnGroup(
|
||||
function(Group)
|
||||
Group:OptionPreferVerticalLanding()
|
||||
self:__HeloOnDuty(1,Group)
|
||||
end
|
||||
)
|
||||
@@ -892,7 +936,7 @@ function AICSAR:_InitMission(Pilot,Index)
|
||||
--local pilotset = SET_GROUP:New()
|
||||
--pilotset:AddGroup(Pilot)
|
||||
|
||||
-- Cargo transport assignment.
|
||||
-- Cargo transport assignment.
|
||||
local opstransport=OPSTRANSPORT:New(Pilot, pickupzone, self.farpzone)
|
||||
--opstransport:SetVerbosity(3)
|
||||
|
||||
@@ -934,6 +978,10 @@ function AICSAR:_InitMission(Pilot,Index)
|
||||
helo:__UnloadingDone(5)
|
||||
end
|
||||
|
||||
function helo:OnAfterLandAtAirbase(From,Event,To,airbase)
|
||||
helo:Despawn(2)
|
||||
end
|
||||
|
||||
self.helos[Index] = helo
|
||||
|
||||
return self
|
||||
@@ -984,7 +1032,9 @@ function AICSAR:_CheckHelos()
|
||||
local name = helo:GetName()
|
||||
self:T("Helo group "..name.." in state "..state)
|
||||
if state == "Arrived" then
|
||||
helo:__Stop(5)
|
||||
--helo:__Stop(5)
|
||||
helo.OnAfterDead = nil
|
||||
helo:Despawn(35)
|
||||
self.helos[_index] = nil
|
||||
end
|
||||
else
|
||||
@@ -1025,7 +1075,7 @@ function AICSAR:_CheckQueue(OpsGroup)
|
||||
if self:_CheckInMashZone(_pilot) then
|
||||
self:T("Pilot" .. _pilot.GroupName .. " rescued!")
|
||||
if OpsGroup then
|
||||
OpsGroup:Despawn(10)
|
||||
--OpsGroup:Despawn(10)
|
||||
else
|
||||
_pilot:Destroy(true,10)
|
||||
end
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
--
|
||||
-- ### Author: **funkyfranky**
|
||||
--
|
||||
-- ### Contributions: FlightControl
|
||||
-- ### Contributions: FlightControl, Applevangelist
|
||||
--
|
||||
-- ====
|
||||
-- @module Functional.Artillery
|
||||
@@ -419,14 +419,6 @@
|
||||
-- arty set, battery "Mortar Bravo", rearming group "Ammo Truck M939"
|
||||
-- Note that the name of the rearming group has to be given in quotation marks and spelt exactly as the group name defined in the mission editor.
|
||||
--
|
||||
-- ## Transporting
|
||||
--
|
||||
-- ARTY groups can be transported to another location as @{Cargo.Cargo} by means of classes such as @{AI.AI_Cargo_APC}, @{AI.AI_Cargo_Dispatcher_APC},
|
||||
-- @{AI.AI_Cargo_Helicopter}, @{AI.AI_Cargo_Dispatcher_Helicopter} or @{AI.AI_Cargo_Airplane}.
|
||||
--
|
||||
-- In order to do this, one needs to define an ARTY object via the @{#ARTY.NewFromCargoGroup}(*cargogroup*, *alias*) function.
|
||||
-- The first argument *cargogroup* has to be a @{Cargo.CargoGroup#CARGO_GROUP} object. The second argument *alias* is a string which can be freely chosen by the user.
|
||||
--
|
||||
-- ## Fine Tuning
|
||||
--
|
||||
-- The mission designer has a few options to tailor the ARTY object according to his needs.
|
||||
@@ -511,24 +503,6 @@
|
||||
-- -- Start ARTY process.
|
||||
-- normandy:Start()
|
||||
--
|
||||
-- ### Transportation as Cargo
|
||||
-- This example demonstates how an ARTY group can be transported to another location as cargo.
|
||||
-- -- Define a group as CARGO_GROUP
|
||||
-- CargoGroupMortars=CARGO_GROUP:New(GROUP:FindByName("Mortars"), "Mortars", "Mortar Platoon Alpha", 100 , 10)
|
||||
--
|
||||
-- -- Define the mortar CARGO GROUP as ARTY object
|
||||
-- mortars=ARTY:NewFromCargoGroup(CargoGroupMortars, "Mortar Platoon Alpha")
|
||||
--
|
||||
-- -- Start ARTY process
|
||||
-- mortars:Start()
|
||||
--
|
||||
-- -- Setup AI cargo dispatcher for e.g. helos
|
||||
-- SetHeloCarriers = SET_GROUP:New():FilterPrefixes("CH-47D"):FilterStart()
|
||||
-- SetCargoMortars = SET_CARGO:New():FilterTypes("Mortars"):FilterStart()
|
||||
-- SetZoneDepoly = SET_ZONE:New():FilterPrefixes("Deploy"):FilterStart()
|
||||
-- CargoHelo=AI_CARGO_DISPATCHER_HELICOPTER:New(SetHeloCarriers, SetCargoMortars, SetZoneDepoly)
|
||||
-- CargoHelo:Start()
|
||||
-- The ARTY group will be transported and resume its normal operation after it has been deployed. New targets can be assigned at any time also during the transportation process.
|
||||
--
|
||||
-- @field #ARTY
|
||||
ARTY={
|
||||
@@ -780,7 +754,7 @@ ARTY.db={
|
||||
|
||||
--- Arty script version.
|
||||
-- @field #string version
|
||||
ARTY.version="1.3.3"
|
||||
ARTY.version="1.3.4"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -1230,37 +1204,6 @@ function ARTY:New(group, alias)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Creates a new ARTY object from a MOOSE CARGO_GROUP object.
|
||||
-- @param #ARTY self
|
||||
-- @param Cargo.CargoGroup#CARGO_GROUP cargogroup The CARGO GROUP object for which artillery tasks should be assigned.
|
||||
-- @param alias (Optional) Alias name the group will be calling itself when sending messages. Default is the group name.
|
||||
-- @return #ARTY ARTY object or nil if group does not exist or is not a ground or naval group.
|
||||
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
|
||||
|
||||
-- Get group belonging to the cargo group.
|
||||
local group=cargogroup:GetObject()
|
||||
|
||||
-- Create ARTY object.
|
||||
local arty=ARTY:New(group,alias)
|
||||
|
||||
-- Set iscargo flag.
|
||||
arty.iscargo=true
|
||||
|
||||
-- Set cargo group object.
|
||||
arty.cargogroup=cargogroup
|
||||
|
||||
return arty
|
||||
end
|
||||
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- User Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -1448,7 +1391,7 @@ end
|
||||
-- @param #ARTY self
|
||||
-- @param Core.Point#COORDINATE coord Coordinates of the new position.
|
||||
-- @param #string time (Optional) Day time at which the group should start moving. Passed as a string in format "08:13:45". Default is now.
|
||||
-- @param #number speed (Optinal) Speed in km/h the group should move at. Default 70% of max posible speed of group.
|
||||
-- @param #number speed (Optional) Speed in km/h the group should move at. Default 70% of max posible speed of group.
|
||||
-- @param #boolean onroad (Optional) If true, group will mainly use roads. Default off, i.e. go directly towards the specified coordinate.
|
||||
-- @param #boolean cancel (Optional) If true, cancel any running attack when move should begin. Default is false.
|
||||
-- @param #string name (Optional) Name of the coordinate. Default is LL DMS string of the coordinate. If the name was already given, the numbering "#01", "#02",... is appended automatically.
|
||||
@@ -1559,7 +1502,7 @@ end
|
||||
|
||||
--- Set minimum firing range. Targets closer than this distance are not engaged.
|
||||
-- @param #ARTY self
|
||||
-- @param #number range Min range in kilometers. Default is 0.1 km.
|
||||
-- @param #number range (Optional) Min range in kilometers. Default is 0.1 km.
|
||||
-- @return self
|
||||
function ARTY:SetMinFiringRange(range)
|
||||
self:F({range=range})
|
||||
@@ -1569,7 +1512,7 @@ end
|
||||
|
||||
--- Set maximum firing range. Targets further away than this distance are not engaged.
|
||||
-- @param #ARTY self
|
||||
-- @param #number range Max range in kilometers. Default is 1000 km.
|
||||
-- @param #number range (Optional) Max range in kilometers. Default is 1000 km.
|
||||
-- @return self
|
||||
function ARTY:SetMaxFiringRange(range)
|
||||
self:F({range=range})
|
||||
@@ -1579,7 +1522,7 @@ end
|
||||
|
||||
--- Set time interval between status updates. During the status check, new events are triggered.
|
||||
-- @param #ARTY self
|
||||
-- @param #number interval Time interval in seconds. Default 10 seconds.
|
||||
-- @param #number interval (Optional) Time interval in seconds. Default 10 seconds.
|
||||
-- @return self
|
||||
function ARTY:SetStatusInterval(interval)
|
||||
self:F({interval=interval})
|
||||
@@ -1589,7 +1532,7 @@ end
|
||||
|
||||
--- Set time interval for weapon tracking.
|
||||
-- @param #ARTY self
|
||||
-- @param #number interval Time interval in seconds. Default 0.2 seconds.
|
||||
-- @param #number interval (Optional) Time interval in seconds. Default 0.2 seconds.
|
||||
-- @return self
|
||||
function ARTY:SetTrackInterval(interval)
|
||||
self.dtTrack=interval or 0.2
|
||||
@@ -1598,7 +1541,7 @@ end
|
||||
|
||||
--- Set time how it is waited a unit the first shot event happens. If no shot is fired after this time, the task to fire is aborted and the target removed.
|
||||
-- @param #ARTY self
|
||||
-- @param #number waittime Time in seconds. Default 300 seconds.
|
||||
-- @param #number waittime (Optional) Time in seconds. Default 300 seconds.
|
||||
-- @return self
|
||||
function ARTY:SetWaitForShotTime(waittime)
|
||||
self:F({waittime=waittime})
|
||||
@@ -1608,7 +1551,7 @@ end
|
||||
|
||||
--- Define the safe distance between ARTY group and rearming unit or rearming place at which rearming process is possible.
|
||||
-- @param #ARTY self
|
||||
-- @param #number distance Safe distance in meters. Default is 100 m.
|
||||
-- @param #number distance (Optional) Safe distance in meters. Default is 100 m.
|
||||
-- @return self
|
||||
function ARTY:SetRearmingDistance(distance)
|
||||
self:F({distance=distance})
|
||||
@@ -1870,7 +1813,7 @@ end
|
||||
|
||||
--- Set nuclear warhead explosion strength.
|
||||
-- @param #ARTY self
|
||||
-- @param #number strength Explosion strength in kilo tons TNT. Default is 0.075 kt.
|
||||
-- @param #number strength (Optional) Explosion strength in kilo tons TNT. Default is 0.075 kt.
|
||||
-- @return self
|
||||
function ARTY:SetTacNukeWarhead(strength)
|
||||
self.nukewarhead=strength or 0.075
|
||||
@@ -2268,7 +2211,7 @@ function ARTY._FuncTrack(weapon, self, target)
|
||||
if _dist<target.radius then
|
||||
|
||||
-- Get random coordinate within a certain radius.
|
||||
local _cr=_coord:GetRandomCoordinateInRadius(_data.target.radius)
|
||||
local _cr=_coord:GetRandomCoordinateInRadius(target.radius)
|
||||
|
||||
-- Fire smoke at this coordinate.
|
||||
_cr:Smoke(self.smokeColor)
|
||||
@@ -3960,7 +3903,7 @@ end
|
||||
|
||||
--- Get the number of shells a unit or group currently has. For a group the ammo count of all units is summed up.
|
||||
-- @param #ARTY self
|
||||
-- @param #boolean display Display ammo table as message to all. Default false.
|
||||
-- @param #boolean display (Optional) Display ammo table as message to all. Default false.
|
||||
-- @return #number Total amount of ammo the whole group has left.
|
||||
-- @return #number Number of shells the group has left.
|
||||
-- @return #number Number of rockets the group has left.
|
||||
|
||||
@@ -105,7 +105,7 @@ AUTOLASE = {
|
||||
debug = false,
|
||||
smokemenu = true,
|
||||
RoundingPrecision = 0,
|
||||
increasegroundawareness = true,
|
||||
increasegroundawareness = false,
|
||||
MonitorFrequency = 30,
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet)
|
||||
self.smokemenu = true
|
||||
self.threatmenu = true
|
||||
self.RoundingPrecision = 0
|
||||
self.increasegroundawareness = true
|
||||
self.increasegroundawareness = false
|
||||
self.MonitorFrequency = 30
|
||||
|
||||
self:EnableSmokeMenu({Angle=math.random(0,359),Distance=math.random(10,20)})
|
||||
@@ -427,7 +427,7 @@ end
|
||||
|
||||
--- (User) Set minimum threat level for target selection, can be 0 (lowest) to 10 (highest).
|
||||
-- @param #AUTOLASE self
|
||||
-- @param #number Level Level used for filtering, defaults to 0. SAM systems and manpads have level 7 to 10, AAA level 6, MTBs and armoured vehicles level 3 to 5, APC, Artillery, Infantry and EWR level 1 to 2.
|
||||
-- @param #number Level (Optional) Level used for filtering, defaults to 0. SAM systems and manpads have level 7 to 10, AAA level 6, MTBs and armoured vehicles level 3 to 5, APC, Artillery, Infantry and EWR level 1 to 2.
|
||||
-- @return #AUTOLASE self
|
||||
-- @usage Filter for level 3 and above:
|
||||
-- `myautolase:SetMinThreatLevel(3)`
|
||||
@@ -493,7 +493,7 @@ end
|
||||
--- (User) Function enable sending messages via SRS.
|
||||
-- @param #AUTOLASE self
|
||||
-- @param #boolean OnOff Switch usage on and off
|
||||
-- @param #string Path Path to SRS directory, e.g. C:\\Program Files\\DCS-SimpleRadio-Standalone
|
||||
-- @param #string Path Path to SRS TTS directory, e.g. C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio
|
||||
-- @param #number Frequency Frequency to send, e.g. 243
|
||||
-- @param #number Modulation Modulation i.e. radio.modulation.AM or radio.modulation.FM
|
||||
-- @param #string Label (Optional) Short label to be used on the SRS Client Overlay
|
||||
@@ -502,13 +502,15 @@ end
|
||||
-- @param #number Port (Optional) Defaults to 5002
|
||||
-- @param #string Voice (Optional) Use a specifc voice with the @{Sound.SRS#SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`.
|
||||
-- Note that this must be installed on your windows system. Can also be Google voice types, if you are using Google TTS.
|
||||
-- @param #number Volume (Optional) Volume - between 0.0 (silent) and 1.0 (loudest)
|
||||
-- @param #string PathToGoogleKey (Optional) Path to your google key if you want to use google TTS
|
||||
-- @param #number Volume (Optional) Volume - between 0.0 (silent) and 1.0 (loudest).
|
||||
-- @param #string PathToGoogleKey (Optional) Path to your google key if you want to use google TTS.
|
||||
-- @param #string Provider (Optional) TTS Provider to be used.
|
||||
-- @param #string Speaker (Optional) Use a specific speaker for a voice if Piper is used as provider (only Hound-TTS backend).
|
||||
-- @return #AUTOLASE self
|
||||
function AUTOLASE:SetUsingSRS(OnOff,Path,Frequency,Modulation,Label,Gender,Culture,Port,Voice,Volume,PathToGoogleKey)
|
||||
function AUTOLASE:SetUsingSRS(OnOff,Path,Frequency,Modulation,Label,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,Provider,Speaker)
|
||||
if OnOff then
|
||||
self.useSRS = true
|
||||
self.SRSPath = Path or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone"
|
||||
self.SRSPath = Path or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio"
|
||||
self.SRSFreq = Frequency or 271
|
||||
self.SRSMod = Modulation or radio.modulation.AM
|
||||
self.Gender = Gender or MSRS.gender or "male"
|
||||
@@ -526,12 +528,18 @@ function AUTOLASE:SetUsingSRS(OnOff,Path,Frequency,Modulation,Label,Gender,Cultu
|
||||
self.SRS:SetCulture(self.Culture)
|
||||
self.SRS:SetPort(self.Port)
|
||||
self.SRS:SetVoice(self.Voice)
|
||||
if Speaker then
|
||||
self.SRS:SetSpeakerPiper(Speaker)
|
||||
end
|
||||
self.SRS:SetCoalition(self.coalition)
|
||||
self.SRS:SetVolume(self.Volume)
|
||||
if self.PathToGoogleKey then
|
||||
if self.PathToGoogleKey and not Provider then
|
||||
self.SRS:SetProviderOptionsGoogle(PathToGoogleKey,PathToGoogleKey)
|
||||
self.SRS:SetProvider(MSRS.Provider.GOOGLE)
|
||||
end
|
||||
if Provider then
|
||||
self.SRS:SetProvider(Provider)
|
||||
end
|
||||
self.SRSQueue = MSRSQUEUE:New(self.alias)
|
||||
else
|
||||
self.useSRS = false
|
||||
@@ -593,8 +601,8 @@ end
|
||||
|
||||
--- (User) Function to force laser cooldown and cool down time
|
||||
-- @param #AUTOLASE self
|
||||
-- @param #boolean OnOff Switch cool down on (true) or off (false) - defaults to true
|
||||
-- @param #number Seconds Number of seconds for cooldown - dafaults to 60 seconds
|
||||
-- @param #boolean OnOff (Optional) Switch cool down on (true) or off (false) - defaults to true
|
||||
-- @param #number Seconds (Optional) Number of seconds for cooldown - dafaults to 60 seconds
|
||||
-- @return #AUTOLASE self
|
||||
function AUTOLASE:SetLaserCoolDown(OnOff, Seconds)
|
||||
self.forcecooldown = OnOff and true
|
||||
@@ -615,8 +623,8 @@ end
|
||||
|
||||
--- (User) Function to set lasing distance in meters and duration in seconds
|
||||
-- @param #AUTOLASE self
|
||||
-- @param #number Distance (Max) distance for lasing in meters - default 5000 meters
|
||||
-- @param #number Duration (Max) duration for lasing in seconds - default 300 secs
|
||||
-- @param #number Distance (Optional) Max distance for lasing in meters - default 5000 meters
|
||||
-- @param #number Duration (Optional) Max duration for lasing in seconds - default 300 secs
|
||||
-- @return #AUTOLASE self
|
||||
function AUTOLASE:SetLasingParameters(Distance, Duration)
|
||||
self.LaseDistance = Distance or 5000
|
||||
@@ -640,7 +648,7 @@ end
|
||||
|
||||
--- (User) Function to set rounding precision for BR distance output.
|
||||
-- @param #AUTOLASE self
|
||||
-- @param #number IDP Rounding precision before/after the decimal sign. Defaults to zero. Positive values round right of the decimal sign, negative ones left of the decimal sign.
|
||||
-- @param #number IDP (Optional) Rounding precision before/after the decimal sign. Defaults to zero. Positive values round right of the decimal sign, negative ones left of the decimal sign.
|
||||
-- @return #AUTOLASE self
|
||||
function AUTOLASE:SetRoundingPrecsion(IDP)
|
||||
self.RoundingPrecision = IDP or 0
|
||||
@@ -693,7 +701,7 @@ end
|
||||
function AUTOLASE:GetLosFromUnit(Unit)
|
||||
local lasedistance = self.LaseDistance
|
||||
local unitheight = Unit:GetHeight()
|
||||
local coord = Unit:GetCoordinate()
|
||||
local coord = Unit:GetCoord()
|
||||
local landheight = coord:GetLandHeight()
|
||||
local asl = unitheight - landheight
|
||||
if asl > 100 then
|
||||
@@ -845,7 +853,7 @@ function AUTOLASE:ShowStatus(Group,Unit)
|
||||
locationstring = entry.coordinate:ToStringLLDDM(settings)
|
||||
elseif settings:IsA2G_BR() then
|
||||
-- attention this is the distance from the ASKING unit to target, not from RECCE to target!
|
||||
local startcoordinate = Unit:GetCoordinate() or Group:GetCoordinate()
|
||||
local startcoordinate = Unit:GetCoord() or Group:GetCoord()
|
||||
locationstring = entry.coordinate:ToStringBR(startcoordinate,settings,false,self.RoundingPrecision)
|
||||
end
|
||||
end
|
||||
@@ -963,8 +971,8 @@ function AUTOLASE:CanLase(Recce,Unit)
|
||||
end
|
||||
end
|
||||
-- calculate LOS
|
||||
local reccecoord = Recce:GetCoordinate()
|
||||
local unitcoord = Unit:GetCoordinate()
|
||||
local reccecoord = Recce:GetCoord()
|
||||
local unitcoord = Unit:GetCoord()
|
||||
local islos = reccecoord:IsLOS(unitcoord,2.5)
|
||||
-- calculate distance
|
||||
local distance = math.floor(reccecoord:Get3DDistance(unitcoord))
|
||||
@@ -1020,8 +1028,8 @@ function AUTOLASE:_Prescient()
|
||||
self:T(self.lid.."Checking possibly visible STATICs for Recce "..unit:GetName())
|
||||
for _,_static in pairs(Statics) do -- DCS static object here
|
||||
local static = STATIC:Find(_static)
|
||||
if static and static:GetCoalition() ~= self.coalition then
|
||||
local IsLOS = position:IsLOS(static:GetCoordinate())
|
||||
if static and static:GetCoalition() ~= self.coalition and static:GetCoord() then
|
||||
local IsLOS = position:IsLOS(static:GetCoord())
|
||||
if IsLOS then
|
||||
unit:KnowUnit(static,true,true)
|
||||
end
|
||||
@@ -1081,7 +1089,7 @@ function AUTOLASE:onafterMonitor(From, Event, To)
|
||||
local threat = contact.threatlevel or 0
|
||||
local reccegrp = UNIT:FindByName(reccename)
|
||||
if reccegrp then
|
||||
local reccecoord = reccegrp:GetCoordinate()
|
||||
local reccecoord = reccegrp:GetCoord()
|
||||
local distance = math.floor(reccecoord:Get3DDistance(coord))
|
||||
local text = string.format("%s of %s | Distance %d km | Threatlevel %d",contact.attribute, contact.groupname, math.floor(distance/1000), contact.threatlevel)
|
||||
report:Add(text)
|
||||
@@ -1120,7 +1128,6 @@ function AUTOLASE:onafterMonitor(From, Event, To)
|
||||
local unit = _unit -- Wrapper.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()
|
||||
-- prefer radar units
|
||||
@@ -1176,16 +1183,16 @@ function AUTOLASE:onafterMonitor(From, Event, To)
|
||||
local code = self:GetLaserCode(reccename)
|
||||
local spot = SPOT:New(recce)
|
||||
spot:LaseOn(unit,code,self.LaseDuration)
|
||||
local locationstring = unit:GetCoordinate():ToStringLLDDM()
|
||||
local locationstring = unit:GetCoord():ToStringLLDDM()
|
||||
if _SETTINGS:IsA2G_MGRS() then
|
||||
local precision = _SETTINGS:GetMGRS_Accuracy()
|
||||
local settings = {}
|
||||
settings.MGRS_Accuracy = precision
|
||||
locationstring = unit:GetCoordinate():ToStringMGRS(settings)
|
||||
locationstring = unit:GetCoord():ToStringMGRS(settings)
|
||||
elseif _SETTINGS:IsA2G_LL_DMS() then
|
||||
locationstring = unit:GetCoordinate():ToStringLLDMS(_SETTINGS)
|
||||
locationstring = unit:GetCoord():ToStringLLDMS(_SETTINGS)
|
||||
elseif _SETTINGS:IsA2G_BR() then
|
||||
locationstring = unit:GetCoordinate():ToStringBULLS(self.coalition,_SETTINGS)
|
||||
locationstring = unit:GetCoord():ToStringBULLS(self.coalition,_SETTINGS)
|
||||
end
|
||||
|
||||
local laserspot = { -- #AUTOLASE.LaserSpot
|
||||
@@ -1198,7 +1205,7 @@ function AUTOLASE:onafterMonitor(From, Event, To)
|
||||
unitname = unitname,
|
||||
reccename = reccename,
|
||||
unittype = unit:GetTypeName(),
|
||||
coordinate = unit:GetCoordinate(),
|
||||
coordinate = unit:GetCoord(),
|
||||
}
|
||||
if self.smoketargets then
|
||||
local coord = unit:GetCoordinate()
|
||||
|
||||
@@ -178,7 +178,7 @@ end
|
||||
-- Note, one can also use the method @{#CLEANUP_AIRBASE.RemoveAirbase}() to remove the airbase from the control process as a whole,
|
||||
-- when an enemy unit is near. That is also an option...
|
||||
-- @param #CLEANUP_AIRBASE self
|
||||
-- @param #string CleanMissiles (Default=true) If true, missiles fired are immediately destroyed. If false missiles are not controlled.
|
||||
-- @param #boolean CleanMissiles (Optional) (Default=true) If true, missiles fired are immediately destroyed. If false missiles are not controlled.
|
||||
-- @return #CLEANUP_AIRBASE
|
||||
function CLEANUP_AIRBASE:SetCleanMissiles( CleanMissiles )
|
||||
|
||||
|
||||
@@ -301,12 +301,11 @@ do -- DESIGNATE
|
||||
|
||||
--- DESIGNATE Constructor. This class is an abstract class and should not be instantiated.
|
||||
-- @param #DESIGNATE self
|
||||
-- @param Tasking.CommandCenter#COMMANDCENTER CC
|
||||
-- @param Wrapper.Group#GROUP CC Group as Commandcenter standin. Currently unused (03/2026)
|
||||
-- @param Functional.Detection#DETECTION_BASE Detection
|
||||
-- @param Core.Set#SET_GROUP AttackSet The Attack collection of GROUP objects to designate and report for.
|
||||
-- @param Tasking.Mission#MISSION Mission (Optional) The Mission where the menu needs to be attached.
|
||||
-- @return #DESIGNATE
|
||||
function DESIGNATE:New( CC, Detection, AttackSet, Mission )
|
||||
function DESIGNATE:New( CC, Detection, AttackSet )
|
||||
|
||||
local self = BASE:Inherit( self, FSM:New() ) -- #DESIGNATE
|
||||
self:F( { Detection } )
|
||||
@@ -468,7 +467,7 @@ do -- DESIGNATE
|
||||
-- @param #DESIGNATE self
|
||||
-- @param #number Delay
|
||||
|
||||
self.CC = CC
|
||||
--self.CC = CC
|
||||
self.Detection = Detection
|
||||
self.AttackSet = AttackSet
|
||||
self.RecceSet = Detection:GetDetectionSet()
|
||||
@@ -480,7 +479,7 @@ do -- DESIGNATE
|
||||
|
||||
self:SetFlashStatusMenu( false )
|
||||
self:SetFlashDetectionMessages( true )
|
||||
self:SetMission( Mission )
|
||||
--self:SetMission( Mission )
|
||||
|
||||
self:SetLaserCodes( { 1688, 1130, 4785, 6547, 1465, 4578 } ) -- set self.LaserCodes
|
||||
self:SetAutoLase( false, false ) -- set self.Autolase and don't send message.
|
||||
@@ -682,7 +681,7 @@ do -- DESIGNATE
|
||||
|
||||
--- Set the lase duration for designations.
|
||||
-- @param #DESIGNATE self
|
||||
-- @param #number LaseDuration The time in seconds a lase will continue to hold on target. The default is 120 seconds.
|
||||
-- @param #number LaseDuration (Optional) The time in seconds a lase will continue to hold on target. The default is 120 seconds.
|
||||
-- @return #DESIGNATE
|
||||
function DESIGNATE:SetLaseDuration( LaseDuration )
|
||||
self.LaseDuration = LaseDuration or 120
|
||||
@@ -754,10 +753,12 @@ do -- DESIGNATE
|
||||
|
||||
if Message then
|
||||
local AutoLaseOnOff = ( self.AutoLase == true ) and "On" or "Off"
|
||||
local CC = self.CC:GetPositionable()
|
||||
if CC then
|
||||
CC:MessageToSetGroup( self.DesignateName .. ": Auto Lase " .. AutoLaseOnOff .. ".", 15, self.AttackSet )
|
||||
end
|
||||
|
||||
MESSAGE:New(self.DesignateName .. ": Auto Lase " .. AutoLaseOnOff .. ".",15):ToSet(self.AttackSet)
|
||||
--local CC = self.CC:GetPositionable()
|
||||
--if CC then
|
||||
--CC:MessageToSetGroup( self.DesignateName .. ": Auto Lase " .. AutoLaseOnOff .. ".", 15, self.AttackSet )
|
||||
--end
|
||||
end
|
||||
|
||||
self:CoordinateLase()
|
||||
@@ -777,14 +778,14 @@ do -- DESIGNATE
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the MISSION object for which designate will function.
|
||||
--- [DEPRECATED DO NOT USE] Set the MISSION object for which designate will function.
|
||||
-- When a MISSION object is assigned, the menu for the designation will be located at the Mission Menu.
|
||||
-- @param #DESIGNATE self
|
||||
-- @param Tasking.Mission#MISSION Mission The MISSION object.
|
||||
-- @return #DESIGNATE
|
||||
function DESIGNATE:SetMission( Mission ) --R2.2
|
||||
|
||||
self.Mission = Mission
|
||||
--self.Mission = Mission
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -830,7 +831,8 @@ do -- DESIGNATE
|
||||
function( AttackGroup )
|
||||
if AttackGroup:IsAlive() == true then
|
||||
local DetectionText = self.Detection:DetectedItemReportSummary( DetectedItem, AttackGroup ):Text( ", " )
|
||||
self.CC:GetPositionable():MessageToGroup( "Targets out of LOS\n" .. DetectionText, 10, AttackGroup, self.DesignateName )
|
||||
--self.CC:GetPositionable():MessageToGroup( "Targets out of LOS\n" .. DetectionText, 10, AttackGroup, self.DesignateName )
|
||||
MESSAGE:New("Targets out of LOS\n" .. DetectionText,10,self.DesignateName):ToGroup(AttackGroup)
|
||||
end
|
||||
end
|
||||
)
|
||||
@@ -855,7 +857,8 @@ do -- DESIGNATE
|
||||
function( AttackGroup )
|
||||
if self.FlashDetectionMessage[AttackGroup] == true then
|
||||
local DetectionText = self.Detection:DetectedItemReportSummary( DetectedItem, AttackGroup ):Text( ", " )
|
||||
self.CC:GetPositionable():MessageToGroup( "Targets detected at \n" .. DetectionText, 10, AttackGroup, self.DesignateName )
|
||||
--self.CC:GetPositionable():MessageToGroup( "Targets detected at \n" .. DetectionText, 10, AttackGroup, self.DesignateName )
|
||||
MESSAGE:New( "Targets detected at \n" .. DetectionText,10,self.DesignateName):ToGroup(AttackGroup)
|
||||
end
|
||||
end
|
||||
)
|
||||
@@ -929,9 +932,10 @@ do -- DESIGNATE
|
||||
end
|
||||
end
|
||||
|
||||
local CC = self.CC:GetPositionable()
|
||||
-- local CC = self.CC:GetPositionable()
|
||||
|
||||
CC:MessageTypeToGroup( DetectedReport:Text( "\n" ), MESSAGE.Type.Information, AttackGroup, self.DesignateName )
|
||||
--CC:MessageTypeToGroup( DetectedReport:Text( "\n" ), MESSAGE.Type.Information, AttackGroup, self.DesignateName )
|
||||
MESSAGE:New( DetectedReport:Text( "\n" ),15,self.DesignateName):ToGroup(AttackGroup)
|
||||
|
||||
local DesignationReport = REPORT:New( "Marking Targets:" )
|
||||
|
||||
@@ -965,10 +969,10 @@ do -- DESIGNATE
|
||||
|
||||
local MissionMenu = nil
|
||||
|
||||
if self.Mission then
|
||||
--if self.Mission then
|
||||
--MissionMenu = self.Mission:GetRootMenu( AttackGroup )
|
||||
MissionMenu = self.Mission:GetMenu( AttackGroup )
|
||||
end
|
||||
--MissionMenu = self.Mission:GetMenu( AttackGroup )
|
||||
--end
|
||||
|
||||
local MenuTime = timer.getTime()
|
||||
|
||||
@@ -1356,12 +1360,13 @@ do -- DESIGNATE
|
||||
-- @param #DESIGNATE self
|
||||
-- @return #DESIGNATE
|
||||
function DESIGNATE:onafterLaseOff( From, Event, To, Index )
|
||||
|
||||
local CC = self.CC:GetPositionable()
|
||||
|
||||
MESSAGE:New("Stopped lasing.",5,self.DesignateName):ToSet(self.AttackSet)
|
||||
--local CC = self.CC:GetPositionable()
|
||||
|
||||
if CC then
|
||||
CC:MessageToSetGroup( "Stopped lasing.", 5, self.AttackSet, self.DesignateName )
|
||||
end
|
||||
--if CC then
|
||||
--CC:MessageToSetGroup( "Stopped lasing.", 5, self.AttackSet, self.DesignateName )
|
||||
--end
|
||||
|
||||
local DetectedItem = self.Detection:GetDetectedItemByIndex( Index )
|
||||
local TargetSetUnit = self.Detection:GetDetectedItemSet( DetectedItem )
|
||||
|
||||
@@ -1052,9 +1052,9 @@ do -- DETECTION_BASE
|
||||
--- Method to make the radar detection less accurate, e.g. for WWII scenarios.
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #number minheight Minimum flight height to be detected, in meters AGL (above ground)
|
||||
-- @param #number thresheight Threshold to escape the radar if flying below minheight, defaults to 90 (90% escape chance)
|
||||
-- @param #number thresblur Threshold to be detected by the radar overall, defaults to 85 (85% chance to be found)
|
||||
-- @param #number closing Closing-in in km - the limit of km from which on it becomes increasingly difficult to escape radar detection if flying towards the radar position. Should be about 1/3 of the radar detection radius in kilometers, defaults to 20.
|
||||
-- @param #number thresheight (Optional) Threshold to escape the radar if flying below minheight, defaults to 90 (90% escape chance)
|
||||
-- @param #number thresblur (Optional) Threshold to be detected by the radar overall, defaults to 85 (85% chance to be found)
|
||||
-- @param #number closing (Optional) Closing-in in km - the limit of km from which on it becomes increasingly difficult to escape radar detection if flying towards the radar position. Should be about 1/3 of the radar detection radius in kilometers, defaults to 20.
|
||||
-- @return #DETECTION_BASE self
|
||||
function DETECTION_BASE:SetRadarBlur(minheight,thresheight,thresblur,closing)
|
||||
self.RadarBlur = true
|
||||
@@ -1104,12 +1104,14 @@ do -- DETECTION_BASE
|
||||
|
||||
--- Set the parameters to calculate to optimal intercept point.
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #boolean Intercept Intercept is true if an intercept point is calculated. Intercept is false if it is disabled. The default Intercept is false.
|
||||
-- @param #boolean Intercept (Optional) Intercept is true if an intercept point is calculated. Intercept is false if it is disabled. The default Intercept is false.
|
||||
-- @param #number InterceptDelay If Intercept is true, then InterceptDelay is the average time it takes to get airplanes airborne.
|
||||
-- @return #DETECTION_BASE self
|
||||
function DETECTION_BASE:SetIntercept( Intercept, InterceptDelay )
|
||||
self:F2()
|
||||
|
||||
Intercept = Intercept or false
|
||||
|
||||
self.Intercept = Intercept
|
||||
self.InterceptDelay = InterceptDelay
|
||||
|
||||
@@ -1611,8 +1613,8 @@ do -- DETECTION_BASE
|
||||
-- The DetectedItem is a table and contains a SET_UNIT in the field Set.
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #string ItemPrefix Prefix of detected item.
|
||||
-- @param #number DetectedItemKey The key of the DetectedItem. Default self.DetectedItemMax. Could also be a string in principle.
|
||||
-- @param Core.Set#SET_UNIT Set (optional) The Set of Units to be added.
|
||||
-- @param #number DetectedItemKey (Optional) The key of the DetectedItem. Default self.DetectedItemMax. Could also be a string in principle.
|
||||
-- @param Core.Set#SET_UNIT Set (Optional) The Set of Units to be added.
|
||||
-- @return #DETECTION_BASE.DetectedItem
|
||||
function DETECTION_BASE:AddDetectedItem( ItemPrefix, DetectedItemKey, Set )
|
||||
|
||||
@@ -1846,7 +1848,7 @@ do -- DETECTION_BASE
|
||||
return self
|
||||
end
|
||||
|
||||
--- Validate if the detected item is locked.
|
||||
--- Validate if the detected item is locked (not in the radar sense, though!).
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem.
|
||||
-- @return #boolean
|
||||
@@ -1856,7 +1858,7 @@ do -- DETECTION_BASE
|
||||
|
||||
end
|
||||
|
||||
--- Lock a detected item.
|
||||
--- Lock a detected item (not in the radar sense, though!).
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem.
|
||||
-- @return #DETECTION_BASE
|
||||
@@ -1867,7 +1869,7 @@ do -- DETECTION_BASE
|
||||
return self
|
||||
end
|
||||
|
||||
--- Unlock a detected item.
|
||||
--- Unlock a detected item (not in the radar sense, though!).
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem.
|
||||
-- @return #DETECTION_BASE
|
||||
@@ -2536,7 +2538,7 @@ do -- DETECTION_AREAS
|
||||
--- DETECTION_AREAS constructor.
|
||||
-- @param #DETECTION_AREAS self
|
||||
-- @param Core.Set#SET_GROUP DetectionSetGroup The @{Core.Set} of GROUPs in the Forward Air Controller role.
|
||||
-- @param #number DetectionZoneRange The range in meters within which targets are grouped upon the first detected target. Default 5000m.
|
||||
-- @param #number DetectionZoneRange (Optional) The range in meters within which targets are grouped upon the first detected target. Default 5000m.
|
||||
-- @return #DETECTION_AREAS
|
||||
function DETECTION_AREAS:New( DetectionSetGroup, DetectionZoneRange )
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ ESCORT = {
|
||||
-- @param Wrapper.Client#CLIENT EscortClient The client escorted by the EscortGroup.
|
||||
-- @param Wrapper.Group#GROUP EscortGroup The group AI escorting the EscortClient.
|
||||
-- @param #string EscortName Name of the escort.
|
||||
-- @param #string EscortBriefing A text showing the ESCORT briefing to the player. Note that if no EscortBriefing is provided, the default briefing will be shown.
|
||||
-- @param #string EscortBriefing (Optional) A text showing the ESCORT briefing to the player. Note that if no EscortBriefing is provided, the default briefing will be shown.
|
||||
-- @return #ESCORT self
|
||||
-- @usage
|
||||
-- -- Declare a new EscortPlanes object as follows:
|
||||
@@ -333,9 +333,9 @@ end
|
||||
--- Defines a menu slot to let the escort hold at their current position and stay low with a specified height during a specified time in seconds.
|
||||
-- This menu will appear under **Hold position**.
|
||||
-- @param #ESCORT self
|
||||
-- @param DCS#Distance Height Optional parameter that sets the height in meters to let the escort orbit at the current location. The default value is 30 meters.
|
||||
-- @param DCS#Time Seconds Optional parameter that lets the escort orbit at the current position for a specified time. (not implemented yet). The default value is 0 seconds, meaning, that the escort will orbit forever until a sequent command is given.
|
||||
-- @param #string MenuTextFormat Optional parameter that shows the menu option text. The text string is formatted, and should contain two %d tokens in the string. The first for the Height, the second for the Time (if given). If no text is given, the default text will be displayed.
|
||||
-- @param DCS#Distance Height (Optional) parameter that sets the height in meters to let the escort orbit at the current location. The default value is 30 meters.
|
||||
-- @param DCS#Time Seconds (Optional) parameter that lets the escort orbit at the current position for a specified time. (not implemented yet). The default value is 0 seconds, meaning, that the escort will orbit forever until a sequent command is given.
|
||||
-- @param #string MenuTextFormat (Optional) parameter that shows the menu option text. The text string is formatted, and should contain two %d tokens in the string. The first for the Height, the second for the Time (if given). If no text is given, the default text will be displayed.
|
||||
-- @return #ESCORT
|
||||
-- TODO: Implement Seconds parameter. Challenge is to first develop the "continue from last activity" function.
|
||||
function ESCORT:MenuHoldAtEscortPosition( Height, Seconds, MenuTextFormat )
|
||||
@@ -394,9 +394,9 @@ end
|
||||
--- Defines a menu slot to let the escort hold at the client position and stay low with a specified height during a specified time in seconds.
|
||||
-- This menu will appear under **Navigation**.
|
||||
-- @param #ESCORT self
|
||||
-- @param DCS#Distance Height Optional parameter that sets the height in meters to let the escort orbit at the current location. The default value is 30 meters.
|
||||
-- @param DCS#Time Seconds Optional parameter that lets the escort orbit at the current position for a specified time. (not implemented yet). The default value is 0 seconds, meaning, that the escort will orbit forever until a sequent command is given.
|
||||
-- @param #string MenuTextFormat Optional parameter that shows the menu option text. The text string is formatted, and should contain one or two %d tokens in the string. The first for the Height, the second for the Time (if given). If no text is given, the default text will be displayed.
|
||||
-- @param DCS#Distance Height (Optional) parameter that sets the height in meters to let the escort orbit at the current location. The default value is 30 meters.
|
||||
-- @param DCS#Time Seconds (Optional) parameter that lets the escort orbit at the current position for a specified time. (not implemented yet). The default value is 0 seconds, meaning, that the escort will orbit forever until a sequent command is given.
|
||||
-- @param #string MenuTextFormat (Optional) parameter that shows the menu option text. The text string is formatted, and should contain one or two %d tokens in the string. The first for the Height, the second for the Time (if given). If no text is given, the default text will be displayed.
|
||||
-- @return #ESCORT
|
||||
-- TODO: Implement Seconds parameter. Challenge is to first develop the "continue from last activity" function.
|
||||
function ESCORT:MenuHoldAtLeaderPosition( Height, Seconds, MenuTextFormat )
|
||||
@@ -455,9 +455,9 @@ end
|
||||
--- Defines a menu slot to let the escort scan for targets at a certain height for a certain time in seconds.
|
||||
-- This menu will appear under **Scan targets**.
|
||||
-- @param #ESCORT self
|
||||
-- @param DCS#Distance Height Optional parameter that sets the height in meters to let the escort orbit at the current location. The default value is 30 meters.
|
||||
-- @param DCS#Time Seconds Optional parameter that lets the escort orbit at the current position for a specified time. (not implemented yet). The default value is 0 seconds, meaning, that the escort will orbit forever until a sequent command is given.
|
||||
-- @param #string MenuTextFormat Optional parameter that shows the menu option text. The text string is formatted, and should contain one or two %d tokens in the string. The first for the Height, the second for the Time (if given). If no text is given, the default text will be displayed.
|
||||
-- @param DCS#Distance Height (Optional) parameter that sets the height in meters to let the escort orbit at the current location. The default value is 30 meters.
|
||||
-- @param DCS#Time Seconds (Optional) parameter that lets the escort orbit at the current position for a specified time. (not implemented yet). The default value is 0 seconds, meaning, that the escort will orbit forever until a sequent command is given.
|
||||
-- @param #string MenuTextFormat (Optional) parameter that shows the menu option text. The text string is formatted, and should contain one or two %d tokens in the string. The first for the Height, the second for the Time (if given). If no text is given, the default text will be displayed.
|
||||
-- @return #ESCORT
|
||||
function ESCORT:MenuScanForTargets( Height, Seconds, MenuTextFormat )
|
||||
self:F( { Height, Seconds, MenuTextFormat } )
|
||||
@@ -514,7 +514,7 @@ end
|
||||
-- This menu will appear under **Navigation**.
|
||||
-- The flare will be fired from the first unit in the group.
|
||||
-- @param #ESCORT self
|
||||
-- @param #string MenuTextFormat Optional parameter that shows the menu option text. If no text is given, the default text will be displayed.
|
||||
-- @param #string MenuTextFormat (Optional) parameter that shows the menu option text. If no text is given, the default text will be displayed.
|
||||
-- @return #ESCORT
|
||||
function ESCORT:MenuFlare( MenuTextFormat )
|
||||
self:F()
|
||||
@@ -546,7 +546,7 @@ end
|
||||
-- Note that smoke menu options will only be displayed for ships and ground units. Not for air units.
|
||||
-- The smoke will be fired from the first unit in the group.
|
||||
-- @param #ESCORT self
|
||||
-- @param #string MenuTextFormat Optional parameter that shows the menu option text. If no text is given, the default text will be displayed.
|
||||
-- @param #string MenuTextFormat (Optional) parameter that shows the menu option text. If no text is given, the default text will be displayed.
|
||||
-- @return #ESCORT
|
||||
function ESCORT:MenuSmoke( MenuTextFormat )
|
||||
self:F()
|
||||
@@ -580,7 +580,7 @@ end
|
||||
-- This menu will appear under **Report targets**.
|
||||
-- Note that if a report targets menu is not specified, no targets will be detected by the escort, and the attack and assisted attack menus will not be displayed.
|
||||
-- @param #ESCORT self
|
||||
-- @param DCS#Time Seconds Optional parameter that lets the escort report their current detected targets after specified time interval in seconds. The default time is 30 seconds.
|
||||
-- @param DCS#Time Seconds (Optional) parameter that lets the escort report their current detected targets after specified time interval in seconds. The default time is 30 seconds.
|
||||
-- @return #ESCORT
|
||||
function ESCORT:MenuReportTargets( Seconds )
|
||||
self:F( { Seconds } )
|
||||
|
||||
+329
-611
File diff suppressed because it is too large
Load Diff
@@ -459,7 +459,7 @@ end
|
||||
--- Set explosion power. This is an "artificial" explosion generated when the missile is destroyed. Just for the visual effect.
|
||||
-- Don't set the explosion power too big or it will harm the aircraft in the vicinity.
|
||||
-- @param #FOX self
|
||||
-- @param #number power Explosion power in kg TNT. Default 0.1 kg.
|
||||
-- @param #number power (Optional) Explosion power in kg TNT. Default 0.1 kg.
|
||||
-- @return #FOX self
|
||||
function FOX:SetExplosionPower(power)
|
||||
|
||||
@@ -470,7 +470,7 @@ end
|
||||
|
||||
--- Set missile-player distance when missile is destroyed.
|
||||
-- @param #FOX self
|
||||
-- @param #number distance Distance in meters. Default 200 m.
|
||||
-- @param #number distance (Optional) Distance in meters. Default 200 m.
|
||||
-- @return #FOX self
|
||||
function FOX:SetExplosionDistance(distance)
|
||||
|
||||
@@ -481,8 +481,8 @@ end
|
||||
|
||||
--- Set missile-player distance when BIG missiles are destroyed.
|
||||
-- @param #FOX self
|
||||
-- @param #number distance Distance in meters. Default 500 m.
|
||||
-- @param #number explosivemass Explosive mass of missile threshold in kg TNT. Default 50 kg.
|
||||
-- @param #number distance (Optional) Distance in meters. Default 500 m.
|
||||
-- @param #number explosivemass (Optional) Explosive mass of missile threshold in kg TNT. Default 50 kg.
|
||||
-- @return #FOX self
|
||||
function FOX:SetExplosionDistanceBigMissiles(distance, explosivemass)
|
||||
|
||||
@@ -516,7 +516,7 @@ end
|
||||
|
||||
--- Set verbosity level.
|
||||
-- @param #FOX self
|
||||
-- @param #number VerbosityLevel Level of output (higher=more). Default 0.
|
||||
-- @param #number VerbosityLevel (Optional) Level of output (higher=more). Default 0.
|
||||
-- @return #FOX self
|
||||
function FOX:SetVerbosity(VerbosityLevel)
|
||||
self.verbose=VerbosityLevel or 0
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,736 +0,0 @@
|
||||
--- **Functional** - Train missile defence and deflection.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Track the missiles fired at you and other players, providing bearing and range information of the missiles towards the airplanes.
|
||||
-- * Provide alerts of missile launches, including detailed information of the units launching, including bearing, range
|
||||
-- * Provide alerts when a missile would have killed your aircraft.
|
||||
-- * Provide alerts when the missile self destructs.
|
||||
-- * Enable / Disable and Configure the Missile Trainer using the various menu options.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Missions:
|
||||
--
|
||||
-- [MIT - Missile Trainer](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/Functional/MissileTrainer)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- Uses the MOOSE messaging system to be alerted of any missiles fired, and when a missile would hit your aircraft,
|
||||
-- the class will destroy the missile within a certain range, to avoid damage to your aircraft.
|
||||
--
|
||||
-- When running a mission where the missile trainer is used, the following radio menu structure ( 'Radio Menu' -> 'Other (F10)' -> 'MissileTrainer' ) options are available for the players:
|
||||
--
|
||||
-- * **Messages**: Menu to configure all messages.
|
||||
-- * **Messages On**: Show all messages.
|
||||
-- * **Messages Off**: Disable all messages.
|
||||
-- * **Tracking**: Menu to configure missile tracking messages.
|
||||
-- * **To All**: Shows missile tracking messages to all players.
|
||||
-- * **To Target**: Shows missile tracking messages only to the player where the missile is targeted at.
|
||||
-- * **Tracking On**: Show missile tracking messages.
|
||||
-- * **Tracking Off**: Disable missile tracking messages.
|
||||
-- * **Frequency Increase**: Increases the missile tracking message frequency with one second.
|
||||
-- * **Frequency Decrease**: Decreases the missile tracking message frequency with one second.
|
||||
-- * **Alerts**: Menu to configure alert messages.
|
||||
-- * **To All**: Shows alert messages to all players.
|
||||
-- * **To Target**: Shows alert messages only to the player where the missile is (was) targeted at.
|
||||
-- * **Hits On**: Show missile hit alert messages.
|
||||
-- * **Hits Off**: Disable missile hit alert messages.
|
||||
-- * **Launches On**: Show missile launch messages.
|
||||
-- * **Launches Off**: Disable missile launch messages.
|
||||
-- * **Details**: Menu to configure message details.
|
||||
-- * **Range On**: Shows range information when a missile is fired to a target.
|
||||
-- * **Range Off**: Disable range information when a missile is fired to a target.
|
||||
-- * **Bearing On**: Shows bearing information when a missile is fired to a target.
|
||||
-- * **Bearing Off**: Disable bearing information when a missile is fired to a target.
|
||||
-- * **Distance**: Menu to configure the distance when a missile needs to be destroyed when near to a player, during tracking. This will improve/influence hit calculation accuracy, but has the risk of damaging the aircraft when the missile reaches the aircraft before the distance is measured.
|
||||
-- * **50 meter**: Destroys the missile when the distance to the aircraft is below or equal to 50 meter.
|
||||
-- * **100 meter**: Destroys the missile when the distance to the aircraft is below or equal to 100 meter.
|
||||
-- * **150 meter**: Destroys the missile when the distance to the aircraft is below or equal to 150 meter.
|
||||
-- * **200 meter**: Destroys the missile when the distance to the aircraft is below or equal to 200 meter.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE.
|
||||
-- Therefore, this class is considered to be deprecated and superseded by the [Functional.Fox](https://flightcontrol-master.github.io/MOOSE_DOCS_DEVELOP/Documentation/Functional.Fox.html) class, which provides the same functionality.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Authors: **FlightControl**
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- * **Stuka (Danny)**: Who you can search on the Eagle Dynamics Forums. Working together with Danny has resulted in the MISSILETRAINER class.
|
||||
-- Danny has shared his ideas and together we made a design.
|
||||
-- Together with the **476 virtual team**, we tested the MISSILETRAINER class, and got much positive feedback!
|
||||
-- * **132nd Squadron**: Testing and optimizing the logic.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Functional.MissileTrainer
|
||||
-- @image Missile_Trainer.JPG
|
||||
|
||||
---
|
||||
-- @type MISSILETRAINER
|
||||
-- @field Core.Set#SET_CLIENT DBClients
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
---
|
||||
--
|
||||
-- # Constructor:
|
||||
--
|
||||
-- Create a new MISSILETRAINER object with the @{#MISSILETRAINER.New} method:
|
||||
--
|
||||
-- * @{#MISSILETRAINER.New}: Creates a new MISSILETRAINER object taking the maximum distance to your aircraft to evaluate when a missile needs to be destroyed.
|
||||
--
|
||||
-- MISSILETRAINER will collect each unit declared in the mission with a skill level "Client" and "Player", and will monitor the missiles shot at those.
|
||||
--
|
||||
-- # Initialization:
|
||||
--
|
||||
-- A MISSILETRAINER object will behave differently based on the usage of initialization methods:
|
||||
--
|
||||
-- * @{#MISSILETRAINER.InitMessagesOnOff}: Sets by default the display of any message to be ON or OFF.
|
||||
-- * @{#MISSILETRAINER.InitTrackingToAll}: Sets by default the missile tracking report for all players or only for those missiles targeted to you.
|
||||
-- * @{#MISSILETRAINER.InitTrackingOnOff}: Sets by default the display of missile tracking report to be ON or OFF.
|
||||
-- * @{#MISSILETRAINER.InitTrackingFrequency}: Increases, decreases the missile tracking message display frequency with the provided time interval in seconds.
|
||||
-- * @{#MISSILETRAINER.InitAlertsToAll}: Sets by default the display of alerts to be shown to all players or only to you.
|
||||
-- * @{#MISSILETRAINER.InitAlertsHitsOnOff}: Sets by default the display of hit alerts ON or OFF.
|
||||
-- * @{#MISSILETRAINER.InitAlertsLaunchesOnOff}: Sets by default the display of launch alerts ON or OFF.
|
||||
-- * @{#MISSILETRAINER.InitRangeOnOff}: Sets by default the display of range information of missiles ON of OFF.
|
||||
-- * @{#MISSILETRAINER.InitBearingOnOff}: Sets by default the display of bearing information of missiles ON of OFF.
|
||||
-- * @{#MISSILETRAINER.InitMenusOnOff}: Allows to configure the options through the radio menu.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE.
|
||||
-- Therefore, this class is considered to be deprecated and superseded by the [Functional.Fox](https://flightcontrol-master.github.io/MOOSE_DOCS_DEVELOP/Documentation/Functional.Fox.html) class, which provides the same functionality.
|
||||
--
|
||||
-- @field #MISSILETRAINER
|
||||
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 ) -- Menu#MENU_GROUP
|
||||
|
||||
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
|
||||
|
||||
--- Creates the main object which is handling missile tracking.
|
||||
-- When a missile is fired a SCHEDULER is set off that follows the missile. When near a certain a client player, the missile will be destroyed.
|
||||
-- @param #MISSILETRAINER self
|
||||
-- @param #number Distance The distance in meters when a tracked missile needs to be destroyed when close to a player.
|
||||
-- @param #string Briefing (Optional) Will show a text to the players when starting their mission. Can be used for briefing purposes.
|
||||
-- @return #MISSILETRAINER
|
||||
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()
|
||||
|
||||
|
||||
-- for ClientID, Client in pairs( self.DBClients.Database ) do
|
||||
-- self:F( "ForEach:" .. Client.UnitName )
|
||||
-- Client:Alive( self._Alive, self )
|
||||
-- end
|
||||
--
|
||||
self.DBClients:ForEachClient(
|
||||
function( Client )
|
||||
self:F( "ForEach:" .. Client.UnitName )
|
||||
Client:Alive( self._Alive, self )
|
||||
end
|
||||
)
|
||||
|
||||
|
||||
|
||||
-- self.DB:ForEachClient(
|
||||
-- -- @param Wrapper.Client#CLIENT Client
|
||||
-- function( Client )
|
||||
--
|
||||
-- ... actions ...
|
||||
--
|
||||
-- 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
|
||||
|
||||
-- Initialization methods.
|
||||
|
||||
|
||||
|
||||
--- Sets by default the display of any message to be ON or OFF.
|
||||
-- @param #MISSILETRAINER self
|
||||
-- @param #boolean MessagesOnOff true or false
|
||||
-- @return #MISSILETRAINER self
|
||||
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
|
||||
|
||||
--- Sets by default the missile tracking report for all players or only for those missiles targeted to you.
|
||||
-- @param #MISSILETRAINER self
|
||||
-- @param #boolean TrackingToAll true or false
|
||||
-- @return #MISSILETRAINER self
|
||||
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
|
||||
|
||||
--- Sets by default the display of missile tracking report to be ON or OFF.
|
||||
-- @param #MISSILETRAINER self
|
||||
-- @param #boolean TrackingOnOff true or false
|
||||
-- @return #MISSILETRAINER self
|
||||
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
|
||||
|
||||
--- Increases, decreases the missile tracking message display frequency with the provided time interval in seconds.
|
||||
-- The default frequency is a 3 second interval, so the Tracking Frequency parameter specifies the increase or decrease from the default 3 seconds or the last frequency update.
|
||||
-- @param #MISSILETRAINER self
|
||||
-- @param #number TrackingFrequency Provide a negative or positive value in seconds to incraese or decrease the display frequency.
|
||||
-- @return #MISSILETRAINER self
|
||||
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
|
||||
|
||||
--- Sets by default the display of alerts to be shown to all players or only to you.
|
||||
-- @param #MISSILETRAINER self
|
||||
-- @param #boolean AlertsToAll true or false
|
||||
-- @return #MISSILETRAINER self
|
||||
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
|
||||
|
||||
--- Sets by default the display of hit alerts ON or OFF.
|
||||
-- @param #MISSILETRAINER self
|
||||
-- @param #boolean AlertsHitsOnOff true or false
|
||||
-- @return #MISSILETRAINER self
|
||||
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
|
||||
|
||||
--- Sets by default the display of launch alerts ON or OFF.
|
||||
-- @param #MISSILETRAINER self
|
||||
-- @param #boolean AlertsLaunchesOnOff true or false
|
||||
-- @return #MISSILETRAINER self
|
||||
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
|
||||
|
||||
--- Sets by default the display of range information of missiles ON of OFF.
|
||||
-- @param #MISSILETRAINER self
|
||||
-- @param #boolean DetailsRangeOnOff true or false
|
||||
-- @return #MISSILETRAINER self
|
||||
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
|
||||
|
||||
--- Sets by default the display of bearing information of missiles ON of OFF.
|
||||
-- @param #MISSILETRAINER self
|
||||
-- @param #boolean DetailsBearingOnOff true or false
|
||||
-- @return #MISSILETRAINER self
|
||||
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
|
||||
|
||||
--- Enables / Disables the menus.
|
||||
-- @param #MISSILETRAINER self
|
||||
-- @param #boolean MenusOnOff true or false
|
||||
-- @return #MISSILETRAINER self
|
||||
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
|
||||
|
||||
|
||||
-- Menu functions
|
||||
|
||||
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
|
||||
|
||||
--- Detects if an SA site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME.
|
||||
-- @param #MISSILETRAINER self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function MISSILETRAINER:OnEventShot( EVentData )
|
||||
self:F( { EVentData } )
|
||||
|
||||
local TrainerSourceDCSUnit = EVentData.IniDCSUnit
|
||||
local TrainerSourceDCSUnitName = EVentData.IniDCSUnitName
|
||||
local TrainerWeapon = EVentData.Weapon -- Identify the weapon fired
|
||||
local TrainerWeaponName = EVentData.WeaponName -- return weapon type
|
||||
|
||||
self:T( "Missile Launched = " .. TrainerWeaponName )
|
||||
|
||||
local TrainerTargetDCSUnit = TrainerWeapon:getTarget() -- Identify target
|
||||
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 )
|
||||
--self:T( self.TrackingMissiles )
|
||||
end
|
||||
else
|
||||
-- TODO: some weapons don't know the target unit... Need to develop a workaround for this.
|
||||
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
|
||||
|
||||
-- ALERTS PART
|
||||
|
||||
-- Loop for all Player Clients to check the alerts and deletion of missiles.
|
||||
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
|
||||
-- Hit alert
|
||||
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
|
||||
-- Weapon does not exist anymore. Delete from Table
|
||||
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 -- Only do this when tracking information needs to be displayed.
|
||||
|
||||
-- TRACKING PART
|
||||
|
||||
-- For the current client, the missile range and bearing details are displayed To the Player Client.
|
||||
-- For the other clients, the missile range and bearing details are displayed To the other Player Clients.
|
||||
-- To achieve this, a cross loop is done for each Player Client <-> Other Player Client missile information.
|
||||
|
||||
-- Main Player Client loop
|
||||
for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do
|
||||
|
||||
local Client = ClientData.Client
|
||||
--self:T2( { Client:GetName() } )
|
||||
|
||||
|
||||
ClientData.MessageToClient = ""
|
||||
ClientData.MessageToAll = ""
|
||||
|
||||
-- Other Players Client loop
|
||||
for TrackingDataID, TrackingData in pairs( self.TrackingMissiles ) do
|
||||
|
||||
for MissileDataID, MissileData in pairs( TrackingData.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
|
||||
|
||||
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
|
||||
|
||||
-- Once the Player Client and the Other Player Client tracking messages are prepared, show them.
|
||||
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
|
||||
@@ -100,7 +100,7 @@ PSEUDOATC.id="PseudoATC | "
|
||||
|
||||
--- PSEUDOATC version.
|
||||
-- @field #number version
|
||||
PSEUDOATC.version="0.10.5"
|
||||
PSEUDOATC.version="0.10.6"
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -172,7 +172,7 @@ end
|
||||
|
||||
--- Set duration how long messages are displayed.
|
||||
-- @param #PSEUDOATC self
|
||||
-- @param #number duration Time in seconds. Default is 30 sec.
|
||||
-- @param #number duration (Optional) Time in seconds. Default is 30 sec.
|
||||
function PSEUDOATC:SetMessageDuration(duration)
|
||||
self.mdur=duration or 30
|
||||
end
|
||||
@@ -186,21 +186,21 @@ end
|
||||
|
||||
--- Set time interval after which the F10 radio menu is refreshed.
|
||||
-- @param #PSEUDOATC self
|
||||
-- @param #number interval Interval in seconds. Default is every 120 sec.
|
||||
-- @param #number interval (Optional) Interval in seconds. Default is every 120 sec.
|
||||
function PSEUDOATC:SetMenuRefresh(interval)
|
||||
self.mrefresh=interval or 120
|
||||
end
|
||||
|
||||
--- [Deprecated] Enable/disable event handling by MOOSE or DCS.
|
||||
-- @param #PSEUDOATC self
|
||||
-- @param #boolean switch If true, events are handled by MOOSE (default). If false, events are handled directly by DCS.
|
||||
-- @param #boolean switch (Optional) If true, events are handled by MOOSE (default). If false, events are handled directly by DCS.
|
||||
function PSEUDOATC:SetEventsMoose(switch)
|
||||
self.eventsmoose=switch
|
||||
end
|
||||
|
||||
--- Set time interval for reporting altitude until touchdown.
|
||||
-- @param #PSEUDOATC self
|
||||
-- @param #number interval Interval in seconds. Default is every 3 sec.
|
||||
-- @param #number interval (Optional) Interval in seconds. Default is every 3 sec.
|
||||
function PSEUDOATC:SetReportAltInterval(interval)
|
||||
self.talt=interval or 3
|
||||
end
|
||||
@@ -579,6 +579,9 @@ function PSEUDOATC:MenuAirports(GID,UID)
|
||||
-- Create menu reporting commands
|
||||
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)
|
||||
if self.radios then
|
||||
missionCommands.addCommandForGroup(GID, "Radios", submenu, self.ReportRadios, self, GID, UID, pos, name)
|
||||
end
|
||||
|
||||
-- Debug message.
|
||||
self:T(string.format(PSEUDOATC.id.."Creating airport menu item %s for ID %d", name, GID))
|
||||
@@ -705,7 +708,30 @@ function PSEUDOATC:ReportWeather(GID, UID, position, location)
|
||||
|
||||
end
|
||||
|
||||
--- Report absolute bearing and range form player unit to airport.
|
||||
--- Report airport radio information.
|
||||
-- @param #PSEUDOATC self
|
||||
-- @param #number GID Group id of player unit.
|
||||
-- @param #number UID Unit id of player.
|
||||
-- @param Core.Point#COORDINATE position Coordinate of the airport.
|
||||
-- @param #string location Name of the airport.
|
||||
function PSEUDOATC:ReportRadios(GID, UID, position, location)
|
||||
self:F({GID=GID, UID=UID, position=position, location=location})
|
||||
if self.radios then
|
||||
local Text=""
|
||||
local radio = self.radios:GetClosestRadio(position,9) --Navigation.Radios#RADIOS.Radio
|
||||
if radio then
|
||||
Text=self.radios:_GetMarkerText(radio)
|
||||
else
|
||||
Text=self.group[GID].player[UID].playername..", no radio information found!"
|
||||
end
|
||||
-- Send message
|
||||
self:_DisplayMessageToGroup(self.group[GID].player[UID].unit, Text, self.mdur, true)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Report absolute bearing and range from player unit to airport.
|
||||
-- @param #PSEUDOATC self
|
||||
-- @param #number GID Group id of player unit.
|
||||
-- @param #number UID Unit id of player.
|
||||
@@ -979,3 +1005,22 @@ function PSEUDOATC:_myname(unitname)
|
||||
|
||||
return string.format("%s (%s)", csign, pname)
|
||||
end
|
||||
|
||||
--- Returns a string which consits of this callsign and the player name.
|
||||
-- @param #PSEUDOATC self
|
||||
-- @param #string path Path to map data, e.g. `[[<DCS_Install_Directory>\Mods\terrains\<Map_Name>\Radio.lua]]` (replace with correct path).
|
||||
-- Needs `lfs` and `io` to be desanitized in the `MissionScripting.lua` in `<DCS_Install_Directory>\Scripts`
|
||||
-- @return #PSEUDOATC self
|
||||
-- @usage
|
||||
--
|
||||
-- mypseudoatc:SetUsingRadioInformationFromMap([[C:\Program Files\Eagle Dynamics\DCS World.Openbeta\Mods\terrains\Caucasus\Radio.lua]])
|
||||
--
|
||||
function PSEUDOATC:SetUsingRadioInformationFromMap(path)
|
||||
if RADIOS and lfs and io then
|
||||
self.radios = RADIOS:NewFromFile(path)
|
||||
else
|
||||
self:E("PSEUDOATC:SetUsingRadioInformationFromMap Needs `lfs`and `io` to be desanitized in the `MissionScripting.lua` in `<DCS_Install_Directory>/Scripts`")
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@@ -1055,7 +1055,7 @@ end
|
||||
|
||||
--- Set the friendly coalitions from which the airports can be used as departure and destination.
|
||||
-- @param #RAT self
|
||||
-- @param #string friendly "same"=own coalition+neutral (default), "sameonly"=own coalition only, "neutral"=all neutral airports.
|
||||
-- @param #string friendly (Optional) "same"=own coalition+neutral (default), "sameonly"=own coalition only, "neutral"=all neutral airports.
|
||||
-- Default is "same", so aircraft will use airports of the coalition their spawn template has plus all neutral airports.
|
||||
-- @return #RAT RAT self object.
|
||||
-- @usage yak:SetCoalition("neutral") will spawn aircraft randomly on all neutral airports.
|
||||
@@ -1137,7 +1137,7 @@ end
|
||||
|
||||
--- Set the scan radius around parking spots. Parking spot is considered to be occupied if any obstacle is found with the radius.
|
||||
-- @param #RAT self
|
||||
-- @param #number radius Radius in meters. Default 50 m.
|
||||
-- @param #number radius (Optional) Radius in meters. Default 50 m.
|
||||
-- @return #RAT RAT self object.
|
||||
function RAT:SetParkingScanRadius(radius)
|
||||
self:F2(radius)
|
||||
@@ -1503,7 +1503,7 @@ end
|
||||
|
||||
--- Set the delay before first group is spawned.
|
||||
-- @param #RAT self
|
||||
-- @param #number delay Delay in seconds. Default is 5 seconds. Minimum delay is 0.5 seconds.
|
||||
-- @param #number delay (Optional) Delay in seconds. Default is 5 seconds. Minimum delay is 0.5 seconds.
|
||||
-- @return #RAT RAT self object.
|
||||
function RAT:SetSpawnDelay(delay)
|
||||
self:F2(delay)
|
||||
@@ -1514,7 +1514,7 @@ end
|
||||
|
||||
--- Set the interval between spawnings of the template group.
|
||||
-- @param #RAT self
|
||||
-- @param #number interval Interval in seconds. Default is 5 seconds. Minimum is 0.5 seconds.
|
||||
-- @param #number interval (Optional) Interval in seconds. Default is 5 seconds. Minimum is 0.5 seconds.
|
||||
-- @return #RAT RAT self object.
|
||||
function RAT:SetSpawnInterval(interval)
|
||||
self:F2(interval)
|
||||
@@ -1525,7 +1525,7 @@ end
|
||||
|
||||
--- Set max number of groups that will be spawned. When this limit is reached, no more RAT groups are spawned.
|
||||
-- @param #RAT self
|
||||
-- @param #number Nmax Max number of groups. Default `nil`=unlimited.
|
||||
-- @param #number Nmax (Optional) Max number of groups. Default `nil`=unlimited.
|
||||
-- @return #RAT RAT self object.
|
||||
function RAT:SetSpawnLimit(Nmax)
|
||||
self.NspawnMax=Nmax
|
||||
@@ -1545,7 +1545,7 @@ end
|
||||
|
||||
--- Sets the delay between despawning and respawning aircraft.
|
||||
-- @param #RAT self
|
||||
-- @param #number delay Delay in seconds until respawn happens. Default is 1 second. Minimum is 1 second.
|
||||
-- @param #number delay (Optional) Delay in seconds until respawn happens. Default is 1 second. Minimum is 1 second.
|
||||
-- @return #RAT RAT self object.
|
||||
function RAT:SetRespawnDelay(delay)
|
||||
self:F2(delay)
|
||||
@@ -1566,7 +1566,7 @@ end
|
||||
|
||||
--- Number of tries to respawn an aircraft in case it has accidentally been spawned on runway.
|
||||
-- @param #RAT self
|
||||
-- @param #number n Number of retries. Default is 3.
|
||||
-- @param #number n (Optional) Number of retries. Default is 3.
|
||||
-- @return #RAT RAT self object.
|
||||
function RAT:SetMaxRespawnTriedWhenSpawnedOnRunway(n)
|
||||
self:F2(n)
|
||||
@@ -1624,7 +1624,7 @@ end
|
||||
--- Check if aircraft have accidentally been spawned on the runway. If so they will be removed immediately.
|
||||
-- @param #RAT self
|
||||
-- @param #boolean switch If true, check is performed. If false, this check is omitted.
|
||||
-- @param #number radius Distance in meters until a unit is considered to have spawned accidentally on the runway. Default is 75 m.
|
||||
-- @param #number radius (Optional) Distance in meters until a unit is considered to have spawned accidentally on the runway. Default is 75 m.
|
||||
-- @return #RAT RAT self object.
|
||||
function RAT:CheckOnRunway(switch, distance)
|
||||
self:F2(switch)
|
||||
@@ -1639,7 +1639,7 @@ end
|
||||
--- Check if aircraft have accidentally been spawned on top of each other. If yes, they will be removed immediately.
|
||||
-- @param #RAT self
|
||||
-- @param #boolean switch If true, check is performed. If false, this check is omitted.
|
||||
-- @param #number radius Radius in meters until which a unit is considered to be on top of each other. Default is 2 m.
|
||||
-- @param #number radius (Optional) Radius in meters until which a unit is considered to be on top of each other. Default is 2 m.
|
||||
-- @return #RAT RAT self object.
|
||||
function RAT:CheckOnTop(switch, radius)
|
||||
self:F2(switch)
|
||||
@@ -1755,10 +1755,10 @@ end
|
||||
|
||||
--- Define how aircraft that are spawned in uncontrolled state are activate.
|
||||
-- @param #RAT self
|
||||
-- @param #number maxactivated Maximal numnber of activated aircraft. Absolute maximum will be the number of spawned groups. Default is 1.
|
||||
-- @param #number delay Time delay in seconds before (first) aircraft is activated. Default is 1 second.
|
||||
-- @param #number delta Time difference in seconds before next aircraft is activated. Default is 1 second.
|
||||
-- @param #number frand Factor [0,...,1] for randomization of time difference between aircraft activations. Default is 0, i.e. no randomization.
|
||||
-- @param #number maxactivated (Optional) Maximal numnber of activated aircraft. Absolute maximum will be the number of spawned groups. Default is 1.
|
||||
-- @param #number delay (Optional) Time delay in seconds before (first) aircraft is activated. Default is 1 second.
|
||||
-- @param #number delta (Optional) Time difference in seconds before next aircraft is activated. Default is 1 second.
|
||||
-- @param #number frand (Optional) Factor [0,...,1] for randomization of time difference between aircraft activations. Default is 0, i.e. no randomization.
|
||||
-- @return #RAT RAT self object.
|
||||
function RAT:ActivateUncontrolled(maxactivated, delay, delta, frand)
|
||||
self:F2({max=maxactivated, delay=delay, delta=delta, rand=frand})
|
||||
@@ -1784,7 +1784,7 @@ end
|
||||
|
||||
--- Set the time after which inactive groups will be destroyed.
|
||||
-- @param #RAT self
|
||||
-- @param #number time Time in seconds. Default is 600 seconds = 10 minutes. Minimum is 60 seconds.
|
||||
-- @param #number time (Optional) Time in seconds. Default is 600 seconds = 10 minutes. Minimum is 60 seconds.
|
||||
-- @return #RAT RAT self object.
|
||||
function RAT:TimeDestroyInactive(time)
|
||||
self:F2(time)
|
||||
@@ -1805,9 +1805,20 @@ function RAT:SetMaxCruiseSpeed(speed)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the minimum cruise speed of the aircraft.
|
||||
-- @param #RAT self
|
||||
-- @param #number speed Speed in km/h.
|
||||
-- @return #RAT RAT self object.
|
||||
function RAT:SetMinCruiseSpeed(speed)
|
||||
self:F2(speed)
|
||||
-- Convert to m/s.
|
||||
self.Vcruisemin=speed/3.6
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the climb rate. This automatically sets the climb angle.
|
||||
-- @param #RAT self
|
||||
-- @param #number rate Climb rate in ft/min. Default is 1500 ft/min. Minimum is 100 ft/min. Maximum is 15,000 ft/min.
|
||||
-- @param #number rate (Optional) Climb rate in ft/min. Default is 1500 ft/min. Minimum is 100 ft/min. Maximum is 15,000 ft/min.
|
||||
-- @return #RAT RAT self object.
|
||||
function RAT:SetClimbRate(rate)
|
||||
self:F2(rate)
|
||||
@@ -1901,7 +1912,7 @@ end
|
||||
|
||||
--- Max number of planes that get landing clearance of the RAT ATC. This setting effects all RAT objects and groups!
|
||||
-- @param #RAT self
|
||||
-- @param #number n Number of aircraft that are allowed to land simultaniously. Default is 2.
|
||||
-- @param #number n (Optional) Number of aircraft that are allowed to land simultaniously. Default is 2.
|
||||
-- @return #RAT RAT self object.
|
||||
function RAT:ATC_Clearance(n)
|
||||
self:F2(n)
|
||||
@@ -1911,7 +1922,7 @@ end
|
||||
|
||||
--- Delay between granting landing clearance for simultanious landings. This setting effects all RAT objects and groups!
|
||||
-- @param #RAT self
|
||||
-- @param #number time Delay time when the next aircraft will get landing clearance event if the previous one did not land yet. Default is 240 sec.
|
||||
-- @param #number time (Optional) Delay time when the next aircraft will get landing clearance event if the previous one did not land yet. Default is 240 sec.
|
||||
-- @return #RAT RAT self object.
|
||||
function RAT:ATC_Delay(time)
|
||||
self:F2(time)
|
||||
@@ -2091,6 +2102,8 @@ function RAT:_InitAircraft(DCSgroup)
|
||||
local DCSdesc=DCSunit:getDesc()
|
||||
local DCScategory=DCSgroup:getCategory()
|
||||
local DCStype=DCSunit:getTypeName()
|
||||
self:I({typename=DCStype})
|
||||
UTILS.PrintTableToLog(DCSdesc.box,1,noprint,3,seen)
|
||||
|
||||
-- set category
|
||||
if DCScategory==Group.Category.AIRPLANE then
|
||||
@@ -2125,7 +2138,12 @@ function RAT:_InitAircraft(DCSgroup)
|
||||
|
||||
-- Store all descriptors.
|
||||
--self.aircraft.descriptors=DCSdesc
|
||||
|
||||
|
||||
-- Tomcat sizing as default
|
||||
self.aircraft.length=12
|
||||
self.aircraft.height=4
|
||||
self.aircraft.width=10.3
|
||||
|
||||
-- aircraft dimensions
|
||||
if DCSdesc.box then
|
||||
self.aircraft.length=DCSdesc.box.max.x
|
||||
@@ -2135,10 +2153,22 @@ function RAT:_InitAircraft(DCSgroup)
|
||||
self.aircraft.length=16
|
||||
self.aircraft.height=5
|
||||
self.aircraft.width=9
|
||||
elseif DCStype == "Saab340" then -- <- These lines added
|
||||
self.aircraft.length=19.73 -- <- These lines added
|
||||
self.aircraft.height=6.97 -- <- These lines added
|
||||
self.aircraft.width=21.44 -- <- These lines added
|
||||
elseif DCStype == "Saab340" then
|
||||
self.aircraft.length=19.73
|
||||
self.aircraft.height=6.97
|
||||
self.aircraft.width=21.44
|
||||
elseif DCStype == "vwv_l-1049" then
|
||||
self.aircraft.length=35.41
|
||||
self.aircraft.height=7.54
|
||||
self.aircraft.width=38.47
|
||||
elseif DCStype == "uh2b" then
|
||||
self.aircraft.length=11.48
|
||||
self.aircraft.height=4.11
|
||||
self.aircraft.width=13.41
|
||||
elseif DCStype == "F-14A-135-GR" then
|
||||
self.aircraft.length=12
|
||||
self.aircraft.height=4
|
||||
self.aircraft.width=10.3
|
||||
end
|
||||
|
||||
self.aircraft.box=math.max(self.aircraft.length,self.aircraft.width)
|
||||
@@ -2748,7 +2778,7 @@ end
|
||||
--- Despawn group. The `FLIGHTGROUP` is despawned and stopped. The ratcraft is removed from the self.ratcraft table. Menues are removed.
|
||||
-- @param #RAT self
|
||||
-- @param Wrapper.Group#GROUP group Group to be despawned.
|
||||
-- @param #number delay Delay in seconds before the despawn happens. Default is immidiately.
|
||||
-- @param #number delay (Optional) Delay in seconds before the despawn happens. Default is immidiately.
|
||||
function RAT:_Despawn(group, delay)
|
||||
|
||||
if delay and delay>0 then
|
||||
@@ -2813,9 +2843,15 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint)
|
||||
-- Max cruise speed 90% of Vmax or 900 km/h whichever is lower.
|
||||
VxCruiseMax = math.min(self.aircraft.Vmax*0.90, 250)
|
||||
end
|
||||
|
||||
-- Min cruise speed 70% of max cruise or 600 km/h whichever is lower.
|
||||
local VxCruiseMin = math.min(VxCruiseMax*0.70, 166)
|
||||
|
||||
-- Min cruise speed.
|
||||
local VxCruiseMin
|
||||
if self.Vcruisemin then
|
||||
VxCruiseMin = self.Vcruisemin
|
||||
else
|
||||
-- Min cruise speed 70% of max cruise or 600 km/h whichever is lower.
|
||||
VxCruiseMin = math.min(VxCruiseMax*0.70, 166)
|
||||
end
|
||||
|
||||
-- Cruise speed (randomized). Expectation value at midpoint between min and max.
|
||||
local VxCruise = UTILS.RandomGaussian((VxCruiseMax-VxCruiseMin)/2+VxCruiseMin, (VxCruiseMax-VxCruiseMax)/4, VxCruiseMin, VxCruiseMax)
|
||||
@@ -3017,6 +3053,14 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint)
|
||||
if landing==RAT.wp.air then
|
||||
local vec2=destination:GetRandomVec2()
|
||||
Pdestination=COORDINATE:NewFromVec2(vec2)
|
||||
elseif destination:IsShip() then
|
||||
-- Crudely predict where the ship will be
|
||||
local _ship = UNIT:FindByName(destination:GetName())
|
||||
local _shipHeading = _ship:GetHeading()
|
||||
Pdestination=destination:GetCoordinate()
|
||||
local _transitTime = Pdeparture:Get2DDistance(Pdestination) / VxCruise
|
||||
Pdestination.x = Pdestination.x + (_ship:GetGroundSpeed() * math.cos(math.rad(_shipHeading)) * _transitTime)
|
||||
Pdestination.z = Pdestination.z + (_ship:GetGroundSpeed() * math.sin(math.rad(_shipHeading)) * _transitTime)
|
||||
else
|
||||
Pdestination=destination:GetCoordinate()
|
||||
end
|
||||
@@ -4672,7 +4716,7 @@ end
|
||||
|
||||
--- Anticipated group name from alias and spawn index.
|
||||
-- @param #RAT self
|
||||
-- @param #number index Spawnindex of group if given or self.SpawnIndex+1 by default.
|
||||
-- @param #number index (Optional) Spawnindex of group if given or self.SpawnIndex+1 by default.
|
||||
-- @return #string Name the group will get after it is spawned.
|
||||
function RAT:_AnticipatedGroupName(index)
|
||||
local index=index or self.SpawnIndex+1
|
||||
@@ -5584,7 +5628,7 @@ end
|
||||
-- @param #string dest Name of the destination airport.
|
||||
function RAT:_ATCAddFlight(name, dest)
|
||||
-- Debug info
|
||||
BASE:I(RAT.id..string.format("ATC %s: Adding flight %s with destination %s.", dest, name, dest))
|
||||
BASE:T(RAT.id..string.format("ATC %s: Adding flight %s with destination %s.", dest, name, dest))
|
||||
|
||||
-- Create new flight
|
||||
local flight={} --#RAT.AtcFlight
|
||||
@@ -5603,7 +5647,7 @@ end
|
||||
function RAT._ATCDelFlight(t,entry)
|
||||
for k,_ in pairs(t) do
|
||||
if k==entry then
|
||||
BASE:I(RAT.id..string.format("Removing flight %s from queue", entry))
|
||||
BASE:T(RAT.id..string.format("Removing flight %s from queue", entry))
|
||||
t[entry]=nil
|
||||
end
|
||||
end
|
||||
@@ -5614,7 +5658,7 @@ end
|
||||
-- @param #string name Group name of the flight.
|
||||
-- @param #number time Time the fight first registered.
|
||||
function RAT._ATCRegisterFlight(name, time)
|
||||
BASE:I(RAT.id..string.format("Flight %s registered at ATC for landing clearance.", name))
|
||||
BASE:T(RAT.id..string.format("Flight %s registered at ATC for landing clearance.", name))
|
||||
RAT.ATC.flight[name].Tarrive=time
|
||||
RAT.ATC.flight[name].holding=0
|
||||
end
|
||||
@@ -5649,15 +5693,16 @@ function RAT._ATCStatus()
|
||||
|
||||
-- Aircraft is holding.
|
||||
local text=string.format("ATC %s: Flight %s is holding for %i:%02d. %s.", dest, name, hold/60, hold%60, busy)
|
||||
BASE:I(RAT.id..text)
|
||||
BASE:T(RAT.id..text)
|
||||
|
||||
elseif hold==RAT.ATC.onfinal then
|
||||
|
||||
-- Aircarft is on final approach for landing.
|
||||
local Tfinal=Tnow-flight.Tonfinal
|
||||
local Tonfinal = flight.Tonfinal or timer.getTime()-1
|
||||
local Tfinal=Tnow-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:I(RAT.id..text)
|
||||
BASE:T(RAT.id..text)
|
||||
|
||||
elseif hold==RAT.ATC.unregistered then
|
||||
|
||||
@@ -5711,13 +5756,13 @@ function RAT._ATCCheck()
|
||||
-- Debug message.
|
||||
local text=string.format("ATC %s: Flight %s runway is busy. You are #%d of %d in landing queue. Your holding time is %i:%02d.",
|
||||
airportname, flightname, qID, nqueue, flight.holding/60, flight.holding%60)
|
||||
BASE:I(RAT.id..text)
|
||||
BASE:T(RAT.id..text)
|
||||
|
||||
else
|
||||
|
||||
local text=string.format("ATC %s: Flight %s was cleared for landing. Your holding time was %i:%02d.",
|
||||
airportname, flightname, flight.holding/60, flight.holding%60)
|
||||
BASE:I(RAT.id..text)
|
||||
BASE:T(RAT.id..text)
|
||||
|
||||
-- Clear flight for landing.
|
||||
RAT._ATCClearForLanding(airportname, flightname)
|
||||
@@ -5772,7 +5817,7 @@ function RAT._ATCClearForLanding(airportname, flightname)
|
||||
|
||||
|
||||
-- Debug message.
|
||||
BASE:I(RAT.id..string.format("ATC %s: Flight %s cleared for landing", airportname, flightname))
|
||||
BASE:T(RAT.id..string.format("ATC %s: Flight %s cleared for landing", airportname, flightname))
|
||||
|
||||
if string.find(flightname,"#") then
|
||||
flightname = string.match(flightname,"^(.+)#")
|
||||
@@ -5799,8 +5844,9 @@ function RAT._ATCFlightLanded(name)
|
||||
|
||||
-- Times for holding and final approach.
|
||||
local Tnow=timer.getTime()
|
||||
local Tfinal=Tnow-flight.Tonfinal
|
||||
local Thold=flight.Tonfinal-flight.Tarrive
|
||||
local Tonfinal = flight.Tonfinal or timer.getTime()-1
|
||||
local Tfinal=Tnow-Tonfinal
|
||||
local Thold=Tonfinal-flight.Tarrive
|
||||
|
||||
local airport=RAT.ATC.airport[dest] --#RAT.AtcAirport
|
||||
|
||||
@@ -5823,9 +5869,9 @@ function RAT._ATCFlightLanded(name)
|
||||
local TrafficPerHour=airport.traffic/(timer.getTime()-RAT.ATC.T0)*3600
|
||||
|
||||
-- Debug info
|
||||
BASE:I(RAT.id..string.format("ATC %s: Flight %s landed. Tholding = %i:%02d, Tfinal = %i:%02d.", dest, name, Thold/60, Thold%60, Tfinal/60, Tfinal%60))
|
||||
BASE:I(RAT.id..string.format("ATC %s: Number of flights still on final %d.", dest, airport.Nonfinal))
|
||||
BASE:I(RAT.id..string.format("ATC %s: Traffic report: Number of planes landed in total %d. Flights/hour = %3.2f.", dest, airport.traffic, TrafficPerHour))
|
||||
BASE:T(RAT.id..string.format("ATC %s: Flight %s landed. Tholding = %i:%02d, Tfinal = %i:%02d.", dest, name, Thold/60, Thold%60, Tfinal/60, Tfinal%60))
|
||||
BASE:T(RAT.id..string.format("ATC %s: Number of flights still on final %d.", dest, airport.Nonfinal))
|
||||
BASE:T(RAT.id..string.format("ATC %s: Traffic report: Number of planes landed in total %d. Flights/hour = %3.2f.", dest, airport.traffic, TrafficPerHour))
|
||||
|
||||
if string.find(name,"#") then
|
||||
name = string.match(name,"^(.+)#")
|
||||
@@ -5978,7 +6024,7 @@ end
|
||||
--- Adds a RAT object to the RAT manager. Parameter min specifies the limit how many RAT groups are at least alive.
|
||||
-- @param #RATMANAGER self
|
||||
-- @param #RAT ratobject RAT object to be managed.
|
||||
-- @param #number min Minimum number of groups for this RAT object. Default is 1.
|
||||
-- @param #number min (Optional) Minimum number of groups for this RAT object. Default is 1.
|
||||
-- @return #RATMANAGER RATMANAGER self object.
|
||||
function RATMANAGER:Add(ratobject,min)
|
||||
|
||||
@@ -6006,7 +6052,7 @@ end
|
||||
|
||||
--- Starts the RAT manager and spawns the initial random number RAT groups for each RAT object.
|
||||
-- @param #RATMANAGER self
|
||||
-- @param #number delay Time delay in seconds after which the RAT manager is started. Default is 5 seconds.
|
||||
-- @param #number delay (Optional) Time delay in seconds after which the RAT manager is started. Default is 5 seconds.
|
||||
-- @return #RATMANAGER RATMANAGER self object.
|
||||
function RATMANAGER:Start(delay)
|
||||
|
||||
@@ -6079,7 +6125,7 @@ end
|
||||
|
||||
--- Stops the RAT manager.
|
||||
-- @param #RATMANAGER self
|
||||
-- @param #number delay Delay in seconds before the manager is stopped. Default is 1 second.
|
||||
-- @param #number delay (Optional) Delay in seconds before the manager is stopped. Default is 1 second.
|
||||
-- @return #RATMANAGER RATMANAGER self object.
|
||||
function RATMANAGER:Stop(delay)
|
||||
delay=delay or 1
|
||||
@@ -6115,7 +6161,7 @@ end
|
||||
|
||||
--- Sets the time interval between spawning of groups.
|
||||
-- @param #RATMANAGER self
|
||||
-- @param #number dt Time interval in seconds. Default is 1 second.
|
||||
-- @param #number dt (Optional) Time interval in seconds. Default is 1 second.
|
||||
-- @return #RATMANAGER RATMANAGER self object.
|
||||
function RATMANAGER:SetTspawn(dt)
|
||||
self.dTspawn=dt or 1.0
|
||||
|
||||
@@ -109,7 +109,6 @@
|
||||
-- @field Core.Menu#MENU_MISSION menuF10root Specific user defined root F10 menu.
|
||||
-- @field #number ceilingaltitude Range ceiling altitude in ft MSL. Aircraft above this altitude are not considered to be in the range. Default is 20000 ft.
|
||||
-- @field #boolean ceilingenabled Range has a ceiling and is not unlimited. Default is false.
|
||||
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
--- *Don't only practice your art, but force your way into its secrets; art deserves that, for it and knowledge can raise man to the Divine.* - Ludwig van Beethoven
|
||||
@@ -950,7 +949,7 @@ end
|
||||
|
||||
--- Set maximal strafing altitude. Player entering a strafe pit above that altitude are not registered for a valid pass.
|
||||
-- @param #RANGE self
|
||||
-- @param #number maxalt Maximum altitude in meters AGL. Default is 914 m = 3000 ft.
|
||||
-- @param #number maxalt (Optional) Maximum altitude in meters AGL. Default is 914 m = 3000 ft.
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetMaxStrafeAlt( maxalt )
|
||||
self.strafemaxalt = maxalt or RANGE.Defaults.strafemaxalt
|
||||
@@ -959,7 +958,7 @@ end
|
||||
|
||||
--- Set time interval for tracking bombs. A smaller time step increases accuracy but needs more CPU time.
|
||||
-- @param #RANGE self
|
||||
-- @param #number dt Time interval in seconds. Default is 0.005 s.
|
||||
-- @param #number dt (Optional) Time interval in seconds. Default is 0.005 s.
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetBombtrackTimestep( dt )
|
||||
self.dtBombtrack = dt or RANGE.Defaults.dtBombtrack
|
||||
@@ -968,7 +967,7 @@ end
|
||||
|
||||
--- Set time how long (most) messages are displayed.
|
||||
-- @param #RANGE self
|
||||
-- @param #number time Time in seconds. Default is 30 s.
|
||||
-- @param #number time (Optional) Time in seconds. Default is 30 s.
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetMessageTimeDuration( time )
|
||||
self.Tmsg = time or RANGE.Defaults.Tmsg
|
||||
@@ -1010,8 +1009,8 @@ end
|
||||
--- Set FunkMan socket. Bombing and strafing results will be send to your Discord bot.
|
||||
-- **Requires running FunkMan program**.
|
||||
-- @param #RANGE self
|
||||
-- @param #number Port Port. Default `10042`.
|
||||
-- @param #string Host Host. Default "127.0.0.1".
|
||||
-- @param #number Port (Optional) Port. Default `10042`.
|
||||
-- @param #string Host (Optional) Host. Default "127.0.0.1".
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetFunkManOn(Port, Host)
|
||||
|
||||
@@ -1033,7 +1032,7 @@ end
|
||||
|
||||
--- Set max number of player results that are displayed.
|
||||
-- @param #RANGE self
|
||||
-- @param #number nmax Number of results. Default is 10.
|
||||
-- @param #number nmax (Optional) Number of results. Default is 10.
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetDisplayedMaxPlayerResults( nmax )
|
||||
self.ndisplayresult = nmax or RANGE.Defaults.ndisplayresult
|
||||
@@ -1042,7 +1041,7 @@ end
|
||||
|
||||
--- Set range radius. Defines the area in which e.g. bomb impacts are smoked.
|
||||
-- @param #RANGE self
|
||||
-- @param #number radius Radius in km. Default 5 km.
|
||||
-- @param #number radius (Optional) Radius in km. Default 5 km.
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetRangeRadius( radius )
|
||||
self.rangeradius = radius * 1000 or RANGE.Defaults.rangeradius
|
||||
@@ -1051,7 +1050,7 @@ end
|
||||
|
||||
--- Set player setting whether bomb impact points are smoked or not.
|
||||
-- @param #RANGE self
|
||||
-- @param #boolean switch If true nor nil default is to smoke impact points of bombs.
|
||||
-- @param #boolean switch (Optional) If true nor nil default is to smoke impact points of bombs.
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetDefaultPlayerSmokeBomb( switch )
|
||||
if switch == true or switch == nil then
|
||||
@@ -1064,7 +1063,7 @@ end
|
||||
|
||||
--- Set bomb track threshold distance. Bombs/rockets/missiles are only tracked if player-range distance is less than this distance. Default 25 km.
|
||||
-- @param #RANGE self
|
||||
-- @param #number distance Threshold distance in km. Default 25 km.
|
||||
-- @param #number distance (Optional) Threshold distance in km. Default 25 km.
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetBombtrackThreshold( distance )
|
||||
self.BombtrackThreshold = (distance or 25) * 1000
|
||||
@@ -1111,7 +1110,7 @@ end
|
||||
|
||||
--- Enable range ceiling. Aircraft must be below the ceiling altitude to be considered in the range zone.
|
||||
-- @param #RANGE self
|
||||
-- @param #boolean enabled True if you would like to enable the ceiling check. If no value give, will Default to false.
|
||||
-- @param #boolean enabled (Optional) True if you would like to enable the ceiling check. If no value give, will Default to false.
|
||||
-- @return #RANGE self
|
||||
function RANGE:EnableRangeCeiling( enabled )
|
||||
self:T(self.lid.."EnableRangeCeiling")
|
||||
@@ -1127,7 +1126,7 @@ end
|
||||
|
||||
--- Set smoke color for marking bomb targets. By default bomb targets are marked by red smoke.
|
||||
-- @param #RANGE self
|
||||
-- @param Utilities.Utils#SMOKECOLOR colorid Color id. Default `SMOKECOLOR.Red`.
|
||||
-- @param Utilities.Utils#SMOKECOLOR colorid (Optional) Color id. Default `SMOKECOLOR.Red`.
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetBombTargetSmokeColor( colorid )
|
||||
self.BombSmokeColor = colorid or SMOKECOLOR.Red
|
||||
@@ -1136,7 +1135,7 @@ end
|
||||
|
||||
--- Set score bomb distance.
|
||||
-- @param #RANGE self
|
||||
-- @param #number distance Distance in meters. Default 1000 m.
|
||||
-- @param #number distance (Optional) Distance in meters. Default 1000 m.
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetScoreBombDistance( distance )
|
||||
self.scorebombdistance = distance or 1000
|
||||
@@ -1145,7 +1144,7 @@ end
|
||||
|
||||
--- Set smoke color for marking strafe targets. By default strafe targets are marked by green smoke.
|
||||
-- @param #RANGE self
|
||||
-- @param Utilities.Utils#SMOKECOLOR colorid Color id. Default `SMOKECOLOR.Green`.
|
||||
-- @param Utilities.Utils#SMOKECOLOR colorid (Optional) Color id. Default `SMOKECOLOR.Green`.
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetStrafeTargetSmokeColor( colorid )
|
||||
self.StrafeSmokeColor = colorid or SMOKECOLOR.Green
|
||||
@@ -1154,7 +1153,7 @@ end
|
||||
|
||||
--- Set smoke color for marking strafe pit approach boxes. By default strafe pit boxes are marked by white smoke.
|
||||
-- @param #RANGE self
|
||||
-- @param Utilities.Utils#SMOKECOLOR colorid Color id. Default `SMOKECOLOR.White`.
|
||||
-- @param Utilities.Utils#SMOKECOLOR colorid (Optional) Color id. Default `SMOKECOLOR.White`.
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetStrafePitSmokeColor( colorid )
|
||||
self.StrafePitSmokeColor = colorid or SMOKECOLOR.White
|
||||
@@ -1163,7 +1162,7 @@ end
|
||||
|
||||
--- Set time delay between bomb impact and starting to smoke the impact point.
|
||||
-- @param #RANGE self
|
||||
-- @param #number delay Time delay in seconds. Default is 3 seconds.
|
||||
-- @param #number delay (Optional) Time delay in seconds. Default is 3 seconds.
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetSmokeTimeDelay( delay )
|
||||
self.TdelaySmoke = delay or RANGE.Defaults.TdelaySmoke
|
||||
@@ -1253,14 +1252,16 @@ end
|
||||
--- Use SRS Simple-Text-To-Speech for transmissions. No sound files necessary.
|
||||
-- @param #RANGE self
|
||||
-- @param #string PathToSRS Path to SRS directory.
|
||||
-- @param #number Port SRS port. Default 5002.
|
||||
-- @param #number Coalition Coalition side, e.g. `coalition.side.BLUE` or `coalition.side.RED`. Default `coalition.side.BLUE`.
|
||||
-- @param #number Frequency Frequency to use. Default is 256 MHz for range control and 305 MHz for instructor. If given, both control and instructor get this frequency.
|
||||
-- @param #number Modulation Modulation to use, defaults to radio.modulation.AM
|
||||
-- @param #number Volume Volume, between 0.0 and 1.0. Defaults to 1.0
|
||||
-- @param #number Port (Optional) SRS port. Default 5002.
|
||||
-- @param #number Coalition (Optional) Coalition side, e.g. `coalition.side.BLUE` or `coalition.side.RED`. Default `coalition.side.BLUE`.
|
||||
-- @param #number Frequency (Optional) Frequency to use. Default is 256 MHz for range control and 305 MHz for instructor. If given, both control and instructor get this frequency.
|
||||
-- @param #number Modulation (Optional) Modulation to use, defaults to radio.modulation.AM
|
||||
-- @param #number Volume (Optional) Volume, between 0.0 and 1.0. Defaults to 1.0
|
||||
-- @param #string PathToGoogleKey Path to Google TTS credentials.
|
||||
-- @param #string Provider (Optional) TTS Provider to be used.
|
||||
-- @param #string Backend (Optional) TTS Backend to be used.
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume, PathToGoogleKey)
|
||||
function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume, PathToGoogleKey,Provider,Backend)
|
||||
|
||||
if PathToSRS or MSRS.path then
|
||||
|
||||
@@ -1271,6 +1272,12 @@ function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume,
|
||||
self.controlmsrs:SetCoalition(Coalition or coalition.side.BLUE)
|
||||
self.controlmsrs:SetLabel("RANGEC")
|
||||
self.controlmsrs:SetVolume(Volume or 1.0)
|
||||
if self.rangezone then
|
||||
self.controlmsrs:SetCoordinate(self.rangezone:GetCoordinate())
|
||||
end
|
||||
if Backend then
|
||||
self.controlmsrs:SetBackend(Backend)
|
||||
end
|
||||
self.controlsrsQ = MSRSQUEUE:New("CONTROL")
|
||||
|
||||
self.instructmsrs=MSRS:New(PathToSRS or MSRS.path, Frequency or 305, Modulation or radio.modulation.AM)
|
||||
@@ -1278,6 +1285,9 @@ function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume,
|
||||
self.instructmsrs:SetCoalition(Coalition or coalition.side.BLUE)
|
||||
self.instructmsrs:SetLabel("RANGEI")
|
||||
self.instructmsrs:SetVolume(Volume or 1.0)
|
||||
if self.rangezone then
|
||||
self.instructmsrs:SetCoordinate(self.rangezone:GetCoordinate())
|
||||
end
|
||||
self.instructsrsQ = MSRSQUEUE:New("INSTRUCT")
|
||||
|
||||
if PathToGoogleKey then
|
||||
@@ -1286,7 +1296,13 @@ function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume,
|
||||
self.instructmsrs:SetProviderOptionsGoogle(PathToGoogleKey,PathToGoogleKey)
|
||||
self.instructmsrs:SetProvider(MSRS.Provider.GOOGLE)
|
||||
end
|
||||
|
||||
if Backend then
|
||||
self.instructmsrs:SetBackend(Backend)
|
||||
end
|
||||
if Provider then
|
||||
self.controlmsrs:SetProvider(Provider)
|
||||
self.instructmsrs:SetProvider(Provider)
|
||||
end
|
||||
else
|
||||
self:E(self.lid..string.format("ERROR: No SRS path specified!"))
|
||||
end
|
||||
@@ -1295,14 +1311,15 @@ end
|
||||
|
||||
--- (SRS) Set range control frequency and voice. Use `RANGE:SetSRS()` once first before using this function.
|
||||
-- @param #RANGE self
|
||||
-- @param #number frequency Frequency in MHz. Default 256 MHz.
|
||||
-- @param #number modulation Modulation, defaults to radio.modulation.AM.
|
||||
-- @param #number frequency (Optional) Frequency in MHz. Default 256 MHz.
|
||||
-- @param #number modulation (Optional) Modulation, defaults to radio.modulation.AM.
|
||||
-- @param #string voice Voice.
|
||||
-- @param #string culture Culture, defaults to "en-US".
|
||||
-- @param #string gender Gender, defaults to "female".
|
||||
-- @param #string culture (Optional) Culture, defaults to "en-US".
|
||||
-- @param #string gender (Optional) Gender, defaults to "female".
|
||||
-- @param #string relayunitname Name of the unit used for transmission location.
|
||||
-- @param #string Speaker (Optional) Use a specific speaker for a voice if Piper is used as provider (only Hound-TTS backend).
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetSRSRangeControl( frequency, modulation, voice, culture, gender, relayunitname )
|
||||
function RANGE:SetSRSRangeControl( frequency, modulation, voice, culture, gender, relayunitname, Speaker )
|
||||
if not self.instructmsrs then
|
||||
self:E(self.lid.."Use myrange:SetSRS() once first before using myrange:SetSRSRangeControl!")
|
||||
return self
|
||||
@@ -1311,27 +1328,36 @@ function RANGE:SetSRSRangeControl( frequency, modulation, voice, culture, gender
|
||||
self.controlmsrs:SetFrequencies(self.rangecontrolfreq)
|
||||
self.controlmsrs:SetModulations(modulation or radio.modulation.AM)
|
||||
self.controlmsrs:SetVoice(voice)
|
||||
if Speaker then
|
||||
self.controlmsrs:SetSpeakerPiper(Speaker)
|
||||
end
|
||||
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
|
||||
if unit then
|
||||
local Coordinate = unit:GetCoordinate()
|
||||
self.rangecontrolrelayname = relayunitname
|
||||
self.controlmsrs:SetCoordinate(Coordinate)
|
||||
else
|
||||
MESSAGE:New("RANGE: Control Relay Unit "..relayunitname.." not found!",15,"ERROR"):ToAllIf(self.Debug):ToLog()
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- (SRS) Set range instructor frequency and voice. Use `RANGE:SetSRS()` once first before using this function.
|
||||
-- @param #RANGE self
|
||||
-- @param #number frequency Frequency in MHz. Default 305 MHz.
|
||||
-- @param #number modulation Modulation, defaults to radio.modulation.AM.
|
||||
-- @param #number frequency (Optional) Frequency in MHz. Default 305 MHz.
|
||||
-- @param #number modulation (Optional) Modulation, defaults to radio.modulation.AM.
|
||||
-- @param #string voice Voice.
|
||||
-- @param #string culture Culture, defaults to "en-US".
|
||||
-- @param #string gender Gender, defaults to "male".
|
||||
-- @param #string culture (Optional) Culture, defaults to "en-US".
|
||||
-- @param #string gender (Optional) Gender, defaults to "male".
|
||||
-- @param #string relayunitname Name of the unit used for transmission location.
|
||||
-- @param #string Speaker (Optional) Use a specific speaker for a voice if Piper is used as provider (only Hound-TTS backend).
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetSRSRangeInstructor( frequency, modulation, voice, culture, gender, relayunitname )
|
||||
function RANGE:SetSRSRangeInstructor( frequency, modulation, voice, culture, gender, relayunitname, Speaker )
|
||||
if not self.instructmsrs then
|
||||
self:E(self.lid.."Use myrange:SetSRS() once first before using myrange:SetSRSRangeInstructor!")
|
||||
return self
|
||||
@@ -1340,21 +1366,28 @@ function RANGE:SetSRSRangeInstructor( frequency, modulation, voice, culture, gen
|
||||
self.instructmsrs:SetFrequencies(self.instructorfreq)
|
||||
self.instructmsrs:SetModulations(modulation or radio.modulation.AM)
|
||||
self.instructmsrs:SetVoice(voice)
|
||||
if Speaker then
|
||||
self.instructmsrs:SetSpeakerPiper(Speaker)
|
||||
end
|
||||
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
|
||||
if unit then
|
||||
local Coordinate = unit:GetCoordinate()
|
||||
self.instructmsrs:SetCoordinate(Coordinate)
|
||||
self.instructorrelayname = relayunitname
|
||||
else
|
||||
MESSAGE:New("RANGE: Instructor Relay Unit "..relayunitname.." not found!",15,"ERROR"):ToAllIf(self.Debug):ToLog()
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Enable range control and set frequency (non-SRS).
|
||||
-- @param #RANGE self
|
||||
-- @param #number frequency Frequency in MHz. Default 256 MHz.
|
||||
-- @param #number frequency (Optional) Frequency in MHz. Default 256 MHz.
|
||||
-- @param #string relayunitname Name of the unit used for transmission.
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetRangeControl( frequency, relayunitname )
|
||||
@@ -1365,7 +1398,7 @@ end
|
||||
|
||||
--- Enable instructor radio and set frequency (non-SRS).
|
||||
-- @param #RANGE self
|
||||
-- @param #number frequency Frequency in MHz. Default 305 MHz.
|
||||
-- @param #number frequency (Optional) Frequency in MHz. Default 305 MHz.
|
||||
-- @param #string relayunitname Name of the unit used for transmission.
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetInstructorRadio( frequency, relayunitname )
|
||||
@@ -1376,7 +1409,7 @@ end
|
||||
|
||||
--- Set sound files folder within miz file.
|
||||
-- @param #RANGE self
|
||||
-- @param #string path Path for sound files. Default "Range Soundfiles/". Mind the slash "/" at the end!
|
||||
-- @param #string path (Optional) Path for sound files. Default "Range Soundfiles/". Mind the slash "/" at the end!
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetSoundfilesPath( path )
|
||||
self.soundpath = tostring( path or "Range Soundfiles/" )
|
||||
@@ -1616,11 +1649,13 @@ end
|
||||
-- @param #RANGE self
|
||||
-- @param #table targetnames Single or multiple (Table) names of unit or static objects serving as bomb targets.
|
||||
-- @param #number goodhitrange (Optional) Max distance from target unit (in meters) which is considered as a good hit. Default is 25 m.
|
||||
-- @param #boolean randommove If true, unit will move randomly within the range. Default is false.
|
||||
-- @param #boolean randommove (Optional) If true, unit will move randomly within the range. Default is false.
|
||||
-- @return #RANGE self
|
||||
function RANGE:AddBombingTargets( targetnames, goodhitrange, randommove )
|
||||
self:F( { targetnames = targetnames, goodhitrange = goodhitrange, randommove = randommove } )
|
||||
|
||||
randommove = randommove or false
|
||||
|
||||
-- Create a table if necessary.
|
||||
if type( targetnames ) ~= "table" then
|
||||
targetnames = { targetnames }
|
||||
@@ -1655,7 +1690,7 @@ end
|
||||
-- @param #RANGE self
|
||||
-- @param Wrapper.Positionable#POSITIONABLE unit Positionable (unit or static) of the bombing target.
|
||||
-- @param #number goodhitrange Max distance from unit which is considered as a good hit.
|
||||
-- @param #boolean randommove If true, unit will move randomly within the range. Default is false.
|
||||
-- @param #boolean randommove (Optional) If true, unit will move randomly within the range. Default is false.
|
||||
-- @return #RANGE self
|
||||
function RANGE:AddBombingTargetUnit( unit, goodhitrange, randommove )
|
||||
self:F( { unit = unit, goodhitrange = goodhitrange, randommove = randommove } )
|
||||
@@ -1771,7 +1806,7 @@ end
|
||||
-- @param #RANGE self
|
||||
-- @param Wrapper.Group#GROUP group Group of bombing targets. Can also be given as group name.
|
||||
-- @param #number goodhitrange Max distance from unit which is considered as a good hit.
|
||||
-- @param #boolean randommove If true, unit will move randomly within the range. Default is false.
|
||||
-- @param #boolean randommove (Optional) If true, unit will move randomly within the range. Default is false.
|
||||
-- @return #RANGE self
|
||||
function RANGE:AddBombingTargetGroup( group, goodhitrange, randommove )
|
||||
self:F( { group = group, goodhitrange = goodhitrange, randommove = randommove } )
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,760 @@
|
||||
--- **Functional** - Enhanced Warsaw Pact GCI Controller.
|
||||
--
|
||||
-- ## Main Features:
|
||||
--
|
||||
-- * Guide AI and human pilots in Warsaw Pact Style. GCI Kernel Functions.
|
||||
-- * Advanced Tactics for Groups.
|
||||
-- * Many additional events that the mission designer can hook into.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **Applevangelist**
|
||||
--
|
||||
-- ===
|
||||
-- @module Functional.REDGCI_KERNEL
|
||||
-- @image Func_RedGCI.png
|
||||
-- @version 1.0.0
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
--- REDGCI_KERNEL class
|
||||
-- @type REDGCI_KERNEL
|
||||
|
||||
|
||||
---
|
||||
-- # RedGCI — Soviet GCI Doctrine & Player Guide
|
||||
--
|
||||
-- ## Philosophy: Централизованное управление (Centralized Control)
|
||||
--
|
||||
-- The fundamental difference between Soviet and NATO GCI is **who makes the tactical decision**.
|
||||
--
|
||||
-- In NATO doctrine, the GCI controller provides situational awareness — bearing, range, altitude, aspect — and the pilot decides how to prosecute the intercept. The pilot is an autonomous tactician. GCI is an advisor.
|
||||
--
|
||||
-- In Soviet doctrine, the GCI controller **directs**. The pilot executes. The controller selects the intercept geometry, assigns the heading, manages the radar, calls weapons free, and coordinates multi-ship tactics. The pilot's job is to fly the numbers and shoot when told. This is not a flaw — it is the system working as designed. Soviet fighter pilots were trained to be precise executors of GCI instructions, not independent tacticians. The ground radar network (PVO) was the brain; the aircraft was the weapon.
|
||||
--
|
||||
-- RedGCI models this philosophy faithfully.
|
||||
--
|
||||
-- ---
|
||||
--
|
||||
-- ## What to Expect as a Player
|
||||
--
|
||||
-- ### You will not be asked what you want to do.
|
||||
--
|
||||
-- There are no "recommend a vector" calls, no "at your discretion" callouts. The controller tells you your heading, your altitude, and your task. Your acknowledgement is assumed.
|
||||
--
|
||||
-- ### The controller manages your radar.
|
||||
--
|
||||
-- You do not decide when to turn your radar on. The GCI will tell you when to switch on (`локатор` / `Radar on`). Before that call, you fly cold and silent. This preserves your emissions discipline and prevents the target from getting an early RWR spike.
|
||||
--
|
||||
-- ### Weapons free is a controlled event.
|
||||
--
|
||||
-- You do not engage until the controller clears you (`цель разрешена` / `WEAPONS FREE`). The controller determines when geometry, range, and aspect are favorable. Shooting early breaks the coordinated intercept and may compromise your wingman's attack.
|
||||
--
|
||||
-- ### Radio calls are short and military.
|
||||
--
|
||||
-- Soviet GCI brevity is terse by design. Expect calls like:
|
||||
--
|
||||
-- - `"Сокол, курс 170, высота 4500."` — vector, altitude
|
||||
-- - `"Сокол, цель, пара, истребитель. Локатор."` — picture call on commit: count, type, radar on
|
||||
-- - `"Сокол, захват. Дальность 20. Цель разрешена."` — lock confirmed, range, weapons free
|
||||
-- - `"Сокол, молодец. Домой."` — good kill, RTB
|
||||
--
|
||||
-- There are no "BOGEY DOPE" requests, no "BRAA" calls, no "DECLARE" queries. The controller has already done that work. You fly the vector.
|
||||
--
|
||||
-- ---
|
||||
--
|
||||
-- ## State Flow — What the GCI is Doing Behind the Scenes
|
||||
--
|
||||
-- RedGCI manages a state machine that progresses through six phases. Understanding these phases helps you anticipate what call is coming next.
|
||||
--
|
||||
-- ```
|
||||
-- VECTOR → COMMIT → RADAR_CONTACT → VISUAL → MERGE → (SPLASH / ABORT / RTB)
|
||||
-- ```
|
||||
--
|
||||
-- ### VECTOR
|
||||
-- The controller has a track. You are being vectored onto an intercept geometry. Your radar is off. The controller is solving a collision course and updating your heading every tick. Altitude calls reflect the intercept geometry — you may be sent below the target (classic Soviet shoot-up doctrine for radar-limited types) or level/above (MiG-29/Su-27 lookdown geometry). Expect heading updates every 10–15 seconds.
|
||||
--
|
||||
-- **What you should do:** Fly the heading. Don't deviate. Don't turn your radar on yet. Speed is expected at 900kph TAS (depending on airframe)
|
||||
--
|
||||
-- ### COMMIT
|
||||
-- Range has closed to approximately 30km. The controller calls the picture: count and type. Your radar comes on. You are now committed to the intercept — turning away is no longer the default option. The controller is building your radar geometry toward a lock.
|
||||
--
|
||||
-- **What you should do:** Activate your radar. Acquire the target. Do not fire yet.
|
||||
--
|
||||
-- ### RADAR_CONTACT
|
||||
-- You have radar lock (or the AI has achieved it). The controller confirms lock and calls range. If geometry and range are favorable, weapons free follows immediately. If not — for example if aspect angle is unfavorable for a stern conversion — the controller holds fire and waits for better geometry.
|
||||
--
|
||||
-- **What you should do:** Maintain lock. Track the target. Wait for the weapons free call.
|
||||
--
|
||||
-- ### VISUAL
|
||||
-- Range has closed to approximately 5km — visual conditions. Weapons free is automatic at this point. You are now in the merge envelope.
|
||||
--
|
||||
-- **What you should do:** Engage.
|
||||
--
|
||||
-- ### MERGE
|
||||
-- Inside 2km. The GCI transitions to merge control: bearing to target, overshoot calls, separation instructions, reattack vectors. At this range the controller cannot see fine-grained geometry — merge calls are based on relative bearing and closure.
|
||||
--
|
||||
-- **What you should do:** Fight. Listen for overshoot, separation, and reattack calls.
|
||||
--
|
||||
-- ### SPLASH / ABORT / RTB
|
||||
-- - `SPLASH` — kill confirmed, RTB
|
||||
-- - `ABORT (THREAT)` — your RWR is spiked or a threat geometry has developed; break off immediately on the given heading
|
||||
-- - `ABORT (BINGO)` — fuel state critical; break off and return
|
||||
--
|
||||
-- ---
|
||||
--
|
||||
-- ## Multi-Ship (2v2) Tactics (REDGCI2v2)
|
||||
--
|
||||
-- When two fighters are dispatched against a threat, the GCI selects a tactic automatically based on the tactical situation. The tactic is applied at COMMIT — until then, both fighters are vectored together toward the intercept midpoint.
|
||||
--
|
||||
-- | Tactic | Description |
|
||||
-- |--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
-- | **PINCER** | Classic bracket. F1 and F2 split left and right, attacking from opposite angles simultaneously. Forces the target to choose which threat to react to. |
|
||||
-- | **HIGH-LOW** | Vertical split. One fighter attacks from below (radar up, clean sky background), one from above. Degrades the target's ability to acquire both simultaneously. |
|
||||
-- | **STAGGER** | BVR timing offset. F1 fires first at long range, F2 follows 8–13km behind to engage a maneuvering or defending target. |
|
||||
-- | **TRAIL** | Close trail. F1 is the shooter, F2 is support — ready to engage if F1 overshoots or is defeated. |
|
||||
-- | **GIRAFFE** | *(Historical — Iraq/Iran War, Mirage F1 vs F-14A)* F1 attacks at normal altitude, binding the AWG-9 radar. F2 flies nap-of-earth |
|
||||
-- | | (300–600m AGL) using ground clutter to degrade radar detection, then pulls up and fires from close range. |
|
||||
--
|
||||
-- During a tactic split, you may receive a heading that seems unusual — a large lateral offset or an unexpected altitude change. **Trust the vector.** The controller is positioning you for the tactic geometry. The merge point will bring you back onto the target.
|
||||
--
|
||||
-- ---
|
||||
--
|
||||
-- ## Dispatcher & CAP Flow (REDGCI_DISPATCHER)
|
||||
--
|
||||
-- When using the dispatcher layer, the full operational flow is:
|
||||
--
|
||||
-- ```
|
||||
-- Spawn at homeplate
|
||||
-- → Taxi and takeoff (template-controlled)
|
||||
-- → Transit to CAP zone
|
||||
-- → Orbit in assigned zone (radar cold, weapons safe)
|
||||
-- ↓ INTEL detects threat cluster
|
||||
-- → "Attention, radar contact. Pair, fighter, 45 kilometers." (all CAP fighters)
|
||||
-- ↓ Dispatcher assigns pair
|
||||
-- → "101 102, intercept. Pair, fighter." (dispatched pair)
|
||||
-- → VECTOR → COMMIT → RADAR_CONTACT → VISUAL → MERGE → SPLASH
|
||||
-- ↓ Engagement complete
|
||||
-- → AI: RTB waypoint → land → despawn → respawn after delay
|
||||
-- → Human: "101, mission complete. RTB, refuel and rearm."
|
||||
-- ↓ After RespawnDelay
|
||||
-- → New AI pair spawns into same CAP zone
|
||||
-- ```
|
||||
--
|
||||
-- Human players are dispatched first when available. If a human and AI are both in the CAP pool, the human is always assigned to the next intercept. AI fills gaps. The dispatcher does not send a single fighter if a pair is available — pairing is always preferred.
|
||||
--
|
||||
-- ---
|
||||
--
|
||||
-- ## Key Differences from NATO GCI at a Glance
|
||||
--
|
||||
-- | | Soviet (RedGCI) | NATO |
|
||||
-- |--------------------|--------------------------------------|------------------------------------|
|
||||
-- | Tactical decision | Controller | Pilot |
|
||||
-- | Radar management | Controller-commanded | Pilot-initiated |
|
||||
-- | Weapons free | Controller-called | Pilot-discretion (after WF) |
|
||||
-- | Heading calls | Prescriptive | Advisory |
|
||||
-- | Brevity style | Terse, military, positional | Standardized (BRAA, DECLARE, etc.) |
|
||||
-- | Multi-ship tactics | Centrally planned, applied at COMMIT | Mutually briefed, pilot-executed |
|
||||
-- | Pilot autonomy | Low (by design) | High |
|
||||
--
|
||||
-- **The Soviet system is not inferior** — it is optimized for a different kind of pilot and a different operational context. Mass interception of large NATO strike packages over defended Soviet airspace demanded centralized, efficient, high-throughput GCI control. RedGCI brings that experience to DCS.
|
||||
--
|
||||
-- @field #REDGCI_KERNEL
|
||||
REDGCI_KERNEL = {}
|
||||
REDGCI_KERNEL.version = "1.0.0"
|
||||
|
||||
-- ─────────────────────────────────────────────────────────────
|
||||
-- Konstanten (aus gci_types.h)
|
||||
-- ─────────────────────────────────────────────────────────────
|
||||
|
||||
REDGCI_KERNEL.C = {
|
||||
RANGE_VECTOR_START = 60000.0,
|
||||
RANGE_COMMIT = 30000.0,
|
||||
RANGE_RADAR_FLOOR = 20000.0,
|
||||
RANGE_VISUAL = 5000.0,
|
||||
RANGE_MERGE = 2000.0,
|
||||
WF_RANGE_MAX = 25000.0,
|
||||
ALT_OFFSET_LOOKDOWN = 0.0,
|
||||
ASPECT_NOTCH_MIN = 80.0,
|
||||
ASPECT_NOTCH_MAX = 100.0,
|
||||
ASPECT_REAR_ATTACK = 120.0,
|
||||
MAX_TTI = 600.0,
|
||||
TICK_INTERVAL = 15.0,
|
||||
FUEL_BINGO = 0.25,
|
||||
DELAY_MIN = 3.0,
|
||||
DELAY_MAX = 8.0,
|
||||
DELAY_MERGE_MIN = 2.0,
|
||||
DELAY_MERGE_MAX = 4.0,
|
||||
TACTIC_PINCER = 0,
|
||||
TACTIC_HIGH_LOW = 1,
|
||||
TACTIC_STAGGER = 2,
|
||||
TACTIC_TRAIL = 3,
|
||||
TACTIC_GIRAFFE = 4, -- Irak/Iran-Doktrin: F1=Decoy normal, F2=Nap-of-Earth im Groundclutter
|
||||
PURSUIT_COLLISION = 0,
|
||||
PURSUIT_LEAD = 1,
|
||||
PURSUIT_PURE = 2,
|
||||
PURSUIT_NO_SOLUTION = 3,
|
||||
}
|
||||
|
||||
-- ─────────────────────────────────────────────────────────────
|
||||
-- Hilfsfunktionen
|
||||
-- ─────────────────────────────────────────────────────────────
|
||||
|
||||
function REDGCI_KERNEL.clamp(v, lo, hi)
|
||||
if v < lo then return lo end
|
||||
if v > hi then return hi end
|
||||
return v
|
||||
end
|
||||
|
||||
function REDGCI_KERNEL.vec2len(x, z)
|
||||
return math.sqrt(x*x + z*z)
|
||||
end
|
||||
|
||||
function REDGCI_KERNEL.bearing(dx, dz)
|
||||
local b = math.deg(math.atan2(dx, dz))
|
||||
return b < 0.0 and b + 360.0 or b
|
||||
end
|
||||
|
||||
function REDGCI_KERNEL.randDelay(lo, hi)
|
||||
return lo + UTILS.LCGRandom() * (hi - lo)
|
||||
end
|
||||
|
||||
-- ─────────────────────────────────────────────────────────────
|
||||
-- Aspect Angle (0=Nose-on, 90=Beam, 180=Tail)
|
||||
-- ─────────────────────────────────────────────────────────────
|
||||
|
||||
function REDGCI_KERNEL.aspectAngle(target, observer)
|
||||
local dx = observer.x - target.x
|
||||
local dz = observer.z - target.z
|
||||
local range = REDGCI_KERNEL.vec2len(dx, dz)
|
||||
if range < 1.0 then return 0.0 end
|
||||
local nx = dx / range
|
||||
local nz = dz / range
|
||||
local spd = (target.speed or 1.0) + 1e-6
|
||||
local tvx = (target.vx or 0.0) / spd
|
||||
local tvz = (target.vz or 0.0) / spd
|
||||
local dot = REDGCI_KERNEL.clamp(tvx*nx + tvz*nz, -1.0, 1.0)
|
||||
return math.deg(math.acos(dot))
|
||||
end
|
||||
|
||||
-- ─────────────────────────────────────────────────────────────
|
||||
-- Closure Rate (positiv = Annäherung)
|
||||
-- ─────────────────────────────────────────────────────────────
|
||||
|
||||
function REDGCI_KERNEL.closureRate(f, t)
|
||||
local dx = t.x - f.x
|
||||
local dz = t.z - f.z
|
||||
local range = REDGCI_KERNEL.vec2len(dx, dz)
|
||||
if range < 1.0 then return 0.0 end
|
||||
local nx = dx / range
|
||||
local nz = dz / range
|
||||
local dvx = (t.vx or 0.0) - (f.vx or 0.0)
|
||||
local dvz = (t.vz or 0.0) - (f.vz or 0.0)
|
||||
return -(dvx*nx + dvz*nz)
|
||||
end
|
||||
|
||||
-- ─────────────────────────────────────────────────────────────
|
||||
-- _solveCollision (intern, GCI-Koordinaten)
|
||||
-- ─────────────────────────────────────────────────────────────
|
||||
|
||||
function REDGCI_KERNEL._solveCollision(f, t)
|
||||
local C = REDGCI_KERNEL.C
|
||||
local dx = t.x - f.x
|
||||
local dz = t.z - f.z
|
||||
local vtx = t.vx or 0.0
|
||||
local vtz = t.vz or 0.0
|
||||
local vf = f.speed or 1.0
|
||||
local a = vtx*vtx + vtz*vtz - vf*vf
|
||||
local b = 2.0 * (dx*vtx + dz*vtz)
|
||||
local c = dx*dx + dz*dz
|
||||
local sol_t = -1.0
|
||||
if math.abs(a) < 1.0 then
|
||||
if math.abs(b) > 0.01 then
|
||||
sol_t = -c / b
|
||||
else
|
||||
return false
|
||||
end
|
||||
else
|
||||
local disc = b*b - 4.0*a*c
|
||||
if disc < 0.0 then return false end
|
||||
local sq = math.sqrt(disc)
|
||||
local t1 = (-b - sq) / (2.0 * a)
|
||||
local t2 = (-b + sq) / (2.0 * a)
|
||||
if t1 > 0.0 and t2 > 0.0 then sol_t = math.min(t1, t2)
|
||||
elseif t1 > 0.0 then sol_t = t1
|
||||
elseif t2 > 0.0 then sol_t = t2
|
||||
else return false
|
||||
end
|
||||
end
|
||||
if sol_t < 0.0 or sol_t > C.MAX_TTI then return false end
|
||||
local ip = {
|
||||
x = t.x + vtx * sol_t,
|
||||
z = t.z + vtz * sol_t,
|
||||
y = math.max(t.y + C.ALT_OFFSET_LOOKDOWN, 300.0),
|
||||
}
|
||||
local hdg = REDGCI_KERNEL.bearing(ip.x - f.x, ip.z - f.z)
|
||||
return true, hdg, sol_t, ip
|
||||
end
|
||||
|
||||
-- ─────────────────────────────────────────────────────────────
|
||||
-- _solveLead (intern, GCI-Koordinaten)
|
||||
-- ─────────────────────────────────────────────────────────────
|
||||
|
||||
function REDGCI_KERNEL._solveLead(f, t)
|
||||
local C = REDGCI_KERNEL.C
|
||||
local dx = t.x - f.x
|
||||
local dz = t.z - f.z
|
||||
local range = REDGCI_KERNEL.vec2len(dx, dz)
|
||||
if range < 1.0 then return 0.0, 0.0 end
|
||||
local base_bearing = REDGCI_KERNEL.bearing(dx, dz)
|
||||
local aspect_rad = math.rad(REDGCI_KERNEL.aspectAngle(t, f))
|
||||
local speed_ratio = (t.speed or 1.0) / ((f.speed or 1.0) + 1e-6)
|
||||
local sin_lead = REDGCI_KERNEL.clamp(speed_ratio * math.sin(aspect_rad), -1.0, 1.0)
|
||||
local lead_deg = math.deg(math.asin(sin_lead))
|
||||
local hdg
|
||||
if math.abs(lead_deg) < 45.0 then
|
||||
hdg = (base_bearing + lead_deg + 360.0) % 360.0
|
||||
else
|
||||
hdg = base_bearing
|
||||
end
|
||||
local closing = REDGCI_KERNEL.closureRate(f, t)
|
||||
if closing < 50.0 then closing = 50.0 end
|
||||
local tti = math.min(range / closing, C.MAX_TTI)
|
||||
return hdg, tti
|
||||
end
|
||||
|
||||
-- ─────────────────────────────────────────────────────────────
|
||||
-- computeIntercept (GCI-Koordinaten intern)
|
||||
-- ─────────────────────────────────────────────────────────────
|
||||
|
||||
function REDGCI_KERNEL.computeIntercept(fighter, target)
|
||||
local C = REDGCI_KERNEL.C
|
||||
local dx = target.x - fighter.x
|
||||
local dz = target.z - fighter.z
|
||||
local sol = {
|
||||
heading_deg = 0.0,
|
||||
time_to_intercept = C.MAX_TTI,
|
||||
intercept_point = { x=target.x, z=target.z, y=target.y },
|
||||
target_alt = target.y,
|
||||
solution_found = false,
|
||||
aspect_angle = REDGCI_KERNEL.aspectAngle(target, fighter),
|
||||
range = REDGCI_KERNEL.vec2len(dx, dz),
|
||||
mode = C.PURSUIT_NO_SOLUTION,
|
||||
weapons_free = false,
|
||||
}
|
||||
local closure = REDGCI_KERNEL.closureRate(fighter, target)
|
||||
local projected_range = sol.range - closure * C.TICK_INTERVAL
|
||||
sol.weapons_free = (sol.range < C.WF_RANGE_MAX) or
|
||||
(closure > 0.0 and projected_range < C.WF_RANGE_MAX)
|
||||
local ok, hdg, tti, ip = REDGCI_KERNEL._solveCollision(fighter, target)
|
||||
if ok then
|
||||
sol.heading_deg = hdg
|
||||
sol.time_to_intercept = tti
|
||||
sol.intercept_point = ip
|
||||
sol.solution_found = true
|
||||
sol.mode = C.PURSUIT_COLLISION
|
||||
return sol
|
||||
end
|
||||
local lhdg, ltti = REDGCI_KERNEL._solveLead(fighter, target)
|
||||
sol.heading_deg = lhdg
|
||||
sol.time_to_intercept = ltti
|
||||
sol.solution_found = true
|
||||
sol.mode = C.PURSUIT_LEAD
|
||||
sol.intercept_point = {
|
||||
x = target.x + (target.vx or 0.0) * ltti,
|
||||
z = target.z + (target.vz or 0.0) * ltti,
|
||||
y = math.max(target.y + C.ALT_OFFSET_LOOKDOWN, 300.0),
|
||||
}
|
||||
return sol
|
||||
end
|
||||
|
||||
-- ─────────────────────────────────────────────────────────────
|
||||
-- computeInterceptDCS (DCS-Koordinaten Ein/Aus)
|
||||
-- Flat-Rückgabe kompatibel mit alter C-API
|
||||
-- ─────────────────────────────────────────────────────────────
|
||||
|
||||
function REDGCI_KERNEL.computeInterceptDCS(f, t)
|
||||
local f_gci = { x=f.z, z=f.x, y=f.y, vx=f.vz, vz=f.vx, vy=f.vy, speed=f.spd }
|
||||
local t_gci = { x=t.z, z=t.x, y=t.y, vx=t.vz, vz=t.vx, vy=t.vy, speed=t.spd }
|
||||
local sol = REDGCI_KERNEL.computeIntercept(f_gci, t_gci)
|
||||
-- GCI→DCS: ip.z(Nord)→DCS.x, ip.x(Ost)→DCS.z
|
||||
return sol.heading_deg,
|
||||
sol.time_to_intercept,
|
||||
sol.mode,
|
||||
sol.weapons_free,
|
||||
sol.range,
|
||||
sol.aspect_angle,
|
||||
sol.intercept_point.z,
|
||||
sol.intercept_point.x,
|
||||
sol.intercept_point.y,
|
||||
sol.target_alt
|
||||
end
|
||||
|
||||
-- ─────────────────────────────────────────────────────────────
|
||||
-- computeSplit (GCI-Koordinaten intern)
|
||||
-- ─────────────────────────────────────────────────────────────
|
||||
|
||||
function REDGCI_KERNEL.computeSplit(f1, f2, tgt, tactic, variation)
|
||||
local C = REDGCI_KERNEL.C
|
||||
variation = REDGCI_KERNEL.clamp(variation or 0.5, 0.0, 1.0)
|
||||
tactic = tactic or C.TACTIC_PINCER
|
||||
local mid_x = (f1.x + f2.x) * 0.5
|
||||
local mid_z = (f1.z + f2.z) * 0.5
|
||||
local dx = tgt.x - mid_x
|
||||
local dz = tgt.z - mid_z
|
||||
local rng = REDGCI_KERNEL.vec2len(dx, dz)
|
||||
if rng < 1.0 then rng = 1.0 end
|
||||
local ax = dx / rng
|
||||
local az = dz / rng
|
||||
local px = -az
|
||||
local pz = ax
|
||||
|
||||
local plan = {
|
||||
tactic = tactic,
|
||||
wp_f1 = { x=0, z=0, y=0 },
|
||||
wp_f2 = { x=0, z=0, y=0 },
|
||||
merge_f1 = { x=0, z=0, y=0 },
|
||||
merge_f2 = { x=0, z=0, y=0 },
|
||||
}
|
||||
|
||||
local function clamp_alt(pt)
|
||||
if pt.y < 300.0 then pt.y = 300.0 end
|
||||
end
|
||||
|
||||
local function clamp_alt_noe(pt)
|
||||
if pt.y < 150.0 then pt.y = 150.0 end
|
||||
end
|
||||
|
||||
-- GIRAFFE F2 fliegt NOE — separater clamp mit niedrigerer Mindesthöhe
|
||||
local function clamp_alt_noe(pt)
|
||||
if pt.y < 150.0 then pt.y = 150.0 end
|
||||
end
|
||||
|
||||
if tactic == C.TACTIC_PINCER then
|
||||
local spread = 12000.0 + variation * 5000.0
|
||||
local approach = rng * 0.45
|
||||
local merge_off = spread * 0.30
|
||||
plan.wp_f1 = { x=mid_x+ax*approach+px*spread, z=mid_z+az*approach+pz*spread, y=tgt.y }
|
||||
plan.wp_f2 = { x=mid_x+ax*approach-px*spread, z=mid_z+az*approach-pz*spread, y=tgt.y }
|
||||
plan.merge_f1 = { x=tgt.x-ax*3000+px*merge_off, z=tgt.z-az*3000+pz*merge_off, y=tgt.y }
|
||||
plan.merge_f2 = { x=tgt.x-ax*3000-px*merge_off, z=tgt.z-az*3000-pz*merge_off, y=tgt.y }
|
||||
|
||||
elseif tactic == C.TACTIC_HIGH_LOW then
|
||||
local vert = 3000.0 + variation * 1500.0
|
||||
local approach = rng * 0.50
|
||||
local side_off = 2000.0
|
||||
plan.wp_f1 = { x=mid_x+ax*approach+px*side_off, z=mid_z+az*approach+pz*side_off, y=tgt.y-500.0 }
|
||||
plan.wp_f2 = { x=mid_x+ax*approach-px*side_off, z=mid_z+az*approach-pz*side_off, y=tgt.y+vert }
|
||||
plan.merge_f1 = { x=tgt.x-ax*3000+px*side_off, z=tgt.z-az*3000+pz*side_off, y=plan.wp_f1.y }
|
||||
plan.merge_f2 = { x=tgt.x-ax*3000-px*side_off, z=tgt.z-az*3000-pz*side_off, y=plan.wp_f2.y }
|
||||
|
||||
elseif tactic == C.TACTIC_STAGGER then
|
||||
local lag = 8000.0 + variation * 3000.0
|
||||
local lead_dist = rng * 0.85
|
||||
plan.wp_f1 = { x=mid_x+ax*lead_dist, z=mid_z+az*lead_dist, y=tgt.y }
|
||||
plan.wp_f2 = { x=mid_x+ax*(lead_dist-lag), z=mid_z+az*(lead_dist-lag), y=tgt.y }
|
||||
plan.merge_f1 = { x=plan.wp_f1.x, z=plan.wp_f1.z, y=plan.wp_f1.y }
|
||||
plan.merge_f2 = { x=plan.wp_f2.x, z=plan.wp_f2.z, y=plan.wp_f2.y }
|
||||
|
||||
elseif tactic == C.TACTIC_TRAIL then
|
||||
local lag = 3000.0 + variation * 2000.0
|
||||
local lead_dist = rng * 0.85
|
||||
local side_off = 500.0
|
||||
plan.wp_f1 = { x=mid_x+ax*lead_dist, z=mid_z+az*lead_dist, y=tgt.y }
|
||||
plan.wp_f2 = { x=mid_x+ax*(lead_dist-lag)+px*side_off, z=mid_z+az*(lead_dist-lag)+pz*side_off, y=tgt.y }
|
||||
plan.merge_f1 = { x=plan.wp_f1.x, z=plan.wp_f1.z, y=plan.wp_f1.y }
|
||||
plan.merge_f2 = { x=plan.wp_f2.x, z=plan.wp_f2.z, y=plan.wp_f2.y }
|
||||
|
||||
elseif tactic == C.TACTIC_GIRAFFE then
|
||||
-- ── GIRAFFE: Irak/Iran-Doktrin (Mirage F1 vs F-14A) ──────────────
|
||||
--
|
||||
-- F1 = Decoy: normale Höhe, direkter Anflug, bindet AWG-9 Radar.
|
||||
-- Zieht Phoenixe auf sich — gibt F2 Zeit für Pull-up.
|
||||
--
|
||||
-- F2 = Killer: Nap-of-Earth (~300-600m AGL/MSL) weit seitlich versetzt.
|
||||
-- Nutzt Groundclutter um AWG-9 Look-Down zu degradieren.
|
||||
-- Nähert sich bis 10km vor Ziel, dann Pull-up und Schuss.
|
||||
-- Seitlicher Versatz ~8-12km damit F2 nicht im Radarsektor
|
||||
-- des AWG-9 liegt während F1 den Lock hält.
|
||||
--
|
||||
-- Historisch: Iran-Irak Krieg 1982-88. Irakische Mirage F1EQ nutzten
|
||||
-- dieses Profil gegen F-14A/AIM-54 Phoenix — der AWG-9 hatte trotz
|
||||
-- Look-Down-Fähigkeit bei sehr tiefen Zielen über unebenen Terrain
|
||||
-- (Zagros-Berge, Khuzestan-Ebene) erhöhte Clutter-Probleme.
|
||||
--
|
||||
-- Höhen: neutral (0.0) — Lua addiert AltOffset + terrain-awareness.
|
||||
-- Mindesthöhe 300m MSL wird am Ende geclampt.
|
||||
|
||||
local noe_alt = 300.0 + variation * 300.0 -- 300-600m MSL (Nap-of-Earth)
|
||||
local side_off = 8000.0 + variation * 4000.0 -- 8-12km seitlich (aus AWG-9 Sektor)
|
||||
local approach = rng * 0.60 -- F1 direkter Anflug, 60% des Weges
|
||||
local noe_dist = rng * 0.75 -- F2 nähert sich weiter ran vor Pull-up
|
||||
|
||||
plan.wp_f1 = { x=mid_x+ax*approach+px*2000, z=mid_z+az*approach+pz*2000, y=tgt.y }
|
||||
plan.merge_f1 = { x=tgt.x-ax*5000+px*2000, z=tgt.z-az*5000+pz*2000, y=tgt.y }
|
||||
plan.wp_f2 = { x=mid_x+ax*noe_dist-px*side_off, z=mid_z+az*noe_dist-pz*side_off, y=noe_alt, absolute_alt=true }
|
||||
plan.merge_f2 = { x=tgt.x-ax*10000-px*side_off*0.3, z=tgt.z-az*10000-pz*side_off*0.3, y=tgt.y }
|
||||
|
||||
else
|
||||
plan.wp_f1 = { x=tgt.x, z=tgt.z, y=tgt.y }
|
||||
plan.wp_f2 = { x=tgt.x, z=tgt.z, y=tgt.y }
|
||||
plan.merge_f1 = { x=tgt.x, z=tgt.z, y=tgt.y }
|
||||
plan.merge_f2 = { x=tgt.x, z=tgt.z, y=tgt.y }
|
||||
end
|
||||
|
||||
clamp_alt(plan.wp_f1)
|
||||
if tactic == C.TACTIC_GIRAFFE then
|
||||
clamp_alt_noe(plan.wp_f2)
|
||||
else
|
||||
clamp_alt(plan.wp_f2)
|
||||
end
|
||||
clamp_alt(plan.merge_f1)
|
||||
clamp_alt(plan.merge_f2)
|
||||
return plan
|
||||
end
|
||||
|
||||
-- ─────────────────────────────────────────────────────────────
|
||||
-- computeSplitDCS (DCS-Koordinaten Ein/Aus)
|
||||
-- ─────────────────────────────────────────────────────────────
|
||||
|
||||
function REDGCI_KERNEL.computeSplitDCS(f1, f2, tgt, tactic, variation)
|
||||
local function dcs2gci(u)
|
||||
return { x=u.z, z=u.x, y=u.y, vx=u.vz or 0, vz=u.vx or 0, speed=u.spd or 0 }
|
||||
end
|
||||
local function gci2dcs(pt)
|
||||
return { x=pt.z, z=pt.x, y=pt.y, absolute_alt=pt.absolute_alt }
|
||||
end
|
||||
local tgt_gci = { x=tgt.z, z=tgt.x, y=tgt.y }
|
||||
local plan = REDGCI_KERNEL.computeSplit(
|
||||
dcs2gci(f1), dcs2gci(f2), tgt_gci, tactic, variation)
|
||||
plan.wp_f1 = gci2dcs(plan.wp_f1)
|
||||
plan.wp_f2 = gci2dcs(plan.wp_f2)
|
||||
plan.merge_f1 = gci2dcs(plan.merge_f1)
|
||||
plan.merge_f2 = gci2dcs(plan.merge_f2)
|
||||
return plan
|
||||
end
|
||||
|
||||
-- ─────────────────────────────────────────────────────────────
|
||||
-- buildTransmission (Port von gci_build_transmission)
|
||||
--
|
||||
-- ctx/prev Felder: state, prev_state, ticks_in_state, range,
|
||||
-- aspect_angle, closure_rate, altitude_delta, fuel_fraction
|
||||
-- sol Felder: heading_deg, time_to_intercept, target_alt,
|
||||
-- range, aspect_angle, weapons_free
|
||||
-- Rückgabe: { token_str, delay_sec, weapons_free, silence }
|
||||
-- ─────────────────────────────────────────────────────────────
|
||||
|
||||
function REDGCI_KERNEL.buildTransmission(ctx, prev, sol)
|
||||
local C = REDGCI_KERNEL.C
|
||||
local tx = {
|
||||
token_str = "",
|
||||
delay_sec = REDGCI_KERNEL.clamp(
|
||||
REDGCI_KERNEL.randDelay(C.DELAY_MIN, C.DELAY_MAX), 3.0, 8.0),
|
||||
weapons_free = sol.weapons_free,
|
||||
silence = false,
|
||||
priority = 50,
|
||||
}
|
||||
|
||||
local hdg_i = math.floor((sol.heading_deg or 0) + 0.5)
|
||||
local alt_i = math.floor((sol.target_alt or 0) / 100.0 + 0.5) * 100
|
||||
local rng_km = math.floor((ctx.range or 0) / 1000.0 + 0.5)
|
||||
local aspect_i = math.floor((ctx.aspect_angle or 0) + 0.5)
|
||||
local delay = tx.delay_sec
|
||||
local state = ctx.state or "VECTOR"
|
||||
local prev_state = prev.state or "VECTOR"
|
||||
local ticks = ctx.ticks_in_state or 0
|
||||
|
||||
local function emit(fmt, ...)
|
||||
tx.token_str = string.format(fmt, ...)
|
||||
end
|
||||
|
||||
if state == "VECTOR" then
|
||||
if prev_state ~= "VECTOR" then
|
||||
local tti_m = math.floor((sol.time_to_intercept or 0) / 60.0)
|
||||
local tti_s = math.floor(sol.time_to_intercept or 0) % 60
|
||||
if tti_m > 0 then
|
||||
emit("VECTOR_WITH_TTI|hdg=%d|alt=%d|rng=%d|tti_m=%d|tti_s=%d|delay=%.1f",
|
||||
hdg_i, alt_i, rng_km, tti_m, tti_s, delay)
|
||||
else
|
||||
emit("VECTOR|hdg=%d|alt=%d|rng=%d|delay=%.1f",
|
||||
hdg_i, alt_i, rng_km, delay)
|
||||
end
|
||||
else
|
||||
local hdg_delta = math.abs((sol.heading_deg or 0) - (ctx.aspect_angle or 0))
|
||||
if hdg_delta < 5.0 and ticks > 3 then
|
||||
tx.silence = true
|
||||
else
|
||||
emit("VECTOR|hdg=%d|alt=%d|rng=%d|delay=%.1f",
|
||||
hdg_i, alt_i, rng_km, delay)
|
||||
end
|
||||
end
|
||||
|
||||
elseif state == "COMMIT" then
|
||||
if prev_state ~= "COMMIT" then
|
||||
tx.priority = 100
|
||||
emit("COMMIT_FIRST|hdg=%d|alt=%d|rng=%d|aspect=%d|delay=%.1f",
|
||||
hdg_i, alt_i, rng_km, aspect_i, delay)
|
||||
elseif ticks == 6 then
|
||||
emit("COMMIT_NO_LOCK|hdg=%d|rng=%d|aspect=%d|delay=%.1f",
|
||||
hdg_i, rng_km, aspect_i, delay)
|
||||
elseif ticks > 8 then
|
||||
emit("COMMIT_NUDGE|hdg=%d|aspect=%d|delay=%.1f",
|
||||
hdg_i, aspect_i, delay)
|
||||
else
|
||||
tx.silence = true
|
||||
end
|
||||
|
||||
elseif state == "RADAR_CONTACT" then
|
||||
if prev_state ~= "RADAR_CONTACT" then
|
||||
if sol.weapons_free then
|
||||
tx.priority = 100
|
||||
emit("RADAR_LOCK_WF|rng=%d|delay=%.1f", rng_km, delay)
|
||||
tx.weapons_free = true
|
||||
else
|
||||
emit("RADAR_LOCK_HOLD|rng=%d|delay=%.1f", rng_km, delay)
|
||||
end
|
||||
elseif not sol.weapons_free
|
||||
and (ctx.range or 0) < C.WF_RANGE_MAX
|
||||
and (ctx.aspect_angle or 0) > C.ASPECT_REAR_ATTACK
|
||||
and prev_state == "RADAR_CONTACT" then
|
||||
emit("RADAR_WF_NOW|rng=%d|delay=%.1f", rng_km, delay)
|
||||
tx.priority = 100
|
||||
tx.weapons_free = true
|
||||
else
|
||||
tx.silence = true
|
||||
end
|
||||
|
||||
elseif state == "VISUAL" then
|
||||
if prev_state ~= "VISUAL" then
|
||||
emit("VISUAL_CONFIRM|rng=%d|delay=%.1f", rng_km, delay)
|
||||
tx.weapons_free = true
|
||||
else
|
||||
tx.silence = true
|
||||
end
|
||||
|
||||
elseif state == "NOTCH" then
|
||||
if prev_state ~= "NOTCH" then
|
||||
emit("NOTCH_ENTRY|delay=1.5")
|
||||
elseif ticks % 8 == 0 then
|
||||
emit("NOTCH_UPDATE|rng=%d|aspect=%d|delay=%.1f",
|
||||
rng_km, aspect_i, delay)
|
||||
else
|
||||
tx.silence = true
|
||||
end
|
||||
|
||||
elseif state == "ABORT" then
|
||||
tx.delay_sec = 1.5
|
||||
if (ctx.fuel_fraction or 1.0) < C.FUEL_BINGO then
|
||||
emit("ABORT_BINGO|hdg=%d|delay=1.5", hdg_i)
|
||||
else
|
||||
emit("ABORT_THREAT|hdg=%d|delay=1.5", hdg_i)
|
||||
end
|
||||
|
||||
else
|
||||
tx.silence = true
|
||||
end
|
||||
|
||||
return tx
|
||||
end
|
||||
|
||||
-- ─────────────────────────────────────────────────────────────
|
||||
-- mergeTransition (Port von gci_merge_transition)
|
||||
--
|
||||
-- ctx Felder: phase, ticks_in_phase, range, bearing_to_target,
|
||||
-- closure_rate, altitude_delta, pass_count, radar_lost
|
||||
-- Rückgabe: phase string
|
||||
-- "ENTRY"|"OVERSHOOT"|"SEPARATION"|"REATTACK"|"LOST"|"SPLASH"
|
||||
-- ─────────────────────────────────────────────────────────────
|
||||
|
||||
function REDGCI_KERNEL.mergeTransition(ctx, prev)
|
||||
if ctx.radar_lost then return "LOST" end
|
||||
if (ctx.closure_rate or 0) < -30.0 and (ctx.range or 0) > 3000.0 then
|
||||
return "SEPARATION"
|
||||
end
|
||||
local brg = ctx.bearing_to_target or 0
|
||||
if brg > 100.0 and brg < 260.0 and (ctx.range or 0) < 5000.0 then
|
||||
return "OVERSHOOT"
|
||||
end
|
||||
if ctx.phase == "SEPARATION" and (ctx.pass_count or 0) < 3 then
|
||||
return "REATTACK"
|
||||
end
|
||||
return ctx.phase
|
||||
end
|
||||
|
||||
-- ─────────────────────────────────────────────────────────────
|
||||
-- buildMergeTransmission (Port von gci_build_merge_transmission)
|
||||
-- ─────────────────────────────────────────────────────────────
|
||||
|
||||
function REDGCI_KERNEL.buildMergeTransmission(ctx, prev)
|
||||
local C = REDGCI_KERNEL.C
|
||||
local tx = {
|
||||
token_str = "",
|
||||
delay_sec = REDGCI_KERNEL.clamp(
|
||||
REDGCI_KERNEL.randDelay(C.DELAY_MERGE_MIN, C.DELAY_MERGE_MAX), 2.0, 5.0),
|
||||
weapons_free = false,
|
||||
silence = false,
|
||||
priority = 50,
|
||||
}
|
||||
|
||||
local brg = math.floor((ctx.bearing_to_target or 0) + 0.5)
|
||||
local rng_km = math.floor((ctx.range or 0) / 1000.0 + 0.5)
|
||||
local delay = tx.delay_sec
|
||||
local phase = ctx.phase or "ENTRY"
|
||||
local prev_phase = prev.phase or "ENTRY"
|
||||
local ticks = ctx.ticks_in_phase or 0
|
||||
|
||||
local dir_rl = ((ctx.bearing_to_target or 0) < 180.0) and "right" or "left"
|
||||
local alt_d = ctx.altitude_delta or 0
|
||||
local alt_rel = ""
|
||||
if alt_d > 400.0 then alt_rel = "low"
|
||||
elseif alt_d < -400.0 then alt_rel = "high"
|
||||
end
|
||||
|
||||
local function emit(fmt, ...)
|
||||
tx.token_str = string.format(fmt, ...)
|
||||
end
|
||||
|
||||
if phase == "ENTRY" then
|
||||
tx.priority = 100
|
||||
emit("MERGE_ENTRY|brg=%d|dir_rl=%s|delay=%.1f", brg, dir_rl, delay)
|
||||
|
||||
elseif phase == "OVERSHOOT" then
|
||||
emit("MERGE_OVERSHOOT|brg=%d|dir_rl=%s|alt_rel=%s|delay=%.1f",
|
||||
brg, dir_rl, alt_rel, delay)
|
||||
|
||||
elseif phase == "SEPARATION" then
|
||||
if (ctx.pass_count or 0) < 3 then
|
||||
emit("MERGE_REATTACK|brg=%d|rng=%d|delay=%.1f", brg, rng_km, delay)
|
||||
else
|
||||
emit("ABORT_THREAT|hdg=%d|delay=%.1f", brg, delay)
|
||||
end
|
||||
|
||||
elseif phase == "REATTACK" then
|
||||
if prev_phase ~= "REATTACK" or ticks % 3 == 0 then
|
||||
emit("MERGE_REATTACK|brg=%d|rng=%d|delay=%.1f", brg, rng_km, delay)
|
||||
else
|
||||
tx.silence = true
|
||||
end
|
||||
|
||||
elseif phase == "LOST" then
|
||||
if prev_phase ~= "LOST" or ticks == 4 then
|
||||
emit("MERGE_LOST|brg=%d|rng=%d|delay=%.1f", brg, rng_km, delay)
|
||||
else
|
||||
tx.silence = true
|
||||
end
|
||||
|
||||
elseif phase == "SPLASH" then
|
||||
tx.delay_sec = 1.5
|
||||
tx.priority = 100
|
||||
emit("MERGE_SPLASH|delay=1.5")
|
||||
|
||||
else
|
||||
tx.silence = true
|
||||
end
|
||||
|
||||
return tx
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- END of Class
|
||||
-------------------------------------------------------------------------------
|
||||
@@ -90,7 +90,8 @@
|
||||
-- @image Scoring.JPG
|
||||
|
||||
--- @type SCORING
|
||||
-- @field Players A collection of the current players that have joined the game.
|
||||
-- @field #table Players A collection of the current players that have joined the game.
|
||||
-- @field Core.Set#SET_SCENERY ScoringScenery
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- SCORING class
|
||||
@@ -229,7 +230,10 @@ SCORING = {
|
||||
ClassID = 0,
|
||||
Players = {},
|
||||
AutoSave = true,
|
||||
version = "1.18.4"
|
||||
version = "1.18.5",
|
||||
ScoringScenery = nil, -- Core.Set#SET_SCENERY
|
||||
SceneryHitsInZone = false,
|
||||
LoadSave = false,
|
||||
}
|
||||
|
||||
local _SCORINGCoalition = {
|
||||
@@ -248,15 +252,16 @@ local _SCORINGCategory = {
|
||||
--- Creates a new SCORING object to administer the scoring achieved by players.
|
||||
-- @param #SCORING self
|
||||
-- @param #string GameName The name of the game. This name is also logged in the CSV score file.
|
||||
-- @param #string SavePath (Optional) Path where to save the CSV file, defaults to your **<User>\\Saved Games\\DCS\\Logs** folder.
|
||||
-- @param #boolean AutoSave (Optional) If passed as `false`, then swith autosave off.
|
||||
-- @param #string SavePath (Optional) Path where to save the CSV files, defaults to your **<User>\\Saved Games\\DCS\\Logs** folder. See next two options.
|
||||
-- @param #boolean AutoSave (Optional) If passed as `false`, then swith autosave off. This stores a detailed table which will never be loaded again by SCORING (for e.g. Discord purposes).
|
||||
-- @param #boolean LoadSave (Optional) If passed as `true` save summary scores per player, and load at restart of the mission.
|
||||
-- @return #SCORING self
|
||||
-- @usage
|
||||
--
|
||||
-- -- Define a new scoring object for the mission Gori Valley.
|
||||
-- ScoringObject = SCORING:New( "Gori Valley" )
|
||||
--
|
||||
function SCORING:New( GameName, SavePath, AutoSave )
|
||||
function SCORING:New( GameName, SavePath, AutoSave, LoadSave )
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #SCORING
|
||||
@@ -264,7 +269,7 @@ function SCORING:New( GameName, SavePath, AutoSave )
|
||||
if GameName then
|
||||
self.GameName = GameName
|
||||
else
|
||||
error( "A game name must be given to register the scoring results" )
|
||||
error( "A game name must be given to register the scoring results!" )
|
||||
end
|
||||
|
||||
-- Additional Object scores
|
||||
@@ -299,6 +304,12 @@ function SCORING:New( GameName, SavePath, AutoSave )
|
||||
self.penaltyoncoalitionchange = true
|
||||
|
||||
self:SetDisplayMessagePrefix()
|
||||
|
||||
self.SceneryHitsInZone = false
|
||||
|
||||
if LoadSave then
|
||||
self.LoadSave = LoadSave
|
||||
end
|
||||
|
||||
-- Event handlers
|
||||
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
|
||||
@@ -320,16 +331,70 @@ function SCORING:New( GameName, SavePath, AutoSave )
|
||||
|
||||
-- Create the CSV file.
|
||||
self.AutoSavePath = SavePath
|
||||
self.AutoSave = AutoSave or true
|
||||
self:OpenCSV( GameName )
|
||||
self.AutoSave = (AutoSave == nil or AutoSave == true) and true or false
|
||||
if self.AutoSavePath and self.AutoSave == true then
|
||||
self:OpenCSV( GameName )
|
||||
end
|
||||
|
||||
self:I("SCORING "..tostring(GameName).." started! v"..self.version)
|
||||
|
||||
if LoadSave == true then
|
||||
self:_LoadPlayerSummaryScore()
|
||||
end
|
||||
|
||||
return self
|
||||
|
||||
end
|
||||
|
||||
--- [Internal] Helper to load scores from disk at scoring start
|
||||
-- @param #SCORING self
|
||||
-- @return #SCORING self
|
||||
function SCORING:_LoadPlayerSummaryScore()
|
||||
|
||||
if lfs and io and self.LoadSave == true then
|
||||
local path = self.AutoSavePath or lfs.writedir() .. [[Logs\]]
|
||||
local filename = self.GameName or "PlayerScoresSummary"
|
||||
filename = filename..".csv"
|
||||
if UTILS.CheckFileExists(path,filename) then
|
||||
local ok, data = UTILS.LoadFromFile(path,filename)
|
||||
-- Playername;;Score;;Penalty
|
||||
table.remove(data,1)
|
||||
for _,_data in pairs(data) do
|
||||
local line = UTILS.Split(_data,";;")
|
||||
local playername = tostring(line[1])
|
||||
local score = tonumber(line[2])
|
||||
local penalty = tonumber(line[3])
|
||||
self:I(string.format("Player %s Score %d Penalty %d",playername,score,penalty))
|
||||
local PlayerData = self.Players[playername]
|
||||
if not PlayerData then
|
||||
PlayerData = {}
|
||||
PlayerData.Hit = {}
|
||||
PlayerData.Destroy = {}
|
||||
PlayerData.Goals = {}
|
||||
PlayerData.Goals[self.GameName] = {Score = score, Penalty = penalty}
|
||||
PlayerData.Mission = {}
|
||||
PlayerData.HitPlayers = {}
|
||||
PlayerData.Score = score
|
||||
PlayerData.Penalty = penalty
|
||||
PlayerData.PenaltyCoalition = 0
|
||||
PlayerData.PenaltyWarning = 0
|
||||
self.Players[playername] = PlayerData
|
||||
else
|
||||
PlayerData.Score = score
|
||||
PlayerData.Penalty =penalty
|
||||
self.Players[playername] = PlayerData
|
||||
PlayerData.Goals[self.GameName] = {Score = score, Penalty = penalty}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a prefix string that will be displayed at each scoring message sent.
|
||||
-- @param #SCORING self
|
||||
-- @param #string DisplayMessagePrefix (Default="Scoring: ") The scoring prefix string.
|
||||
-- @param #string DisplayMessagePrefix (Optional) (Default="Scoring: ") The scoring prefix string.
|
||||
-- @return #SCORING
|
||||
function SCORING:SetDisplayMessagePrefix( DisplayMessagePrefix )
|
||||
self.DisplayMessagePrefix = DisplayMessagePrefix or ""
|
||||
@@ -375,7 +440,7 @@ function SCORING:AddUnitScore( ScoreUnit, Score )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Removes a @{Wrapper.Unit} for additional scoring when the @{Wrapper.Unit} is destroyed.
|
||||
--- Removes a @{Wrapper.Unit} for scoring when the @{Wrapper.Unit} is destroyed.
|
||||
-- @param #SCORING self
|
||||
-- @param Wrapper.Unit#UNIT ScoreUnit The @{Wrapper.Unit} for which the Score needs to be given.
|
||||
-- @return #SCORING
|
||||
@@ -392,10 +457,26 @@ end
|
||||
-- Note that if there was already a @{Wrapper.Static} declared within the scoring with the same name,
|
||||
-- then the old @{Wrapper.Static} will be replaced with the new @{Wrapper.Static}.
|
||||
-- @param #SCORING self
|
||||
-- @param Wrapper.Static#UNIT ScoreStatic The @{Wrapper.Static} for which the Score needs to be given.
|
||||
-- @param Wrapper.Static#STATIC ScoreStatic The @{Wrapper.Static} for which the Score needs to be given.
|
||||
-- @param #number Score The Score value.
|
||||
-- @return #SCORING
|
||||
function SCORING:AddStaticScore( ScoreStatic, Score )
|
||||
return self:AddScoreStatic( ScoreStatic, Score )
|
||||
end
|
||||
|
||||
--- Add a @{Wrapper.Static} for additional scoring when the @{Wrapper.Static} is destroyed.
|
||||
-- Note that if there was already a @{Wrapper.Static} declared within the scoring with the same name,
|
||||
-- then the old @{Wrapper.Static} will be replaced with the new @{Wrapper.Static}.
|
||||
-- @param #SCORING self
|
||||
-- @param Wrapper.Static#STATIC ScoreStatic The @{Wrapper.Static} for which the Score needs to be given.
|
||||
-- @param #number Score The Score value.
|
||||
-- @return #SCORING
|
||||
function SCORING:AddScoreStatic( ScoreStatic, Score )
|
||||
|
||||
if ScoreStatic == nil then
|
||||
BASE:E("SCORING.AddStaticScore: Parameter ScoreStatic is nil!")
|
||||
return self
|
||||
end
|
||||
|
||||
local StaticName = ScoreStatic:GetName()
|
||||
|
||||
@@ -404,7 +485,61 @@ function SCORING:AddStaticScore( ScoreStatic, Score )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Removes a @{Wrapper.Static} for additional scoring when the @{Wrapper.Static} is destroyed.
|
||||
--- Add a @{Wrapper.Scenery} for additional scoring when the @{Wrapper.Scenery} is destroyed.
|
||||
-- @param #SCORING self
|
||||
-- @param Wrapper.Scenery#SCENERY ScoreScenery The @{Wrapper.Scenery} for which the Score needs to be given.
|
||||
-- @param #number Score The Score value.
|
||||
-- @return #SCORING
|
||||
function SCORING:AddScoreScenery( ScoreScenery, Score )
|
||||
|
||||
if ScoreScenery == nil then
|
||||
self:E("SCORING.ScoreScenery: Parameter ScoreScenery is nil!")
|
||||
return self
|
||||
end
|
||||
|
||||
if not self.ScoringScenery then
|
||||
self.ScoringScenery = SET_SCENERY:New() -- Core.Set#SET_SCENERY
|
||||
end
|
||||
|
||||
local StaticName = ScoreScenery:GetName()
|
||||
self:T("Scenery name = ".. StaticName)
|
||||
|
||||
self.ScoringScenery:AddScenery(ScoreScenery)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Removes a @{Wrapper.Scenery} for scoring when the @{Wrapper.Scenery} is destroyed.
|
||||
-- @param #SCORING self
|
||||
-- @param Wrapper.Scenery#SCENERY ScoreStatic The @{Wrapper.Scenery} for which the Score needs to be given.
|
||||
-- @return #SCORING
|
||||
function SCORING:RemoveSceneryScore( ScoreScenery )
|
||||
|
||||
local StaticName = ScoreScenery:GetName()
|
||||
|
||||
self.ScoringObjects[StaticName] = nil
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Specify a special additional score for a @{Core.Set#SET_SCENERY}.
|
||||
-- @param #SCORING self
|
||||
-- @param Core.Set#SET_SCENERY Set The @{Core.Set#SET_SCENERY} for which each @{Wrapper.Scenery} in the SET a Score is given.
|
||||
-- @param #number Score The Score value.
|
||||
-- @return #SCORING
|
||||
function SCORING:AddScoreSetScenery(Set, Score)
|
||||
local set = Set.Set
|
||||
|
||||
for _,_static in pairs (set) do
|
||||
if _static ~= nil then
|
||||
self:AddScoreScenery(_static,Score)
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Removes a @{Wrapper.Static} for scoring when the @{Wrapper.Static} is destroyed.
|
||||
-- @param #SCORING self
|
||||
-- @param Wrapper.Static#UNIT ScoreStatic The @{Wrapper.Static} for which the Score needs to be given.
|
||||
-- @return #SCORING
|
||||
@@ -459,6 +594,31 @@ function SCORING:AddScoreSetGroup(Set, Score)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Specify a special additional score for a @{Core.Set#SET_STATIC}.
|
||||
-- @param #SCORING self
|
||||
-- @param Core.Set#SET_STATIC Set The @{Core.Set#SET_STATIC} for which each @{Wrapper.Static} in the SET a Score is given.
|
||||
-- @param #number Score The Score value.
|
||||
-- @return #SCORING
|
||||
function SCORING:AddScoreSetStatic(Set, Score)
|
||||
local set = Set:GetSetObjects()
|
||||
|
||||
for _,_static in pairs (set) do
|
||||
if _static and _static:IsAlive() then
|
||||
self:AddStaticScore(_static,Score)
|
||||
end
|
||||
end
|
||||
|
||||
local function AddScore(static)
|
||||
self:AddStaticScore(static,Score)
|
||||
end
|
||||
|
||||
function Set:OnAfterAdded(From,Event,To,ObjectName,Object)
|
||||
AddScore(Object)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add a @{Core.Zone} to define additional scoring when any object is destroyed in that zone.
|
||||
-- Note that if a @{Core.Zone} with the same name is already within the scoring added, the @{Core.Zone} (type) and Score will be replaced!
|
||||
-- This allows for a dynamic destruction zone evolution within your mission.
|
||||
@@ -478,7 +638,40 @@ function SCORING:AddZoneScore( ScoreZone, Score )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove a @{Core.Zone} for additional scoring.
|
||||
--- Allow Scenery hits in Zones to count (no specific(!) scenery targets). NOTE - Allowing this can spam your scoring display!
|
||||
-- @param #SCORING self
|
||||
-- @return #SCORING self
|
||||
function SCORING:EnableSceneryHitsinZones()
|
||||
self.SceneryHitsInZone = true
|
||||
return self
|
||||
end
|
||||
|
||||
--- Disallow Scenery hits in Zones to count (no specific(!) scenery targets).
|
||||
-- @param #SCORING self
|
||||
-- @return #SCORING self
|
||||
function SCORING:DisableSceneryHitsinZones()
|
||||
self.SceneryHitsInZone = false
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add a @{Core.Set#SET_ZONE} to define additional scoring when any object is destroyed in that zone.
|
||||
-- Note that if a @{Core.Zone} with the same name is already within the scoring added, the @{Core.Zone} (type) and Score will be replaced!
|
||||
-- This allows for a dynamic destruction zone evolution within your mission.
|
||||
-- @param #SCORING self
|
||||
-- @param Core.Set#SET_ZONE ScoreZoneSet The @{Core.Set#SET_ZONE} which defines the destruction score perimeters.
|
||||
-- Note that a zone can be a polygon or a moving zone.
|
||||
-- @param #number Score The Score value.
|
||||
-- @return #SCORING
|
||||
function SCORING:AddZoneScoreSet( ScoreZoneSet, Score )
|
||||
|
||||
for _,_zone in pairs(ScoreZoneSet.Set or {}) do
|
||||
self:AddZoneScore(_zone,Score)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove a @{Core.Zone} for scoring.
|
||||
-- The scoring will search if any @{Core.Zone} is added with the given name, and will remove that zone from the scoring.
|
||||
-- This allows for a dynamic destruction zone evolution within your mission.
|
||||
-- @param #SCORING self
|
||||
@@ -759,6 +952,20 @@ function SCORING:AddGoalScorePlayer( PlayerName, GoalTag, Text, Score )
|
||||
-- PlayerName can be nil, if the Unit with the player crashed or due to another reason.
|
||||
if PlayerName then
|
||||
local PlayerData = self.Players[PlayerName]
|
||||
if not PlayerData then
|
||||
PlayerData = {}
|
||||
PlayerData.Goals = {}
|
||||
PlayerData.Hit = {}
|
||||
PlayerData.Destroy = {}
|
||||
PlayerData.Goals = {}
|
||||
PlayerData.Mission = {}
|
||||
PlayerData.HitPlayers = {}
|
||||
PlayerData.Score = 0
|
||||
PlayerData.Penalty = 0
|
||||
PlayerData.PenaltyCoalition = 0
|
||||
PlayerData.PenaltyWarning = 0
|
||||
self.Players[PlayerName] = PlayerData
|
||||
end
|
||||
|
||||
PlayerData.Goals[GoalTag] = PlayerData.Goals[GoalTag] or { Score = 0 }
|
||||
PlayerData.Goals[GoalTag].Score = PlayerData.Goals[GoalTag].Score + Score
|
||||
@@ -956,7 +1163,7 @@ end
|
||||
-- @param #SCORING self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
function SCORING:_EventOnHit( Event )
|
||||
self:F( { Event } )
|
||||
--self:F( { Event } )
|
||||
|
||||
local InitUnit = nil
|
||||
local InitUNIT = nil
|
||||
@@ -985,6 +1192,8 @@ function SCORING:_EventOnHit( Event )
|
||||
local TargetUnitCoalition = nil
|
||||
local TargetUnitCategory = nil
|
||||
local TargetUnitType = nil
|
||||
local TargetIsScenery = false
|
||||
local TargetSceneryObject = nil
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
|
||||
@@ -1006,7 +1215,7 @@ function SCORING:_EventOnHit( Event )
|
||||
InitUnitCategory = _SCORINGCategory[InitCategory]
|
||||
InitUnitType = InitType
|
||||
|
||||
self:T( { InitUnitName, InitGroupName, InitPlayerName, InitCoalition, InitCategory, InitType, InitUnitCoalition, InitUnitCategory, InitUnitType } )
|
||||
--self:T( { InitUnitName, InitGroupName, InitPlayerName, InitCoalition, InitCategory, InitType, InitUnitCoalition, InitUnitCategory, InitUnitType } )
|
||||
end
|
||||
|
||||
if Event.TgtDCSUnit then
|
||||
@@ -1024,12 +1233,23 @@ function SCORING:_EventOnHit( Event )
|
||||
-- TargetCategory = TargetUnit:getDesc().category
|
||||
TargetCategory = Event.TgtCategory
|
||||
TargetType = Event.TgtTypeName
|
||||
|
||||
|
||||
-- Scenery hit
|
||||
if TargetUNIT ~= nil and TargetUNIT:IsInstanceOf("SCENERY") then
|
||||
TargetCategory = Unit.Category.STRUCTURE
|
||||
TargetIsScenery = true
|
||||
TargetType = "Scenery"
|
||||
TargetSceneryObject = TargetUNIT
|
||||
self:T("***** Target is Scenery and TargetUNIT is SCENERY object!")
|
||||
--UTILS.PrintTableToLog(TargetSceneryObject)
|
||||
end
|
||||
|
||||
TargetUnitCoalition = _SCORINGCoalition[TargetCoalition]
|
||||
TargetUnitCategory = _SCORINGCategory[TargetCategory]
|
||||
TargetUnitType = TargetType
|
||||
|
||||
self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType, TargetUnitCoalition, TargetUnitCategory, TargetUnitType } )
|
||||
--self:T( { TargetUnitName=TargetUnitName, TargetGroupName=TargetGroupName, TargetPlayerName=TargetPlayerName, TargetCoalition=TargetCoalition, TargetCategory=TargetCategory, TargetType=TargetType, TargetUnitCoalition=TargetUnitCoalition, TargetUnitCategory=TargetUnitCategory, TargetUnitType=TargetUnitType } )
|
||||
end
|
||||
|
||||
if InitPlayerName ~= nil then -- It is a player that is hitting something
|
||||
@@ -1042,7 +1262,7 @@ function SCORING:_EventOnHit( Event )
|
||||
self:T( "Hitting Something" )
|
||||
|
||||
-- What is he hitting?
|
||||
if TargetCategory then
|
||||
if (TargetCategory ~=nil) and (TargetIsScenery == false) then
|
||||
|
||||
-- A target got hit, score it.
|
||||
-- Player contains the score data from self.Players[InitPlayerName]
|
||||
@@ -1062,7 +1282,7 @@ function SCORING:_EventOnHit( Event )
|
||||
PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0
|
||||
PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT
|
||||
-- After an instant kill we can't compute the threat level anymore. To fix this we compute at OnEventBirth
|
||||
if PlayerHit.UNIT.ThreatType == nil then
|
||||
if PlayerHit.UNIT and PlayerHit.UNIT.ThreatType == nil then
|
||||
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
|
||||
-- if this fails for some reason, set a good default value
|
||||
if PlayerHit.ThreatType == nil or PlayerHit.ThreatType == "" then
|
||||
@@ -1117,17 +1337,22 @@ function SCORING:_EventOnHit( Event )
|
||||
MESSAGE.Type.Update )
|
||||
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
||||
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
||||
else
|
||||
elseif TargetIsScenery ~= true then
|
||||
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() )
|
||||
elseif TargetIsScenery == true then
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit scenery object." .. " 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 -- A scenery object was hit.
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit scenery object.",
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit nothing special.",
|
||||
MESSAGE.Type.Update )
|
||||
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
||||
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
||||
@@ -1143,21 +1368,21 @@ function SCORING:_EventOnHit( Event )
|
||||
|
||||
-- It is a weapon initiated by a player, that is hitting something
|
||||
-- This seems to occur only with scenery and static objects.
|
||||
if Event.WeaponPlayerName ~= nil then
|
||||
self:_AddPlayerFromUnit( Event.WeaponUNIT )
|
||||
if self.Players[Event.WeaponPlayerName] then -- This should normally not happen, but i'll test it anyway.
|
||||
if TargetPlayerName ~= nil then -- It is a player hitting another player ...
|
||||
self:_AddPlayerFromUnit( TargetUNIT )
|
||||
end
|
||||
if Event.WeaponPlayerName ~= nil or TargetIsScenery == true then
|
||||
|
||||
local playername = Event.WeaponPlayerName or Event.IniPlayerName or "Ghost"
|
||||
|
||||
--self:_AddPlayerFromUnit( Event.WeaponUNIT )
|
||||
if self.Players[playername] then -- This should normally not happen, but i'll test it anyway.
|
||||
|
||||
self:T( "Hitting Scenery" )
|
||||
self:T( "Hitting Scenery or Static" )
|
||||
|
||||
-- What is he hitting?
|
||||
if TargetCategory then
|
||||
if Event.TgtObjectCategory then
|
||||
|
||||
-- A scenery or static got hit, score it.
|
||||
-- Player contains the score data from self.Players[WeaponPlayerName]
|
||||
local Player = self.Players[Event.WeaponPlayerName]
|
||||
local Player = self.Players[playername]
|
||||
|
||||
-- Ensure there is a hit table per TargetCategory and TargetUnitName.
|
||||
Player.Hit[TargetCategory] = Player.Hit[TargetCategory] or {}
|
||||
@@ -1165,15 +1390,17 @@ function SCORING:_EventOnHit( Event )
|
||||
|
||||
-- PlayerHit contains the score counters and data per unit that was hit.
|
||||
local PlayerHit = Player.Hit[TargetCategory][TargetUnitName]
|
||||
|
||||
|
||||
-- Init player scores
|
||||
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
|
||||
|
||||
-- After an instant kill we can't compute the threat level anymore. To fix this we compute at OnEventBirth
|
||||
if PlayerHit.UNIT.ThreatType == nil then
|
||||
if PlayerHit.UNIT and PlayerHit.UNIT.ThreatType == nil then
|
||||
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
|
||||
-- if this fails for some reason, set a good default value
|
||||
if PlayerHit.ThreatType == nil then
|
||||
@@ -1181,8 +1408,8 @@ function SCORING:_EventOnHit( Event )
|
||||
PlayerHit.ThreatType = "Unknown"
|
||||
end
|
||||
else
|
||||
PlayerHit.ThreatLevel = PlayerHit.UNIT.ThreatLevel
|
||||
PlayerHit.ThreatType = PlayerHit.UNIT.ThreatType
|
||||
PlayerHit.ThreatLevel = PlayerHit.UNIT and PlayerHit.UNIT.ThreatLevel or 1
|
||||
PlayerHit.ThreatType = PlayerHit.UNIT and PlayerHit.UNIT.ThreatType or "Unknown"
|
||||
end
|
||||
|
||||
-- Only grant hit scores if there was more than one second between the last hit.
|
||||
@@ -1190,50 +1417,69 @@ function SCORING:_EventOnHit( Event )
|
||||
PlayerHit.TimeStamp = timer.getTime()
|
||||
|
||||
local Score = 0
|
||||
|
||||
if InitCoalition then -- A coalition object was hit, probably a static.
|
||||
if InitCoalition == TargetCoalition then
|
||||
-- TODO: Penalty according scale
|
||||
local Penalty = 10
|
||||
Player.Penalty = Player.Penalty + Penalty --* self.ScaleDestroyPenalty
|
||||
PlayerHit.Penalty = PlayerHit.Penalty + Penalty --* self.ScaleDestroyPenalty
|
||||
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
|
||||
|
||||
local TgtName = Event.TgtDCSUnit and Event.TgtDCSUnit.getName and Event.TgtDCSUnit:getName() or "Unknown"
|
||||
|
||||
--if InitCoalition then -- A coalition object was hit, probably a static.
|
||||
if TargetIsScenery == true and self.ScoringScenery:IsInSet(TargetSceneryObject) then
|
||||
Player.Score = Player.Score + self.ScoreIncrementOnHit
|
||||
PlayerHit.Score = PlayerHit.Score + self.ScoreIncrementOnHit
|
||||
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit enemy target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. playername .. "' hit scenery 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 -- A scenery object was hit.
|
||||
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( playername, TargetPlayerName, "HIT_SCORE", 1, 1, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
|
||||
elseif TargetIsScenery == false and Event.TgtObjectCategory == Object.Category.STATIC and self.ScoringObjects[TgtName] then
|
||||
Player.Score = Player.Score + self.ScoreIncrementOnHit
|
||||
PlayerHit.Score = PlayerHit.Score + self.ScoreIncrementOnHit
|
||||
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. playername .. "' hit static 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, "", "HIT_SCORE", 1, 0, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, "", "Scenery", TargetUnitType )
|
||||
end
|
||||
self:ScoreCSV( playername, TargetPlayerName, "HIT_SCORE", 1, 1, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
|
||||
|
||||
else
|
||||
self:E("Hit unregistered scenery or static object - NO target! ("..TgtName..")")
|
||||
end
|
||||
--end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if there are Zones where the destruction happened.
|
||||
for ZoneName, ScoreZoneData in pairs( self.ScoringZones ) do
|
||||
self:F( { ScoringZone = ScoreZoneData } )
|
||||
local hit=Event.TgtUnit
|
||||
local ScoreZone = ScoreZoneData.ScoreZone -- Core.Zone#ZONE_BASE
|
||||
local Score = ScoreZoneData.Score
|
||||
if TargetUNIT and ScoreZone:IsVec2InZone( TargetUNIT:GetVec2() ) then
|
||||
-- A scenery or static got hit, score it.
|
||||
-- Player contains the score data from self.Players[WeaponPlayerName]
|
||||
local PlayerName = Event.IniPlayerName or "Ghost"
|
||||
local Player = self.Players[PlayerName]
|
||||
if Player then
|
||||
Player.Score = Player.Score + Score
|
||||
Player.Score = Player.Score + self.ScoreIncrementOnHit
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "hit 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, "", "HIT_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Zone", TargetUnitType )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Track DEAD or CRASH events for the scoring.
|
||||
-- @param #SCORING self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
@@ -1291,7 +1537,7 @@ function SCORING:_EventOnDeadOrCrash( Event )
|
||||
local Destroyed = false
|
||||
|
||||
-- What is the player destroying?
|
||||
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 -- Was there a hit for this unit for this player before registered???
|
||||
if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] and Player.Hit[TargetCategory][TargetUnitName].TimeStamp ~= 0 and TargetUnit and (TargetUnit.BirthTime == nil or Player.Hit[TargetCategory][TargetUnitName].TimeStamp > TargetUnit.BirthTime) then -- Was there a hit for this unit for this player before registered???
|
||||
|
||||
local TargetThreatLevel = Player.Hit[TargetCategory][TargetUnitName].ThreatLevel
|
||||
local TargetThreatType = Player.Hit[TargetCategory][TargetUnitName].ThreatType
|
||||
@@ -1411,22 +1657,24 @@ function SCORING:_EventOnDeadOrCrash( Event )
|
||||
|
||||
end
|
||||
else
|
||||
-- Check if there are Zones where the destruction happened.
|
||||
for ZoneName, ScoreZoneData in pairs( self.ScoringZones ) do
|
||||
self:F( { ScoringZone = ScoreZoneData } )
|
||||
local ScoreZone = ScoreZoneData.ScoreZone -- Core.Zone#ZONE_BASE
|
||||
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
|
||||
if self.SceneryHitsInZone == true then
|
||||
-- Check if there are Zones where the destruction happened.
|
||||
for ZoneName, ScoreZoneData in pairs( self.ScoringZones ) do
|
||||
self:F( { ScoringZone = ScoreZoneData } )
|
||||
local ScoreZone = ScoreZoneData.ScoreZone -- Core.Zone#ZONE_BASE
|
||||
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
|
||||
end
|
||||
@@ -1770,9 +2018,12 @@ end
|
||||
--- Report all players score
|
||||
-- @param #SCORING self
|
||||
-- @param Wrapper.Group#GROUP PlayerGroup The player group.
|
||||
function SCORING:ReportScoreAllSummary( PlayerGroup )
|
||||
-- @param #boolean JustScore If this is true, return just a table with playernames and overall scores.
|
||||
-- @return #table ReportTable Table returned if JustScore is true.
|
||||
function SCORING:ReportScoreAllSummary( PlayerGroup, JustScore )
|
||||
|
||||
local PlayerMessage = ""
|
||||
local ReportTable = {}
|
||||
|
||||
self:T( { "Summary Score Report of All Players", Players = self.Players } )
|
||||
|
||||
@@ -1804,20 +2055,28 @@ function SCORING:ReportScoreAllSummary( PlayerGroup )
|
||||
|
||||
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 )
|
||||
|
||||
if JustScore~=true then
|
||||
PlayerMessage =
|
||||
string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )",
|
||||
PlayerName,
|
||||
PlayerScore - PlayerPenalty,
|
||||
PlayerScore,
|
||||
PlayerPenalty
|
||||
)
|
||||
MESSAGE:NewType( PlayerMessage, MESSAGE.Type.Overview ):ToGroup( PlayerGroup )
|
||||
else
|
||||
ReportTable[PlayerName] = {["Score"]=PlayerScore,["Penalty"]=PlayerPenalty}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return ReportTable
|
||||
end
|
||||
|
||||
--- Opens a score CSV file to log the scores.
|
||||
-- @param #SCORING self
|
||||
-- @param #number sSeconds
|
||||
-- @return #string ClockString
|
||||
function SCORING:SecondsToClock( sSeconds )
|
||||
local nSeconds = sSeconds
|
||||
if nSeconds == 0 then
|
||||
@@ -1923,7 +2182,7 @@ function SCORING:ScoreCSV( PlayerName, TargetPlayerName, ScoreType, ScoreTimes,
|
||||
TargetUnitType = TargetUnitType or ""
|
||||
TargetUnitName = TargetUnitName or ""
|
||||
|
||||
if lfs and io and os and self.AutoSave then
|
||||
if lfs and io and os and self.AutoSave == true and self.CSVFile ~= nil then
|
||||
self.CSVFile:write(
|
||||
'"' .. self.GameName .. '"' .. ',' ..
|
||||
'"' .. self.RunTime .. '"' .. ',' ..
|
||||
@@ -1945,6 +2204,23 @@ function SCORING:ScoreCSV( PlayerName, TargetPlayerName, ScoreType, ScoreTimes,
|
||||
|
||||
self.CSVFile:write( "\n" )
|
||||
end
|
||||
|
||||
if lfs and io and self.LoadSave == true then
|
||||
local path = self.AutoSavePath or lfs.writedir() .. [[Logs\]]
|
||||
local filename = self.GameName or "PlayerScoresSummary"
|
||||
filename = filename..".csv"
|
||||
local data = self:ReportScoreAllSummary("",true)
|
||||
local text = "-- Playername;;Score;;Penalty\n"
|
||||
for _playername,_data in pairs(data or {}) do
|
||||
-- ReportTable[PlayerName] = {["Score"]=PlayerScore,["Penalty"]=PlayerPenalty}
|
||||
local Playername = _playername or "Ghost"
|
||||
local Score = _data.Score or 0
|
||||
local Penalty = _data.Penalty or 0
|
||||
text = text..string.format("%s;;%d;;%d\n",Playername,Score,Penalty)
|
||||
end
|
||||
UTILS.SaveToFile(path,filename,text)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Close CSV file
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
--
|
||||
-- ### Authors: **applevangelist**, **FlightControl**
|
||||
--
|
||||
-- Last Update: Dec 2024
|
||||
-- Last Update: January 2026
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -157,7 +157,7 @@ function SEAD:New( SEADGroupPrefixes, Padding )
|
||||
self:AddTransition("*", "ManageEvasion", "*")
|
||||
self:AddTransition("*", "CalculateHitZone", "*")
|
||||
|
||||
self:I("*** SEAD - Started Version 0.4.9")
|
||||
self:I("*** SEAD - Started Version 0.4.11")
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -182,7 +182,7 @@ end
|
||||
|
||||
--- Sets the engagement range of the SAMs. Defaults to 75% to make it more deadly. Feature Request #1355
|
||||
-- @param #SEAD self
|
||||
-- @param #number range Set the engagement range in percent, e.g. 55 (default 75)
|
||||
-- @param #number range (Optional) Set the engagement range in percent, e.g. 55 (default 75)
|
||||
-- @return #SEAD self
|
||||
function SEAD:SetEngagementRange(range)
|
||||
self:T( { range } )
|
||||
@@ -197,7 +197,7 @@ end
|
||||
|
||||
--- Set the padding in seconds, which extends the radar off time calculated by SEAD
|
||||
-- @param #SEAD self
|
||||
-- @param #number Padding Extra number of seconds to add for the switch-on (default 10 seconds)
|
||||
-- @param #number Padding (Optional) Extra number of seconds to add for the switch-on (default 10 seconds)
|
||||
-- @return #SEAD self
|
||||
function SEAD:SetPadding(Padding)
|
||||
self:T( { Padding } )
|
||||
@@ -318,7 +318,7 @@ function SEAD:onafterCalculateHitZone(From,Event,To,SEADWeapon,pos0,height,SEADG
|
||||
elseif height <= 12500 then
|
||||
Ropt = Ropt * 0.98
|
||||
end
|
||||
|
||||
local WeaponWrapper = WEAPON:New(SEADWeapon)
|
||||
-- look at a couple of zones across the trajectory
|
||||
for n=1,3 do
|
||||
local dist = Ropt - ((n-1)*20000)
|
||||
@@ -342,7 +342,7 @@ function SEAD:onafterCalculateHitZone(From,Event,To,SEADWeapon,pos0,height,SEADG
|
||||
_targetgroupname = tgtgrp:GetName() -- group name
|
||||
_targetskill = tgtgrp:GetUnit(1):GetSkill()
|
||||
self:T("*** Found Target = ".. _targetgroupname)
|
||||
self:ManageEvasion(_targetskill,_targetgroup,pos0,"AGM_88",SEADGroup, 20)
|
||||
self:ManageEvasion(_targetskill,_targetgroup,pos0,"AGM_88",SEADGroup, 20, WeaponWrapper)
|
||||
end
|
||||
--end
|
||||
end
|
||||
@@ -442,7 +442,18 @@ function SEAD:onafterManageEvasion(From,Event,To,_targetskill,_targetgroup,SEADP
|
||||
local SuppressionStartTime = timer.getTime() + delay
|
||||
local SuppressionEndTime = timer.getTime() + delay + _tti + self.Padding + delay
|
||||
local _targetgroupname = _targetgroup:GetName()
|
||||
if not self.SuppressedGroups[_targetgroupname] then
|
||||
local shoradactive = _targetgroup:GetProperty("SHORAD_ACTIVE")
|
||||
if not self.SuppressedGroups[_targetgroupname] and shoradactive ~= true then
|
||||
-- TODO: ask callback if suppression is allowed BEFORE scheduling timers
|
||||
local allow = true
|
||||
if self.UseCallBack and self.CallBack and self.CallBack.SeadAllowSuppression then
|
||||
allow = self.CallBack:SeadAllowSuppression(_targetgroup,_targetgroupname,SEADGroup,SEADWeaponName,Weapon,_tti,delay)
|
||||
end
|
||||
if not allow then
|
||||
self:T(string.format("*** SEAD - %s | Suppression vetoed by callback", _targetgroupname))
|
||||
-- Important: do NOT schedule SuppressionStart/Stop and do NOT flip SuppressedGroups true.
|
||||
return self
|
||||
end
|
||||
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)
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
-- @image Functional.Shorad.jpg
|
||||
--
|
||||
-- Date: Nov 2021
|
||||
-- Last Update: Jan 2025
|
||||
-- Last Update: Jan 2026
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
--- **SHORAD** class, extends Core.Base#BASE
|
||||
@@ -48,7 +48,9 @@
|
||||
-- @field #number minscootdist Min distance of the next zone
|
||||
-- @field #number maxscootdist Max distance of the next zone
|
||||
-- @field #boolean scootrandomcoord If true, use a random coordinate in the zone and not the center
|
||||
-- @field #string scootformation Formation to take for scooting, e.g. "Vee" or "Cone"
|
||||
-- @field #string scootformation Formation to take for scooting, e.g. "Vee" or "Cone"
|
||||
-- @field #boolean SmokeDecoy = false,
|
||||
-- @field #number SmokeDecoyColor = SMOKECOLOR.White
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
@@ -114,7 +116,9 @@ SHORAD = {
|
||||
SkateZones = nil,
|
||||
minscootdist = 100,
|
||||
maxscootdist = 3000,
|
||||
scootrandomcoord = false,
|
||||
scootrandomcoord = false,
|
||||
SmokeDecoy = false,
|
||||
SmokeDecoyColor = SMOKECOLOR.White
|
||||
}
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
@@ -154,15 +158,17 @@ do
|
||||
|
||||
--- Instantiates a new SHORAD object
|
||||
-- @param #SHORAD self
|
||||
-- @param #string Name Name of this SHORAD
|
||||
-- @param #string ShoradPrefix Filter for the Shorad #SET_GROUP
|
||||
-- @param Core.Set#SET_GROUP Samset The #SET_GROUP of SAM sites to defend
|
||||
-- @param #number Radius Defense radius in meters, used to switch on SHORAD groups **within** this radius
|
||||
-- @param #number ActiveTimer Determines how many seconds the systems stay on red alert after wake-up call
|
||||
-- @param #string Name (Optional) Name of this SHORAD. Default "MyShorad"
|
||||
-- @param #string ShoradPrefix (Optional) Filter for the Shorad #SET_GROUP. Default "SAM SHORAD"
|
||||
-- @param Core.Set#SET_GROUP Samset (Optional) The #SET_GROUP of SAM sites to defend. Default is based on default ShoradPrefix and default Coalition.
|
||||
-- @param #number Radius (Optional) Defense radius in meters, used to switch on SHORAD groups **within** this radius. Default is 20000m.
|
||||
-- @param #number ActiveTimer (Optional) Determines how many seconds the systems stay on red alert after wake-up call. Default 600s
|
||||
-- @param #string Coalition Coalition, i.e. "blue", "red", or "neutral"
|
||||
-- @param #boolean UseEmOnOff Use Emissions On/Off rather than Alarm State Red/Green (default: use Emissions switch)
|
||||
-- @param #boolean SmokeDecoy (Optional) Throw smoke decoy when getting activated. Defaults to false.
|
||||
-- @param #number SmokeDecoyColor (Optional) SMOLECOLOR to use. Defaults to SMOLECOLOR.White
|
||||
-- @return #SHORAD self
|
||||
function SHORAD:New(Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition, UseEmOnOff)
|
||||
function SHORAD:New(Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition, UseEmOnOff, SmokeDecoy, SmokeDecoyColor)
|
||||
local self = BASE:Inherit( self, FSM:New() )
|
||||
self:T({Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition})
|
||||
|
||||
@@ -171,6 +177,7 @@ do
|
||||
self.name = Name or "MyShorad"
|
||||
self.Prefixes = ShoradPrefix or "SAM SHORAD"
|
||||
self.Radius = Radius or 20000
|
||||
if type(Coalition) == "number" then Coalition = string.lower(UTILS.GetCoalitionName(Coalition)) end
|
||||
self.Coalition = Coalition or "blue"
|
||||
self.Samset = Samset or GroupSet
|
||||
self.ActiveTimer = ActiveTimer or 600
|
||||
@@ -181,8 +188,15 @@ do
|
||||
self.DefenseLowProb = 70 -- probability to detect a missile shot, low margin
|
||||
self.DefenseHighProb = 90 -- probability to detect a missile shot, high margin
|
||||
self.UseEmOnOff = true -- Decide if we are using Emission on/off (default) or AlarmState red/green
|
||||
|
||||
if UseEmOnOff == false then self.UseEmOnOff = UseEmOnOff end
|
||||
self:I("*** SHORAD - Started Version 0.3.4")
|
||||
|
||||
if SmokeDecoy then
|
||||
self.SmokeDecoy = SmokeDecoy
|
||||
self.SmokeDecoyColor = SmokeDecoyColor or SMOKECOLOR.White
|
||||
end
|
||||
|
||||
self:I("*** SHORAD - Started Version 0.3.6")
|
||||
-- Set the string id for output to DCS.log file.
|
||||
self.lid=string.format("SHORAD %s | ", self.name)
|
||||
self:_InitState()
|
||||
@@ -226,9 +240,9 @@ do
|
||||
--- Add a SET_ZONE of zones for Shoot&Scoot
|
||||
-- @param #SHORAD self
|
||||
-- @param Core.Set#SET_ZONE ZoneSet Set of zones to be used. Units will move around to the next (random) zone between 100m and 3000m away.
|
||||
-- @param #number Number Number of closest zones to be considered, defaults to 3.
|
||||
-- @param #number Number (Optional) Number of closest zones to be considered, defaults to 3.
|
||||
-- @param #boolean Random If true, use a random coordinate inside the next zone to scoot to.
|
||||
-- @param #string Formation Formation to use, defaults to "Cone". See mission editor dropdown for options.
|
||||
-- @param #string Formation (Optional) Formation to use, defaults to "Cone". See mission editor dropdown for options.
|
||||
-- @return #SHORAD self
|
||||
function SHORAD:AddScootZones(ZoneSet, Number, Random, Formation)
|
||||
self:T(self.lid .. " AddScootZones")
|
||||
@@ -451,6 +465,43 @@ do
|
||||
return returnname
|
||||
end
|
||||
|
||||
--- Set an object to call back when going evasive.
|
||||
-- @param #SHORAD self
|
||||
-- @param #table Object The object to call.
|
||||
-- @return #SHORAD self
|
||||
function SHORAD:AddCallBack(Object)
|
||||
self:T({Class=Object.ClassName})
|
||||
self.CallBack = Object
|
||||
self.UseCallBack = true
|
||||
return self
|
||||
end
|
||||
|
||||
--- Smoke a SHORAD Group
|
||||
-- @param #SHORAD self
|
||||
-- @param Wrapper.Group#GROUP Group The Shorad Group to Smoke
|
||||
-- @return self
|
||||
function SHORAD:_SmokeUnits(Group)
|
||||
if self.SmokeDecoy == true then
|
||||
if Group and Group:IsAlive() then
|
||||
local units = Group:GetUnits()
|
||||
for _,_unit in pairs(units) do
|
||||
local unit = _unit -- Wrapper.Unit#UNIT
|
||||
if unit and unit:IsAlive() then
|
||||
local coordinate = unit:GetCoordinate()
|
||||
if coordinate then
|
||||
coordinate:SwitchSmokeOffsetOn()
|
||||
coordinate:Smoke(self.SmokeDecoyColor,Duration,nil,Name,true,1,20)
|
||||
coordinate:Smoke(self.SmokeDecoyColor,Duration,nil,Name,true,180,20)
|
||||
coordinate:Smoke(self.SmokeDecoyColor,Duration,nil,Name,true,270,20)
|
||||
coordinate:Smoke(self.SmokeDecoyColor,Duration,nil,Name,true,90,20)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Calculate if the missile shot is detected
|
||||
-- @param #SHORAD self
|
||||
-- @return #boolean Returns true for a detection, else false
|
||||
@@ -484,7 +535,56 @@ do
|
||||
-- mymantis:Start()
|
||||
function SHORAD:onafterWakeUpShorad(From, Event, To, TargetGroup, Radius, ActiveTimer, TargetCat, ShotAt)
|
||||
self:T(self.lid .. " WakeUpShorad")
|
||||
self:T({TargetGroup, Radius, ActiveTimer, TargetCat})
|
||||
--self:T({TargetGroup, Radius, ActiveTimer, TargetCat})
|
||||
|
||||
local TDiff = 4
|
||||
|
||||
-- local function to switch off shorad again
|
||||
local function SleepShorad(group)
|
||||
if group and group:IsAlive() then
|
||||
local groupname = group:GetName()
|
||||
self.ActiveGroups[groupname] = nil
|
||||
if self.UseEmOnOff then
|
||||
group:EnableEmission(false)
|
||||
else
|
||||
group:OptionAlarmStateGreen()
|
||||
end
|
||||
group:SetProperty("SHORAD_ACTIVE",false)
|
||||
local text = string.format("Sleeping SHORAD %s", group:GetName())
|
||||
self:T(text)
|
||||
local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
|
||||
--Shoot and Scoot
|
||||
if self.shootandscoot then
|
||||
self:__ShootAndScoot(1,group)
|
||||
else
|
||||
--group:RelocateGroundRandomInRadius(30,500,false,true,"Diamond",true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function WakeUp(_group,groupname)
|
||||
-- shot at a group we protect
|
||||
local text = string.format("Waking up SHORAD %s", _group:GetName())
|
||||
self:T(text)
|
||||
local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
|
||||
if self.UseEmOnOff then
|
||||
_group:EnableEmission(true)
|
||||
end
|
||||
_group:OptionAlarmStateRed()
|
||||
_group:SetProperty("SHORAD_ACTIVE",true)
|
||||
self:_SmokeUnits(_group)
|
||||
if self.ActiveGroups[groupname] == nil then -- no timer yet for this group
|
||||
self.ActiveGroups[groupname] = { Timing = ActiveTimer }
|
||||
local endtime = timer.getTime() + (ActiveTimer * math.random(75,100) / 100 ) -- randomize wakeup a bit
|
||||
self.ActiveGroups[groupname].Timer = TIMER:New(SleepShorad,_group):Start(endtime)
|
||||
--Shoot and Scoot
|
||||
if self.shootandscoot then
|
||||
self:__ShootAndScoot(TDiff,_group)
|
||||
TDiff=TDiff+1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local targetcat = TargetCat or Object.Category.UNIT
|
||||
local targetgroup = TargetGroup
|
||||
local targetvec2 = nil
|
||||
@@ -501,69 +601,41 @@ do
|
||||
local groupset = self.Groupset --Core.Set#SET_GROUP
|
||||
local shoradset = groupset:GetAliveSet() --#table
|
||||
|
||||
-- local function to switch off shorad again
|
||||
local function SleepShorad(group)
|
||||
if group and group:IsAlive() then
|
||||
local groupname = group:GetName()
|
||||
self.ActiveGroups[groupname] = nil
|
||||
if self.UseEmOnOff then
|
||||
group:EnableEmission(false)
|
||||
else
|
||||
group:OptionAlarmStateGreen()
|
||||
end
|
||||
local text = string.format("Sleeping SHORAD %s", group:GetName())
|
||||
self:T(text)
|
||||
local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
|
||||
--Shoot and Scoot
|
||||
if self.shootandscoot then
|
||||
self:__ShootAndScoot(1,group)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- go through set and find the one(s) to activate
|
||||
local TDiff = 4
|
||||
|
||||
for _,_group in pairs (shoradset) do
|
||||
|
||||
local groupname = _group:GetName()
|
||||
|
||||
if groupname == TargetGroup and ShotAt==true then
|
||||
-- Shot at a SHORAD group
|
||||
if self.UseEmOnOff then
|
||||
_group:EnableEmission(false)
|
||||
local allow = false
|
||||
if self.CallBack and self.UseCallBack == true then
|
||||
allow = self.CallBack:SeadAllowSuppression(_group,groupname)
|
||||
end
|
||||
_group:OptionAlarmStateGreen()
|
||||
self.ActiveGroups[groupname] = nil
|
||||
local text = string.format("Shot at SHORAD %s! Evading!", _group:GetName())
|
||||
self:T(text)
|
||||
local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
|
||||
|
||||
--Shoot and Scoot
|
||||
if self.shootandscoot then
|
||||
self:__ShootAndScoot(1,_group)
|
||||
end
|
||||
|
||||
elseif _group:IsAnyInZone(targetzone) or groupname == TargetGroup then
|
||||
-- shot at a group we protect
|
||||
local text = string.format("Waking up SHORAD %s", _group:GetName())
|
||||
self:T(text)
|
||||
local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
|
||||
if self.UseEmOnOff then
|
||||
_group:EnableEmission(true)
|
||||
end
|
||||
_group:OptionAlarmStateRed()
|
||||
if self.ActiveGroups[groupname] == nil then -- no timer yet for this group
|
||||
self.ActiveGroups[groupname] = { Timing = ActiveTimer }
|
||||
local endtime = timer.getTime() + (ActiveTimer * math.random(75,100) / 100 ) -- randomize wakeup a bit
|
||||
self.ActiveGroups[groupname].Timer = TIMER:New(SleepShorad,_group):Start(endtime)
|
||||
if allow == true then
|
||||
if self.UseEmOnOff then
|
||||
_group:EnableEmission(false)
|
||||
end
|
||||
_group:OptionAlarmStateGreen()
|
||||
self.ActiveGroups[groupname] = nil
|
||||
local text = string.format("Shot at SHORAD %s! Evading!", _group:GetName())
|
||||
self:T(text)
|
||||
local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
|
||||
self:_SmokeUnits(_group)
|
||||
--Shoot and Scoot
|
||||
if self.shootandscoot then
|
||||
self:__ShootAndScoot(TDiff,_group)
|
||||
TDiff=TDiff+1
|
||||
self:__ShootAndScoot(1,_group)
|
||||
else
|
||||
_group:RelocateGroundRandomInRadius(30,500,false,true,"Diamond",true)
|
||||
end
|
||||
else
|
||||
WakeUp(_group,groupname)
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif _group:IsAnyInZone(targetzone) or groupname == TargetGroup then
|
||||
WakeUp(_group,groupname)
|
||||
end -- end if
|
||||
end -- end in pairs
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -679,11 +751,12 @@ do
|
||||
-- @param Core.Event#EVENTDATA EventData The event details table data set
|
||||
-- @return #SHORAD self
|
||||
function SHORAD:HandleEventShot( EventData )
|
||||
self:T( { EventData } )
|
||||
--self:T( { EventData.id } )
|
||||
self:T(self.lid .. " HandleEventShot")
|
||||
local ShootingWeapon = EventData.Weapon -- Identify the weapon fired
|
||||
local ShootingWeaponName = EventData.WeaponName -- return weapon type
|
||||
-- get firing coalition
|
||||
if not EventData.IniGroup then return self end
|
||||
local weaponcoalition = EventData.IniGroup:GetCoalition()
|
||||
-- get detection probability
|
||||
if self:_CheckCoalition(weaponcoalition) then --avoid overhead on friendly fire
|
||||
@@ -703,7 +776,7 @@ do
|
||||
-- Is there target data?
|
||||
if not targetdata or self.debug then
|
||||
if string.find(ShootingWeaponName,"AGM_88",1,true) then
|
||||
self:I("**** Tracking AGM-88 with no target data.")
|
||||
self:T("**** Tracking AGM-88 with no target data.")
|
||||
local pos0 = EventData.IniUnit:GetCoordinate()
|
||||
local fheight = EventData.IniUnit:GetHeight()
|
||||
self:__CalculateHitZone(20,ShootingWeapon,pos0,fheight,EventData.IniGroup)
|
||||
@@ -722,15 +795,20 @@ do
|
||||
local tgtgrp1 = self.Samset:FindNearestGroupFromPointVec2(tgtcoord)
|
||||
local tgtcoord1 = tgtgrp1:GetCoordinate()
|
||||
local tgtgrp2 = self.Groupset:FindNearestGroupFromPointVec2(tgtcoord)
|
||||
local tgtcoord2 = tgtgrp2:GetCoordinate()
|
||||
local dist1 = tgtcoord:Get2DDistance(tgtcoord1)
|
||||
local dist2 = tgtcoord:Get2DDistance(tgtcoord2)
|
||||
|
||||
if dist1 < dist2 then
|
||||
targetunit = tgtgrp1
|
||||
targetcat = Object.Category.UNIT
|
||||
if tgtgrp2 then
|
||||
local tgtcoord2 = tgtgrp2:GetCoordinate()
|
||||
local dist1 = tgtcoord:Get2DDistance(tgtcoord1)
|
||||
local dist2 = tgtcoord:Get2DDistance(tgtcoord2)
|
||||
|
||||
if dist1 < dist2 then
|
||||
targetunit = tgtgrp1
|
||||
targetcat = Object.Category.UNIT
|
||||
else
|
||||
targetunit = tgtgrp2
|
||||
targetcat = Object.Category.UNIT
|
||||
end
|
||||
else
|
||||
targetunit = tgtgrp2
|
||||
targetunit = tgtgrp1
|
||||
targetcat = Object.Category.UNIT
|
||||
end
|
||||
end
|
||||
|
||||
@@ -251,7 +251,7 @@ STRATEGO.Type = {
|
||||
-- @param #STRATEGO self
|
||||
-- @param #string Name Name of the Adviser.
|
||||
-- @param #number Coalition Coalition, e.g. coalition.side.BLUE.
|
||||
-- @param #number MaxDist Maximum distance of a single route in kilometers, defaults to 150.
|
||||
-- @param #number MaxDist (Optional) Maximum distance of a single route in kilometers, defaults to 150.
|
||||
-- @return #STRATEGO self
|
||||
function STRATEGO:New(Name,Coalition,MaxDist)
|
||||
-- Inherit everything from FSM class.
|
||||
@@ -362,7 +362,7 @@ end
|
||||
--- [USER] Set up usage of budget and set an initial budget in points.
|
||||
-- @param #STRATEGO self
|
||||
-- @param #boolean Usebudget If true, use budget for advisory calculations.
|
||||
-- @param #number StartBudget Initial budget to be used, defaults to 500.
|
||||
-- @param #number StartBudget (Optional) Initial budget to be used, defaults to 500.
|
||||
function STRATEGO:SetUsingBudget(Usebudget,StartBudget)
|
||||
self:T(self.lid.."SetUsingBudget")
|
||||
self.usebudget = Usebudget
|
||||
@@ -394,10 +394,10 @@ end
|
||||
|
||||
--- [USER] Set weights for nodes and routes to determine their importance.
|
||||
-- @param #STRATEGO self
|
||||
-- @param #number MaxRunways Set the maximum number of runways the big (equals strategic) airbases on the map have. Defaults to 3. The weight of an airbase node hence equals the number of runways.
|
||||
-- @param #number PortWeight Set what weight a port node has. Defaults to 3.
|
||||
-- @param #number POIWeight Set what weight a POI node has. Defaults to 1.
|
||||
-- @param #number RouteFactor Defines which weight each route between two defined nodes gets: Weight * RouteFactor.
|
||||
-- @param #number MaxRunways (Optional) Set the maximum number of runways the big (equals strategic) airbases on the map have. Defaults to 3. The weight of an airbase node hence equals the number of runways.
|
||||
-- @param #number PortWeight (Optional) Set what weight a port node has. Defaults to 3.
|
||||
-- @param #number POIWeight (Optional) Set what weight a POI node has. Defaults to 1.
|
||||
-- @param #number RouteFactor (Optional) Defines which weight each route between two defined nodes gets: Weight * RouteFactor. Defaults to 5.
|
||||
-- @return #STRATEGO self
|
||||
function STRATEGO:SetWeights(MaxRunways,PortWeight,POIWeight,RouteFactor)
|
||||
self:T(self.lid.."SetWeights")
|
||||
@@ -410,7 +410,7 @@ end
|
||||
|
||||
--- [USER] Set neutral benefit, i.e. how many points it is cheaper to decide for a neutral vs an enemy node when taking decisions.
|
||||
-- @param #STRATEGO self
|
||||
-- @param #number NeutralBenefit Pointsm defaults to 100.
|
||||
-- @param #number NeutralBenefit (Optional) Pointsm defaults to 100.
|
||||
-- @return #STRATEGO self
|
||||
function STRATEGO:SetNeutralBenefit(NeutralBenefit)
|
||||
self:T(self.lid.."SetNeutralBenefit")
|
||||
@@ -420,9 +420,9 @@ end
|
||||
|
||||
--- [USER] Set how many units of which minimum threat level are needed to capture one node (i.e. the underlying OpsZone).
|
||||
-- @param #STRATEGO self
|
||||
-- @param #number CaptureUnits Number of units needed, defaults to three.
|
||||
-- @param #number CaptureThreatlevel Threat level needed, can be 0..10, defaults to one.
|
||||
-- @param #table CaptureCategories Table of object categories which can capture a node, defaults to `{Object.Category.UNIT}`.
|
||||
-- @param #number CaptureUnits (Optional) Number of units needed, defaults to 3.
|
||||
-- @param #number CaptureThreatlevel (Optional) Threat level needed, can be 0..10, defaults to 1.
|
||||
-- @param #table CaptureCategories (Optional) Table of object categories which can capture a node, defaults to `{Object.Category.UNIT}`.
|
||||
-- @return #STRATEGO self
|
||||
function STRATEGO:SetCaptureOptions(CaptureUnits,CaptureThreatlevel,CaptureCategories)
|
||||
self:T(self.lid.."SetCaptureOptions")
|
||||
|
||||
@@ -308,7 +308,7 @@ SUPPRESSION.version="0.9.4"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Creates a new AI_suppression object.
|
||||
--- Creates a new suppression object.
|
||||
-- @param #SUPPRESSION self
|
||||
-- @param Wrapper.Group#GROUP group The GROUP object for which suppression should be applied.
|
||||
-- @return #SUPPRESSION self
|
||||
@@ -642,7 +642,7 @@ end
|
||||
|
||||
--- Set average, minimum and maximum time a unit is suppressed each time it gets hit.
|
||||
-- @param #SUPPRESSION self
|
||||
-- @param #number Tave Average time [seconds] a group will be suppressed. Default is 15 seconds.
|
||||
-- @param #number Tave (Optional) Average time [seconds] a group will be suppressed. Default is 15 seconds.
|
||||
-- @param #number Tmin (Optional) Minimum time [seconds] a group will be suppressed. Default is 5 seconds.
|
||||
-- @param #number Tmax (Optional) Maximum time a group will be suppressed. Default is 25 seconds.
|
||||
function SUPPRESSION:SetSuppressionTime(Tave, Tmin, Tmax)
|
||||
@@ -697,7 +697,7 @@ end
|
||||
|
||||
--- Set the formation a group uses for fall back, hide or retreat.
|
||||
-- @param #SUPPRESSION self
|
||||
-- @param #string formation Formation of the group. Default "Vee".
|
||||
-- @param #string formation (Optional) Formation of the group. Default "Vee".
|
||||
function SUPPRESSION:SetFormation(formation)
|
||||
self:F(formation)
|
||||
self.Formation=formation or "Vee"
|
||||
@@ -705,7 +705,7 @@ end
|
||||
|
||||
--- Set speed a group moves at for fall back, hide or retreat.
|
||||
-- @param #SUPPRESSION self
|
||||
-- @param #number speed Speed in km/h of group. Default max speed the group can do.
|
||||
-- @param #number speed (Optional) Speed in km/h of group. Default max speed the group can do.
|
||||
function SUPPRESSION:SetSpeed(speed)
|
||||
self:F(speed)
|
||||
self.Speed=speed or self.SpeedMax
|
||||
@@ -793,7 +793,7 @@ end
|
||||
-- If the group consists of only a singe unit, this referrs to the life of the unit.
|
||||
-- If the group consists of more than one unit, this referrs to the group strength relative to its initial strength.
|
||||
-- @param #SUPPRESSION self
|
||||
-- @param #number damage Damage in percent. If group gets damaged above this value, the group will retreat. Default 50 %.
|
||||
-- @param #number damage (Optional) Damage in percent. If group gets damaged above this value, the group will retreat. Default 50 %.
|
||||
function SUPPRESSION:SetRetreatDamage(damage)
|
||||
self:F(damage)
|
||||
self.RetreatDamage=damage or 50
|
||||
@@ -801,7 +801,7 @@ end
|
||||
|
||||
--- Set time a group waits in the retreat zone before it resumes its mission. Default is two hours.
|
||||
-- @param #SUPPRESSION self
|
||||
-- @param #number time Time in seconds. Default 7200 seconds = 2 hours.
|
||||
-- @param #number time (Optional) Time in seconds. Default 7200 seconds = 2 hours.
|
||||
function SUPPRESSION:SetRetreatWait(time)
|
||||
self:F(time)
|
||||
self.RetreatWait=time or 7200
|
||||
@@ -809,7 +809,7 @@ end
|
||||
|
||||
--- Set alarm state a group will get after it returns from a fall back or take cover.
|
||||
-- @param #SUPPRESSION self
|
||||
-- @param #string alarmstate Alarm state. Possible "Auto", "Green", "Red". Default is "Auto".
|
||||
-- @param #string alarmstate (Optional) Alarm state. Possible "Auto", "Green", "Red". Default is "Auto".
|
||||
function SUPPRESSION:SetDefaultAlarmState(alarmstate)
|
||||
self:F(alarmstate)
|
||||
if alarmstate:lower()=="auto" then
|
||||
@@ -825,7 +825,7 @@ end
|
||||
|
||||
--- Set Rules of Engagement (ROE) a group will get when it recovers from suppression.
|
||||
-- @param #SUPPRESSION self
|
||||
-- @param #string roe ROE after suppression. Possible "Free", "Hold" or "Return". Default "Free".
|
||||
-- @param #string roe (Optional) ROE after suppression. Possible "Free", "Hold" or "Return". Default "Free".
|
||||
function SUPPRESSION:SetDefaultROE(roe)
|
||||
self:F(roe)
|
||||
if roe:lower()=="free" then
|
||||
@@ -841,7 +841,7 @@ end
|
||||
|
||||
--- Create an F10 menu entry for the suppressed group. The menu is mainly for Debugging purposes.
|
||||
-- @param #SUPPRESSION self
|
||||
-- @param #boolean switch Enable=true or disable=false menu group. Default is true.
|
||||
-- @param #boolean switch (Optional) Enable=true or disable=false menu group. Default is true.
|
||||
function SUPPRESSION:MenuOn(switch)
|
||||
self:F(switch)
|
||||
if switch==nil then
|
||||
@@ -909,7 +909,7 @@ function SUPPRESSION:StatusReport(message)
|
||||
self:GetState(), nunits, self.IniGroupStrength, self.CurrentROE, self.CurrentAlarmState, self.Nhit, life_min, life_max, life_ave, life_ave0, ammotot, detectedG, detectedU)
|
||||
|
||||
MESSAGE:New(text, 10):ToAllIf(message or self.Debug)
|
||||
self:I(self.lid..text)
|
||||
self:T(self.lid..text)
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -1194,7 +1194,7 @@ function SUPPRESSION:onafterFightBack(Controllable, From, Event, To)
|
||||
local Waypoints = group:GetTemplateRoutePoints()
|
||||
|
||||
-- env.info("FF waypoints",showMessageBox)
|
||||
-- self:I(Waypoints)
|
||||
self:T2({Waypoints})
|
||||
|
||||
group:Route(Waypoints, 5)
|
||||
|
||||
@@ -1337,7 +1337,7 @@ function SUPPRESSION:onafterOutOfAmmo(Controllable, From, Event, To)
|
||||
self:_EventFromTo("onafterOutOfAmmo", Event, From, To)
|
||||
|
||||
-- Info to log.
|
||||
self:I(self.lid..string.format("Out of ammo!"))
|
||||
self:T(self.lid..string.format("Out of ammo!"))
|
||||
|
||||
-- Order retreat if retreat zone was specified.
|
||||
if self.RetreatZone then
|
||||
@@ -1485,7 +1485,7 @@ function SUPPRESSION:onafterStop(Controllable, From, Event, 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:T(self.lid..text)
|
||||
|
||||
-- Clear all pending schedules
|
||||
self.CallScheduler:Clear()
|
||||
@@ -1682,9 +1682,9 @@ end
|
||||
--- Make group run/drive to a certain point. We put in several intermediate waypoints because sometimes the group stops before it arrived at the desired point.
|
||||
--@param #SUPPRESSION self
|
||||
--@param Core.Point#COORDINATE fin Coordinate where we want to go.
|
||||
--@param #number speed Speed of group. Default is 20.
|
||||
--@param #string formation Formation of group. Default is "Vee".
|
||||
--@param #number wait Time the group will wait/hold at final waypoint. Default is 30 seconds.
|
||||
--@param #number speed (Optional) Speed of group. Default is 20.
|
||||
--@param #string formation (Optional) Formation of group. Default is "Vee".
|
||||
--@param #number wait (Optional) Time the group will wait/hold at final waypoint. Default is 30 seconds.
|
||||
function SUPPRESSION:_Run(fin, speed, formation, wait)
|
||||
|
||||
speed=speed or 20
|
||||
@@ -1946,7 +1946,7 @@ end
|
||||
|
||||
--- Sets the ROE for the group and updates the current ROE variable.
|
||||
-- @param #SUPPRESSION self
|
||||
-- @param #string roe ROE the group will get. Possible "Free", "Hold", "Return". Default is self.DefaultROE.
|
||||
-- @param #string roe (Optional) ROE the group will get. Possible "Free", "Hold", "Return". Default is self.DefaultROE.
|
||||
function SUPPRESSION:_SetROE(roe)
|
||||
local group=self.Controllable --Wrapper.Controllable#CONTROLLABLE
|
||||
|
||||
@@ -1975,7 +1975,7 @@ end
|
||||
|
||||
--- Sets the alarm state of the group and updates the current alarm state variable.
|
||||
-- @param #SUPPRESSION self
|
||||
-- @param #string state Alarm state the group will get. Possible "Auto", "Green", "Red". Default is self.DefaultAlarmState.
|
||||
-- @param #string state (Optional) Alarm state the group will get. Possible "Auto", "Green", "Red". Default is self.DefaultAlarmState.
|
||||
function SUPPRESSION:_SetAlarmState(state)
|
||||
local group=self.Controllable --Wrapper.Controllable#CONTROLLABLE
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -35,7 +35,6 @@
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **funkyfranky**
|
||||
-- ### Co-author: FlightControl (cargo dispatcher classes)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -132,14 +131,7 @@
|
||||
-- a reasonable degree in DCS at the moment and hence cannot be used yet.
|
||||
--
|
||||
-- Furthermore, ground assets can be transferred between warehouses by transport units. These are APCs, helicopters and airplanes. The transportation process is modeled
|
||||
-- in a realistic way by using the corresponding cargo dispatcher classes, i.e.
|
||||
--
|
||||
-- * @{AI.AI_Cargo_Dispatcher_APC#AI_DISPATCHER_APC}
|
||||
-- * @{AI.AI_Cargo_Dispatcher_Helicopter#AI_DISPATCHER_HELICOPTER}
|
||||
-- * @{AI.AI_Cargo_Dispatcher_Airplane#AI_DISPATCHER_AIRPLANE}
|
||||
--
|
||||
-- Depending on which cargo dispatcher is used (ground or airbore), similar considerations like in the self propelled case are necessary. Howver, note that
|
||||
-- the dispatchers as of yet cannot use user defined off road paths for example since they are classes of their own and use a different routing logic.
|
||||
-- in a realistic way by using the @{Ops.OpsTransport#OPSTRANSPORT} class.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -230,18 +222,6 @@
|
||||
-- of 630 kg. This is important as groups cannot be split between carrier units when transporting, i.e. the total weight of the whole group must be smaller than the
|
||||
-- cargo bay of the transport carrier.
|
||||
--
|
||||
-- ### Setting the Load Radius
|
||||
-- Boading and loading of cargo into a carrier is modeled in a realistic fashion in the AI\_CARGO\DISPATCHER classes, which are used inernally by the WAREHOUSE class.
|
||||
-- Meaning that troops (cargo) will board, i.e. run or drive to the carrier, and only once they are in close proximity to the transporter they will be loaded (disappear).
|
||||
--
|
||||
-- Unfortunately, there are some situations where problems can occur. For example, in DCS tanks have the strong tentendcy not to drive around obstacles but rather to roll over them.
|
||||
-- I have seen cases where an aircraft of the same coalition as the tank was in its way and the tank drove right through the plane waiting on a parking spot and destroying it.
|
||||
--
|
||||
-- As a workaround it is possible to set a larger load radius so that the cargo units are despawned further away from the carrier via the optional **loadradius** parameter:
|
||||
--
|
||||
-- warehouseBatumi:AddAsset("Leopard 2", nil, nil, nil, nil, 250)
|
||||
--
|
||||
-- Adding the asset like this will cause the units to be loaded into the carrier already at a distance of 250 meters.
|
||||
--
|
||||
-- ### Setting the AI Skill
|
||||
--
|
||||
@@ -486,7 +466,7 @@
|
||||
-- and the road connection is less than 3 km.
|
||||
--
|
||||
-- The user can set the road connection manually with the @{#WAREHOUSE.SetRoadConnection} function. This is only functional for self propelled assets at the moment
|
||||
-- and not if using the AI dispatcher classes since these have a different logic to find the route.
|
||||
-- and not if using the OPSTRANSPORT class since this has a different logic to find the route.
|
||||
--
|
||||
-- ## Off Road Connections
|
||||
--
|
||||
@@ -595,7 +575,7 @@
|
||||
--
|
||||
-- ## Cargo Bay and Weight Limitations
|
||||
--
|
||||
-- The transportation of cargo is handled by the AI\_Dispatcher classes. These take the cargo bay of a carrier and the weight of
|
||||
-- The transportation of cargo is handled by the `OPSTRANSPORT` class. This takes the cargo bay of a carrier and the weight of
|
||||
-- the cargo into account so that a carrier can only load a realistic amount of cargo.
|
||||
--
|
||||
-- However, if troops are supposed to be transported between warehouses, there is one important limitations one has to keep in mind.
|
||||
@@ -1132,7 +1112,7 @@
|
||||
-- specify an "Assignment". This can be later used to identify the request and take the right actions.
|
||||
--
|
||||
-- Once the request is processed, the @{#WAREHOUSE.OnAfterSelfRequest} function is called. This is where we hook in and postprocess the spawned assets.
|
||||
-- In particular, we use the @{AI.AI_Formation#AI_FORMATION} class to make some nice escorts for our carrier.
|
||||
-- In particular, we use the @{Functional.Formation#FORMATION} class to make some nice escorts for our carrier.
|
||||
--
|
||||
-- When the resue helo is spawned, we can check that this is the correct asset and make the helo go into formation with the carrier.
|
||||
-- Once the helo runs out of fuel, it will automatically return to the ship and land. For the warehouse, this means that the "cargo", i.e. the helicopter
|
||||
@@ -1175,7 +1155,7 @@
|
||||
--
|
||||
-- -- Define AI Formation object.
|
||||
-- -- Note that this has to be a global variable or the garbage collector will remove it for some reason!
|
||||
-- CarrierFormationLeft = AI_FORMATION:New(Mother, groupset, "Left Formation with Carrier", "Escort Carrier.")
|
||||
-- CarrierFormationLeft = FORMATION:New(Mother, groupset, "Left Formation with Carrier")
|
||||
--
|
||||
-- -- Formation parameters.
|
||||
-- CarrierFormationLeft:FormationLeftWing(200 ,50, 0, 0, 500, 50)
|
||||
@@ -1190,7 +1170,7 @@
|
||||
--
|
||||
-- -- Define AI Formation object.
|
||||
-- -- Note that this has to be a global variable or the garbage collector will remove it for some reason!
|
||||
-- CarrierFormationRight = AI_FORMATION:New(Mother, groupset, "Right Formation with Carrier", "Escort Carrier.")
|
||||
-- CarrierFormationRight = FORMATION:New(Mother, groupset, "Right Formation with Carrier")
|
||||
--
|
||||
-- -- Formation parameters.
|
||||
-- CarrierFormationRight:FormationRightWing(200 ,50, 0, 0, 500, 50)
|
||||
@@ -1208,7 +1188,7 @@
|
||||
-- group:StartUncontrolled()
|
||||
--
|
||||
-- -- Define AI Formation object.
|
||||
-- CarrierFormationHelo = AI_FORMATION:New(Mother, groupset, "Helo Formation with Carrier", "Fly Formation.")
|
||||
-- CarrierFormationHelo = FORMATION:New(Mother, groupset, "Helo Formation with Carrier")
|
||||
--
|
||||
-- -- Formation parameters.
|
||||
-- CarrierFormationHelo:FormationCenterWing(-150, 50, 20, 50, 100, 50)
|
||||
@@ -1798,12 +1778,13 @@ _WAREHOUSEDB = {
|
||||
|
||||
--- Warehouse class version.
|
||||
-- @field #string version
|
||||
WAREHOUSE.version="1.0.2a"
|
||||
WAREHOUSE.version="2.0.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO: Warehouse todo list.
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- DONE: Switch from AI Dispatchers to OPSTRANSPORT
|
||||
-- TODO: Add check if assets "on the move" are stationary. Can happen if ground units get stuck in buildings. If stationary auto complete transport by adding assets to request warehouse? Time?
|
||||
-- TODO: Optimize findpathonroad. Do it only once (first time) and safe paths between warehouses similar to off-road paths.
|
||||
-- NOGO: Spawn assets only virtually, i.e. remove requested assets from stock but do NOT spawn them ==> Interface to A2A dispatcher! Maybe do a negative sign on asset number?
|
||||
@@ -2486,14 +2467,14 @@ function WAREHOUSE:New(warehouse, alias)
|
||||
--- Triggers the FSM event "Save" when the warehouse assets are saved to file on disk.
|
||||
-- @function [parent=#WAREHOUSE] Save
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param #string path Path where the file is saved. Default is the DCS installation root directory.
|
||||
-- @param #string path (Optional) Path where the file is saved. Default is the DCS installation root directory.
|
||||
-- @param #string filename (Optional) File name. Default is WAREHOUSE-<UID>_<ALIAS>.txt.
|
||||
|
||||
--- Triggers the FSM event "Save" with a delay when the warehouse assets are saved to a file.
|
||||
-- @function [parent=#WAREHOUSE] __Save
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param #number delay Delay in seconds.
|
||||
-- @param #string path Path where the file is saved. Default is the DCS installation root directory.
|
||||
-- @param #string path (Optional) Path where the file is saved. Default is the DCS installation root directory.
|
||||
-- @param #string filename (Optional) File name. Default is WAREHOUSE-<UID>_<ALIAS>.txt.
|
||||
|
||||
--- On after "Save" event user function. Called when the warehouse assets are saved to disk.
|
||||
@@ -2502,21 +2483,21 @@ function WAREHOUSE:New(warehouse, alias)
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param #string path Path where the file is saved. Default is the DCS installation root directory.
|
||||
-- @param #string path (Optional) Path where the file is saved. Default is the DCS installation root directory.
|
||||
-- @param #string filename (Optional) File name. Default is WAREHOUSE-<UID>_<ALIAS>.txt.
|
||||
|
||||
|
||||
--- Triggers the FSM event "Load" when the warehouse is loaded from a file on disk.
|
||||
-- @function [parent=#WAREHOUSE] Load
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param #string path Path where the file is located. Default is the DCS installation root directory.
|
||||
-- @param #string path (Optional) Path where the file is located. Default is the DCS installation root directory.
|
||||
-- @param #string filename (Optional) File name. Default is WAREHOUSE-<UID>_<ALIAS>.txt.
|
||||
|
||||
--- Triggers the FSM event "Load" with a delay when the warehouse assets are loaded from disk.
|
||||
-- @function [parent=#WAREHOUSE] __Load
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param #number delay Delay in seconds.
|
||||
-- @param #string path Path where the file is located. Default is the DCS installation root directory.
|
||||
-- @param #string path (Optional) Path where the file is located. Default is the DCS installation root directory.
|
||||
-- @param #string filename (Optional) File name. Default is WAREHOUSE-<UID>_<ALIAS>.txt.
|
||||
|
||||
--- On after "Load" event user function. Called when the warehouse assets are loaded from disk.
|
||||
@@ -2525,7 +2506,7 @@ function WAREHOUSE:New(warehouse, alias)
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param #string path Path where the file is located. Default is the DCS installation root directory.
|
||||
-- @param #string path (Optional) Path where the file is located. Default is the DCS installation root directory.
|
||||
-- @param #string filename (Optional) File name. Default is WAREHOUSE-<UID>_<ALIAS>.txt.
|
||||
|
||||
|
||||
@@ -2595,7 +2576,7 @@ end
|
||||
|
||||
--- Set low fuel threshold. If one unit of an asset has less fuel than this number, the event AssetLowFuel will be fired.
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param #number threshold Relative low fuel threshold, i.e. a number in [0,1]. Default 0.15 (15%).
|
||||
-- @param #number threshold (Optional) Relative low fuel threshold, i.e. a number in [0,1]. Default 0.15 (15%).
|
||||
-- @return #WAREHOUSE self
|
||||
function WAREHOUSE:SetLowFuelThreshold(threshold)
|
||||
self.lowfuelthresh=threshold or 0.15
|
||||
@@ -2613,7 +2594,7 @@ end
|
||||
|
||||
--- Set verbosity level.
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param #number VerbosityLevel Level of output (higher=more). Default 0.
|
||||
-- @param #number VerbosityLevel (Optional) Level of output (higher=more). Default 0.
|
||||
-- @return #WAREHOUSE self
|
||||
function WAREHOUSE:SetVerbosityLevel(VerbosityLevel)
|
||||
self.verbosity=VerbosityLevel or 0
|
||||
@@ -2724,7 +2705,7 @@ end
|
||||
--- Enable auto save of warehouse assets at mission end event.
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param #string path Path where to save the asset data file.
|
||||
-- @param #string filename File name. Default is generated automatically from warehouse id.
|
||||
-- @param #string filename (Optional) File name. Default is generated automatically from warehouse id.
|
||||
-- @return #WAREHOUSE self
|
||||
function WAREHOUSE:SetSaveOnMissionEnd(path, filename)
|
||||
self.autosave=true
|
||||
@@ -3153,7 +3134,7 @@ end
|
||||
-- @param #WAREHOUSE self
|
||||
-- @return Core.Point#COORDINATE The coordinate of the warehouse.
|
||||
function WAREHOUSE:GetCoordinate()
|
||||
return self.warehouse:GetCoordinate()
|
||||
return self.warehouse:GetCoord()
|
||||
end
|
||||
|
||||
--- Get 3D vector of warehouse static.
|
||||
@@ -3340,7 +3321,6 @@ function WAREHOUSE:FindAssetInDB(group)
|
||||
if aid~=nil then
|
||||
|
||||
local asset=_WAREHOUSEDB.Assets[aid]
|
||||
self:T2({asset=asset})
|
||||
if asset==nil then
|
||||
self:_ErrorMessage(string.format("ERROR: Asset for group %s not found in the data base!", group:GetName()), 0)
|
||||
end
|
||||
@@ -3369,7 +3349,7 @@ end
|
||||
-- Note that this is the time, the DCS engine uses not something we can control on a user level or we could get via scripting.
|
||||
-- You need to input the value. On the DCS forum it was stated that this is currently one hour. Hence this is the default value.
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param #number RepairTime Time in seconds until the runway is repaired. Default 3600 sec (one hour).
|
||||
-- @param #number RepairTime (Optional) Time in seconds until the runway is repaired. Default 3600 sec (one hour).
|
||||
-- @return #WAREHOUSE self
|
||||
function WAREHOUSE:SetRunwayRepairtime(RepairTime)
|
||||
self.runwayrepairtime=RepairTime or 3600
|
||||
@@ -3744,12 +3724,15 @@ function WAREHOUSE:_JobDone()
|
||||
|
||||
-- Check conditions for being back home.
|
||||
local ishome=false
|
||||
if category==Group.Category.GROUND or category==Group.Category.HELICOPTER then
|
||||
-- Units go back to the spawn zone, helicopters land and they should not move any more.
|
||||
ishome=inspawnzone and onground and notmoving
|
||||
if category==Group.Category.GROUND then
|
||||
-- Ground units go back to the spawn zone and they should not move any more.
|
||||
ishome=inspawnzone and notmoving
|
||||
elseif category==Group.Category.AIRPLANE then
|
||||
-- Planes need to be on ground at their home airbase and should not move any more.
|
||||
ishome=athomebase and onground and notmoving
|
||||
elseif category==Group.Category.HELICOPTER then
|
||||
-- Helicopters go back to their airbase or spawn zone and should not move any more.
|
||||
ishome=(athomebase or inspawnzone) and onground and notmoving
|
||||
end
|
||||
|
||||
-- Debug text.
|
||||
@@ -3908,7 +3891,7 @@ end
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Wrapper.Group#GROUP group Group or template group to be added to the warehouse stock.
|
||||
-- @param #number ngroups Number of groups to add to the warehouse stock. Default is 1.
|
||||
-- @param #number ngroups (Optional) Number of groups to add to the warehouse stock. Default is 1.
|
||||
-- @param #WAREHOUSE.Attribute forceattribute (Optional) Explicitly force a generalized attribute for the asset. This has to be an @{#WAREHOUSE.Attribute}.
|
||||
-- @param #number forcecargobay (Optional) Explicitly force cargobay weight limit in kg for cargo carriers. This is for each *unit* of the group.
|
||||
-- @param #number forceweight (Optional) Explicitly force weight in kg of each unit in the group.
|
||||
@@ -3918,7 +3901,7 @@ end
|
||||
-- @param #string assignment A free to choose string specifying an assignment for the asset. This can be used with the @{#WAREHOUSE.OnAfterNewAsset} function.
|
||||
-- @param #table other (Optional) Table of other useful data. Can be collected via WAREHOUSE.OnAfterNewAsset() function for example
|
||||
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})
|
||||
--self:T({group=group:GetName(), ngroups=ngroups, forceattribute=forceattribute, forcecargobay=forcecargobay, forceweight=forceweight})
|
||||
|
||||
-- Set default.
|
||||
local n=ngroups or 1
|
||||
@@ -4113,10 +4096,10 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay,
|
||||
|
||||
-- Get name of template group.
|
||||
local templategroupname=group:GetName()
|
||||
|
||||
local Descriptors=group:GetUnit(1):GetDesc()
|
||||
local unit = group:GetUnit(1)
|
||||
local Descriptors= (unit and unit:IsAlive()~=nil) and unit:GetDesc() or {}
|
||||
local Category=group:GetCategory()
|
||||
local TypeName=group:GetTypeName()
|
||||
local TypeName=group:GetTypeName() or "none"
|
||||
local SpeedMax=group:GetSpeedMax()
|
||||
local RangeMin=group:GetRange()
|
||||
local smax,sx,sy,sz=_GetObjectSize(Descriptors)
|
||||
@@ -4132,7 +4115,7 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay,
|
||||
for _i,_unit in pairs(group:GetUnits()) do
|
||||
local unit=_unit --Wrapper.Unit#UNIT
|
||||
local Desc=unit:GetDesc()
|
||||
|
||||
|
||||
-- Weight. We sum up all units in the group.
|
||||
local unitweight=forceweight or Desc.massEmpty
|
||||
if unitweight then
|
||||
@@ -4247,6 +4230,16 @@ function WAREHOUSE:_AssetItemInfo(asset)
|
||||
self:T3({Template=asset.template})
|
||||
end
|
||||
|
||||
--- This function uses Disposition and other fallback logic to find better ground positions for ground units.
|
||||
--- NOTE: This is not a spawn randomizer.
|
||||
--- It will try to find clear ground locations avoiding trees, water, roads, runways, map scenery, statics and other units in the area and modifies the provided positions table.
|
||||
--- Maintains the original layout and unit positions as close as possible by searching for the next closest valid position to each unit.
|
||||
--- Uses UTILS.ValidateAndRepositionGroundUnits.
|
||||
-- @param #boolean Enabled Enable/disable the feature.
|
||||
function WAREHOUSE:SetValidateAndRepositionGroundUnits(Enabled)
|
||||
self.ValidateAndRepositionGroundUnits = Enabled
|
||||
end
|
||||
|
||||
--- On after "NewAsset" event. A new asset has been added to the warehouse stock.
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param #string From From state.
|
||||
@@ -4436,7 +4429,6 @@ end
|
||||
-- @param #WAREHOUSE.Queueitem Request Information table of the request.
|
||||
-- @return #boolean If true, request is granted.
|
||||
function WAREHOUSE:onbeforeRequest(From, Event, To, Request)
|
||||
self:T3({warehouse=self.alias, request=Request})
|
||||
|
||||
-- Distance from warehouse to requesting warehouse.
|
||||
local distance=self:GetCoordinate():Get2DDistance(Request.warehouse:GetCoordinate())
|
||||
@@ -4689,136 +4681,48 @@ function WAREHOUSE:onafterRequestSpawned(From, Event, To, Request, CargoGroupSet
|
||||
------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Prepare cargo groups for transport
|
||||
------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: set asset.weight for cargos
|
||||
|
||||
-- Board radius, i.e. when the cargo will begin to board the carrier
|
||||
local _boardradius=500
|
||||
-------------------------
|
||||
-- Create OPSTRANSPORT --
|
||||
-------------------------
|
||||
|
||||
if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE then
|
||||
_boardradius=5000
|
||||
elseif Request.transporttype==WAREHOUSE.TransportType.HELICOPTER then
|
||||
--_loadradius=1000
|
||||
--_boardradius=nil
|
||||
elseif Request.transporttype==WAREHOUSE.TransportType.APC then
|
||||
--_boardradius=nil
|
||||
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
|
||||
|
||||
-- Empty cargo group set.
|
||||
local CargoGroups=SET_CARGO:New()
|
||||
|
||||
-- Add cargo groups to set.
|
||||
for _,_group in pairs(CargoGroupSet:GetSetObjects()) do
|
||||
|
||||
-- Find asset belonging to this group.
|
||||
local asset=self:FindAssetInDB(_group)
|
||||
-- New cargo group object.
|
||||
local cargogroup=CARGO_GROUP:New(_group, _cargotype,_group:GetName(),_boardradius, asset.loadradius)
|
||||
|
||||
-- Set weight for this group.
|
||||
cargogroup:SetWeight(asset.weight)
|
||||
|
||||
-- Add group to group set.
|
||||
CargoGroups:AddCargo(cargogroup)
|
||||
|
||||
end
|
||||
|
||||
------------------------
|
||||
-- Create Dispatchers --
|
||||
------------------------
|
||||
|
||||
-- Cargo dispatcher.
|
||||
local CargoTransport --AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER
|
||||
-- OPSTRANSPORT
|
||||
local CargoTransport --Ops.OpsTransport#OPSTRANSPORT
|
||||
|
||||
if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE then
|
||||
|
||||
-- Pickup and deploy zones.
|
||||
local PickupAirbaseSet = SET_ZONE:New():AddZone(ZONE_AIRBASE:New(self.airbase:GetName()))
|
||||
local DeployAirbaseSet = SET_ZONE:New():AddZone(ZONE_AIRBASE:New(Request.airbase:GetName()))
|
||||
|
||||
-- Define dispatcher for this task.
|
||||
CargoTransport = AI_CARGO_DISPATCHER_AIRPLANE:New(TransportGroupSet, CargoGroups, PickupAirbaseSet, DeployAirbaseSet)
|
||||
|
||||
-- Set home zone.
|
||||
CargoTransport:SetHomeZone(ZONE_AIRBASE:New(self.airbase:GetName()))
|
||||
CargoTransport = OPSTRANSPORT:New(CargoGroupSet, ZONE_AIRBASE:New(self.airbase:GetName()), ZONE_AIRBASE:New(Request.airbase:GetName()))
|
||||
CargoTransport:SetEmbarkZone(self.spawnzone)
|
||||
CargoTransport:SetDisembarkZone(Request.warehouse.spawnzone)
|
||||
|
||||
elseif Request.transporttype==WAREHOUSE.TransportType.HELICOPTER then
|
||||
|
||||
-- Pickup and deploy zones.
|
||||
local PickupZoneSet = SET_ZONE:New():AddZone(self.spawnzone)
|
||||
local DeployZoneSet = SET_ZONE:New():AddZone(Request.warehouse.spawnzone)
|
||||
|
||||
-- Define dispatcher for this task.
|
||||
CargoTransport = AI_CARGO_DISPATCHER_HELICOPTER:New(TransportGroupSet, CargoGroups, PickupZoneSet, DeployZoneSet)
|
||||
|
||||
-- Home zone.
|
||||
CargoTransport:SetHomeZone(self.spawnzone)
|
||||
|
||||
CargoTransport = OPSTRANSPORT:New(CargoGroupSet, self.spawnzone, Request.warehouse.spawnzone)
|
||||
|
||||
elseif Request.transporttype==WAREHOUSE.TransportType.APC then
|
||||
|
||||
-- Pickup and deploy zones.
|
||||
local PickupZoneSet = SET_ZONE:New():AddZone(self.spawnzone)
|
||||
local DeployZoneSet = SET_ZONE:New():AddZone(Request.warehouse.spawnzone)
|
||||
|
||||
-- Define dispatcher for this task.
|
||||
CargoTransport = AI_CARGO_DISPATCHER_APC:New(TransportGroupSet, CargoGroups, PickupZoneSet, DeployZoneSet, 0)
|
||||
|
||||
-- Set home zone.
|
||||
CargoTransport:SetHomeZone(self.spawnzone)
|
||||
CargoTransport = OPSTRANSPORT:New(CargoGroupSet, self.spawnzone, Request.warehouse.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
|
||||
|
||||
-- Pickup and deploy zones.
|
||||
local PickupZoneSet = SET_ZONE:New():AddZone(self.portzone)
|
||||
PickupZoneSet:AddZone(self.harborzone)
|
||||
local DeployZoneSet = SET_ZONE:New():AddZone(Request.warehouse.harborzone)
|
||||
|
||||
or Request.transporttype==WAREHOUSE.TransportType.ARMEDSHIP or Request.transporttype==WAREHOUSE.TransportType.WARSHIP then
|
||||
|
||||
CargoTransport = OPSTRANSPORT:New(CargoGroupSet, self.portzone, Request.warehouse.portzone)
|
||||
CargoTransport:SetEmbarkZone(self.spawnzone)
|
||||
CargoTransport:SetDisembarkZone(Request.warehouse.spawnzone)
|
||||
|
||||
-- Get the shipping lane to use and pass it to the Dispatcher
|
||||
local remotename = Request.warehouse.warehouse:GetName()
|
||||
local ShippingLane = self.shippinglanes[remotename][math.random(#self.shippinglanes[remotename])]
|
||||
|
||||
-- Define dispatcher for this task.
|
||||
CargoTransport = AI_CARGO_DISPATCHER_SHIP:New(TransportGroupSet, CargoGroups, PickupZoneSet, DeployZoneSet, ShippingLane)
|
||||
|
||||
-- Set home zone
|
||||
CargoTransport:SetHomeZone(self.portzone)
|
||||
|
||||
-- TODO: Add shipping lane
|
||||
-- CargoTransport:AddPathTransport(PathGroup)
|
||||
|
||||
else
|
||||
self:E(self.lid.."ERROR: Unknown transporttype!")
|
||||
end
|
||||
|
||||
-- Set pickup and deploy radii.
|
||||
-- The 20 m inner radius are to ensure that the helo does not land on the warehouse itself in the middle of the default spawn zone.
|
||||
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)
|
||||
|
||||
|
||||
-- Adjust carrier units. This has to come AFTER the dispatchers have been defined because they set the cargobay free weight!
|
||||
Request.carriercargo={}
|
||||
@@ -4839,111 +4743,63 @@ function WAREHOUSE:onafterRequestSpawned(From, Event, To, Request, CargoGroupSet
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------
|
||||
-- Dispatcher Event Functions --
|
||||
--------------------------------
|
||||
----------------------------------
|
||||
-- Opstransport Event Functions --
|
||||
----------------------------------
|
||||
|
||||
--- Function called after carrier picked up something.
|
||||
function CargoTransport:OnAfterPickedUp(From, Event, To, Carrier, PickupZone)
|
||||
|
||||
-- Get warehouse state.
|
||||
local warehouse=Carrier:GetState(Carrier, "WAREHOUSE") --#WAREHOUSE
|
||||
|
||||
-- Debug message.
|
||||
local text=string.format("Carrier group %s picked up at pickup zone %s.", Carrier:GetName(), PickupZone:GetName())
|
||||
warehouse:T(warehouse.lid..text)
|
||||
|
||||
end
|
||||
|
||||
--- Function called if something was deployed.
|
||||
function CargoTransport:OnAfterDeployed(From, Event, To, Carrier, DeployZone)
|
||||
|
||||
-- Get warehouse state.
|
||||
local warehouse=Carrier:GetState(Carrier, "WAREHOUSE") --#WAREHOUSE
|
||||
|
||||
-- Debug message.
|
||||
-- TODO: Depoloy zone is nil!
|
||||
--local text=string.format("Carrier group %s deployed at deploy zone %s.", Carrier:GetName(), DeployZone:GetName())
|
||||
--warehouse:T(warehouse.lid..text)
|
||||
|
||||
end
|
||||
|
||||
--- Function called if carrier group is going home.
|
||||
function CargoTransport:OnAfterHome(From, Event, To, Carrier, Coordinate, Speed, Height, HomeZone)
|
||||
|
||||
-- Get warehouse state.
|
||||
local warehouse=Carrier:GetState(Carrier, "WAREHOUSE") --#WAREHOUSE
|
||||
|
||||
-- Debug message.
|
||||
local text=string.format("Carrier group %s going home to zone %s.", Carrier:GetName(), HomeZone:GetName())
|
||||
warehouse:T(warehouse.lid..text)
|
||||
|
||||
end
|
||||
CargoTransport.warehouse = self
|
||||
|
||||
--- Function called when a carrier unit has loaded a cargo group.
|
||||
function CargoTransport:OnAfterLoaded(From, Event, To, Carrier, Cargo, CarrierUnit, PickupZone)
|
||||
function CargoTransport:OnAfterLoaded(From, Event, To, OpsGroupCargo, OpsGroupCarrier, CarrierElement)
|
||||
|
||||
-- Get warehouse state.
|
||||
local warehouse=Carrier:GetState(Carrier, "WAREHOUSE") --#WAREHOUSE
|
||||
|
||||
-- Debug message.
|
||||
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 warehouse=CargoTransport.warehouse --#WAREHOUSE
|
||||
|
||||
-- Get cargo group object.
|
||||
local group=Cargo:GetObject() --Wrapper.Group#GROUP
|
||||
local group=OpsGroupCargo:GetGroup() --Cargo:GetObject() --Wrapper.Group#GROUP
|
||||
|
||||
-- Get request.
|
||||
local request=warehouse:_GetRequestOfGroup(group, warehouse.pending)
|
||||
|
||||
-- Add cargo group to this carrier.
|
||||
table.insert(request.carriercargo[CarrierUnit:GetName()], warehouse:_GetNameWithOut(Cargo:GetName()))
|
||||
table.insert(request.carriercargo[CarrierElement.name], warehouse:_GetNameWithOut(group:GetName()))
|
||||
|
||||
end
|
||||
|
||||
--- Function called when cargo has arrived and was unloaded.
|
||||
function CargoTransport:OnAfterUnloaded(From, Event, To, Carrier, Cargo, CarrierUnit, DeployZone)
|
||||
function CargoTransport:OnAfterUnloaded(From, Event, To, OpsGroupCargo, OpsGroupCarrier)
|
||||
|
||||
-- Get warehouse state.
|
||||
local warehouse=Carrier:GetState(Carrier, "WAREHOUSE") --#WAREHOUSE
|
||||
local warehouse=CargoTransport.warehouse --Carrier:GetState(Carrier, "WAREHOUSE") --#WAREHOUSE
|
||||
|
||||
-- Get group obejet.
|
||||
local group=Cargo:GetObject() --Wrapper.Group#GROUP
|
||||
local group=OpsGroupCargo:GetGroup() --Cargo:GetObject() --Wrapper.Group#GROUP
|
||||
|
||||
-- Debug message.
|
||||
local text=string.format("Cargo group %s was unloaded from carrier unit %s.", tostring(group:GetName()), tostring(CarrierUnit:GetName()))
|
||||
local text=string.format("Cargo group %s was unloaded from carrier group %s.", tostring(group:GetName()), tostring(OpsGroupCarrier:GetName()))
|
||||
warehouse:T(warehouse.lid..text)
|
||||
|
||||
-- Load the cargo in the warehouse.
|
||||
--Cargo:Load(warehouse.warehouse)
|
||||
|
||||
-- Trigger Arrived event.
|
||||
warehouse:Arrived(group)
|
||||
end
|
||||
|
||||
-- TODO: Probably can also add some cargo/carrier dead functions here to simplify things at other places
|
||||
|
||||
--- On after BackHome event.
|
||||
function CargoTransport:OnAfterBackHome(From, Event, To, Carrier)
|
||||
|
||||
-- Intellisense.
|
||||
local carrier=Carrier --Wrapper.Group#GROUP
|
||||
|
||||
-- Get warehouse state.
|
||||
local warehouse=carrier:GetState(carrier, "WAREHOUSE") --#WAREHOUSE
|
||||
carrier:SmokeWhite()
|
||||
|
||||
-- Debug info.
|
||||
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)
|
||||
|
||||
-- Call arrived event for carrier.
|
||||
warehouse:__Arrived(1, Carrier)
|
||||
|
||||
-- Assign cargo to carriers
|
||||
for _,carriergroup in pairs(TransportGroupSet:GetSetObjects()) do
|
||||
local opsgroup=nil
|
||||
if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE or Request.transporttype==WAREHOUSE.TransportType.HELICOPTER then
|
||||
opsgroup=FLIGHTGROUP:New(carriergroup)
|
||||
elseif Request.transporttype==WAREHOUSE.TransportType.APC then
|
||||
opsgroup=ARMYGROUP:New(carriergroup)
|
||||
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
|
||||
opsgroup=NAVYGROUP:New(carriergroup)
|
||||
end
|
||||
opsgroup:AddOpsTransport(CargoTransport)
|
||||
end
|
||||
|
||||
-- Start dispatcher.
|
||||
CargoTransport:__Start(5)
|
||||
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -5965,6 +5821,10 @@ function WAREHOUSE:_SpawnAssetGroundNaval(alias, asset, request, spawnzone, late
|
||||
template.y = coord.z
|
||||
template.alt = coord.y
|
||||
|
||||
if self.ValidateAndRepositionGroundUnits then
|
||||
UTILS.ValidateAndRepositionGroundUnits(template.units)
|
||||
end
|
||||
|
||||
-- Spawn group.
|
||||
local group=_DATABASE:Spawn(template) --Wrapper.Group#GROUP
|
||||
|
||||
@@ -6120,6 +5980,7 @@ function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrol
|
||||
unit.onboard_num=asset.modex[i]
|
||||
end
|
||||
if asset.callsign then
|
||||
--UTILS.PrintTableToLog(asset.callsign)
|
||||
unit.callsign=asset.callsign[i]
|
||||
end
|
||||
|
||||
@@ -6139,9 +6000,6 @@ function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrol
|
||||
-- Uncontrolled spawning.
|
||||
template.uncontrolled=uncontrolled
|
||||
|
||||
-- Debug info.
|
||||
self:T2({airtemplate=template})
|
||||
|
||||
-- Spawn group.
|
||||
local group=_DATABASE:Spawn(template) --Wrapper.Group#GROUP
|
||||
|
||||
@@ -6368,7 +6226,7 @@ end
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param Wrapper.Group#GROUP Group The train group.
|
||||
-- @param Core.Point#COORDINATE Coordinate of the destination. Tail will be routed to the closest point
|
||||
-- @param #number Speed Speed in km/h to drive to the destination coordinate. Default is 60% of max possible speed the unit can go.
|
||||
-- @param #number Speed (Optional) Speed in km/h to drive to the destination coordinate. Default is 60% of max possible speed the unit can go.
|
||||
function WAREHOUSE:_RouteTrain(Group, Coordinate, Speed)
|
||||
|
||||
if Group and Group:IsAlive() then
|
||||
@@ -6893,7 +6751,7 @@ function WAREHOUSE:_CheckConquered()
|
||||
for _,_unit in pairs(units) do
|
||||
local unit=_unit --Wrapper.Unit#UNIT
|
||||
|
||||
local distance=coord:Get2DDistance(unit:GetCoordinate())
|
||||
local distance=coord:Get2DDistance(unit:GetCoord())
|
||||
|
||||
-- Filter only alive groud units. Also check distance again, because the scan routine might give some larger distances.
|
||||
if unit:IsGround() and unit:IsAlive() and distance <= radius then
|
||||
@@ -8587,6 +8445,8 @@ function WAREHOUSE:_DeleteStockItem(stockitem)
|
||||
local item=self.stock[i] --#WAREHOUSE.Assetitem
|
||||
if item.uid==stockitem.uid then
|
||||
table.remove(self.stock,i)
|
||||
-- remove also from warehouse DB (not good! causes an error if the asset needs to be added to a requesting wherehouse)
|
||||
--_WAREHOUSEDB.Assets[stockitem.uid]=nil
|
||||
break
|
||||
end
|
||||
end
|
||||
@@ -8923,7 +8783,7 @@ end
|
||||
--- Info Message. Message send to coalition if reports or debug mode activated (and duration > 0). Text self:I(text) added to DCS.log file.
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param #string text The text of the error message.
|
||||
-- @param #number duration Message display duration in seconds. Default 20 sec. If duration is zero, no message is displayed.
|
||||
-- @param #number duration (Optional) Message display duration in seconds. Default 20 sec. If duration is zero, no message is displayed.
|
||||
function WAREHOUSE:_InfoMessage(text, duration)
|
||||
duration=duration or 20
|
||||
if duration>0 and self.Debug or self.Report then
|
||||
@@ -8936,7 +8796,7 @@ end
|
||||
--- Debug message. Message send to all if debug mode is activated (and duration > 0). Text self:T(text) added to DCS.log file.
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param #string text The text of the error message.
|
||||
-- @param #number duration Message display duration in seconds. Default 20 sec. If duration is zero, no message is displayed.
|
||||
-- @param #number duration (Optional) Message display duration in seconds. Default 20 sec. If duration is zero, no message is displayed.
|
||||
function WAREHOUSE:_DebugMessage(text, duration)
|
||||
duration=duration or 20
|
||||
if self.Debug and duration>0 then
|
||||
@@ -8948,7 +8808,7 @@ end
|
||||
--- Error message. Message send to all (if duration > 0). Text self:E(text) added to DCS.log file.
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param #string text The text of the error message.
|
||||
-- @param #number duration Message display duration in seconds. Default 20 sec. If duration is zero, no message is displayed.
|
||||
-- @param #number duration (Optional) Message display duration in seconds. Default 20 sec. If duration is zero, no message is displayed.
|
||||
function WAREHOUSE:_ErrorMessage(text, duration)
|
||||
duration=duration or 20
|
||||
if duration>0 then
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
|
||||
do -- ZONE_CAPTURE_COALITION
|
||||
|
||||
-- @type ZONE_CAPTURE_COALITION
|
||||
--- @type ZONE_CAPTURE_COALITION
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #number MarkBlue ID of blue F10 mark.
|
||||
-- @field #number MarkRed ID of red F10 mark.
|
||||
@@ -220,36 +220,6 @@ do -- ZONE_CAPTURE_COALITION
|
||||
-- A capture zone has been setup that guards the presence of the troops.
|
||||
-- Troops are guarded by red forces. Blue is required to destroy the red forces and capture the zones.
|
||||
--
|
||||
-- At first, we setup the Command Centers
|
||||
--
|
||||
-- do
|
||||
--
|
||||
-- RU_CC = COMMANDCENTER:New( GROUP:FindByName( "REDHQ" ), "Russia HQ" )
|
||||
-- US_CC = COMMANDCENTER:New( GROUP:FindByName( "BLUEHQ" ), "USA HQ" )
|
||||
--
|
||||
-- end
|
||||
--
|
||||
-- Next, we define the mission, and add some scoring to it.
|
||||
--
|
||||
-- do -- Missions
|
||||
--
|
||||
-- US_Mission_EchoBay = MISSION:New( US_CC, "Echo Bay", "Primary",
|
||||
-- "Welcome trainee. The airport Groom Lake in Echo Bay needs to be captured.\n" ..
|
||||
-- "There are five random capture zones located at the airbase.\n" ..
|
||||
-- "Move to one of the capture zones, destroy the fuel tanks in the capture zone, " ..
|
||||
-- "and occupy each capture zone with a platoon.\n " ..
|
||||
-- "Your orders are to hold position until all capture zones are taken.\n" ..
|
||||
-- "Use the map (F10) for a clear indication of the location of each capture zone.\n" ..
|
||||
-- "Note that heavy resistance can be expected at the airbase!\n" ..
|
||||
-- "Mission 'Echo Bay' is complete when all five capture zones are taken, and held for at least 5 minutes!"
|
||||
-- , coalition.side.RED )
|
||||
--
|
||||
-- US_Mission_EchoBay:Start()
|
||||
--
|
||||
-- end
|
||||
--
|
||||
--
|
||||
-- Now the real work starts.
|
||||
-- We define a **CaptureZone** object, which is a ZONE object.
|
||||
-- Within the mission, a trigger zone is created with the name __CaptureZone__, with the defined radius within the mission editor.
|
||||
--
|
||||
@@ -280,12 +250,12 @@ do -- ZONE_CAPTURE_COALITION
|
||||
-- self:E( { Coalition = Coalition } )
|
||||
-- if Coalition == coalition.side.BLUE then
|
||||
-- ZoneCaptureCoalition:Smoke( SMOKECOLOR.Blue )
|
||||
-- US_CC:MessageTypeToCoalition( string.format( "%s is under protection of the USA", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information )
|
||||
-- RU_CC:MessageTypeToCoalition( string.format( "%s is under protection of the USA", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information )
|
||||
-- MESSAGE:New(string.format( "%s is under protection of the USA", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.BLUE)
|
||||
-- MESSAGE:New(string.format( "%s is under protection of the USA", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.RED)
|
||||
-- else
|
||||
-- ZoneCaptureCoalition:Smoke( SMOKECOLOR.Red )
|
||||
-- RU_CC:MessageTypeToCoalition( string.format( "%s is under protection of Russia", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information )
|
||||
-- US_CC:MessageTypeToCoalition( string.format( "%s is under protection of Russia", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information )
|
||||
-- MESSAGE:New(string.format( "%s is under protection of Russia", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.BLUE)
|
||||
-- MESSAGE:New(string.format( "%s is under protection of Russia", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.RED)
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
@@ -297,8 +267,8 @@ do -- ZONE_CAPTURE_COALITION
|
||||
-- -- @param Functional.Protect#ZONE_CAPTURE_COALITION self
|
||||
-- function ZoneCaptureCoalition:OnEnterEmpty()
|
||||
-- self:Smoke( SMOKECOLOR.Green )
|
||||
-- US_CC:MessageTypeToCoalition( string.format( "%s is unprotected, and can be captured!", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information )
|
||||
-- RU_CC:MessageTypeToCoalition( string.format( "%s is unprotected, and can be captured!", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information )
|
||||
-- MESSAGE:New(string.format( "%s is unprotected, and can be captured!", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.BLUE)
|
||||
-- MESSAGE:New(string.format( "%s is unprotected, and can be captured!", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.RED)
|
||||
-- end
|
||||
--
|
||||
-- The next Event Handlers speak for itself.
|
||||
@@ -310,11 +280,11 @@ do -- ZONE_CAPTURE_COALITION
|
||||
-- local Coalition = self:GetCoalition()
|
||||
-- self:E({Coalition = Coalition})
|
||||
-- if Coalition == coalition.side.BLUE then
|
||||
-- US_CC:MessageTypeToCoalition( string.format( "%s is under attack by Russia", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information )
|
||||
-- RU_CC:MessageTypeToCoalition( string.format( "We are attacking %s", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information )
|
||||
-- MESSAGE:New(string.format( "%s is under attack by Russia", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.BLUE)
|
||||
-- MESSAGE:New(string.format( "We are attacking %s", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.RED)
|
||||
-- else
|
||||
-- RU_CC:MessageTypeToCoalition( string.format( "%s is under attack by the USA", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information )
|
||||
-- US_CC:MessageTypeToCoalition( string.format( "We are attacking %s", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information )
|
||||
-- MESSAGE:New(string.format( "%s is under attack by the USA", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.RED)
|
||||
-- MESSAGE:New(string.format( "We are attacking %s", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.BLUE)
|
||||
-- end
|
||||
-- end
|
||||
--
|
||||
@@ -326,12 +296,12 @@ do -- ZONE_CAPTURE_COALITION
|
||||
-- local Coalition = self:GetCoalition()
|
||||
-- self:E({Coalition = Coalition})
|
||||
-- if Coalition == coalition.side.BLUE then
|
||||
-- RU_CC:MessageTypeToCoalition( string.format( "%s is captured by the USA, we lost it!", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information )
|
||||
-- US_CC:MessageTypeToCoalition( string.format( "We captured %s, Excellent job!", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information )
|
||||
-- MESSAGE:New(string.format( "%s is captured by the USA, we lost it!", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.RED)
|
||||
-- MESSAGE:New(string.format( "We captured %s, Excellent job!", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.BLUE)
|
||||
-- else
|
||||
-- US_CC:MessageTypeToCoalition( string.format( "%s is captured by Russia, we lost it!", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information )
|
||||
-- RU_CC:MessageTypeToCoalition( string.format( "We captured %s, Excellent job!", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information )
|
||||
-- end
|
||||
-- MESSAGE:New(string.format( "%s is captured by Russia, we lost it!", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.BLUE)
|
||||
-- MESSAGE:New(string.format( "We captured %s, Excellent job!", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.RED)
|
||||
-- end
|
||||
--
|
||||
-- self:__Guard( 30 )
|
||||
-- end
|
||||
@@ -365,8 +335,8 @@ do -- ZONE_CAPTURE_COALITION
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param Core.Zone#ZONE Zone A @{Core.Zone} object with the goal to be achieved. Alternatively, can be handed as the name of late activated group describing a @{Core.Zone#ZONE_POLYGON} with its waypoints.
|
||||
-- @param #number Coalition The initial coalition owning the zone.
|
||||
-- @param #table UnitCategories Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}.
|
||||
-- @param #table ObjectCategories Table of unit categories. See [DCS Class Object](https://wiki.hoggitworld.com/view/DCS_Class_Object). Default {Object.Category.UNIT, Object.Category.STATIC}, i.e. all UNITS and STATICS.
|
||||
-- @param #table UnitCategories (Optional) Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}.
|
||||
-- @param #table ObjectCategories (Optional) Table of unit categories. See [DCS Class Object](https://wiki.hoggitworld.com/view/DCS_Class_Object). Default {Object.Category.UNIT, Object.Category.STATIC}, i.e. all UNITS and STATICS.
|
||||
-- @return #ZONE_CAPTURE_COALITION
|
||||
-- @usage
|
||||
--
|
||||
|
||||
@@ -1,458 +0,0 @@
|
||||
--- **Functional** - Base class that models processes to achieve goals involving a Zone and Cargo.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ZONE_GOAL_CARGO models processes that have a Goal with a defined achievement involving a Zone and Cargo.
|
||||
-- Derived classes implement the ways how the achievements can be realized.
|
||||
--
|
||||
-- # Developer Note
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE
|
||||
-- Therefore, this class is considered to be deprecated
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Functional.ZoneGoalCargo
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
do -- ZoneGoal
|
||||
|
||||
-- @type ZONE_GOAL_CARGO
|
||||
-- @extends Functional.ZoneGoal#ZONE_GOAL
|
||||
|
||||
|
||||
--- Models processes that have a Goal with a defined achievement involving a Zone and Cargo.
|
||||
-- Derived classes implement the ways how the achievements can be realized.
|
||||
--
|
||||
-- ## 1. ZONE_GOAL_CARGO constructor
|
||||
--
|
||||
-- * @{#ZONE_GOAL_CARGO.New}(): Creates a new ZONE_GOAL_CARGO object.
|
||||
--
|
||||
-- ## 2. ZONE_GOAL_CARGO is a finite state machine (FSM).
|
||||
--
|
||||
-- ### 2.1 ZONE_GOAL_CARGO States
|
||||
--
|
||||
-- * **Deployed**: The Zone has been captured by an other coalition.
|
||||
-- * **Airborne**: The Zone is currently intruded by an other coalition. There are units of the owning coalition and an other coalition in the Zone.
|
||||
-- * **Loaded**: The Zone is guarded by the owning coalition. There is no other unit of an other coalition in the Zone.
|
||||
-- * **Empty**: The Zone is empty. There is not valid unit in the Zone.
|
||||
--
|
||||
-- ### 2.2 ZONE_GOAL_CARGO Events
|
||||
--
|
||||
-- * **Capture**: The Zone has been captured by an other coalition.
|
||||
-- * **Attack**: The Zone is currently intruded by an other coalition. There are units of the owning coalition and an other coalition in the Zone.
|
||||
-- * **Guard**: The Zone is guarded by the owning coalition. There is no other unit of an other coalition in the Zone.
|
||||
-- * **Empty**: The Zone is empty. There is not valid unit in the Zone.
|
||||
--
|
||||
-- ### 2.3 ZONE_GOAL_CARGO State Machine
|
||||
--
|
||||
-- @field #ZONE_GOAL_CARGO
|
||||
ZONE_GOAL_CARGO = {
|
||||
ClassName = "ZONE_GOAL_CARGO",
|
||||
}
|
||||
|
||||
-- @field #table ZONE_GOAL_CARGO.States
|
||||
ZONE_GOAL_CARGO.States = {}
|
||||
|
||||
--- ZONE_GOAL_CARGO Constructor.
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param Core.Zone#ZONE Zone A @{Core.Zone} object with the goal to be achieved.
|
||||
-- @param #number Coalition The initial coalition owning the zone.
|
||||
-- @return #ZONE_GOAL_CARGO
|
||||
function ZONE_GOAL_CARGO:New( Zone, Coalition )
|
||||
|
||||
local self = BASE:Inherit( self, ZONE_GOAL:New( Zone ) ) -- #ZONE_GOAL_CARGO
|
||||
self:F( { Zone = Zone, Coalition = Coalition } )
|
||||
|
||||
self:SetCoalition( Coalition )
|
||||
|
||||
do
|
||||
|
||||
--- Captured State Handler OnLeave for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnLeaveCaptured
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Captured State Handler OnEnter for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnEnterCaptured
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
|
||||
--- Attacked State Handler OnLeave for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnLeaveAttacked
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Attacked State Handler OnEnter for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnEnterAttacked
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
end
|
||||
|
||||
do
|
||||
|
||||
--- Guarded State Handler OnLeave for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnLeaveGuarded
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Guarded State Handler OnEnter for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnEnterGuarded
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
|
||||
--- Empty State Handler OnLeave for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnLeaveEmpty
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Empty State Handler OnEnter for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnEnterEmpty
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
end
|
||||
|
||||
self:AddTransition( "*", "Guard", "Guarded" )
|
||||
|
||||
--- Guard Handler OnBefore for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnBeforeGuard
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Guard Handler OnAfter for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnAfterGuard
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- Guard Trigger for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] Guard
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
|
||||
--- Guard Asynchronous Trigger for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] __Guard
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #number Delay
|
||||
|
||||
self:AddTransition( "*", "Empty", "Empty" )
|
||||
|
||||
--- Empty Handler OnBefore for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnBeforeEmpty
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Empty Handler OnAfter for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnAfterEmpty
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- Empty Trigger for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] Empty
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
|
||||
--- Empty Asynchronous Trigger for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] __Empty
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #number Delay
|
||||
|
||||
|
||||
self:AddTransition( { "Guarded", "Empty" }, "Attack", "Attacked" )
|
||||
|
||||
--- Attack Handler OnBefore for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnBeforeAttack
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Attack Handler OnAfter for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnAfterAttack
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- Attack Trigger for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] Attack
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
|
||||
--- Attack Asynchronous Trigger for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] __Attack
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #number Delay
|
||||
|
||||
self:AddTransition( { "Guarded", "Attacked", "Empty" }, "Capture", "Captured" )
|
||||
|
||||
--- Capture Handler OnBefore for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnBeforeCapture
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Capture Handler OnAfter for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnAfterCapture
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- Capture Trigger for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] Capture
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
|
||||
--- Capture Asynchronous Trigger for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] __Capture
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #number Delay
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set the owning coalition of the zone.
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #number Coalition
|
||||
function ZONE_GOAL_CARGO:SetCoalition( Coalition )
|
||||
self.Coalition = Coalition
|
||||
end
|
||||
|
||||
|
||||
--- Get the owning coalition of the zone.
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @return #number Coalition.
|
||||
function ZONE_GOAL_CARGO:GetCoalition()
|
||||
return self.Coalition
|
||||
end
|
||||
|
||||
|
||||
--- Get the owning coalition name of the zone.
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @return #string Coalition name.
|
||||
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
|
||||
|
||||
|
||||
|
||||
--- Mark.
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
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
|
||||
|
||||
--- Bound.
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
function ZONE_GOAL_CARGO:onenterGuarded()
|
||||
|
||||
--self:GetParent( self ):onenterGuarded()
|
||||
|
||||
if self.Coalition == coalition.side.BLUE then
|
||||
--elf.ProtectZone:BoundZone( 12, country.id.USA )
|
||||
else
|
||||
--self.ProtectZone:BoundZone( 12, country.id.RUSSIA )
|
||||
end
|
||||
|
||||
self:Mark()
|
||||
|
||||
end
|
||||
|
||||
function ZONE_GOAL_CARGO:onenterCaptured()
|
||||
|
||||
--self:GetParent( self ):onenterCaptured()
|
||||
|
||||
local NewCoalition = self.Zone:GetCoalition()
|
||||
self:F( { NewCoalition = NewCoalition } )
|
||||
self:SetCoalition( NewCoalition )
|
||||
|
||||
self:Mark()
|
||||
end
|
||||
|
||||
|
||||
function ZONE_GOAL_CARGO:onenterEmpty()
|
||||
|
||||
--self:GetParent( self ):onenterEmpty()
|
||||
|
||||
self:Mark()
|
||||
end
|
||||
|
||||
|
||||
function ZONE_GOAL_CARGO:onenterAttacked()
|
||||
|
||||
--self:GetParent( self ):onenterAttacked()
|
||||
|
||||
self:Mark()
|
||||
end
|
||||
|
||||
|
||||
--- When started, check the Coalition status.
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
function ZONE_GOAL_CARGO:onafterGuard()
|
||||
|
||||
--self:F({BASE:GetParent( self )})
|
||||
--BASE:GetParent( self ).onafterGuard( self )
|
||||
|
||||
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
|
||||
|
||||
--- Check status Coalition ownership.
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
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
|
||||
|
||||
@@ -54,8 +54,8 @@ do -- ZoneGoal
|
||||
--- ZONE_GOAL_COALITION Constructor.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @param Core.Zone#ZONE Zone A @{Core.Zone} object with the goal to be achieved.
|
||||
-- @param #number Coalition The initial coalition owning the zone. Default coalition.side.NEUTRAL.
|
||||
-- @param #table UnitCategories Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}.
|
||||
-- @param #number Coalition (Optional) The initial coalition owning the zone. Default coalition.side.NEUTRAL.
|
||||
-- @param #table UnitCategories (Optional) Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}.
|
||||
-- @return #ZONE_GOAL_COALITION
|
||||
function ZONE_GOAL_COALITION:New( Zone, Coalition, UnitCategories )
|
||||
|
||||
@@ -90,7 +90,7 @@ do -- ZoneGoal
|
||||
|
||||
--- Set the owning coalition of the zone.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @param #table UnitCategories Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}.
|
||||
-- @param #table UnitCategories (Optional) Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}.
|
||||
-- @return #ZONE_GOAL_COALITION
|
||||
function ZONE_GOAL_COALITION:SetUnitCategories( UnitCategories )
|
||||
|
||||
@@ -105,7 +105,7 @@ do -- ZoneGoal
|
||||
|
||||
--- Set the owning coalition of the zone.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @param #table ObjectCategories Table of unit categories. See [DCS Class Object](https://wiki.hoggitworld.com/view/DCS_Class_Object). Default {Object.Category.UNIT, Object.Category.STATIC}, i.e. all UNITS and STATICS.
|
||||
-- @param #table ObjectCategories (Optional) Table of unit categories. See [DCS Class Object](https://wiki.hoggitworld.com/view/DCS_Class_Object). Default {Object.Category.UNIT, Object.Category.STATIC}, i.e. all UNITS and STATICS.
|
||||
-- @return #ZONE_GOAL_COALITION
|
||||
function ZONE_GOAL_COALITION:SetObjectCategories( ObjectCategories )
|
||||
|
||||
|
||||
@@ -13,13 +13,17 @@ _DATABASE = DATABASE:New() -- Core.Database#DATABASE
|
||||
_SETTINGS = SETTINGS:Set() -- Core.Settings#SETTINGS
|
||||
_SETTINGS:SetPlayerMenuOn()
|
||||
|
||||
--- Register cargos.
|
||||
_DATABASE:_RegisterCargos()
|
||||
|
||||
--- Register zones.
|
||||
_DATABASE:_RegisterZones()
|
||||
_DATABASE:_RegisterAirbases()
|
||||
|
||||
--- Function that writes to DCS log file
|
||||
-- @param #string text Formatted text.
|
||||
-- @param ... Format passed to string.format().
|
||||
function printf(text, ...)
|
||||
env.info(string.format(text, ...))
|
||||
end
|
||||
|
||||
--- Check if os etc is available.
|
||||
BASE:I("Checking de-sanitization of os, io and lfs:")
|
||||
local __na = false
|
||||
|
||||
@@ -32,6 +32,7 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/MarkerOps_Base.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/TextAndSound.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/Pathline.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/ClientMenu.lua')
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/Vector.lua')
|
||||
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Object.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Identifiable.lua' )
|
||||
@@ -49,18 +50,12 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Net.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Storage.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/DynamicCargo.lua' )
|
||||
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Cargo/Cargo.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Cargo/CargoUnit.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Cargo/CargoSlingload.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Cargo/CargoCrate.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Cargo/CargoGroup.lua' )
|
||||
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Scoring.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/CleanUp.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Movement.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Sead.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Escort.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/MissileTrainer.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/ATC_Ground.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Detection.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/DetectionZones.lua' )
|
||||
@@ -80,16 +75,24 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Shorad.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/AICSAR.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/AmmoTruck.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Autolase.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/ZoneGoalCargo.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Tiresias.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Stratego.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/ClientWatch.lua')
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Formation.lua')
|
||||
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/RedGCIKernel.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/RedGCI.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/RedGCI2v2.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/RedGCIDispatcher.lua' )
|
||||
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Airboss.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/RecoveryTanker.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/RescueHelo.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/ATIS.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/CTLD.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/CTLD_CARGO.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/CTLD_Localization.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/CTLD_Hercules.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/CSAR.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/AirWing.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/ArmyGroup.lua' )
|
||||
@@ -116,43 +119,8 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/PlayerRecce.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Squadron.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Target.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/EasyGCICAP.lua' )
|
||||
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Balancer.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Air.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Air_Patrol.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Air_Engage.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_A2A_Patrol.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_A2A_Cap.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_A2A_Gci.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_A2A_Dispatcher.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_A2G_BAI.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_A2G_CAS.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_A2G_SEAD.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_A2G_Dispatcher.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Patrol.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_CAP.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_CAS.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_BAI.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Formation.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Escort.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Escort_Request.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Escort_Dispatcher.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Escort_Dispatcher_Request.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Cargo.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Cargo_APC.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Cargo_Helicopter.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Cargo_Airplane.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Cargo_Ship.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Cargo_Dispatcher.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Cargo_Dispatcher_APC.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Cargo_Dispatcher_Ship.lua' )
|
||||
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Actions/Act_Assign.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Actions/Act_Route.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Actions/Act_Account.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Actions/Act_Assist.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/EasyA2G.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/TARS.lua' )
|
||||
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Shapes/ShapeBase.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Shapes/Circle.lua' )
|
||||
@@ -169,21 +137,9 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Sound/RadioQueue.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Sound/RadioSpeech.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Sound/SRS.lua' )
|
||||
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/CommandCenter.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Mission.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/TaskInfo.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Manager.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/DetectionManager.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_A2G_Dispatcher.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_A2G.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_A2A_Dispatcher.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_A2A.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_CARGO.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Cargo_Transport.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Cargo_CSAR.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Cargo_Dispatcher.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Capture_Zone.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Capture_Dispatcher.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Point.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Beacons.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Radios.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Towns.lua' )
|
||||
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Globals.lua' )
|
||||
|
||||
@@ -1,181 +0,0 @@
|
||||
__Moose.Include( 'Utilities\\Enums.lua' )
|
||||
__Moose.Include( 'Utilities\\Utils.lua' )
|
||||
__Moose.Include( 'Utilities\\Profiler.lua' )
|
||||
--__Moose.Include( 'Utilities\\STTS.lua' )
|
||||
__Moose.Include( 'Utilities\\FiFo.lua' )
|
||||
__Moose.Include( 'Utilities\\Socket.lua' )
|
||||
|
||||
__Moose.Include( 'Core\\Base.lua' )
|
||||
__Moose.Include( 'Core\\Beacon.lua' )
|
||||
__Moose.Include( 'Core\\UserFlag.lua' )
|
||||
__Moose.Include( 'Core\\Report.lua' )
|
||||
__Moose.Include( 'Core\\Scheduler.lua' )
|
||||
__Moose.Include( 'Core\\ScheduleDispatcher.lua' )
|
||||
__Moose.Include( 'Core\\Event.lua' )
|
||||
__Moose.Include( 'Core\\Settings.lua' )
|
||||
__Moose.Include( 'Core\\Menu.lua' )
|
||||
__Moose.Include( 'Core\\Zone.lua' )
|
||||
__Moose.Include( 'Core\\Velocity.lua' )
|
||||
__Moose.Include( 'Core\\Database.lua' )
|
||||
__Moose.Include( 'Core\\Set.lua' )
|
||||
__Moose.Include( 'Core\\Point.lua' )
|
||||
__Moose.Include( 'Core\\Pathline.lua' )
|
||||
__Moose.Include( 'Core\\Message.lua' )
|
||||
__Moose.Include( 'Core\\Fsm.lua' )
|
||||
__Moose.Include( 'Core\\Spawn.lua' )
|
||||
__Moose.Include( 'Core\\SpawnStatic.lua' )
|
||||
__Moose.Include( 'Core\\Timer.lua' )
|
||||
__Moose.Include( 'Core\\Goal.lua' )
|
||||
__Moose.Include( 'Core\\Spot.lua' )
|
||||
__Moose.Include( 'Core\\Astar.lua' )
|
||||
__Moose.Include( 'Core\\MarkerOps_Base.lua' )
|
||||
__Moose.Include( 'Core\\TextAndSound.lua' )
|
||||
__Moose.Include( 'Core\\Condition.lua' )
|
||||
__Moose.Include( 'Core\\ClientMenu.lua' )
|
||||
|
||||
__Moose.Include( 'Wrapper\\Object.lua' )
|
||||
__Moose.Include( 'Wrapper\\Identifiable.lua' )
|
||||
__Moose.Include( 'Wrapper\\Positionable.lua' )
|
||||
__Moose.Include( 'Wrapper\\Controllable.lua' )
|
||||
__Moose.Include( 'Wrapper\\Group.lua' )
|
||||
__Moose.Include( 'Wrapper\\Unit.lua' )
|
||||
__Moose.Include( 'Wrapper\\Client.lua' )
|
||||
__Moose.Include( 'Wrapper\\Static.lua' )
|
||||
__Moose.Include( 'Wrapper\\Airbase.lua' )
|
||||
__Moose.Include( 'Wrapper\\Scenery.lua' )
|
||||
__Moose.Include( 'Wrapper\\Marker.lua' )
|
||||
__Moose.Include( 'Wrapper\\Net.lua' )
|
||||
__Moose.Include( 'Wrapper\\Weapon.lua' )
|
||||
__Moose.Include( 'Wrapper\\Storage.lua' )
|
||||
__Moose.Include( 'Wrapper\\DynamicCargo.lua' )
|
||||
|
||||
__Moose.Include( 'Cargo\\Cargo.lua' )
|
||||
__Moose.Include( 'Cargo\\CargoUnit.lua' )
|
||||
__Moose.Include( 'Cargo\\CargoSlingload.lua' )
|
||||
__Moose.Include( 'Cargo\\CargoCrate.lua' )
|
||||
__Moose.Include( 'Cargo\\CargoGroup.lua' )
|
||||
|
||||
__Moose.Include( 'Functional\\Scoring.lua' )
|
||||
__Moose.Include( 'Functional\\CleanUp.lua' )
|
||||
__Moose.Include( 'Functional\\Movement.lua' )
|
||||
__Moose.Include( 'Functional\\Sead.lua' )
|
||||
__Moose.Include( 'Functional\\Escort.lua' )
|
||||
__Moose.Include( 'Functional\\MissileTrainer.lua' )
|
||||
__Moose.Include( 'Functional\\ATC_Ground.lua' )
|
||||
__Moose.Include( 'Functional\\Detection.lua' )
|
||||
__Moose.Include( 'Functional\\DetectionZones.lua' )
|
||||
__Moose.Include( 'Functional\\Designate.lua' )
|
||||
__Moose.Include( 'Functional\\RAT.lua' )
|
||||
__Moose.Include( 'Functional\\Range.lua' )
|
||||
__Moose.Include( 'Functional\\ZoneGoal.lua' )
|
||||
__Moose.Include( 'Functional\\ZoneGoalCoalition.lua' )
|
||||
__Moose.Include( 'Functional\\ZoneCaptureCoalition.lua' )
|
||||
__Moose.Include( 'Functional\\Artillery.lua' )
|
||||
__Moose.Include( 'Functional\\Suppression.lua' )
|
||||
__Moose.Include( 'Functional\\PseudoATC.lua' )
|
||||
__Moose.Include( 'Functional\\Warehouse.lua' )
|
||||
__Moose.Include( 'Functional\\Fox.lua' )
|
||||
__Moose.Include( 'Functional\\Mantis.lua' )
|
||||
__Moose.Include( 'Functional\\Shorad.lua' )
|
||||
__Moose.Include( 'Functional\\Autolase.lua' )
|
||||
__Moose.Include( 'Functional\\AICSAR.lua' )
|
||||
__Moose.Include( 'Functional\\AmmoTruck.lua' )
|
||||
__Moose.Include( 'Functional\\Tiresias.lua' )
|
||||
__Moose.Include( 'Functional\\Stratego.lua' )
|
||||
__Moose.Include( 'Functional\\ClientWatch.lua' )
|
||||
|
||||
__Moose.Include( 'Ops\\Airboss.lua' )
|
||||
__Moose.Include( 'Ops\\RecoveryTanker.lua' )
|
||||
__Moose.Include( 'Ops\\RescueHelo.lua' )
|
||||
__Moose.Include( 'Ops\\ATIS.lua' )
|
||||
__Moose.Include( 'Ops\\Auftrag.lua' )
|
||||
__Moose.Include( 'Ops\\Target.lua' )
|
||||
__Moose.Include( 'Ops\\OpsGroup.lua' )
|
||||
__Moose.Include( 'Ops\\FlightGroup.lua' )
|
||||
__Moose.Include( 'Ops\\NavyGroup.lua' )
|
||||
__Moose.Include( 'Ops\\ArmyGroup.lua' )
|
||||
__Moose.Include( 'Ops\\Cohort.lua' )
|
||||
__Moose.Include( 'Ops\\Squadron.lua' )
|
||||
__Moose.Include( 'Ops\\Platoon.lua' )
|
||||
__Moose.Include( 'Ops\\Legion.lua' )
|
||||
__Moose.Include( 'Ops\\AirWing.lua' )
|
||||
__Moose.Include( 'Ops\\Brigade.lua' )
|
||||
__Moose.Include( 'Ops\\Intelligence.lua' )
|
||||
__Moose.Include( 'Ops\\Commander.lua' )
|
||||
__Moose.Include( 'Ops\\OpsTransport.lua' )
|
||||
__Moose.Include( 'Ops\\CSAR.lua' )
|
||||
__Moose.Include( 'Ops\\CTLD.lua' )
|
||||
__Moose.Include( 'Ops\\OpsZone.lua' )
|
||||
__Moose.Include( 'Ops\\Chief.lua' )
|
||||
__Moose.Include( 'Ops\\Flotilla.lua' )
|
||||
__Moose.Include( 'Ops\\Fleet.lua' )
|
||||
__Moose.Include( 'Ops\\Awacs.lua' )
|
||||
__Moose.Include( 'Ops\\PlayerTask.lua' )
|
||||
__Moose.Include( 'Ops\\Operation.lua' )
|
||||
__Moose.Include( 'Ops\\FlightControl.lua' )
|
||||
__Moose.Include( 'Ops\\PlayerRecce.lua' )
|
||||
__Moose.Include( 'Ops\\EasyGCICAP.lua' )
|
||||
__Moose.Include( 'Ops\\EasyA2G.lua' )
|
||||
|
||||
__Moose.Include( 'AI\\AI_Balancer.lua' )
|
||||
__Moose.Include( 'AI\\AI_Air.lua' )
|
||||
__Moose.Include( 'AI\\AI_Air_Patrol.lua' )
|
||||
__Moose.Include( 'AI\\AI_Air_Engage.lua' )
|
||||
__Moose.Include( 'AI\\AI_A2A_Patrol.lua' )
|
||||
__Moose.Include( 'AI\\AI_A2A_Cap.lua' )
|
||||
__Moose.Include( 'AI\\AI_A2A_Gci.lua' )
|
||||
__Moose.Include( 'AI\\AI_A2A_Dispatcher.lua' )
|
||||
__Moose.Include( 'AI\\AI_A2G_BAI.lua' )
|
||||
__Moose.Include( 'AI\\AI_A2G_CAS.lua' )
|
||||
__Moose.Include( 'AI\\AI_A2G_SEAD.lua' )
|
||||
__Moose.Include( 'AI\\AI_A2G_Dispatcher.lua' )
|
||||
__Moose.Include( 'AI\\AI_Patrol.lua' )
|
||||
__Moose.Include( 'AI\\AI_Cap.lua' )
|
||||
__Moose.Include( 'AI\\AI_Cas.lua' )
|
||||
__Moose.Include( 'AI\\AI_Bai.lua' )
|
||||
__Moose.Include( 'AI\\AI_Formation.lua' )
|
||||
__Moose.Include( 'AI\\AI_Escort.lua' )
|
||||
__Moose.Include( 'AI\\AI_Escort_Request.lua' )
|
||||
__Moose.Include( 'AI\\AI_Escort_Dispatcher.lua' )
|
||||
__Moose.Include( 'AI\\AI_Escort_Dispatcher_Request.lua' )
|
||||
__Moose.Include( 'AI\\AI_Cargo.lua' )
|
||||
__Moose.Include( 'AI\\AI_Cargo_APC.lua' )
|
||||
__Moose.Include( 'AI\\AI_Cargo_Helicopter.lua' )
|
||||
__Moose.Include( 'AI\\AI_Cargo_Airplane.lua' )
|
||||
__Moose.Include( 'AI\\AI_Cargo_Ship.lua' )
|
||||
__Moose.Include( 'AI\\AI_Cargo_Dispatcher.lua' )
|
||||
__Moose.Include( 'AI\\AI_Cargo_Dispatcher_APC.lua' )
|
||||
__Moose.Include( 'AI\\AI_Cargo_Dispatcher_Helicopter.lua' )
|
||||
__Moose.Include( 'AI\\AI_Cargo_Dispatcher_Airplane.lua' )
|
||||
__Moose.Include( 'AI\\AI_Cargo_Dispatcher_Ship.lua' )
|
||||
|
||||
__Moose.Include( 'Actions\\Act_Assign.lua' )
|
||||
__Moose.Include( 'Actions\\Act_Route.lua' )
|
||||
__Moose.Include( 'Actions\\Act_Account.lua' )
|
||||
__Moose.Include( 'Actions\\Act_Assist.lua' )
|
||||
|
||||
__Moose.Include( 'Sound\\UserSound.lua' )
|
||||
__Moose.Include( 'Sound\\SoundOutput.lua' )
|
||||
__Moose.Include( 'Sound\\Radio.lua' )
|
||||
__Moose.Include( 'Sound\\RadioQueue.lua' )
|
||||
__Moose.Include( 'Sound\\RadioSpeech.lua' )
|
||||
__Moose.Include( 'Sound\\SRS.lua' )
|
||||
|
||||
__Moose.Include( 'Tasking\\CommandCenter.lua' )
|
||||
__Moose.Include( 'Tasking\\Mission.lua' )
|
||||
__Moose.Include( 'Tasking\\Task.lua' )
|
||||
__Moose.Include( 'Tasking\\TaskInfo.lua' )
|
||||
__Moose.Include( 'Tasking\\Task_Manager.lua' )
|
||||
__Moose.Include( 'Tasking\\DetectionManager.lua' )
|
||||
__Moose.Include( 'Tasking\\Task_A2G_Dispatcher.lua' )
|
||||
__Moose.Include( 'Tasking\\Task_A2G.lua' )
|
||||
__Moose.Include( 'Tasking\\Task_A2A_Dispatcher.lua' )
|
||||
__Moose.Include( 'Tasking\\Task_A2A.lua' )
|
||||
__Moose.Include( 'Tasking\\Task_Cargo.lua' )
|
||||
__Moose.Include( 'Tasking\\Task_Cargo_Transport.lua' )
|
||||
__Moose.Include( 'Tasking\\Task_Cargo_CSAR.lua' )
|
||||
__Moose.Include( 'Tasking\\Task_Cargo_Dispatcher.lua' )
|
||||
__Moose.Include( 'Tasking\\Task_Capture_Zone.lua' )
|
||||
__Moose.Include( 'Tasking\\Task_Capture_Dispatcher.lua' )
|
||||
|
||||
__Moose.Include( 'Globals.lua' )
|
||||
@@ -0,0 +1,434 @@
|
||||
--- **NAVIGATION** - Beacons of the map/theatre.
|
||||
--
|
||||
-- **Main Features:**
|
||||
--
|
||||
-- * Access beacons of the map
|
||||
-- * Find closest beacon
|
||||
-- * Get frequencies and channels
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Example Missions:
|
||||
--
|
||||
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Navigation%20-%20Beacons).
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **funkyfranky**
|
||||
--
|
||||
-- ===
|
||||
-- @module Navigation.Beacons
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- BEACONS class.
|
||||
-- @type BEACONS
|
||||
--
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #number verbose Verbosity of output.
|
||||
-- @field #table beacons Beacons.
|
||||
--
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- *Hope is the beacon that guides lost ships back to the shore.*
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # The BEACONS Concept
|
||||
--
|
||||
-- This class is designed to make information about beacons of a map/theatre easier accessible. The information contains location, type and frequencies of all or specific beacons of the map.
|
||||
--
|
||||
-- **Note** that try to avoid hard coding stuff in Moose since DCS is updated frequently and things change. Therefore, the main source of information is either a file `beacons.lua` that can be
|
||||
-- found in the installation directory of DCS for each map or a table that the user needs to provide.
|
||||
-- **Note** your `MissionScripting` environment needs to be desanitized to read this data. `Package` als needs to be available.
|
||||
--
|
||||
-- # Basic Setup
|
||||
--
|
||||
-- A new `BEACONS` object can be created with the @{#BEACONS.NewFromFile}(*beacons_lua_file*) function.
|
||||
--
|
||||
-- local beacons=BEACONS:NewFromFile("<DCS_Install_Directory>\Mods\terrains\<Map_Name>\beacons.lua")
|
||||
-- beacons:MarkerShow()
|
||||
--
|
||||
-- This will load the beacons from the `<DCS_Install_Directory>` for the specific map and place markers on the F10 map. This is the first step you should do to ensure that the file
|
||||
-- you provided is correct and all relevant beacons are present.
|
||||
--
|
||||
-- # User Functions
|
||||
--
|
||||
-- ## F10 Map Markers
|
||||
--
|
||||
-- ## Position
|
||||
--
|
||||
-- ## Get Closest Beacon
|
||||
--
|
||||
--
|
||||
-- @field #BEACONS
|
||||
BEACONS = {
|
||||
ClassName = "BEACONS",
|
||||
verbose = 1,
|
||||
beacons = {},
|
||||
}
|
||||
|
||||
--- Mission capability.
|
||||
-- @type BEACONS.Beacon
|
||||
-- @field #function display_name Function that returns the localized name.
|
||||
-- @field #number type Beacon type.
|
||||
-- @field #string beaconId Beacon ID.
|
||||
-- @field #string callsign Call sign.
|
||||
-- @field #number frequency Frequency in Hz.
|
||||
-- @field #number channel TACAN, RSBN or PRMG channel depending on type.
|
||||
-- @field #table position Position table.
|
||||
-- @field #number direction Direction in degrees.
|
||||
-- @field #table positionGeo Table with latitude and longitude.
|
||||
-- @field #table sceneObjects Table with scenery objects, e.g. `{t:393396742}`.
|
||||
-- @field #number chartOffsetX No idea what this offset is?!
|
||||
-- @field DCS#Vec3 vec3 Position vector 3D.
|
||||
-- @field #number markerID ID for the F10 marker.
|
||||
-- @field #string typeName Name of becon type.
|
||||
-- @field Wrapper.Scenery#SCENERY scenery The scenery object.
|
||||
|
||||
--- BEACONS class version.
|
||||
-- @field #string version
|
||||
BEACONS.version="0.1.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- ToDo list
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: A lot...
|
||||
-- DONE: TACAN channel from frequency (was already in beacon.lua as channel)
|
||||
-- DONE: Scenery object
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Constructor(s)
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Create a new BEACONS class instance from a given table.
|
||||
-- @param #BEACONS self
|
||||
-- @param #table BeaconTable Table with beacon info.
|
||||
-- @return #BEACONS self
|
||||
function BEACONS:NewFromTable(BeaconTable)
|
||||
|
||||
-- Inherit everything from BASE class.
|
||||
self=BASE:Inherit(self, BASE:New()) -- #BEACONS
|
||||
|
||||
for _,_beacon in pairs(BeaconTable) do
|
||||
local beacon=_beacon --#BEACONS.Beacon
|
||||
|
||||
-- Get 3D vector
|
||||
beacon.vec3={x=beacon.position[1], y=beacon.position[2], z=beacon.position[3]}
|
||||
|
||||
-- Get coordinate
|
||||
beacon.coordinate=COORDINATE:NewFromVec3(beacon.vec3)
|
||||
|
||||
-- Get type name
|
||||
beacon.typeName=self:_GetTypeName(beacon.type)
|
||||
|
||||
-- Find closest scenery object from scan
|
||||
beacon.scenery=beacon.coordinate:FindClosestScenery(20)
|
||||
|
||||
-- Debug stuff for scenery object
|
||||
if false then
|
||||
if beacon.scenery then
|
||||
env.info(string.format("FF Beacon %s %s %s got scenery object %s, %s", beacon.callsign, beacon.beaconId, beacon.typeName, beacon.scenery:GetName(), beacon.scenery:GetTypeName() ))
|
||||
UTILS.PrintTableToLog(beacon.scenery.SceneryObject)
|
||||
UTILS.PrintTableToLog(beacon.sceneObjects)
|
||||
else
|
||||
env.info(string.format("FF NO scenery object %s %s %s ", beacon.callsign, beacon.beaconId, beacon.typeName))
|
||||
end
|
||||
end
|
||||
|
||||
-- Add to table
|
||||
table.insert(self.beacons, beacon)
|
||||
end
|
||||
|
||||
-- Debug output
|
||||
self:I(string.format("Added %d beacons", #self.beacons))
|
||||
|
||||
if self.verbose > 0 then
|
||||
local text="Beacon types:"
|
||||
for typeName,typeID in pairs(BEACON.Type) do
|
||||
local n=self:CountBeacons(typeID)
|
||||
text=text..string.format("\n%s = %d", typeName, n)
|
||||
end
|
||||
self:I(text)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Create a new BEACONS class instance from a given file.
|
||||
-- @param #BEACONS self
|
||||
-- @param #string FileName Full path to the file containing the map beacons.
|
||||
-- @return #BEACONS self
|
||||
function BEACONS:NewFromFile(FileName)
|
||||
|
||||
-- Inherit everything from BASE class.
|
||||
self=BASE:Inherit(self, BASE:New()) -- #BEACONS
|
||||
|
||||
local exists=UTILS.FileExists(FileName)
|
||||
|
||||
if exists==false then
|
||||
self:E(string.format("ERROR: file with beacon info does not exist!"))
|
||||
return nil
|
||||
end
|
||||
|
||||
-- This will create a global table `beacons`
|
||||
dofile(FileName)
|
||||
|
||||
-- Get beacons from table.
|
||||
self=self:NewFromTable(beacons)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- User Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Get 3D position vector of a specific beacon.
|
||||
-- @param #BEACONS self
|
||||
-- @param #BEACONS.Beacon beacon The beacon data structure.
|
||||
-- @return DCS#Vec3 Position vector.
|
||||
function BEACONS:GetVec3(beacon)
|
||||
return beacon.vec3
|
||||
end
|
||||
|
||||
--- Get COORDINATE of a specific beacon.
|
||||
-- @param #BEACONS self
|
||||
-- @param #BEACONS.Beacon beacon The beacon data structure.
|
||||
-- @return Core.Point#COORDINATE The coordinate.
|
||||
function BEACONS:GetCoordinate(beacon)
|
||||
local coordinate=COORDINATE:NewFromVec3(beacon.vec3)
|
||||
return coordinate
|
||||
end
|
||||
|
||||
--- Find closest beacon to a given coordinate.
|
||||
-- @param #BEACONS self
|
||||
-- @param Core.Point#COORDINATE Coordinate The reference coordinate.
|
||||
-- @param #number TypeID (Optional) Only search for specific beacon types, *e.g.* `BEACON.Type.TACAN`.
|
||||
-- @param #number DistMax (Optional) Max search distance in meters.
|
||||
-- @param #table ExcludeList (Optional) List of beacons to exclude.
|
||||
-- @return #BEACONS.Beacon The closest beacon.
|
||||
function BEACONS:GetClosestBeacon(Coordinate, TypeID, DistMax, ExcludeList)
|
||||
|
||||
local beacon=nil --#BEACONS.Beacon
|
||||
local distmin=math.huge
|
||||
|
||||
ExcludeList=ExcludeList or {}
|
||||
|
||||
for _,_beacon in pairs(self.beacons) do
|
||||
local bc=_beacon --#BEACONS.Beacon
|
||||
|
||||
if (TypeID==nil or TypeID==bc.type) and (not UTILS.IsInTable(ExcludeList, bc, "beaconId")) then
|
||||
|
||||
local dist=Coordinate:Get2DDistance(bc.vec3)
|
||||
|
||||
if dist<distmin and (DistMax==nil or dist<=DistMax) then
|
||||
distmin=dist
|
||||
beacon=bc
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return beacon
|
||||
end
|
||||
|
||||
--- Find closest beacons to a given coordinate.
|
||||
-- @param #BEACONS self
|
||||
-- @param Core.Point#COORDINATE Coordinate The reference coordinate.
|
||||
-- @param #number Nmax (Optional) Max number of beacons. Default 5.
|
||||
-- @param #number TypeID (Optional) Only search for specific beacon types, *e.g.* `BEACON.Type.TACAN`.
|
||||
-- @param #number DistMax (Optional) Max search distance in meters.
|
||||
-- @return #table Table of #BEACONS.Beacon closest beacons.
|
||||
function BEACONS:GetClosestBeacons(Coordinate, Nmax, TypeID, DistMax)
|
||||
|
||||
Nmax=Nmax or 5
|
||||
|
||||
local closest={}
|
||||
for i=1,Nmax do
|
||||
|
||||
local beacon=self:GetClosestBeacon(Coordinate, TypeID, DistMax, closest)
|
||||
|
||||
if beacon then
|
||||
table.insert(closest, beacon)
|
||||
else
|
||||
break
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return closest
|
||||
end
|
||||
|
||||
--- Get table of all beacons, optionally of a given type.
|
||||
-- @param #BEACONS self
|
||||
-- @param #number TypeID (Optional) Only return specific beacon types, *e.g.* `BEACON.Type.TACAN`. Can be handed in as tanle of beacon types.
|
||||
-- @return #table Table of beacons. Each element is of type #BEACON.Beacon.
|
||||
function BEACONS:GetBeacons(TypeID)
|
||||
|
||||
local beacons={}
|
||||
local keys = {}
|
||||
|
||||
if TypeID~=nil and type(TypeID) ~= "table" then
|
||||
TypeID = {TypeID}
|
||||
end
|
||||
|
||||
for _,_typeid in pairs(TypeID or {}) do
|
||||
if _typeid ~= nil then
|
||||
keys[_typeid] = _typeid
|
||||
end
|
||||
end
|
||||
|
||||
for _,_beacon in pairs(self.beacons) do
|
||||
local bc=_beacon --#BEACONS.Beacon
|
||||
|
||||
if TypeID==nil or keys[bc.type] ~= nil then
|
||||
table.insert(beacons, bc)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return beacons
|
||||
end
|
||||
|
||||
--- Count beacons, optionally of a given type.
|
||||
-- @param #BEACONS self
|
||||
-- @param #number TypeID (Optional) Only count specific beacon types, *e.g.* `BEACON.Type.TACAN`.
|
||||
-- @return #number Number of beacons.
|
||||
function BEACONS:CountBeacons(TypeID)
|
||||
|
||||
local n=0
|
||||
|
||||
if TypeID then
|
||||
for _,_beacon in pairs(self.beacons) do
|
||||
local bc=_beacon --#BEACONS.Beacon
|
||||
|
||||
if TypeID==bc.type then
|
||||
n=n+1
|
||||
end
|
||||
end
|
||||
else
|
||||
n=#self.beacons
|
||||
end
|
||||
|
||||
return n
|
||||
end
|
||||
|
||||
--- Add markers for all beacons on the F10 map. Optionally, only a specific beacon or a certain beacon type can be marked.
|
||||
-- @param #BEACONS self
|
||||
-- @param #BEACONS.Beacon Beacon (Optional) Only this specifc beacon.
|
||||
-- @param #number TypeID (Optional) Only show specific beacon types, *e.g.* `BEACON.Type.TACAN`.
|
||||
-- @return #BEACONS self
|
||||
function BEACONS:MarkerShow(Beacon, TypeID)
|
||||
|
||||
for _,_beacon in pairs(self.beacons) do
|
||||
local beacon=_beacon --#BEACONS.Beacon
|
||||
if Beacon==nil or Beacon.beaconId==beacon.beaconId then
|
||||
if TypeID==nil or beacon.type==TypeID then
|
||||
local text=self:_GetMarkerText(beacon)
|
||||
local coord=COORDINATE:NewFromVec3(beacon.vec3)
|
||||
if beacon.markerID then
|
||||
UTILS.RemoveMark(beacon.markerID)
|
||||
end
|
||||
beacon.markerID=coord:MarkToAll(text)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove markers of all beacons from the F10 map. Optionally, remove only marker of a specific beacon or a certain beacon type.
|
||||
-- @param #BEACONS self
|
||||
-- @param #BEACONS.Beacon Beacon (Optional) Only this specifc beacon.
|
||||
-- @param #number TypeID (Optional) Only show specific beacon types, *e.g.* `BEACON.Type.TACAN`.
|
||||
-- @return #BEACONS self
|
||||
function BEACONS:MarkerRemove(Beacon, TypeID)
|
||||
|
||||
for _,_beacon in pairs(self.beacons) do
|
||||
local beacon=_beacon --#BEACONS.Beacon
|
||||
if Beacon==nil or Beacon.beaconId==beacon.beaconId then
|
||||
if TypeID==nil or beacon.type==TypeID then
|
||||
if beacon.markerID then
|
||||
UTILS.RemoveMark(beacon.markerID)
|
||||
beacon.markerID=nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Private Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Get text displayed in the F10 marker.
|
||||
-- @param #BEACONS self
|
||||
-- @param #BEACONS.Beacon beacon The beacon data structure.
|
||||
-- @return #string Marker text.
|
||||
function BEACONS:_GetMarkerText(beacon)
|
||||
|
||||
local frequency, funit=self:_GetFrequency(beacon.frequency)
|
||||
local direction=beacon.direction~=nil and beacon.direction or -1
|
||||
|
||||
local text=string.format("Beacon %s [ID=%s]", tostring(beacon.typeName), tostring(beacon.beaconId))
|
||||
text=text..string.format("\nCallsign: %s", tostring(beacon.callsign))
|
||||
if UTILS.IsInTable({BEACON.Type.TACAN, BEACON.Type.RSBN, BEACON.Type.PRMG_GLIDESLOPE, BEACON.Type.PRMG_LOCALIZER}, beacon.type) then
|
||||
text=text..string.format("\nChannel: %s", tostring(beacon.channel))
|
||||
end
|
||||
text=text..string.format("\nFrequency: %.3f %s", frequency, funit)
|
||||
text=text..string.format("\nDirection: %.1f°", direction)
|
||||
|
||||
return text
|
||||
end
|
||||
|
||||
|
||||
--- Get converted frequency.
|
||||
-- @param #BEACONS self
|
||||
-- @param #number freq Frequency in Hz.
|
||||
-- @return #number Frequency in better unit.
|
||||
-- @return #string Unit ("Hz", "kHz", "MHz").
|
||||
function BEACONS:_GetFrequency(freq)
|
||||
|
||||
freq=freq or 0
|
||||
local unit="Hz"
|
||||
|
||||
if freq>=1e6 then
|
||||
freq=freq/1e6
|
||||
unit="MHz"
|
||||
elseif freq>=1e3 then
|
||||
freq=freq/1e3
|
||||
unit="kHz"
|
||||
end
|
||||
|
||||
return freq, unit
|
||||
end
|
||||
|
||||
--- Get name of beacon type.
|
||||
-- @param #BEACONS self
|
||||
-- @param #number typeID Beacon type number.
|
||||
-- @return #string Type name.
|
||||
function BEACONS:_GetTypeName(typeID)
|
||||
|
||||
if typeID~=nil then
|
||||
for typeName,_typeID in pairs(BEACON.Type) do
|
||||
if _typeID==typeID then
|
||||
return typeName
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return "Unknown"
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,602 @@
|
||||
--- **NAVIGATION** - Navigation Airspace Points, Fixes and Aids.
|
||||
--
|
||||
-- **Main Features:**
|
||||
--
|
||||
-- * Navigation Fixes
|
||||
-- * Navigation Aids
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Example Missions:
|
||||
--
|
||||
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Navigation%20-%20NavFix).
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **funkyfranky**
|
||||
--
|
||||
-- ===
|
||||
-- @module Navigation.Point
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- NAVFIX class.
|
||||
-- @type NAVFIX
|
||||
--
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #number verbose Verbosity of output.
|
||||
-- @field #string name Name of the point.
|
||||
-- @field #string typePoint Type of the point, *e.g. "Intersection", "VOR", "Airport".
|
||||
-- @field Core.Vector#VECTOR vector Position vector of the fix.
|
||||
-- @field Wrapper.Marker#MARKER marker Marker on F10 map.
|
||||
-- @field #number altMin Minimum altitude in meters.
|
||||
-- @field #number altMax Maximum altitude in meters.
|
||||
-- @field #number speedMin Minimum speed in knots.
|
||||
-- @field #number speedMax Maximum speed in knots.
|
||||
--
|
||||
-- @field #boolean isCompulsory Is this a compulsory fix.
|
||||
-- @field #boolean isFlyover Is this a flyover fix (`true`) or turning point otherwise.
|
||||
-- @field #boolean isFAF Is this a final approach fix.
|
||||
-- @field #boolean isIAF Is this an initial approach fix.
|
||||
-- @field #boolean isIF Is this an initial fix.
|
||||
-- @field #boolean isMAF Is this an initial fix.
|
||||
--
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- *A fleet of British ships at war are the best negotiators.* -- Horatio Nelson
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # The NAVFIX Concept
|
||||
--
|
||||
-- The NAVFIX class has a great concept!
|
||||
--
|
||||
-- A NAVFIX describes a geo position and can, *e.g.*, be part of a FLIGHTPLAN. It has a unique name and is of a certain type, *e.g.* "Intersection", "VOR", "Airbase" etc.
|
||||
-- It can also have further properties as min/max altitudes and speeds that aircraft need to obey when they pass the point.
|
||||
--
|
||||
-- # Basic Setup
|
||||
--
|
||||
-- A new `NAVFIX` object can be created with the @{#NAVFIX.New}() function.
|
||||
--
|
||||
-- myNavPoint=NAVFIX:New()
|
||||
-- myTemplate:SetXYZ(X, Y, Z)
|
||||
--
|
||||
-- This is how it works.
|
||||
--
|
||||
-- @field #NAVFIX
|
||||
NAVFIX = {
|
||||
ClassName = "NAVFIX",
|
||||
verbose = 0,
|
||||
}
|
||||
|
||||
--- Type of point.
|
||||
-- @type NAVFIX.Type
|
||||
-- @field #string POINT Waypoint.
|
||||
-- @field #string INTERSECTION Intersection of airway.
|
||||
-- @field #string AIRPORT Airport.
|
||||
-- @field #string VOR Very High Frequency Omnidirectional Range Station.
|
||||
-- @field #string DME Distance Measuring Equipment.
|
||||
-- @field #string NDB Non-Directional Beacon.
|
||||
-- @field #string VORDME Combined VHF omnidirectional range (VOR) with a distance-measuring equipment (DME).
|
||||
-- @field #string LOC Localizer.
|
||||
-- @field #string ILS Instrument Landing System.
|
||||
-- @field #string TACAN TACtical Air Navigation System (TACAN).
|
||||
NAVFIX.Type={
|
||||
POINT="Point",
|
||||
INTERSECTION="Intersection",
|
||||
AIRPORT="Airport",
|
||||
NDB="NDB",
|
||||
VOR="VOR",
|
||||
DME="DME",
|
||||
VORDME="VOR/DME",
|
||||
LOC="Localizer",
|
||||
ILS="ILS",
|
||||
TACAN="TACAN"
|
||||
}
|
||||
|
||||
--- NAVFIX class version.
|
||||
-- @field #string version
|
||||
NAVFIX.version="0.1.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- ToDo list
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: A lot...
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Constructor(s)
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Create a new NAVFIX class instance from a given VECTOR.
|
||||
-- @param #NAVFIX self
|
||||
-- @param #string Name Name/ident of the point. Should be unique!
|
||||
-- @param #string Type (Optional) Type of the point. Default `NAVFIX.Type.POINT`.
|
||||
-- @param Core.Vector#VECTOR Vector Position vector of the navpoint.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:NewFromVector(Name, Type, Vector)
|
||||
|
||||
-- Inherit everything from BASE class.
|
||||
self=BASE:Inherit(self, BASE:New()) -- #NAVFIX
|
||||
|
||||
-- Vector of point.
|
||||
self.vector=Vector
|
||||
|
||||
-- Name of point.
|
||||
self.name=Name
|
||||
|
||||
-- Type of the point.
|
||||
self.typePoint=Type or NAVFIX.Type.POINT
|
||||
|
||||
local coord=COORDINATE:NewFromVec3(self.vector)
|
||||
|
||||
-- Marker on F10.
|
||||
self.marker=MARKER:New(coord, self:_GetMarkerText())
|
||||
|
||||
-- Log ID string.
|
||||
self.lid=string.format("NAVFIX %s [%s] | ", tostring(self.name), tostring(self.typePoint))
|
||||
|
||||
-- Debug info.
|
||||
self:I(self.lid..string.format("Created NAVFIX"))
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Create a new NAVFIX class instance from a given COORDINATE.
|
||||
-- @param #NAVFIX self
|
||||
-- @param #string Name Name of the fix. Should be unique!
|
||||
-- @param #string Type (Optional) Type of the point. Default `NAVFIX.Type.POINT`.
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate of the point.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:NewFromCoordinate(Name, Type, Coordinate)
|
||||
|
||||
-- Create a VECTOR from the coordinate.
|
||||
local Vector=VECTOR:NewFromVec(Coordinate)
|
||||
|
||||
-- Create NAVFIX.
|
||||
self=NAVFIX:NewFromVector(Name, Type, Vector)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Create a new NAVFIX instance from given latitude and longitude in degrees, minutes and seconds (DMS).
|
||||
-- @param #NAVFIX self
|
||||
-- @param #string Name Name of the fix. Should be unique!
|
||||
-- @param #string Type (Optional) Type of the point. Default `NAVFIX.Type.POINT`.
|
||||
-- @param #string Latitude Latitude in DMS as string.
|
||||
-- @param #string Longitude Longitude in DMS as string.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:NewFromLLDMS(Name, Type, Latitude, Longitude)
|
||||
|
||||
-- Create a VECTOR from the coordinate.
|
||||
local Vector=VECTOR:NewFromLLDMS(Latitude, Longitude)
|
||||
|
||||
-- Create NAVFIX.
|
||||
self=NAVFIX:NewFromVector(Name, Type, Vector)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Create a new NAVFIX instance from given latitude and longitude in decimal degrees (DD).
|
||||
-- @param #NAVFIX self
|
||||
-- @param #string Name Name of the fix. Should be unique!
|
||||
-- @param #string Type (Optional) Type of the point. Default `NAVFIX.Type.POINT`.
|
||||
-- @param #number Latitude Latitude in DD.
|
||||
-- @param #number Longitude Longitude in DD.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:NewFromLLDD(Name, Type, Latitude, Longitude)
|
||||
|
||||
-- Create a VECTOR from the coordinate.
|
||||
local Vector=VECTOR:NewFromLLDD(Latitude, Longitude)
|
||||
|
||||
-- Create NAVFIX.
|
||||
self=NAVFIX:NewFromVector(Name, Type, Vector)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Create a new NAVFIX class instance relative to a given other NAVFIX.
|
||||
-- You have to specify the distance and bearing from the new point to the given point. *E.g.*, for a distance of 5 NM and a bearing of 090° (West), the
|
||||
-- new nav point is created 5 NM East of the given nav point. The reason is that this corresponts to convention used in most maps.
|
||||
-- You can, however, use the `Reciprocal` switch to create the new point in the direction you specify.
|
||||
-- @param #NAVFIX self
|
||||
-- @param #string Name Name of the fix. Should be unique!
|
||||
-- @param #string Type Type of navfix.
|
||||
-- @param #NAVFIX NavFix The given/existing navigation fix relative to which the new fix is created.
|
||||
-- @param #number Distance Distance from the given to the new point in nautical miles.
|
||||
-- @param #number Bearing Bearing [Deg] from the new point to the given one.
|
||||
-- @param #boolean Reciprocal If `true` the reciprocal `Bearing` is taken so it specifies the direction from the given point to the new one.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:NewFromNavFix(Name, Type, NavFix, Distance, Bearing, Reciprocal)
|
||||
|
||||
-- Convert magnetic to true bearing by adding magnetic declination, e.g. mag. bearing 10°M ==> true bearing 16°M (for 6° variation on Caucasus map)
|
||||
Bearing=Bearing+UTILS.GetMagneticDeclination()
|
||||
|
||||
if Reciprocal then
|
||||
Bearing=Bearing-180
|
||||
end
|
||||
|
||||
-- Translate.
|
||||
local Vector=NavFix.vector:Translate(UTILS.NMToMeters(Distance), Bearing, true)
|
||||
|
||||
self=NAVFIX:NewFromVector(Name, Type, Vector)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Create a new NAVFIX class instance from BEACONS.Beacon data.
|
||||
-- @param #NAVFIX self
|
||||
-- @param Navigation.Beacons#BEACONS.Beacon Beacon The beacon data.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:NewFromBeacon(Beacon)
|
||||
local frequency, unit = BEACONS:_GetFrequency(Beacon.frequency)
|
||||
frequency = string.format("%.3f",frequency)
|
||||
if Beacon.typeName == "TACAN" then
|
||||
frequency = Beacon.channel
|
||||
unit = "X"
|
||||
end
|
||||
self = NAVFIX:NewFromVector(string.format("%s %s %s",Beacon.typeName,frequency,unit),Beacon.typeName,Beacon.vec3)
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- User Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Set whether this is the intermediate fix (IF).
|
||||
-- @param #NAVFIX self
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:SetIntermediateFix(IntermediateFix)
|
||||
self.isIF=IntermediateFix
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set whether this is an initial approach fix (IAF).
|
||||
-- The IAF is the point where the initial approach segment of an instrument approach begins.
|
||||
-- It is usually a designated intersection, VHF omidirectional range (VOR) non-directional beacon (NDB)
|
||||
-- or distance measuring equipment (DME) fix.
|
||||
-- The IAF may be collocated with the intermediate fix (IF) of the instrument apprach an in such case they designate the
|
||||
-- beginning of the intermediate segment of the approach. When the IAF and the IF are combined, there is no inital approach segment.
|
||||
-- @param #NAVFIX self
|
||||
-- @param #boolean IntermediateFix If `true`, this is an intermediate fix.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:SetInitialApproachFix(IntermediateFix)
|
||||
self.isIAF=IntermediateFix
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set whether this is the final approach fix (FAF).
|
||||
-- @param #NAVFIX self
|
||||
-- @param #boolean FinalApproachFix If `true`, this is a final approach fix.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:SetFinalApproachFix(FinalApproachFix)
|
||||
self.isFAF=FinalApproachFix
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set whether this is the final approach fix (FAF).
|
||||
-- @param #NAVFIX self
|
||||
-- @param #boolean FinalApproachFix If `true`, this is a final approach fix.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:SetMissedApproachFix(MissedApproachFix)
|
||||
self.isMAF=MissedApproachFix
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set minimum altitude.
|
||||
-- @param #NAVFIX self
|
||||
-- @param #number Altitude Min altitude in feet.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:SetAltMin(Altitude)
|
||||
|
||||
self.altMin=Altitude
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set maximum altitude.
|
||||
-- @param #NAVFIX self
|
||||
-- @param #number Altitude Max altitude in feet.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:SetAltMax(Altitude)
|
||||
|
||||
self.altMax=Altitude
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set mandatory altitude (min alt = max alt).
|
||||
-- @param #NAVFIX self
|
||||
-- @param #number Altitude Altitude in feet.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:SetAltMandatory(Altitude)
|
||||
|
||||
self.altMin=Altitude
|
||||
self.altMax=Altitude
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set minimum allowed speed at this fix.
|
||||
-- @param #NAVFIX self
|
||||
-- @param #number Speed Min speed in knots.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:SetSpeedMin(Speed)
|
||||
|
||||
self.speedMin=Speed
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set maximum allowed speed at this fix.
|
||||
-- @param #NAVFIX self
|
||||
-- @param #number Speed Max speed in knots.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:SetSpeedMax(Speed)
|
||||
|
||||
self.speedMax=Speed
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set mandatory speed (min speed = max speed) at this fix.
|
||||
-- @param #NAVFIX self
|
||||
-- @param #number Speed Mandatory speed in knots.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:SetSpeedMandatory(Speed)
|
||||
|
||||
self.speedMin=Speed
|
||||
self.speedMax=Speed
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set whether this fix is compulsory.
|
||||
-- @param #NAVFIX self
|
||||
-- @param #boolean Compulsory If `true`, this is a compulsory fix. If `false` or nil, it is non-compulsory.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:SetCompulsory(Compulsory)
|
||||
self.isCompulsory=Compulsory
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set whether this is a fly-over fix fix.
|
||||
-- @param #NAVFIX self
|
||||
-- @param #boolean FlyOver If `true`, this is a fly over fix. If `false` or nil, it is not.
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:SetFlyOver(FlyOver)
|
||||
self.isFlyover=FlyOver
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Get the altitude in feet MSL. If min and max altitudes are set, it will return a random altitude between min and max.
|
||||
-- @param #NAVFIX self
|
||||
-- @return #number Altitude in feet MSL. Can be `nil`, if neither min nor max altitudes have beeen set.
|
||||
function NAVFIX:GetAltitude()
|
||||
|
||||
local alt=nil
|
||||
if self.altMin and self.altMax and self.altMin~=self.altMax then
|
||||
alt=math.random(self.altMin, self.altMax)
|
||||
elseif self.altMin then
|
||||
alt=self.altMin
|
||||
elseif self.altMax then
|
||||
alt=self.altMax
|
||||
end
|
||||
|
||||
return alt
|
||||
end
|
||||
|
||||
|
||||
--- Get the speed. If min and max speeds are set, it will return a random speed between min and max.
|
||||
-- @param #NAVFIX self
|
||||
-- @return #number Speed in knots. Can be `nil`, if neither min nor max speeds have beeen set.
|
||||
function NAVFIX:GetSpeed()
|
||||
|
||||
local speed=nil
|
||||
if self.speedMin and self.speedMax and self.speedMin~=self.speedMax then
|
||||
speed=math.random(self.speedMin, self.speedMax)
|
||||
elseif self.speedMin then
|
||||
speed=self.speedMin
|
||||
elseif self.speedMax then
|
||||
speed=self.speedMax
|
||||
end
|
||||
|
||||
return speed
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Add marker the NAVFIX on the F10 map.
|
||||
-- @param #NAVFIX self
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:MarkerShow()
|
||||
|
||||
self.marker:ToAll()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove marker of the NAVFIX from the F10 map.
|
||||
-- @param #NAVFIX self
|
||||
-- @return #NAVFIX self
|
||||
function NAVFIX:MarkerRemove()
|
||||
|
||||
self.marker:Remove()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Private Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Get text displayed in the F10 marker.
|
||||
-- @param #NAVFIX self
|
||||
-- @return #string Marker text.
|
||||
function NAVFIX:_GetMarkerText()
|
||||
|
||||
local altmin=self.altMin and tostring(self.altMin) or ""
|
||||
local altmax=self.altMax and tostring(self.altMax) or ""
|
||||
local speedmin=self.speedMin and tostring(self.speedMin) or ""
|
||||
local speedmax=self.speedMax and tostring(self.speedMax) or ""
|
||||
|
||||
|
||||
local text=string.format("NAVFIX %s", self.name)
|
||||
if self.isIAF then
|
||||
text=text..string.format(" (IAF)")
|
||||
end
|
||||
if self.isIF then
|
||||
text=text..string.format(" (IF)")
|
||||
end
|
||||
text=text..string.format("\nAltitude [ft]: %s - %s", altmin, altmax)
|
||||
text=text..string.format("\nSpeed [knots]: %s - %s", speedmin, speedmax)
|
||||
text=text..string.format("\nCompulsory: %s", tostring(self.isCompulsory))
|
||||
text=text..string.format("\nFly Over: %s", tostring(self.isFlyover))
|
||||
|
||||
return text
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- NAVAID class.
|
||||
-- @type NAVAID
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #number verbose Verbosity of output.
|
||||
-- @extends Navigation.Point#NAVFIX
|
||||
|
||||
--- *A fleet of British ships at war are the best negotiators.* -- Horatio Nelson
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # The NAVAID Concept
|
||||
--
|
||||
-- A NAVAID consists of one or multiple FLOTILLAs. These flotillas "live" in a WAREHOUSE that has a phyiscal struction (STATIC or UNIT) and can be captured or destroyed.
|
||||
--
|
||||
-- # Basic Setup
|
||||
--
|
||||
-- A new `NAVAID` object can be created with the @{#NAVAID.New}(`WarehouseName`, `FleetName`) function, where `WarehouseName` is the name of the static or unit object hosting the fleet
|
||||
-- and `FleetName` is the name you want to give the fleet. This must be *unique*!
|
||||
--
|
||||
-- myFleet=NAVAID:New("myWarehouseName", "1st Fleet")
|
||||
-- myFleet:SetPortZone(ZonePort1stFleet)
|
||||
-- myFleet:Start()
|
||||
--
|
||||
-- A fleet needs a *port zone*, which is set via the @{#NAVAID.SetPortZone}(`PortZone`) function. This is the zone where the naval assets are spawned and return to.
|
||||
--
|
||||
-- Finally, the fleet needs to be started using the @{#NAVAID.Start}() function. If the fleet is not started, it will not process any requests.
|
||||
--
|
||||
-- @field #NAVAID
|
||||
NAVAID = {
|
||||
ClassName = "NAVAID",
|
||||
verbose = 0,
|
||||
}
|
||||
|
||||
--- NAVAID class version.
|
||||
-- @field #string version
|
||||
NAVAID.version="0.1.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- ToDo list
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: Add frequencies. Which unit MHz, kHz, Hz?
|
||||
-- TODO: Add radial function
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Constructor
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Create a new NAVAID class instance.
|
||||
-- @param #NAVAID self
|
||||
-- @param #string Name Name/ident of this navaid.
|
||||
-- @param #string Type (Optional) Type of the point. Default `NAVFIX.Type.POINT`.
|
||||
-- @param #string ZoneName Name of the zone to scan the scenery.
|
||||
-- @param #string SceneryName Name of the scenery object.
|
||||
-- @return #NAVAID self
|
||||
function NAVAID:NewFromScenery(Name, Type, ZoneName, SceneryName)
|
||||
|
||||
-- Get the zone.
|
||||
local zone=ZONE:FindByName(ZoneName)
|
||||
|
||||
-- Get coordinate.
|
||||
local Coordinate=zone:GetCoordinate()
|
||||
|
||||
-- Inherit everything from NAVFIX class.
|
||||
self=BASE:Inherit(self, NAVFIX:NewFromCoordinate(Name, Type, Coordinate)) -- #NAVAID
|
||||
|
||||
-- Set zone.
|
||||
self.zone=ZONE:FindByName(ZoneName)
|
||||
|
||||
-- Try to get the scenery object. Note not all can be found unfortunately.
|
||||
if SceneryName then
|
||||
self.scenery=SCENERY:FindByNameInZone(SceneryName, ZoneName)
|
||||
if not self.scenery then
|
||||
self:E(string.format("ERROR: Could not find scenery object %s in zone %s", SceneryName, ZoneName))
|
||||
end
|
||||
end
|
||||
|
||||
-- Alias.
|
||||
self.alias=string.format("%s %s %s", tostring(ZoneName), tostring(SceneryName), tostring(Type))
|
||||
|
||||
-- Set some string id for output to DCS.log file.
|
||||
self.lid=string.format("NAVAID %s | ", self.alias)
|
||||
|
||||
-- Debug info.
|
||||
self:I(self.lid..string.format("Created NAVAID!"))
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- User Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Set frequency the beacon transmits on.
|
||||
-- @param #NAVAID self
|
||||
-- @param #number Frequency Frequency in Hz.
|
||||
-- @return #NAVAID self
|
||||
function NAVAID:SetFrequency(Frequency)
|
||||
|
||||
self.frequency=Frequency
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set channel of, *e.g.*, TACAN beacons.
|
||||
-- @param #NAVAID self
|
||||
-- @param #number Channel The channel.
|
||||
-- @param #string Band (Optional) The band either `"X"` (default) or `"Y"`.
|
||||
-- @return #NAVAID self
|
||||
function NAVAID:SetChannel(Channel, Band)
|
||||
|
||||
self.channel=Channel
|
||||
self.band=Band or "X"
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Private Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- Add private CLASS functions here.
|
||||
-- No private NAVAID functions yet.
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,423 @@
|
||||
--- **NAVIGATION** - Airbase radios.
|
||||
--
|
||||
-- **Main Features:**
|
||||
--
|
||||
-- * Get radio frequencies of airbases
|
||||
-- * Find closest airbase radios
|
||||
-- * Mark radio frequencies on F10 map
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Example Missions:
|
||||
--
|
||||
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Navigation%20-%20Radios).
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **funkyfranky**
|
||||
--
|
||||
-- ===
|
||||
-- @module Navigation.Radios
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- RADIOS class.
|
||||
-- @type RADIOS
|
||||
--
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #number verbose Verbosity of output.
|
||||
-- @field #table radios Radios.
|
||||
--
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- *It's not true I had nothing on, I had the radio on.* -- *Marilyn Monroe*
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # The RADIOS Concept
|
||||
--
|
||||
-- This class is designed to make information about radios of a map/theatre easier accessible. The information contains mostly the frequencies of airbases of the map.
|
||||
--
|
||||
-- **Note** that try to avoid hard coding stuff in Moose since DCS is updated frequently and things change. Therefore, the main source of information is either a file `radio.lua` that can be
|
||||
-- found in the installation directory of DCS for each map or a table that the user needs to provide.
|
||||
-- **Note** your `MissionScripting` environment needs to be desanitized to read this data.
|
||||
--
|
||||
-- # Basic Setup
|
||||
--
|
||||
-- A new `RADIOS` object can be created with the @{#RADIOS.NewFromFile}(*radio_lua_file*) function.
|
||||
--
|
||||
-- local radios=RADIOS:NewFromFile("<DCS_Install_Directory>\Mods\terrains\<Map_Name>\Radio.lua")
|
||||
-- radios:MarkerShow()
|
||||
--
|
||||
-- This will load the radios from the `<DCS_Install_Directory>` for the specific map and place markers on the F10 map. This is the first step you should do to ensure that the file
|
||||
-- you provided is correct and all relevant radios are present.
|
||||
--
|
||||
-- # User Functions
|
||||
--
|
||||
-- ## F10 Map Markers
|
||||
--
|
||||
-- ## Position
|
||||
--
|
||||
-- ## Closest Radio
|
||||
--
|
||||
--
|
||||
-- @field #RADIOS
|
||||
RADIOS = {
|
||||
ClassName = "RADIOS",
|
||||
verbose = 0,
|
||||
radios = {},
|
||||
}
|
||||
|
||||
--- Radio item data structure.
|
||||
-- @type RADIOS.Radio
|
||||
-- @field #string radioId Radio ID.
|
||||
-- @field #table role Roles of the radio (usually {"ground", "tower", "approach"}).
|
||||
-- @field #table callsign Callsigns of the radio (usually the airbase name).
|
||||
-- @field #table frequency Frequencies of the radios.
|
||||
-- @field #table position Position table.
|
||||
-- @field #table sceneObjects Scenery objects.
|
||||
-- @field #string name Name of the airbase.
|
||||
-- @field Wrapper.Airbase#AIRBASE airbase Airbase.
|
||||
-- @field Core.Point#COORDINATE coordinate The COORDINATE of the radio.
|
||||
-- @field DCS#Vec3 vec3 3D vector.
|
||||
-- @field #number markerID Marker ID.
|
||||
|
||||
--- Radio item data structure.
|
||||
-- @type RADIOS.Frequency
|
||||
-- @field #number modu Modulation type.
|
||||
-- @field #number freq Frequency in Hz.
|
||||
|
||||
|
||||
--- RADIOS class version.
|
||||
-- @field #string version
|
||||
RADIOS.version="0.1.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- ToDo list
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: A lot...
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Constructor(s)
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Create a new RADIOS class instance from a given table.
|
||||
-- @param #RADIOS self
|
||||
-- @param #table RadioTable Table with radios info.
|
||||
-- @return #RADIOS self
|
||||
function RADIOS:NewFromTable(RadioTable)
|
||||
|
||||
-- Inherit everything from BASE class.
|
||||
self=BASE:Inherit(self, BASE:New()) -- #RADIOS
|
||||
|
||||
--local airbasenames=AIRBASE.GetAllAirbaseNames()
|
||||
|
||||
-- Get all airdromes
|
||||
local airdromes=AIRBASE.GetAllAirbases(nil, Airbase.Category.AIRDROME)
|
||||
|
||||
for _,_radio in pairs(RadioTable) do
|
||||
local radio=_radio --#RADIOS.Radio
|
||||
|
||||
-- The table structure of callsign is a bit awkward. We need to get the airbase name.
|
||||
-- Note that unfortunately, the callsign does not always correspond to the airbase name.
|
||||
if false then
|
||||
local cs=radio.callsign[1]
|
||||
if cs and cs.common then
|
||||
radio.name=cs.common[1]
|
||||
elseif cs and cs.nato then
|
||||
radio.name=cs.nato[1]
|
||||
else
|
||||
radio.name="Unknown"
|
||||
end
|
||||
radio.name=self:_GetAirbaseName(airbasenames, radio.name)
|
||||
radio.airbase=AIRBASE:FindByName(radio.name)
|
||||
end
|
||||
|
||||
-- Each radio item has a key radioId = 'airfield106_0', where 106 is the UID of the airbase.
|
||||
-- So we can use that to get the airbase.
|
||||
local aid = tonumber(string.match(radio.radioId, "airfield(%d+)_"))
|
||||
|
||||
-- Get airbase
|
||||
radio.airbase=self:_GetAirbaseByID(airdromes, aid)
|
||||
|
||||
-- Set other stuff
|
||||
if radio.airbase then
|
||||
radio.coordinate=radio.airbase:GetCoordinate()
|
||||
radio.vec3=radio.airbase:GetVec3()
|
||||
radio.name=radio.airbase:GetName()
|
||||
end
|
||||
|
||||
-- Add to table
|
||||
table.insert(self.radios, radio)
|
||||
end
|
||||
|
||||
-- Debug output
|
||||
self:I(string.format("Added %d radios", #self.radios))
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Create a new RADIOS class instance from a given file.
|
||||
-- @param #RADIOS self
|
||||
-- @param #string FileName Full path to the file containing the map radios.
|
||||
-- @return #RADIOS self
|
||||
function RADIOS:NewFromFile(FileName)
|
||||
|
||||
-- Inherit everything from BASE class.
|
||||
self=BASE:Inherit(self, BASE:New()) -- #RADIOS
|
||||
|
||||
local exists=UTILS.FileExists(FileName)
|
||||
|
||||
if exists==false then
|
||||
self:E(string.format("ERROR: file with radios info does not exist! File=%s", tostring(FileName)))
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Backup DCS radio table
|
||||
local radiobak=UTILS.DeepCopy(radio)
|
||||
|
||||
-- This will create a global table `radio`
|
||||
dofile(FileName)
|
||||
|
||||
-- Get radios from table.
|
||||
self=self:NewFromTable(radio)
|
||||
|
||||
-- Restore DCS radio table
|
||||
radio=UTILS.DeepCopy(radiobak)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- User Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Get 3D position vector of a specific radio.
|
||||
-- @param #RADIOS self
|
||||
-- @param #RADIOS.Radio radio The radio data structure.
|
||||
-- @return DCS#Vec3 Position vector.
|
||||
function RADIOS:GetVec3(radio)
|
||||
return radio.vec3
|
||||
end
|
||||
|
||||
--- Get COORDINATE of a specific radio.
|
||||
-- @param #RADIOS self
|
||||
-- @param #RADIOS.Radio radio The radio data structure.
|
||||
-- @return Core.Point#COORDINATE The coordinate.
|
||||
function RADIOS:GetCoordinate(radio)
|
||||
return radio.coordinate
|
||||
end
|
||||
|
||||
|
||||
--- Find closest radio to a given coordinate.
|
||||
-- @param #RADIOS self
|
||||
-- @param Core.Point#COORDINATE Coordinate The reference coordinate.
|
||||
-- @param #number DistMax (Optional) Max search distance in meters.
|
||||
-- @param #table ExcludeList (Optional) List of radios to exclude.
|
||||
-- @return #RADIOS.Radio The closest radio.
|
||||
function RADIOS:GetClosestRadio(Coordinate, DistMax, ExcludeList)
|
||||
|
||||
local radio=nil --#RADIOS.Radio
|
||||
local distmin=math.huge
|
||||
|
||||
ExcludeList=ExcludeList or {}
|
||||
|
||||
for _,_radio in pairs(self.radios) do
|
||||
local ra=_radio --#RADIOS.Radio
|
||||
|
||||
if (not UTILS.IsInTable(ExcludeList, ra, "radioId")) then
|
||||
|
||||
local dist=Coordinate:Get2DDistance(ra.coordinate)
|
||||
|
||||
if dist<distmin and (DistMax==nil or dist<=DistMax) then
|
||||
distmin=dist
|
||||
radio=ra
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return radio
|
||||
end
|
||||
|
||||
--- Find closest radios to a given coordinate.
|
||||
-- @param #RADIOS self
|
||||
-- @param Core.Point#COORDINATE Coordinate The reference coordinate.
|
||||
-- @param #number Nmax (Optional) Max number of radios. Default 5.
|
||||
-- @param #number DistMax (Optional) Max search distance in meters.
|
||||
-- @return #table Table of #RADIOS.Radio closest radios.
|
||||
function RADIOS:GetClosestRadios(Coordinate, Nmax, DistMax)
|
||||
|
||||
Nmax=Nmax or 5
|
||||
|
||||
local closest={}
|
||||
for i=1,Nmax do
|
||||
|
||||
local radio=self:GetClosestRadio(Coordinate, DistMax, closest)
|
||||
|
||||
if radio then
|
||||
table.insert(closest, radio)
|
||||
else
|
||||
break
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return closest
|
||||
end
|
||||
|
||||
|
||||
--- Add markers for all radios on the F10 map.
|
||||
-- @param #RADIOS self
|
||||
-- @param #RADIOS.Radio Radio (Optional) Only this specifc radio.
|
||||
-- @return #RADIOS self
|
||||
function RADIOS:MarkerShow(Radio)
|
||||
|
||||
for _,_radio in pairs(self.radios) do
|
||||
local radio=_radio --#RADIOS.Radio
|
||||
if Radio==nil or Radio.radioId==radio.radioId then
|
||||
local coord=self:GetCoordinate(radio)
|
||||
if coord then
|
||||
local text=self:_GetMarkerText(radio)
|
||||
if radio.markerID then
|
||||
UTILS.RemoveMark(radio.markerID)
|
||||
end
|
||||
radio.markerID=coord:MarkToAll(text)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove markers of all radios from the F10 map.
|
||||
-- @param #RADIOS self
|
||||
-- @param #RADIOS.Radio Radio (Optional) Only this specifc radio.
|
||||
-- @return #RADIOS self
|
||||
function RADIOS:MarkerRemove(Radio)
|
||||
|
||||
for _,_radio in pairs(self.radios) do
|
||||
local radio=_radio --#RADIOS.Radio
|
||||
if Radio==nil or Radio.radioId==radio.radioId then
|
||||
if radio.markerID then
|
||||
UTILS.RemoveMark(radio.markerID)
|
||||
radio.markerID=nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Private Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Get text displayed in the F10 marker.
|
||||
-- @param #RADIOS self
|
||||
-- @param #RADIOS.Radio radio The radio data structure.
|
||||
-- @return #string Marker text.
|
||||
function RADIOS:_GetMarkerText(radio)
|
||||
|
||||
local text=string.format("Radio %s", tostring(radio.name))
|
||||
for b,f in pairs(radio.frequency) do
|
||||
local frequency=f --#RADIOS.Frequency
|
||||
local mod=frequency[1]
|
||||
local fre=frequency[2]
|
||||
local freq, funit=self:_GetFrequency(fre)
|
||||
--UTILS.PrintTableToLog(frequency)
|
||||
local band=self:_GetBandName(b)
|
||||
text=text..string.format("\n%s: %.3f %s", band, freq, funit)
|
||||
end
|
||||
|
||||
return text
|
||||
end
|
||||
|
||||
|
||||
--- Get converted frequency.
|
||||
-- @param #RADIOS self
|
||||
-- @param #number freq Frequency in Hz.
|
||||
-- @return #number Frequency in better unit.
|
||||
-- @return #string Unit ("Hz", "kHz", "MHz").
|
||||
function RADIOS:_GetFrequency(freq)
|
||||
|
||||
freq=freq or 0
|
||||
local unit="Hz"
|
||||
|
||||
if freq>=1e6 then
|
||||
freq=freq/1e6
|
||||
unit="MHz"
|
||||
elseif freq>=1e3 then
|
||||
freq=freq/1e3
|
||||
unit="kHz"
|
||||
end
|
||||
|
||||
return freq, unit
|
||||
end
|
||||
|
||||
--- Get name of frequency band.
|
||||
-- @param #RADIOS self
|
||||
-- @param #number BandNumber Band as number.
|
||||
-- @return #string Band name.
|
||||
function RADIOS:_GetBandName(BandNumber)
|
||||
|
||||
if BandNumber~=nil then
|
||||
for bandName,bandNumber in pairs(ENUMS.FrequencyBand) do
|
||||
if bandNumber==BandNumber then
|
||||
return bandName
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return "Unknown"
|
||||
end
|
||||
|
||||
--- Get name of frequency band.
|
||||
-- @param #RADIOS self
|
||||
-- @param #table airbasenames Names of all airbases.
|
||||
-- @param #string name Name of airbase.
|
||||
-- @return #string Name of airbase
|
||||
function RADIOS:_GetAirbaseName(airbasenames, name)
|
||||
|
||||
local airbase=AIRBASE:FindByName(name)
|
||||
|
||||
if airbase then
|
||||
return name
|
||||
else
|
||||
for _,airbasename in pairs(airbasenames) do
|
||||
if string.find(airbasename, name) then
|
||||
return airbasename
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return "Unknown"
|
||||
end
|
||||
|
||||
--- Get name of frequency band.
|
||||
-- @param #RADIOS self
|
||||
-- @param #table airbases Table of airbases.
|
||||
-- @param #number aid Airbase ID.
|
||||
-- @return Wrapper.Airbase#AIRBASE Airbase matching the ID or nil.
|
||||
function RADIOS:_GetAirbaseByID(airbases, aid)
|
||||
|
||||
for _,_airbase in pairs(airbases) do
|
||||
local airbase=_airbase --Wrapper.Airbase#AIRBASE
|
||||
local id=airbase:GetID(true)
|
||||
if id==aid then
|
||||
return airbase
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,332 @@
|
||||
--- **NAVIGATION** - Towns of the map/theatre.
|
||||
--
|
||||
-- **Main Features:**
|
||||
--
|
||||
-- * Find towns of map
|
||||
-- * Road and rail connections
|
||||
-- * Find closest town to a given coordinate
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Example Missions:
|
||||
--
|
||||
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Navigation%20-%20Towns).
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **funkyfranky**
|
||||
--
|
||||
-- ===
|
||||
-- @module Navigation.Towns
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- TOWNS class.
|
||||
-- @type TOWNS
|
||||
--
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #number verbose Verbosity of output.
|
||||
-- @field #table towns Towns.
|
||||
--
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- *Hope is the beacon that guides lost ships back to the shore.*
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # The TOWNS Concept
|
||||
--
|
||||
-- This class is designed to make information about towns of a map/theatre easier accessible. The information contains location and road/rail connections of the towns.
|
||||
--
|
||||
-- **Note** that try to avoid hard coding stuff in Moose since DCS is updated frequently and things change. Therefore, the main source of information is either a file `towns.lua` that can be
|
||||
-- found in the installation directory of DCS for each map or a table that the user needs to provide.
|
||||
-- **Note** your `MissionScripting` environment needs to be desanitized to read this data.
|
||||
--
|
||||
-- # Basic Setup
|
||||
--
|
||||
-- A new `TOWNS` object can be created with the @{#TOWNS.NewFromFile}(*towns_lua_file*) function.
|
||||
--
|
||||
-- local towns=TOWNS:NewFromFile("<DCS_Install_Directory>\Mods\terrains\<Map_Name>\map\towns.lua")
|
||||
-- towns:MarkerShow()
|
||||
--
|
||||
-- This will load the towns from the `<DCS_Install_Directory>` for the specific map and place markers on the F10 map. This is the first step you should do to ensure that the file
|
||||
-- you provided is correct and all relevant towns are present.
|
||||
--
|
||||
-- # User Functions
|
||||
--
|
||||
-- ## F10 Map Markers
|
||||
--
|
||||
-- ## Position
|
||||
--
|
||||
-- ## Get Closest Town
|
||||
--
|
||||
--
|
||||
-- @field #TOWNS
|
||||
TOWNS = {
|
||||
ClassName = "TOWNS",
|
||||
verbose = 0,
|
||||
towns = {},
|
||||
}
|
||||
|
||||
--- Town data.
|
||||
-- @type TOWNS.Town
|
||||
-- @field #string display_name Displayed name.
|
||||
-- @field #string name Name of the town.
|
||||
-- @field #number latitude Latitude.
|
||||
-- @field #number longitude Longitude
|
||||
-- @field DCS#Vec3 vec3 Position vector 3D.
|
||||
-- @field Core.Point#COORDINATE coordinate The coordinate.
|
||||
-- @field Core.Point#COORDINATE coordRoad The coordinate of the closest road.
|
||||
-- @field Core.Point#COORDINATE coordRail The coordinate of the closest railway.
|
||||
-- @field #number markerID ID for the F10 marker.
|
||||
|
||||
--- TOWNS class version.
|
||||
-- @field #string version
|
||||
TOWNS.version="0.1.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- ToDo list
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: A lot...
|
||||
-- DONE: Road connection
|
||||
-- DONE: Rail connection
|
||||
-- DONE: Connection between towns
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Constructor(s)
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Create a new TOWNS class instance from a given table.
|
||||
-- @param #TOWNS self
|
||||
-- @param #table TownTable Table with all towns data.
|
||||
-- @return #TOWNS self
|
||||
function TOWNS:NewFromTable(TownTable)
|
||||
|
||||
-- Inherit everything from BASE class.
|
||||
self=BASE:Inherit(self, BASE:New()) -- #TOWNS
|
||||
|
||||
for TownName,_town in pairs(TownTable) do
|
||||
local town=_town --#TOWNS.Town
|
||||
|
||||
town.name=TownName
|
||||
|
||||
-- Get coordinate
|
||||
town.coordinate=COORDINATE:NewFromLLDD(town.latitude, town.longitude)
|
||||
|
||||
-- Get coordinate of closest road
|
||||
town.coordRoad=town.coordinate:GetClosestPointToRoad()
|
||||
|
||||
-- Get coordinate of closest rail
|
||||
town.coordRail=town.coordinate:GetClosestPointToRoad(true)
|
||||
|
||||
-- Add to table
|
||||
table.insert(self.towns, town)
|
||||
end
|
||||
|
||||
-- Debug output
|
||||
self:I(string.format("Added %d towns", #self.towns))
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Create a new TOWNS class instance from a given file.
|
||||
-- @param #TOWNS self
|
||||
-- @param #string FileName Full path to the file containing the towns data.
|
||||
-- @return #TOWNS self
|
||||
function TOWNS:NewFromFile(FileName)
|
||||
|
||||
-- Inherit everything from BASE class.
|
||||
self=BASE:Inherit(self, BASE:New()) -- #TOWNS
|
||||
|
||||
local exists=UTILS.FileExists(FileName)
|
||||
|
||||
if exists==false then
|
||||
self:E(string.format("ERROR: file with towns info does not exist!"))
|
||||
return nil
|
||||
end
|
||||
|
||||
-- This will create a global table `towns`
|
||||
dofile(FileName)
|
||||
|
||||
-- Get towns from table.
|
||||
self=self:NewFromTable(towns)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- User Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Get 3D position vector of a specific town.
|
||||
-- @param #TOWNS self
|
||||
-- @param #TOWNS.Town town The town data structure.
|
||||
-- @return DCS#Vec3 Position vector.
|
||||
function TOWNS:GetVec3(town)
|
||||
return town.vec3
|
||||
end
|
||||
|
||||
--- Get COORDINATE of a specific town.
|
||||
-- @param #TOWNS self
|
||||
-- @param #TOWNS.Town town The town data structure.
|
||||
-- @return Core.Point#COORDINATE The coordinate.
|
||||
function TOWNS:GetCoordinate(town)
|
||||
return town.coordinate
|
||||
end
|
||||
|
||||
--- Get closest road coordinate of a town.
|
||||
-- @param #TOWNS self
|
||||
-- @param #TOWNS.Town town The town data structure.
|
||||
-- @return Core.Point#COORDINATE The closest road coordinate.
|
||||
function TOWNS:GetCoordRoad(town)
|
||||
return town.coordRoad
|
||||
end
|
||||
|
||||
--- Get closest rail coordinate of a town.
|
||||
-- @param #TOWNS self
|
||||
-- @param #TOWNS.Town town The town data structure.
|
||||
-- @return Core.Point#COORDINATE The closest rail coordinate.
|
||||
function TOWNS:GetCoordRail(town)
|
||||
return town.coordRail
|
||||
end
|
||||
|
||||
--- Get road or rail connection between two towns.
|
||||
-- @param #TOWNS self
|
||||
-- @param #TOWNS.Town townA The town data structure.
|
||||
-- @param #TOWNS.Town townB The town data structure.
|
||||
-- @param #boolean Railroad If `true`, find rail road connection
|
||||
-- @return Core.Pathline#PATHLINE Pathline connecting the two towns on road.
|
||||
function TOWNS:GetConnectionRoad(townA, townB, Railroad)
|
||||
|
||||
local path=townA.coordRoad:GetPathlineOnRoad(townB.coordRoad, false, Railroad)
|
||||
|
||||
return path
|
||||
end
|
||||
|
||||
--- Find closest town to a given coordinate.
|
||||
-- @param #TOWNS self
|
||||
-- @param Core.Point#COORDINATE Coordinate The reference coordinate.
|
||||
-- @param #number DistMax (Optional) Max search distance in meters.
|
||||
-- @param #table ExcludeList (Optional) List of towns excluded from the search.
|
||||
-- @return #TOWNS.Town The closest town.
|
||||
function TOWNS:GetClosestTown(Coordinate, DistMax, ExcludeList)
|
||||
|
||||
local Town=nil --#TOWNS.Town
|
||||
local distmin=math.huge
|
||||
ExcludeList=ExcludeList or {}
|
||||
|
||||
for _,_town in pairs(self.towns) do
|
||||
local town=_town --#TOWNS.Town
|
||||
|
||||
if (not UTILS.IsInTable(ExcludeList, town, "name")) then
|
||||
|
||||
local dist=Coordinate:Get2DDistance(town.coordinate)
|
||||
|
||||
if dist<distmin then
|
||||
distmin=dist
|
||||
Town=town
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return Town
|
||||
end
|
||||
|
||||
--- Find closest towns to a given coordinate.
|
||||
-- @param #TOWNS self
|
||||
-- @param Core.Point#COORDINATE Coordinate The reference coordinate.
|
||||
-- @param #number Nmax (Optional) Max number of towns. Default 5.
|
||||
-- @param #number DistMax (Optional) Max search distance in meters.
|
||||
-- @return #table Table of #TOWNS.Town closest towns.
|
||||
function TOWNS:GetClosestTowns(Coordinate, Nmax, DistMax)
|
||||
|
||||
Nmax=Nmax or 5
|
||||
|
||||
local closest={}
|
||||
for i=1,Nmax do
|
||||
|
||||
local town=self:GetClosestTown(Coordinate, DistMax, closest)
|
||||
|
||||
if town then
|
||||
table.insert(closest, town)
|
||||
else
|
||||
break
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return closest
|
||||
end
|
||||
|
||||
|
||||
--- Get table of all towns, optionally of a given type.
|
||||
-- @param #TOWNS self
|
||||
-- @return #table Table of towns. Each element is of type #TOWN.Town.
|
||||
function TOWNS:GetTowns()
|
||||
return self.towns
|
||||
end
|
||||
|
||||
--- Add markers for all towns on the F10 map.
|
||||
-- @param #TOWNS self
|
||||
-- @param #TOWNS.Town Town (Optional) Only this specifc town.
|
||||
-- @return #TOWNS self
|
||||
function TOWNS:MarkerShow(Town)
|
||||
|
||||
for _,_town in pairs(self.towns) do
|
||||
local town=_town --#TOWNS.Town
|
||||
if Town==nil or Town.name==town.name then
|
||||
local text=self:_GetMarkerText(town)
|
||||
local coord=town.coordinate
|
||||
if town.markerID then
|
||||
UTILS.RemoveMark(town.markerID)
|
||||
end
|
||||
town.markerID=coord:MarkToAll(text)
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove markers of all towns from the F10 map.
|
||||
-- @param #TOWNS self
|
||||
-- @param #TOWNS.Town Town (Optional) Only this specifc town.
|
||||
-- @return #TOWNS self
|
||||
function TOWNS:MarkerRemove(Town)
|
||||
|
||||
for _,_town in pairs(self.towns) do
|
||||
local town=_town --#TOWNS.Town
|
||||
if Town==nil or Town.name==town.name then
|
||||
if town.markerID then
|
||||
UTILS.RemoveMark(town.markerID)
|
||||
town.markerID=nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Private Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Get text displayed in the F10 marker.
|
||||
-- @param #TOWNS self
|
||||
-- @param #TOWNS.Town town The town data structure.
|
||||
-- @return #string Marker text.
|
||||
function TOWNS:_GetMarkerText(town)
|
||||
|
||||
local text=string.format("Town %s", town.name)
|
||||
--text=text..string.format("\nCallsign: %s", tostring(beacon.callsign))
|
||||
|
||||
return text
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user