mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2026-07-17 07:33:56 +00:00
Compare commits
781 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a49288200e | |||
| b94f36dfdf | |||
| 8eae7ac26b | |||
| 7bac1d3b92 | |||
| b31b11cda8 | |||
| fc809964c7 | |||
| d000ad76f8 | |||
| 3f5936bceb | |||
| 29b244d843 | |||
| fef1976a6f | |||
| 1eb6dbc7f2 | |||
| d16eb74e1b | |||
| 945b9bdf31 | |||
| b75e383956 | |||
| 15de1d58b0 | |||
| 79ce3871ea | |||
| 646fa8b7e6 | |||
| 84c68ccf14 | |||
| 93d0a8b674 | |||
| 8a33ccd356 | |||
| 7ff8978ca7 | |||
| 9473cc2b27 | |||
| 448d668b44 | |||
| 635c8dc2ad | |||
| 8c0e021e82 | |||
| 022854fa37 | |||
| a72d3afd1c | |||
| 1f062fe7b4 | |||
| 68aab10902 | |||
| b9be0341f5 | |||
| b712c44567 | |||
| 12a118d08f | |||
| b1b9db7333 | |||
| 28e08035f2 | |||
| 5f920a46fd | |||
| 23e3aefaf1 | |||
| 74e38f9755 | |||
| 69a781a710 | |||
| 2c82513e54 | |||
| 8eeff7efa2 | |||
| 7fcac6159c | |||
| 3cac18f634 | |||
| a5eef3087b | |||
| 1fbfc8f039 | |||
| af891f3fc2 | |||
| 47bb760d86 | |||
| 8e47a17c96 | |||
| c2c78dfe78 | |||
| c24acd2681 | |||
| 26742432f5 | |||
| 6a13e593ec | |||
| b63fe40cc8 | |||
| 8d8b886ad6 | |||
| 2e6cc61450 | |||
| 6859dc9142 | |||
| b871f728df | |||
| e41eec2238 | |||
| 87b87fa2ca | |||
| 3c996b6dd7 | |||
| a39f39646d | |||
| 2012e81acb | |||
| 157f9a82b1 | |||
| 795f58dc9b | |||
| 4c841fd1d2 | |||
| 976f92e5b1 | |||
| 25daa67554 | |||
| ef5c7e5972 | |||
| 33ff723bd8 | |||
| 21c744b27a | |||
| 34f3e24d3a | |||
| 38b36c0344 | |||
| b6a90d4633 | |||
| 5145dd14ea | |||
| 0e960b77b9 | |||
| 093526f4ff | |||
| 8160aad1bf | |||
| 51f40081f3 | |||
| 7e878b5702 | |||
| 804e0ed69d | |||
| 46c0d47175 | |||
| 147df1498e | |||
| f9410a9169 | |||
| a88750908e | |||
| aca3080c9a | |||
| 948158e85a | |||
| 9b3aa423aa | |||
| 07876cb46c | |||
| de48701d15 | |||
| c5678404d5 | |||
| 4e2dcb1c05 | |||
| 98539eee87 | |||
| 223c1650d9 | |||
| aa2133e72a | |||
| 498d58599b | |||
| a0c0fe36ba | |||
| 76b977ff98 | |||
| 878eaf4301 | |||
| 240d3460d7 | |||
| ae0d405350 | |||
| 45fc0eb9da | |||
| 3327c3b24d | |||
| c6521d9c38 | |||
| 049529ecf0 | |||
| c2b8eea010 | |||
| da8856c907 | |||
| 04f7bb7cc7 | |||
| 75c01fbe86 | |||
| 03b79351db | |||
| 33429766a0 | |||
| 83464090d6 | |||
| d77b2655b1 | |||
| 31a537748e | |||
| e873b097cf | |||
| 07a3095392 | |||
| e1b5c7f9c2 | |||
| f005cdbc71 | |||
| 7fa360f32c | |||
| 30c6937910 | |||
| 28724f396c | |||
| fb3ca6d9d5 | |||
| 6e75e58952 | |||
| 0abb0db2a3 | |||
| edb42bbbe9 | |||
| 3bebe7f69c | |||
| 7605b582d1 | |||
| 6ed031762b | |||
| 945efc0516 | |||
| 4e342a59be | |||
| 8e517bb9cf | |||
| dacf5125bf | |||
| 60b561449b | |||
| 88396b5a5e | |||
| a0c5466f7f | |||
| b62b218257 | |||
| 7a3c23527f | |||
| 2f002703ee | |||
| b0c6ed1d59 | |||
| 950e97b6d7 | |||
| 6d54e5a47f | |||
| 87d65123ba | |||
| 35c8803f8b | |||
| d9c8542993 | |||
| d7a6809fec | |||
| f3c38a4288 | |||
| 626b03c93a | |||
| cf5c55209a | |||
| 19921414cf | |||
| c4de2b6990 | |||
| 7907e4b830 | |||
| 9e55118d3e | |||
| a5076eddba | |||
| af11264841 | |||
| 5955c2d61f | |||
| 95e2bf4a21 | |||
| 6f22534b8d | |||
| 5d1123e7df | |||
| 5929b7b0ed | |||
| 0de95b1830 | |||
| ccaf926c6d | |||
| 26fbae8672 | |||
| cdbf1e147e | |||
| 5089e7df25 | |||
| 1de19731d0 | |||
| 0c0d1cc794 | |||
| 21b968a9c6 | |||
| d857e3acad | |||
| d7b0b3c898 | |||
| 704bb8668f | |||
| 763bfb4bb1 | |||
| e43173de60 | |||
| 16706cd483 | |||
| 505c86399c | |||
| 65ddff13f7 | |||
| bedd105649 | |||
| c10e57ec3b | |||
| 046bd37fd5 | |||
| 55c3e7aba6 | |||
| fbf83b3aed | |||
| ea60795e84 | |||
| 4a5e262695 | |||
| 22097987dc | |||
| 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 | |||
| 33f7a7084c | |||
| 07a14bc176 | |||
| 3c57177a11 | |||
| 7cc18b8082 | |||
| bef805d694 | |||
| 3479d08193 | |||
| 8ca9474c1c | |||
| 2047e818ef | |||
| 94b8835340 | |||
| 801b4f01b6 | |||
| 37a5f68339 | |||
| 6058160160 | |||
| 479dfc28b9 | |||
| e3cee8dafe | |||
| 3686e0de63 | |||
| 2931b32ce6 | |||
| b28e66fe6d | |||
| 8392788cdb | |||
| 42221f406a | |||
| 4a1b23c52a | |||
| 1965e24860 | |||
| 55242edbde | |||
| ebbe64f279 | |||
| 654f752ca0 | |||
| 2c5d9f043e | |||
| 2b2acbe244 | |||
| 7d4e103660 | |||
| 3da6187782 | |||
| 0bd79d518f | |||
| 86798ae9ea | |||
| dfc63de745 | |||
| 2830e6d199 | |||
| 8c07573f8f | |||
| f3af0262df | |||
| d7841b47a5 | |||
| 0f270a6a35 | |||
| 92db68c36b | |||
| 8432f46e48 | |||
| faccee88e7 | |||
| e8246b3b90 | |||
| eb4e7b9281 | |||
| e91bcdfe5e | |||
| 1f1a068d45 | |||
| a57c0a8c7d | |||
| 1e2190a6cc | |||
| 1644c1dc5b | |||
| 5fc3798c42 | |||
| dcae8eb7f3 | |||
| 3fa5c92058 | |||
| 512c3c57e9 | |||
| 3a95e75c2d | |||
| fae81cdb1b | |||
| dbae37f151 | |||
| f7d58a0b76 | |||
| 08fb458aa4 | |||
| 2e0d1fd90f | |||
| 3694e9649d | |||
| 2c79fa6492 | |||
| 69e6497655 | |||
| aca713b8d9 | |||
| ef8c7c9084 | |||
| e4eaf72769 | |||
| 5138ced630 | |||
| 534f445f8c | |||
| a2da4f0e77 | |||
| b2cc3e5329 | |||
| efde321616 | |||
| 3a61581608 | |||
| 3a10f0b946 | |||
| 6dc6972bdb | |||
| 5910c9502a | |||
| 405235a59d | |||
| bd8ac9551c | |||
| 9c148625e4 | |||
| a08d82a3d9 | |||
| 48b51f21de | |||
| c850ea10f5 | |||
| 3260279cb7 | |||
| 2fcef1f277 | |||
| 1e60a0a32a | |||
| 7707522ce5 | |||
| 91e26adc6a | |||
| 146f869aaa | |||
| dc863be38a | |||
| 0bd35727f4 | |||
| 5183fcc316 | |||
| bf7596521c | |||
| a85b6c960c | |||
| 8cb91477cf | |||
| a8a69fdbca | |||
| a1aebf0575 | |||
| 0f42218681 | |||
| 5404f9ef19 | |||
| 8a84be19df | |||
| fd4ea81e46 | |||
| 025bd3237c | |||
| 3df79aedb1 | |||
| 4141aa35ba | |||
| 6e45ee558e | |||
| e867ce4c73 | |||
| db138be5f3 | |||
| 5ae6495e69 | |||
| d8281b0103 | |||
| 58f1bc5531 | |||
| 935b52c489 | |||
| aace98545a | |||
| f39236c8fd | |||
| 43b4a6834b | |||
| aa3978b04d | |||
| b51e758516 | |||
| 5a92fb1f65 | |||
| 4a870d0183 | |||
| 5be1832c09 | |||
| 362652ac6c | |||
| aec69884dc | |||
| 47ddc64239 | |||
| 2a32be40f6 | |||
| 8fb4d4c7c6 | |||
| b17748cfeb | |||
| b5524b9a69 | |||
| ec0ff7afcd | |||
| c761d5020b | |||
| d0cf68c2e2 | |||
| 3beb98a5e9 | |||
| dd83ebe0e2 | |||
| 6fdf3957bd | |||
| 608fa1ca6c | |||
| f3d586d455 | |||
| c2f0ce0fa2 | |||
| 8535c39c2c | |||
| da8f023f5b | |||
| 354490d149 | |||
| c5b0be5d21 | |||
| 7123562f85 | |||
| 6301f5a939 | |||
| 5e8676cf8a | |||
| b320ba597a | |||
| 12d16f355b | |||
| f4264cd149 | |||
| 65d1c4187e | |||
| 58b169cb03 | |||
| 04fcb985fe | |||
| 5414adc8d8 | |||
| 95210366e9 | |||
| f73dc6c3be | |||
| 74383e3111 | |||
| 3f8954d21d | |||
| ebf393c58b | |||
| cda1432d04 | |||
| 09e5fca1a5 | |||
| 9c3c27f809 | |||
| 03275938e7 | |||
| a90e321a91 | |||
| e38d73df8b | |||
| 7df90b2d30 | |||
| e531f12f94 | |||
| a917ee8f1e | |||
| 44f3c776eb | |||
| 5d3f5dac1e | |||
| 873879ff79 | |||
| 2f46a63041 | |||
| 6c1907f7e0 | |||
| f43dd389e2 | |||
| 42ecdd3b14 | |||
| 297164a0ee | |||
| 323f09b06c | |||
| e003b91bbe | |||
| efb1d79e77 | |||
| ee23daa651 | |||
| e590701c01 | |||
| 9f1777ca9b | |||
| f1636fc5a9 | |||
| db053398d2 | |||
| 527c4d0de4 | |||
| 2479147fd9 | |||
| 7b7a8c1bab | |||
| 417caf1b62 | |||
| 2ff630d394 | |||
| b6d725920a | |||
| cc76851614 | |||
| 1ddb2aaec0 | |||
| c30d517a00 | |||
| 27fe314c1e | |||
| 2c12cfe4fd | |||
| 9adf342dd8 | |||
| facac82130 | |||
| c96249906e | |||
| 3e095711f4 | |||
| ca1ddb4013 | |||
| 8169235d2f | |||
| 0a56624657 | |||
| 4553785918 | |||
| 78b3efcf00 | |||
| 67cb844550 | |||
| 1b169f007c | |||
| 8efcad123c | |||
| b9cf1e46af | |||
| 47409b11cb | |||
| 4a04d7cce7 | |||
| 5ef6383c63 | |||
| 35f15435a3 | |||
| 38dcd04334 | |||
| b32a8a2899 | |||
| 8cac4dbf9e | |||
| ef2bec72a1 | |||
| 03c3a20b1b | |||
| 4c97d966a2 | |||
| f075c02db5 | |||
| 32e61a7a09 | |||
| 674c6eec81 | |||
| da535eaddc | |||
| c75c3d8777 | |||
| 4fa63986dc | |||
| 029f7a3f5c | |||
| eb6eba25f3 | |||
| e9194c59f4 | |||
| c8d693c8e7 | |||
| 72d8b29699 | |||
| 2341014882 | |||
| eb15fadcfe | |||
| 1e7203320f | |||
| e3b587aa95 | |||
| 13fa8f373e | |||
| c3dc055fb2 | |||
| 4e024f7173 | |||
| 1248006f3d | |||
| b318e8ae13 | |||
| 1af4d44ca5 | |||
| 7e963bef41 | |||
| 933000ffc7 | |||
| 9b217e1c97 | |||
| 324f4944b4 | |||
| b9d738c1ad | |||
| f735f1eb53 | |||
| 7149226283 | |||
| 4164a5288a | |||
| 1992276b07 | |||
| 21a7023b7b | |||
| bd054b26c0 | |||
| f094716b73 | |||
| b3100d2f5e | |||
| 4b1888a34d | |||
| f4cd214823 | |||
| b9be3aa7f8 | |||
| bb6db2b7f8 | |||
| fd2dacaefb | |||
| cc60e85901 | |||
| 9520782a04 | |||
| f172f6efeb | |||
| b6b6686873 | |||
| 5e724e7a3f | |||
| 90f1d1df2a | |||
| a5726c0ed8 | |||
| 1860366986 | |||
| 9db1d38a15 | |||
| 04ceda693b | |||
| 23ff128ac8 | |||
| 3749142670 | |||
| 7d3fc1740a | |||
| b2a084d669 | |||
| 30203668e4 | |||
| ebecc70693 | |||
| 74712b6e27 | |||
| 40253ea8bb | |||
| 7279da7f09 | |||
| 31c40c96f2 | |||
| 7a833365ce | |||
| 4e56078d2a | |||
| 0ac156f2b5 | |||
| 4bbf20ca4e | |||
| a462c5a493 | |||
| 179cb5f589 | |||
| 367014ebf3 | |||
| 9d8d0c2155 | |||
| edfe91274d | |||
| 326b20b08d | |||
| 11b0ce6275 | |||
| 03763e16d6 | |||
| c1e8ee12e0 | |||
| ac8cc408c1 | |||
| dad91cca8c | |||
| 2f9c384ac0 | |||
| f23a2267cf | |||
| ada38fa3ea | |||
| 2ee0597d48 | |||
| b39a83ad6e | |||
| 7ae4cdc8f1 | |||
| 7d47227fab | |||
| 1f09b8fe0b | |||
| 8c92a578ed | |||
| 992f09d9ba | |||
| 096f2caf9c | |||
| 0b37c909b3 | |||
| bdad38e707 | |||
| 1b18ae1597 | |||
| 8dcc3cbe8f | |||
| aa8f3a824c | |||
| b5a19528a1 | |||
| bd20e8fc81 | |||
| 2f806801bc | |||
| be006bd2bf | |||
| 18a08288ec | |||
| c9ac6d73e6 | |||
| f602455d1f | |||
| d5c34a37b0 | |||
| 8a0e9a3400 | |||
| 0e836973fd | |||
| 1b57af88a0 | |||
| be40d7be9a | |||
| 00de8d911c | |||
| 9df3fcfdf7 | |||
| d675e34f37 | |||
| 2d96ba0f56 | |||
| 21c2bd1103 | |||
| f3b7740041 | |||
| 7d7488db6f | |||
| b9197d65d5 | |||
| ec6f190b68 | |||
| 5e4f9f035b | |||
| 7b6bf7f39b | |||
| 2e8875dd2f | |||
| 4964cc2f2d | |||
| f0a4c5b008 | |||
| 1b6412821b | |||
| 926a0733e4 | |||
| 124ebd3240 | |||
| 7728609165 | |||
| da70f4ce6c | |||
| 69b3e9abad | |||
| f9f77bfa7b | |||
| 429db73854 | |||
| 1e9c45c115 | |||
| 727cb3276c | |||
| a68175448d | |||
| 33e63a4819 | |||
| d7431478d1 | |||
| 3543b2c79a | |||
| 4489efff94 | |||
| dcdea379ef | |||
| 6a4bddde99 | |||
| 2088359756 | |||
| dc2511942c | |||
| f0c257c4a5 | |||
| 068d21612f | |||
| 773461aad9 | |||
| f9257b2b0d | |||
| 9e0f03a3cd | |||
| 0ab6a10ec4 | |||
| 73f393c542 | |||
| a467fabdc8 | |||
| a2ab84c45a | |||
| 7ec7228cb8 | |||
| 9fd6729967 | |||
| f1d4f1753a | |||
| 1f268b3b5d | |||
| 6d9c3fd0aa | |||
| 28ae63bd8d | |||
| 8d6d4b3765 | |||
| 7ddb72885d | |||
| 42e7e3f94f | |||
| 4a5204aecb | |||
| 6466c5e95e | |||
| 829f5af25f | |||
| 0d1147bac4 | |||
| 24b47b02e0 | |||
| 10262fd30b | |||
| 3cabc07d58 | |||
| b0546b1e60 | |||
| a988e67490 | |||
| fa951838d2 | |||
| 2594c5bbf0 | |||
| db70fa341c | |||
| 763e3852ac | |||
| 66157d0596 | |||
| 8ec86973c6 | |||
| eb2c6ac6f2 | |||
| 93a8d8bc2d | |||
| cbcc893ce5 | |||
| c19713949d | |||
| 54450935a1 | |||
| d0f5712ca8 | |||
| 382b049c5f | |||
| 6c2cc37abe | |||
| a53763221c | |||
| 83447a3fb4 | |||
| b7bac28113 | |||
| a9edb16554 | |||
| 52cbb7202c | |||
| 1d04f7c945 | |||
| f6a8317c42 | |||
| 2a9a7db9b8 | |||
| 3aeec78f79 | |||
| 0aeb1fc6af | |||
| 26565d7549 | |||
| 62816f217b | |||
| eeeeda4e5e | |||
| 95767c5ef4 | |||
| f5881eda53 | |||
| fca6faa3a8 | |||
| c1997d9f70 | |||
| bb1caa6642 | |||
| 638f083729 | |||
| 59d41cf98b | |||
| 793adafda7 | |||
| dd5ca93f26 | |||
| 0d1a7c770b | |||
| 1889df4952 | |||
| 7ca219748d | |||
| b3f944e82e | |||
| 2fc16ba694 | |||
| 432fc0ef4b | |||
| 20c50c751f | |||
| d3b62d0260 | |||
| d0449265c1 | |||
| a4feafab8e | |||
| 309eedd165 | |||
| 997baf21a0 | |||
| fa676cc750 | |||
| b126cc00d0 | |||
| 1e0c27f599 | |||
| 09b7922b84 | |||
| 66032d6894 | |||
| 7c98a793c7 | |||
| 7a5b9a75f3 | |||
| 0ea5631955 | |||
| 4bab2ee1de | |||
| f5b9713639 | |||
| 1bfb4fc4e1 | |||
| d7defe6f7f | |||
| ebb94c07b3 | |||
| f4cdbec376 | |||
| 21d5a5dfac | |||
| f8947aab9c | |||
| db869bcb6d | |||
| 36a0cfd635 | |||
| ea4a1f9ff9 | |||
| 20406e40ca | |||
| acbcb14cba | |||
| 3b50fee5a0 | |||
| 804004198b | |||
| 67ef6c6e7b | |||
| 5b8b8a5566 | |||
| 0468bacc0b | |||
| eacc43cb5a | |||
| f3dcde075c | |||
| 7eba1349ae | |||
| b6074a4795 | |||
| 615afb7cc4 | |||
| 36c9f551d9 | |||
| 89c3f7310b | |||
| 6791a0e704 | |||
| a6b622ed31 | |||
| f1af3a50b8 | |||
| f999e45323 | |||
| 0c90e90c18 | |||
| 4747f3f48f | |||
| f97ef25104 | |||
| e1a670185e | |||
| 069c0aa03f | |||
| b145588ed5 | |||
| b678e2cde1 | |||
| db23a0bf2b | |||
| 3ad60a95ce | |||
| 5c3b7312c0 | |||
| f177d0a257 | |||
| ac4b620f16 | |||
| c49b56eefc | |||
| ccada18a6a | |||
| 1547d66327 | |||
| e21236655a | |||
| cdad3bd058 | |||
| 0db9c27f7e | |||
| 8042e8bfaf | |||
| dd7b87e9cd | |||
| 68a539923f | |||
| 460d2768ff | |||
| 3c74272749 | |||
| 3442725525 | |||
| 82c409d77a | |||
| 195aac4504 | |||
| 326e27327d | |||
| 08d8f3e25f | |||
| 6f72697e26 | |||
| 296d4b1e93 | |||
| 0f6439cf9f | |||
| 9705b49dbe | |||
| 9364579a18 | |||
| baa3f3234e | |||
| 02a6d8f2c0 | |||
| 2c10943cb1 | |||
| a293c59d6d | |||
| 544db963ea | |||
| d3fd21e6da | |||
| 207698a2dd | |||
| 27cd90a0ce | |||
| d1ae2c0f5e | |||
| 66e6a603f1 | |||
| 0392417189 | |||
| ab5a4c43c3 | |||
| 0678ad17f8 | |||
| be4beea9d0 | |||
| 5da899138b | |||
| 1ec1e00bde | |||
| ce61f454bf | |||
| d7d46d4f1b | |||
| 5d93b33d42 | |||
| b2077bfc74 | |||
| 4772dafe7f | |||
| e958ca103a | |||
| 629925e2d8 | |||
| 17f672dad4 | |||
| 1d9ef869a7 | |||
| 6fdf9a649f | |||
| 90c6c57449 | |||
| d013bbc751 | |||
| e0092fdba0 | |||
| 0a9717a8c2 | |||
| e0049bea2b | |||
| 73525feb68 | |||
| ecce2eff9b | |||
| fbeada439f | |||
| 6c8858d2f5 | |||
| e2b77878df | |||
| 53d7972858 | |||
| 04a55e4104 | |||
| d11acecdac | |||
| 7d3bffcfef | |||
| 5b76ec6b99 | |||
| 4b23c86daa | |||
| 49c11073e6 | |||
| daa5caa125 | |||
| 1a156e7e12 | |||
| 6fe88a6319 | |||
| 1856754614 | |||
| b9f6c1b9c7 | |||
| 6ac452ff15 | |||
| b52176a0ff | |||
| d707a4775c | |||
| ffccc31e38 | |||
| 0405af2bde | |||
| d09f0b1f6f | |||
| e50e572c78 | |||
| 2e688e7da1 | |||
| 3083599158 | |||
| 2921f7a76b | |||
| b7b6c1ea19 | |||
| 8a185c352e | |||
| 5b107ce2da | |||
| 5c1e342a79 | |||
| 3fc8f52796 | |||
| ddf33da787 | |||
| e7cee4d97b | |||
| b0a192a767 | |||
| 986c340211 | |||
| 2109537f86 | |||
| bad9d1ea92 | |||
| 690db7f12f | |||
| 4f3fd06cc9 | |||
| f5b1050086 | |||
| 4074023ed3 | |||
| f729b1d358 | |||
| 6c00b0c7eb | |||
| 5adefe6f7b | |||
| 76dc0d690a | |||
| 13c16b8674 | |||
| 222722225e | |||
| 82e1dcfc04 | |||
| c26220d1fd | |||
| d783f7be99 | |||
| b66e91b11f | |||
| eeca95c77f | |||
| e406fb0c88 |
@@ -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: |
|
||||
|
||||
@@ -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: |
|
||||
|
||||
@@ -1953,7 +1953,7 @@ local function refct_from_id(id) -- refct = refct_from_id(CTypeID)
|
||||
unsigned = refct.unsigned,
|
||||
size = bit.band(bit.rshift(ctype.info, 16), 127),
|
||||
}
|
||||
refct.bool, refct.const, refct.volatile, refct.unsigned = nil
|
||||
refct.bool, refct.const, refct.volatile, refct.unsigned = nil, nil, nil, nil
|
||||
end
|
||||
|
||||
if CT[4] then -- Merge sibling attributes onto this type.
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
|
||||
--- 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.
|
||||
|
||||
@@ -32,7 +32,9 @@
|
||||
-- [DCS WORLD - MOOSE - A2A GCICAP - Build an automatic A2A Defense System](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl0S4KMNUUJpaUs6zZHjLKNx)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- # QUICK START GUIDE
|
||||
--
|
||||
-- There are basically two classes available to model an A2A defense system.
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
--- 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.
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
--- 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.
|
||||
|
||||
@@ -11,11 +11,13 @@
|
||||
-- @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.
|
||||
--- @type AI_A2G_BAI
|
||||
-- @extends AI.AI_Air_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
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
--
|
||||
-- # 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
|
||||
--
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
--
|
||||
-- # QUICK START GUIDE
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The following class is available to model an A2G defense system.
|
||||
--
|
||||
-- AI_A2G_DISPATCHER is the main A2G defense class that models the A2G defense system.
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
--- 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.
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
--- The AI_AIR class implements the core functions to operate an AI @{Wrapper.Group}.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- # 1) AI_AIR constructor
|
||||
--
|
||||
@@ -657,8 +658,8 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
|
||||
--- Create a route point of type air.
|
||||
local FromRTBRoutePoint = FromCoord:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
RTBSpeed,
|
||||
true
|
||||
)
|
||||
@@ -666,8 +667,8 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
|
||||
--- Create a route point of type air.
|
||||
local ToRTBRoutePoint = ToAirbaseCoord:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
RTBSpeed,
|
||||
true
|
||||
)
|
||||
@@ -761,10 +762,10 @@ function AI_AIR:onafterRefuel( AIGroup, From, Event, To )
|
||||
local ToRefuelSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local FromRefuelRoutePoint = FromRefuelCoord:WaypointAir(self.PatrolAltType, POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, ToRefuelSpeed, true)
|
||||
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, POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, ToRefuelSpeed, true)
|
||||
local ToRefuelRoutePoint = Tanker:GetCoordinate():WaypointAir(self.PatrolAltType, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, ToRefuelSpeed, true)
|
||||
|
||||
self:F( { ToRefuelSpeed = ToRefuelSpeed } )
|
||||
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
--
|
||||
-- # QUICK START GUIDE
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The following class is available to model an AIR defense system.
|
||||
--
|
||||
-- AI_AIR_DISPATCHER is the main AIR defense class that models the AIR defense system.
|
||||
|
||||
@@ -13,12 +13,14 @@
|
||||
|
||||
|
||||
|
||||
-- @type AI_AIR_ENGAGE
|
||||
--- @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.
|
||||
@@ -453,7 +455,7 @@ function AI_AIR_ENGAGE:onafterEngageRoute( DefenderGroup, From, Event, To, Attac
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
local FromWP = DefenderCoord:WaypointAir(self.PatrolAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, EngageSpeed, true)
|
||||
local FromWP = DefenderCoord:WaypointAir(self.PatrolAltType or "RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, EngageSpeed, true)
|
||||
|
||||
EngageRoute[#EngageRoute+1] = FromWP
|
||||
|
||||
@@ -462,7 +464,7 @@ function AI_AIR_ENGAGE:onafterEngageRoute( DefenderGroup, From, Event, To, Attac
|
||||
local FromEngageAngle = DefenderCoord:GetAngleDegrees( DefenderCoord:GetDirectionVec3( TargetCoord ) )
|
||||
local ToCoord=DefenderCoord:Translate( EngageDistance, FromEngageAngle, true )
|
||||
|
||||
local ToWP = ToCoord:WaypointAir(self.PatrolAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, EngageSpeed, true)
|
||||
local ToWP = ToCoord:WaypointAir(self.PatrolAltType or "RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, EngageSpeed, true)
|
||||
|
||||
EngageRoute[#EngageRoute+1] = ToWP
|
||||
|
||||
@@ -536,7 +538,7 @@ function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetU
|
||||
local EngageRoute = {}
|
||||
local AttackTasks = {}
|
||||
|
||||
local FromWP = DefenderCoord:WaypointAir(self.EngageAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, EngageSpeed, true)
|
||||
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
|
||||
@@ -544,7 +546,7 @@ function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetU
|
||||
local FromEngageAngle = DefenderCoord:GetAngleDegrees( DefenderCoord:GetDirectionVec3( TargetCoord ) )
|
||||
local ToCoord=DefenderCoord:Translate( EngageDistance, FromEngageAngle, true )
|
||||
|
||||
local ToWP = ToCoord:WaypointAir(self.EngageAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, EngageSpeed, true)
|
||||
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!
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
--- 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.
|
||||
@@ -309,7 +311,7 @@ function AI_AIR_PATROL:onafterPatrolRoute( AIPatrol, From, Event, To )
|
||||
local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
|
||||
local speedkmh=ToTargetSpeed
|
||||
|
||||
local FromWP = CurrentCoord:WaypointAir(self.PatrolAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, ToTargetSpeed, true)
|
||||
local FromWP = CurrentCoord:WaypointAir(self.PatrolAltType or "RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, ToTargetSpeed, true)
|
||||
PatrolRoute[#PatrolRoute+1] = FromWP
|
||||
|
||||
if self.racetrack then
|
||||
@@ -359,9 +361,9 @@ function AI_AIR_PATROL:onafterPatrolRoute( AIPatrol, From, Event, To )
|
||||
else
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToWP = ToTargetCoord:WaypointAir(self.PatrolAltType, POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, ToTargetSpeed, true)
|
||||
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 )
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
|
||||
|
||||
-- @type AI_AIR_SQUADRON
|
||||
--- @type AI_AIR_SQUADRON
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
--
|
||||
-- # 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
|
||||
--
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
|
||||
--- 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.
|
||||
--
|
||||
-- 
|
||||
@@ -521,12 +523,12 @@ function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
|
||||
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||
local CurrentAltitude = self.Controllable:GetAltitude()
|
||||
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local CurrentPointVec3 = COORDINATE:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToEngageZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
self.EngageSpeed,
|
||||
true
|
||||
)
|
||||
@@ -577,13 +579,13 @@ function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
self:T2( ToTargetVec2 )
|
||||
|
||||
--- Obtain a 3D @{Point} from the 2D point + altitude.
|
||||
local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, self.EngageAltitude, ToTargetVec2.y )
|
||||
local ToTargetPointVec3 = COORDINATE:New( ToTargetVec2.x, self.EngageAltitude, ToTargetVec2.y )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToTargetRoutePoint = ToTargetPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
self.EngageSpeed,
|
||||
true
|
||||
)
|
||||
|
||||
@@ -33,8 +33,9 @@
|
||||
-- @field Wrapper.Group#GROUP Test
|
||||
-- @extends Core.Fsm#FSM_SET
|
||||
|
||||
|
||||
--- Monitors and manages as many replacement AI groups as there are
|
||||
--- 
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
@@ -220,16 +221,9 @@ function AI_BALANCER:onenterReturning( SetGroup, From, Event, To, AIGroup )
|
||||
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.
|
||||
--TODO: i need to rework the POINT_VEC2 thing.
|
||||
local PointVec2 = POINT_VEC2:New( AIGroup:GetVec2().x, AIGroup:GetVec2().y )
|
||||
local PointVec2 = COORDINATE:New(AIGroup:GetVec2().x, 0, AIGroup:GetVec2().y)
|
||||
local ClosestAirbase = self.ReturnAirbaseSet:FindNearestAirbaseFromPointVec2( PointVec2 )
|
||||
self:T( ClosestAirbase.AirbaseName )
|
||||
--[[
|
||||
AIGroup:MessageToRed( "Returning to " .. ClosestAirbase:GetName().. " ...", 30 )
|
||||
local RTBRoute = AIGroup:RouteReturnToAirbase( ClosestAirbase )
|
||||
AIGroupTemplate.route = RTBRoute
|
||||
AIGroup:Respawn( AIGroupTemplate )
|
||||
]]
|
||||
AIGroup:RouteRTB(ClosestAirbase)
|
||||
end
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
-- @module AI.AI_CAP
|
||||
-- @image AI_Combat_Air_Patrol.JPG
|
||||
|
||||
-- @type AI_CAP_ZONE
|
||||
--- @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
|
||||
@@ -39,6 +39,8 @@
|
||||
--- 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.
|
||||
@@ -423,12 +425,12 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
|
||||
|
||||
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||
local CurrentAltitude = self.Controllable:GetAltitude()
|
||||
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local CurrentPointVec3 = COORDINATE:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToEngageZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
ToEngageZoneSpeed,
|
||||
true
|
||||
)
|
||||
@@ -445,13 +447,13 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
|
||||
self:T2( { self.PatrolMinSpeed, self.PatrolMaxSpeed, ToTargetSpeed } )
|
||||
|
||||
--- Obtain a 3D @{Point} from the 2D point + altitude.
|
||||
local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y )
|
||||
local ToTargetPointVec3 = COORDINATE:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToPatrolRoutePoint = ToTargetPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
ToTargetSpeed,
|
||||
true
|
||||
)
|
||||
|
||||
@@ -38,6 +38,9 @@
|
||||
-- @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.
|
||||
--
|
||||
-- 
|
||||
@@ -465,12 +468,12 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
|
||||
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||
local CurrentAltitude = self.Controllable:GetAltitude()
|
||||
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local CurrentPointVec3 = COORDINATE:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToEngageZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
self.EngageSpeed,
|
||||
true
|
||||
)
|
||||
@@ -507,13 +510,13 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
self:T2( ToTargetVec2 )
|
||||
|
||||
--- Obtain a 3D @{Point} from the 2D point + altitude.
|
||||
local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, self.EngageAltitude, ToTargetVec2.y )
|
||||
local ToTargetPointVec3 = COORDINATE:New( ToTargetVec2.x, self.EngageAltitude, ToTargetVec2.y )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToTargetRoutePoint = ToTargetPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
self.EngageSpeed,
|
||||
true
|
||||
)
|
||||
|
||||
@@ -9,12 +9,14 @@
|
||||
-- @module AI.AI_Cargo
|
||||
-- @image Cargo.JPG
|
||||
|
||||
-- @type AI_CARGO
|
||||
--- @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.
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
--- 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.
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
|
||||
--- 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.
|
||||
--
|
||||
@@ -440,7 +442,7 @@ function AI_CARGO_AIRPLANE:Route( Airplane, Airbase, Speed, Height, Uncontrolled
|
||||
|
||||
-- To point.
|
||||
local AirbasePointVec2 = Airbase:GetPointVec2()
|
||||
local ToWaypoint = AirbasePointVec2:WaypointAir(POINT_VEC3.RoutePointAltType.BARO, "Land", "Landing", Speed or Airplane:GetSpeedMax()*0.8, true, Airbase)
|
||||
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
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- # The dispatcher concept.
|
||||
--
|
||||
-- Carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation.
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
|
||||
--- 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.
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
|
||||
--- 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.
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
|
||||
--- A dynamic cargo handling capability for AI helicopter groups.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Helicopters can be mobilized to intelligently transport infantry and other cargo within the simulation.
|
||||
--
|
||||
--
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
|
||||
--- 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.
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
--- 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.
|
||||
@@ -367,8 +369,8 @@ function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordina
|
||||
-- local CoordinateFrom = Helicopter:GetCoordinate()
|
||||
-- local WaypointFrom = CoordinateFrom:WaypointAir(
|
||||
-- "RADIO",
|
||||
-- POINT_VEC3.RoutePointType.TurningPoint,
|
||||
-- POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
-- COORDINATE.WaypointType.TurningPoint,
|
||||
-- COORDINATE.WaypointAction.TurningPoint,
|
||||
-- Speed,
|
||||
-- true
|
||||
-- )
|
||||
@@ -380,8 +382,8 @@ function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordina
|
||||
|
||||
local WaypointTo = CoordinateTo:WaypointAir(
|
||||
"RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
50,
|
||||
true
|
||||
)
|
||||
@@ -427,7 +429,7 @@ function AI_CARGO_HELICOPTER:onafterOrbit( Helicopter, From, Event, To, Coordina
|
||||
local landheight = CoordinateTo:GetLandHeight() -- get target height
|
||||
CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground
|
||||
|
||||
local WaypointTo = CoordinateTo:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, 50, true)
|
||||
local WaypointTo = CoordinateTo:WaypointAir("RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, 50, true)
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
local Tasks = {}
|
||||
@@ -496,14 +498,14 @@ function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordin
|
||||
local CoordinateFrom = Helicopter:GetCoordinate()
|
||||
|
||||
--- Create a route point of type air.
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, _speed, true)
|
||||
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", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint,_speed, true)
|
||||
local WaypointTo = CoordinateTo:WaypointAir("RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint,_speed, true)
|
||||
|
||||
Route[#Route+1] = WaypointFrom
|
||||
Route[#Route+1] = WaypointTo
|
||||
@@ -563,7 +565,7 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin
|
||||
|
||||
--- Create a route point of type air.
|
||||
local CoordinateFrom = Helicopter:GetCoordinate()
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, _speed, true)
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, _speed, true)
|
||||
Route[#Route+1] = WaypointFrom
|
||||
Route[#Route+1] = WaypointFrom
|
||||
|
||||
@@ -573,7 +575,7 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin
|
||||
local landheight = CoordinateTo:GetLandHeight() -- get target height
|
||||
CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground
|
||||
|
||||
local WaypointTo = CoordinateTo:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, _speed, true)
|
||||
local WaypointTo = CoordinateTo:WaypointAir("RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, _speed, true)
|
||||
|
||||
Route[#Route+1] = WaypointTo
|
||||
Route[#Route+1] = WaypointTo
|
||||
@@ -631,7 +633,7 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat
|
||||
--- Create a route point of type air.
|
||||
local CoordinateFrom = Helicopter:GetCoordinate()
|
||||
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, Speed, true)
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, Speed, true)
|
||||
Route[#Route+1] = WaypointFrom
|
||||
|
||||
--- Create a route point of type air.
|
||||
@@ -639,7 +641,7 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat
|
||||
local landheight = CoordinateTo:GetLandHeight() -- get target height
|
||||
CoordinateTo.y = landheight + Height -- flight height should be 50m above ground
|
||||
|
||||
local WaypointTo = CoordinateTo:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, Speed, true)
|
||||
local WaypointTo = CoordinateTo:WaypointAir("RADIO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, Speed, true)
|
||||
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
--- 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.
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
--
|
||||
-- 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.
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
--
|
||||
-- # 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
|
||||
--
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
--- 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
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
--
|
||||
-- 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.
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
|
||||
|
||||
--- Build large formations, make AI follow a @{Wrapper.Client#CLIENT} (player) leader or a @{Wrapper.Unit#UNIT} (AI) leader.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- AI_FORMATION makes AI @{Wrapper.Group#GROUP}s fly in formation of various compositions.
|
||||
-- The AI_FORMATION class models formations in a different manner than the internal DCS formation logic!!!
|
||||
@@ -725,7 +727,7 @@ function AI_FORMATION:onafterFormationLine( FollowGroupSet, From , Event , To, X
|
||||
|
||||
for FollowID, FollowGroup in pairs( FollowSet ) do
|
||||
|
||||
local PointVec3 = POINT_VEC3:New()
|
||||
local PointVec3 = COORDINATE:New()
|
||||
PointVec3:SetX( XStart + i * XSpace )
|
||||
PointVec3:SetY( YStart + i * YSpace )
|
||||
PointVec3:SetZ( ZStart + i * ZSpace )
|
||||
@@ -877,7 +879,7 @@ function AI_FORMATION:onafterFormationCenterWing( FollowGroupSet, From , Event ,
|
||||
|
||||
for FollowID, FollowGroup in pairs( FollowSet ) do
|
||||
|
||||
local PointVec3 = POINT_VEC3:New()
|
||||
local PointVec3 = COORDINATE:New()
|
||||
|
||||
local Side = ( i % 2 == 0 ) and 1 or -1
|
||||
local Row = i / 2 + 1
|
||||
@@ -936,7 +938,7 @@ function AI_FORMATION:onafterFormationBox( FollowGroupSet, From , Event , To, XS
|
||||
|
||||
for FollowID, FollowGroup in pairs( FollowSet ) do
|
||||
|
||||
local PointVec3 = POINT_VEC3:New()
|
||||
local PointVec3 = COORDINATE:New()
|
||||
|
||||
local ZIndex = i % ZLevels
|
||||
local XIndex = math.floor( i / ZLevels )
|
||||
|
||||
@@ -48,6 +48,8 @@
|
||||
|
||||
--- 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.
|
||||
@@ -751,12 +753,12 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
|
||||
if not CurrentVec2 then return end
|
||||
--Done: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||
local CurrentAltitude = self.Controllable:GetAltitude()
|
||||
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local CurrentPointVec3 = COORDINATE:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TakeOffParking,
|
||||
POINT_VEC3.RoutePointAction.FromParkingArea,
|
||||
COORDINATE.WaypointType.TakeOffParking,
|
||||
COORDINATE.WaypointAction.FromParkingArea,
|
||||
ToPatrolZoneSpeed,
|
||||
true
|
||||
)
|
||||
@@ -767,12 +769,12 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
|
||||
if not CurrentVec2 then return end
|
||||
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||
local CurrentAltitude = self.Controllable:GetAltitude()
|
||||
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local CurrentPointVec3 = COORDINATE:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
ToPatrolZoneSpeed,
|
||||
true
|
||||
)
|
||||
@@ -792,13 +794,13 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
|
||||
self:T2( { self.PatrolMinSpeed, self.PatrolMaxSpeed, ToTargetSpeed } )
|
||||
|
||||
--- Obtain a 3D @{Point} from the 2D point + altitude.
|
||||
local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y )
|
||||
local ToTargetPointVec3 = COORDINATE:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToTargetRoutePoint = ToTargetPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
ToTargetSpeed,
|
||||
true
|
||||
)
|
||||
@@ -890,12 +892,12 @@ function AI_PATROL_ZONE:onafterRTB()
|
||||
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||
--local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
||||
local CurrentAltitude = self.Controllable:GetAltitude()
|
||||
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local CurrentPointVec3 = COORDINATE:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
COORDINATE.WaypointType.TurningPoint,
|
||||
COORDINATE.WaypointAction.TurningPoint,
|
||||
ToPatrolZoneSpeed,
|
||||
true
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
--- **Actions** - ACT_ACCOUNT_ classes **account for** (detect, count & report) various DCS events occurring on UNITs.
|
||||
--
|
||||
-- 
|
||||
-- 
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -8,9 +8,11 @@
|
||||
-- @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.
|
||||
@@ -133,7 +135,7 @@ do -- ACT_ACCOUNT
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ACCOUNT:onafterEvent( ProcessUnit, From, Event, To, Event )
|
||||
function ACT_ACCOUNT:onafterEvent( ProcessUnit, From, Event, To )
|
||||
|
||||
self:__NoMore( 1 )
|
||||
end
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
--- (SP) (MP) (FSM) Accept or reject process for player (task) assignments.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- # @{#ACT_ASSIGN} FSM template class, extends @{Core.Fsm#FSM_PROCESS}
|
||||
--
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
--- (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.
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- # @{#ACT_ROUTE} FSM class, extends @{Core.Fsm#FSM_PROCESS}
|
||||
--
|
||||
-- ## ACT_ROUTE state machine:
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- # 1) MOOSE Cargo System.
|
||||
--
|
||||
-- #### Those who have used the mission editor, know that the DCS mission editor provides cargo facilities.
|
||||
@@ -275,14 +277,14 @@
|
||||
-- The cargo must be in the **Loaded** state.
|
||||
-- @function [parent=#CARGO] UnBoard
|
||||
-- @param #CARGO self
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Core.Point#POINT_VEC2) to where the cargo should run after onboarding. If not provided, the cargo will run to 60 meters behind the Carrier location.
|
||||
-- @param Core.Point#COORDINATE ToPointVec2 (optional) @{Core.Point#COORDINATE) to where the cargo should run after onboarding. If not provided, the cargo will run to 60 meters behind the Carrier location.
|
||||
|
||||
--- UnBoards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo from the Carrier.
|
||||
-- The cargo must be in the **Loaded** state.
|
||||
-- @function [parent=#CARGO] __UnBoard
|
||||
-- @param #CARGO self
|
||||
-- @param #number DelaySeconds The amount of seconds to delay the action.
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Core.Point#POINT_VEC2) to where the cargo should run after onboarding. If not provided, the cargo will run to 60 meters behind the Carrier location.
|
||||
-- @param Core.Point#COORDINATE ToPointVec2 (optional) @{Core.Point#COORDINATE) to where the cargo should run after onboarding. If not provided, the cargo will run to 60 meters behind the Carrier location.
|
||||
|
||||
|
||||
-- Load
|
||||
@@ -307,14 +309,14 @@
|
||||
-- The cargo must be in the **Loaded** state.
|
||||
-- @function [parent=#CARGO] UnLoad
|
||||
-- @param #CARGO self
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Core.Point#POINT_VEC2) to where the cargo will be placed after unloading. If not provided, the cargo will be placed 60 meters behind the Carrier location.
|
||||
-- @param Core.Point#COORDINATE ToPointVec2 (optional) @{Core.Point#COORDINATE) to where the cargo will be placed after unloading. If not provided, the cargo will be placed 60 meters behind the Carrier location.
|
||||
|
||||
--- UnLoads the cargo to a Carrier. The event will unload the cargo from the Carrier. There will be no movement simulated of the cargo loading.
|
||||
-- The cargo must be in the **Loaded** state.
|
||||
-- @function [parent=#CARGO] __UnLoad
|
||||
-- @param #CARGO self
|
||||
-- @param #number DelaySeconds The amount of seconds to delay the action.
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Core.Point#POINT_VEC2) to where the cargo will be placed after unloading. If not provided, the cargo will be placed 60 meters behind the Carrier location.
|
||||
-- @param Core.Point#COORDINATE ToPointVec2 (optional) @{Core.Point#COORDINATE) to where the cargo will be placed after unloading. If not provided, the cargo will be placed 60 meters behind the Carrier location.
|
||||
|
||||
-- State Transition Functions
|
||||
|
||||
@@ -467,7 +469,7 @@ do -- CARGO
|
||||
self.Type = Type
|
||||
self.Name = Name
|
||||
self.Weight = Weight or 0
|
||||
self.CargoObject = nil
|
||||
self.CargoObject = nil -- Wrapper.Group#GROUP
|
||||
self.CargoCarrier = nil -- Wrapper.Client#CLIENT
|
||||
self.Representable = false
|
||||
self.Slingloadable = false
|
||||
@@ -897,7 +899,7 @@ do -- CARGO
|
||||
|
||||
--- Get the current PointVec2 of the cargo.
|
||||
-- @param #CARGO self
|
||||
-- @return Core.Point#POINT_VEC2
|
||||
-- @return Core.Point#COORDINATE
|
||||
function CARGO:GetPointVec2()
|
||||
return self.CargoObject:GetPointVec2()
|
||||
end
|
||||
@@ -1094,7 +1096,7 @@ do -- CARGO_REPRESENTABLE
|
||||
|
||||
--- Route a cargo unit to a PointVec2.
|
||||
-- @param #CARGO_REPRESENTABLE self
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2
|
||||
-- @param Core.Point#COORDINATE ToPointVec2
|
||||
-- @param #number Speed
|
||||
-- @return #CARGO_REPRESENTABLE
|
||||
function CARGO_REPRESENTABLE:RouteTo( ToPointVec2, Speed )
|
||||
|
||||
@@ -22,6 +22,9 @@ do -- CARGO_CRATE
|
||||
-- @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.
|
||||
--
|
||||
@@ -114,7 +117,7 @@ do -- CARGO_CRATE
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2
|
||||
-- @param Core.Point#COORDINATE
|
||||
function CARGO_CRATE:onenterUnLoaded( From, Event, To, ToPointVec2 )
|
||||
--self:T( { ToPointVec2, From, Event, To } )
|
||||
|
||||
|
||||
@@ -22,9 +22,12 @@ 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:
|
||||
@@ -410,7 +413,7 @@ do -- CARGO_GROUP
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2
|
||||
-- @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 } )
|
||||
@@ -453,7 +456,7 @@ do -- CARGO_GROUP
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2
|
||||
-- @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 } )
|
||||
@@ -491,7 +494,7 @@ do -- CARGO_GROUP
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2
|
||||
-- @param Core.Point#COORDINATE ToPointVec2
|
||||
function CARGO_GROUP:onafterUnLoad( From, Event, To, ToPointVec2, ... )
|
||||
--self:T( { From, Event, To, ToPointVec2 } )
|
||||
|
||||
@@ -771,3 +774,4 @@ do -- CARGO_GROUP
|
||||
|
||||
|
||||
end -- CARGO_GROUP
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@ do -- CARGO_SLINGLOAD
|
||||
--
|
||||
-- # 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
|
||||
--
|
||||
|
||||
@@ -30,6 +30,8 @@ do -- CARGO_UNIT
|
||||
--
|
||||
-- # 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
|
||||
--
|
||||
@@ -72,7 +74,7 @@ do -- CARGO_UNIT
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2
|
||||
-- @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 } )
|
||||
@@ -145,7 +147,7 @@ do -- CARGO_UNIT
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2
|
||||
-- @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 } )
|
||||
@@ -171,7 +173,7 @@ do -- CARGO_UNIT
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2
|
||||
-- @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 } )
|
||||
@@ -197,7 +199,7 @@ do -- CARGO_UNIT
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2
|
||||
-- @param Core.Point#COORDINATE
|
||||
function CARGO_UNIT:onenterUnLoaded( From, Event, To, ToPointVec2 )
|
||||
self:T( { ToPointVec2, From, Event, To } )
|
||||
|
||||
|
||||
@@ -974,7 +974,7 @@ do -- Scheduling
|
||||
-- @param #BASE self
|
||||
-- @param #number Start Specifies the amount of seconds that will be waited before the scheduling is started, and the event function is called.
|
||||
-- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments.
|
||||
-- @param #table ... Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }.
|
||||
-- @param ... Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }.
|
||||
-- @return #string The Schedule ID of the planned schedule.
|
||||
function BASE:ScheduleOnce( Start, SchedulerFunction, ... )
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -577,13 +577,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")
|
||||
|
||||
@@ -872,6 +878,8 @@ end
|
||||
-- @return Wrapper.Group#GROUP The found GROUP.
|
||||
function DATABASE:FindGroup( GroupName )
|
||||
|
||||
if type(GroupName) ~= "string" or GroupName == "" then return end
|
||||
|
||||
local GroupFound = self.GROUPS[GroupName]
|
||||
|
||||
if GroupFound == nil and GroupName ~= nil and self.Templates.Groups[GroupName] == nil then
|
||||
@@ -1110,7 +1118,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
|
||||
@@ -1119,7 +1127,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
|
||||
@@ -1380,7 +1388,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
|
||||
|
||||
@@ -1392,7 +1400,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
|
||||
|
||||
@@ -1404,7 +1412,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
|
||||
|
||||
@@ -1697,7 +1705,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
|
||||
@@ -1812,7 +1820,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
|
||||
|
||||
@@ -1372,11 +1372,12 @@ function EVENT:onEvent( Event )
|
||||
-- Scenery
|
||||
---
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit.getName and Event.IniDCSUnit:getName() or "Scenery no name "..math.random(1,20000)
|
||||
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 )
|
||||
Event.IniCategory = Event.IniDCSUnit.getDesc and Event.IniDCSUnit:getDesc().category
|
||||
Event.IniTypeName = Event.initiator:isExist() and Event.IniDCSUnit:getTypeName() or "SCENERY"
|
||||
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"
|
||||
|
||||
elseif Event.IniObjectCategory == Object.Category.BASE then
|
||||
---
|
||||
@@ -1474,7 +1475,7 @@ 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 )
|
||||
@@ -1507,7 +1508,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
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
|
||||
do -- FSM
|
||||
|
||||
--- FSM class
|
||||
-- @type FSM
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field Core.Scheduler#SCHEDULER CallScheduler Call scheduler.
|
||||
|
||||
@@ -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.
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
@@ -206,7 +206,7 @@ end
|
||||
function MESSAGE:ToGroup( Group, Settings )
|
||||
self:F( Group.GroupName )
|
||||
|
||||
if Group then
|
||||
if Group and Group:IsAlive() then
|
||||
|
||||
if self.MessageType then
|
||||
local Settings = Settings or (Group and _DATABASE:GetPlayerSettings( Group:GetPlayerName() )) or _SETTINGS -- Core.Settings#SETTINGS
|
||||
@@ -231,7 +231,7 @@ end
|
||||
function MESSAGE:ToUnit( Unit, Settings )
|
||||
self:F( Unit.IdentifiableName )
|
||||
|
||||
if Unit then
|
||||
if Unit and Unit:IsAlive() then
|
||||
|
||||
if self.MessageType then
|
||||
local Settings = Settings or ( Unit and _DATABASE:GetPlayerSettings( Unit:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS
|
||||
@@ -452,7 +452,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.
|
||||
@@ -468,13 +468,13 @@ _MESSAGESRS = {}
|
||||
-- @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)
|
||||
|
||||
_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
|
||||
@@ -535,7 +535,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 +567,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 +589,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 +611,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
|
||||
@@ -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 Color as RGB table plus alpha value. Default {1, 0, 0, 1.0}.
|
||||
-- @param #number LineType 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
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 ) ) )
|
||||
|
||||
|
||||
+1392
-1371
File diff suppressed because it is too large
Load Diff
@@ -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.
|
||||
@@ -1631,7 +1648,7 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
|
||||
|
||||
if SpawnTemplate then
|
||||
|
||||
local PointVec3 = POINT_VEC3:New( SpawnTemplate.route.points[1].x, SpawnTemplate.route.points[1].alt, SpawnTemplate.route.points[1].y )
|
||||
local PointVec3 = COORDINATE:New( SpawnTemplate.route.points[1].x, SpawnTemplate.route.points[1].alt, SpawnTemplate.route.points[1].y )
|
||||
--self:T2( { "Current point of ", self.SpawnTemplatePrefix, PointVec3 } )
|
||||
|
||||
-- If RandomizePosition, then Randomize the formation in the zone band, keeping the template.
|
||||
@@ -1650,6 +1667,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 +1849,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
|
||||
@@ -2830,7 +2856,7 @@ end
|
||||
function SPAWN:SpawnFromVec3( Vec3, SpawnIndex )
|
||||
--self:F( { self.SpawnTemplatePrefix, Vec3, SpawnIndex } )
|
||||
|
||||
local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 )
|
||||
local PointVec3 = COORDINATE:NewFromVec3( Vec3 )
|
||||
--self:T2( PointVec3 )
|
||||
|
||||
if SpawnIndex then
|
||||
@@ -2906,7 +2932,7 @@ end
|
||||
-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn.
|
||||
-- You can use the returned group to further define the route to be followed.
|
||||
-- @param #SPAWN self
|
||||
-- @param Core.Point#POINT_VEC3 PointVec3 The PointVec3 coordinates where to spawn the group.
|
||||
-- @param Core.Point#COORDINATE PointVec3 The COORDINATE coordinates where to spawn the group.
|
||||
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
|
||||
-- @return Wrapper.Group#GROUP that was spawned or #nil if nothing was spawned.
|
||||
-- @usage
|
||||
@@ -2954,12 +2980,12 @@ function SPAWN:SpawnFromVec2( Vec2, MinHeight, MaxHeight, SpawnIndex )
|
||||
return self:SpawnFromVec3( { x = Vec2.x, y = Height, z = Vec2.y }, SpawnIndex ) -- y can be nil. In this case, spawn on the ground for vehicles, and in the template altitude for air.
|
||||
end
|
||||
|
||||
--- Will spawn a group from a POINT_VEC2 in 3D space.
|
||||
--- Will spawn a group from a COORDINATE in 3D space.
|
||||
-- This method is mostly advisable to be used if you want to simulate spawning groups on the ground from air units, like vehicles.
|
||||
-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn.
|
||||
-- You can use the returned group to further define the route to be followed.
|
||||
-- @param #SPAWN self
|
||||
-- @param Core.Point#POINT_VEC2 PointVec2 The PointVec2 coordinates where to spawn the group.
|
||||
-- @param Core.Point#COORDINATE PointVec2 The coordinates where to spawn the group.
|
||||
-- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone.
|
||||
-- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone.
|
||||
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
|
||||
@@ -3495,6 +3521,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
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
--
|
||||
-- * @{#SPAWNSTATIC.Spawn}(Heading, NewName) spawns the static with the set parameters. Optionally, heading and name can be given. The name **must be unique**!
|
||||
-- * @{#SPAWNSTATIC.SpawnFromCoordinate}(Coordinate, Heading, NewName) spawn the static at the given coordinate. Optionally, heading and name can be given. The name **must be unique**!
|
||||
-- * @{#SPAWNSTATIC.SpawnFromPointVec2}(PointVec2, Heading, NewName) spawns the static at a POINT_VEC2 coordinate. Optionally, heading and name can be given. The name **must be unique**!
|
||||
-- * @{#SPAWNSTATIC.SpawnFromPointVec2}(PointVec2, Heading, NewName) spawns the static at a COORDINATE coordinate. Optionally, heading and name can be given. The name **must be unique**!
|
||||
-- * @{#SPAWNSTATIC.SpawnFromZone}(Zone, Heading, NewName) spawns the static at the center of a @{Core.Zone}. Optionally, heading and name can be given. The name **must be unique**!
|
||||
--
|
||||
-- @field #SPAWNSTATIC SPAWNSTATIC
|
||||
@@ -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
|
||||
@@ -302,12 +303,16 @@ end
|
||||
-- @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 #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.
|
||||
@@ -411,9 +455,9 @@ function SPAWNSTATIC:Spawn(Heading, NewName)
|
||||
|
||||
end
|
||||
|
||||
--- Creates a new @{Wrapper.Static} from a POINT_VEC2.
|
||||
--- Creates a new @{Wrapper.Static} from a COORDINATE.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param Core.Point#POINT_VEC2 PointVec2 The 2D coordinate where to spawn the static.
|
||||
-- @param Core.Point#COORDINATE PointVec2 The 2D coordinate where to spawn the static.
|
||||
-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360.
|
||||
-- @param #string NewName (Optional) The name of the new static.
|
||||
-- @return Wrapper.Static#STATIC The static 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
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -213,7 +214,7 @@ end
|
||||
|
||||
--- Returns if a PointVec3 is within the zone.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param Core.Point#POINT_VEC3 PointVec3 The PointVec3 to test.
|
||||
-- @param Core.Point#COORDINATE PointVec3 The PointVec3 to test.
|
||||
-- @return #boolean true if the PointVec3 is within the zone.
|
||||
function ZONE_BASE:IsPointVec3InZone( PointVec3 )
|
||||
local InZone = self:IsPointVec2InZone( PointVec3 )
|
||||
@@ -227,16 +228,16 @@ function ZONE_BASE:GetVec2()
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns a @{Core.Point#POINT_VEC2} of the zone.
|
||||
--- Returns a @{Core.Point#COORDINATE} of the zone.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param DCS#Distance Height The height to add to the land height where the center of the zone is located.
|
||||
-- @return Core.Point#POINT_VEC2 The PointVec2 of the zone.
|
||||
-- @return Core.Point#COORDINATE The COORDINATE of the zone.
|
||||
function ZONE_BASE:GetPointVec2()
|
||||
--self:F2( self.ZoneName )
|
||||
|
||||
local Vec2 = self:GetVec2()
|
||||
|
||||
local PointVec2 = POINT_VEC2:NewFromVec2( Vec2 )
|
||||
local PointVec2 = COORDINATE:NewFromVec2( Vec2 )
|
||||
|
||||
--self:T2( { PointVec2 } )
|
||||
|
||||
@@ -261,16 +262,16 @@ function ZONE_BASE:GetVec3( Height )
|
||||
return Vec3
|
||||
end
|
||||
|
||||
--- Returns a @{Core.Point#POINT_VEC3} of the zone.
|
||||
--- Returns a @{Core.Point#COORDINATE} of the zone.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param DCS#Distance Height The height to add to the land height where the center of the zone is located.
|
||||
-- @return Core.Point#POINT_VEC3 The PointVec3 of the zone.
|
||||
-- @return Core.Point#COORDINATE The PointVec3 of the zone.
|
||||
function ZONE_BASE:GetPointVec3( Height )
|
||||
--self:F2( self.ZoneName )
|
||||
|
||||
local Vec3 = self:GetVec3( Height )
|
||||
|
||||
local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 )
|
||||
local PointVec3 = COORDINATE:NewFromVec3( Vec3 )
|
||||
|
||||
--self:T2( { PointVec3 } )
|
||||
|
||||
@@ -330,16 +331,16 @@ function ZONE_BASE:GetRandomVec2()
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Define a random @{Core.Point#POINT_VEC2} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
|
||||
--- Define a random @{Core.Point#COORDINATE} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @return Core.Point#POINT_VEC2 The PointVec2 coordinates.
|
||||
-- @return Core.Point#COORDINATE The COORDINATE coordinates.
|
||||
function ZONE_BASE:GetRandomPointVec2()
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Define a random @{Core.Point#POINT_VEC3} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
|
||||
--- Define a random @{Core.Point#COORDINATE} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @return Core.Point#POINT_VEC3 The PointVec3 coordinates.
|
||||
-- @return Core.Point#COORDINATE The COORDINATE coordinates.
|
||||
function ZONE_BASE:GetRandomPointVec3()
|
||||
return nil
|
||||
end
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -814,8 +845,8 @@ end
|
||||
-- Various functions exist to find random points within the zone.
|
||||
--
|
||||
-- * @{#ZONE_RADIUS.GetRandomVec2}(): Gets a random 2D point in the zone.
|
||||
-- * @{#ZONE_RADIUS.GetRandomPointVec2}(): Gets a @{Core.Point#POINT_VEC2} object representing a random 2D point in the zone.
|
||||
-- * @{#ZONE_RADIUS.GetRandomPointVec3}(): Gets a @{Core.Point#POINT_VEC3} object representing a random 3D point in the zone. Note that the height of the point is at landheight.
|
||||
-- * @{#ZONE_RADIUS.GetRandomPointVec2}(): Gets a @{Core.Point#COORDINATE} object representing a random 2D point in the zone.
|
||||
-- * @{#ZONE_RADIUS.GetRandomPointVec3}(): Gets a @{Core.Point#COORDINATE} object representing a random 3D point in the zone. Note that the height of the point is at landheight.
|
||||
--
|
||||
-- ## Draw zone
|
||||
--
|
||||
@@ -1010,7 +1041,7 @@ function ZONE_RADIUS:SmokeZone( SmokeColor, Points, AddHeight, AngleOffset )
|
||||
local Radial = ( Angle + AngleOffset ) * RadialBase / 360
|
||||
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
|
||||
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
|
||||
POINT_VEC2:New( Point.x, Point.y, AddHeight ):Smoke( SmokeColor )
|
||||
COORDINATE:New( Point.x, AddHeight, Point.y ):Smoke( SmokeColor )
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -1040,7 +1071,7 @@ function ZONE_RADIUS:FlareZone( FlareColor, Points, Azimuth, AddHeight )
|
||||
local Radial = Angle * RadialBase / 360
|
||||
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
|
||||
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
|
||||
POINT_VEC2:New( Point.x, Point.y, AddHeight ):Flare( FlareColor, Azimuth )
|
||||
COORDINATE:New( Point.x, AddHeight, Point.y ):Flare( FlareColor, Azimuth )
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -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}
|
||||
@@ -1561,15 +1630,15 @@ function ZONE_RADIUS:GetRandomVec2(inner, outer, surfacetypes)
|
||||
return point
|
||||
end
|
||||
|
||||
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
|
||||
--- Returns a @{Core.Point#COORDINATE} object reflecting a random 2D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
|
||||
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
|
||||
-- @return Core.Point#POINT_VEC2 The @{Core.Point#POINT_VEC2} object reflecting the random 3D location within the zone.
|
||||
-- @return Core.Point#COORDINATE The @{Core.Point#COORDINATE} object reflecting the random 3D location within the zone.
|
||||
function ZONE_RADIUS:GetRandomPointVec2( inner, outer )
|
||||
--self:F( self.ZoneName, inner, outer )
|
||||
|
||||
local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2( inner, outer ) )
|
||||
local PointVec2 = COORDINATE:NewFromVec2( self:GetRandomVec2( inner, outer ) )
|
||||
|
||||
--self:T3( { PointVec2 } )
|
||||
|
||||
@@ -1592,15 +1661,15 @@ function ZONE_RADIUS:GetRandomVec3( inner, outer )
|
||||
end
|
||||
|
||||
|
||||
--- Returns a @{Core.Point#POINT_VEC3} object reflecting a random 3D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
|
||||
--- Returns a @{Core.Point#COORDINATE} object reflecting a random 3D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
|
||||
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
|
||||
-- @return Core.Point#POINT_VEC3 The @{Core.Point#POINT_VEC3} object reflecting the random 3D location within the zone.
|
||||
-- @return Core.Point#COORDINATE The @{Core.Point#COORDINATE} object reflecting the random 3D location within the zone.
|
||||
function ZONE_RADIUS:GetRandomPointVec3( inner, outer )
|
||||
--self:F( self.ZoneName, inner, outer )
|
||||
|
||||
local PointVec3 = POINT_VEC3:NewFromVec2( self:GetRandomVec2( inner, outer ) )
|
||||
local PointVec3 = COORDINATE:NewFromVec2( self:GetRandomVec2( inner, outer ) )
|
||||
|
||||
--self:T3( { PointVec3 } )
|
||||
|
||||
@@ -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.
|
||||
@@ -2036,15 +2136,15 @@ function ZONE_GROUP:GetRandomVec2()
|
||||
return Point
|
||||
end
|
||||
|
||||
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
|
||||
--- Returns a @{Core.Point#COORDINATE} object reflecting a random 2D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
|
||||
-- @param #ZONE_GROUP self
|
||||
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
|
||||
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
|
||||
-- @return Core.Point#POINT_VEC2 The @{Core.Point#POINT_VEC2} object reflecting the random 3D location within the zone.
|
||||
-- @return Core.Point#COORDINATE The @{Core.Point#COORDINATE} object reflecting the random 3D location within the zone.
|
||||
function ZONE_GROUP:GetRandomPointVec2( inner, outer )
|
||||
--self:F( self.ZoneName, inner, outer )
|
||||
|
||||
local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2() )
|
||||
local PointVec2 = COORDINATE:NewFromVec2( self:GetRandomVec2() )
|
||||
|
||||
--self:T3( { PointVec2 } )
|
||||
|
||||
@@ -2192,8 +2292,8 @@ end
|
||||
-- Various functions exist to find random points within the zone.
|
||||
--
|
||||
-- * @{#ZONE_POLYGON_BASE.GetRandomVec2}(): Gets a random 2D point in the zone.
|
||||
-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec2}(): Return a @{Core.Point#POINT_VEC2} object representing a random 2D point within the zone.
|
||||
-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec3}(): Return a @{Core.Point#POINT_VEC3} object representing a random 3D point at landheight within the zone.
|
||||
-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec2}(): Return a @{Core.Point#COORDINATE} object representing a random 2D point within the zone.
|
||||
-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec3}(): Return a @{Core.Point#COORDINATE} object representing a random 3D point at landheight within the zone.
|
||||
--
|
||||
-- ## Draw zone
|
||||
--
|
||||
@@ -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.
|
||||
@@ -2769,7 +2889,7 @@ function ZONE_POLYGON_BASE:SmokeZone( SmokeColor, Segments )
|
||||
for Segment = 0, Segments do -- We divide each line in 5 segments and smoke a point on the line.
|
||||
local PointX = self._.Polygon[i].x + ( Segment * DeltaX / Segments )
|
||||
local PointY = self._.Polygon[i].y + ( Segment * DeltaY / Segments )
|
||||
POINT_VEC2:New( PointX, PointY ):Smoke( SmokeColor )
|
||||
COORDINATE:New( PointX, 0, PointY ):Smoke( SmokeColor )
|
||||
end
|
||||
j = i
|
||||
i = i + 1
|
||||
@@ -2804,7 +2924,7 @@ function ZONE_POLYGON_BASE:FlareZone( FlareColor, Segments, Azimuth, AddHeight )
|
||||
for Segment = 0, Segments do -- We divide each line in 5 segments and smoke a point on the line.
|
||||
local PointX = self._.Polygon[i].x + ( Segment * DeltaX / Segments )
|
||||
local PointY = self._.Polygon[i].y + ( Segment * DeltaY / Segments )
|
||||
POINT_VEC2:New( PointX, PointY, AddHeight ):Flare(FlareColor, Azimuth)
|
||||
COORDINATE:New( PointX, AddHeight, PointY ):Flare(FlareColor, Azimuth)
|
||||
end
|
||||
j = i
|
||||
i = i + 1
|
||||
@@ -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
|
||||
@@ -2880,26 +3005,26 @@ function ZONE_POLYGON_BASE:GetRandomVec2()
|
||||
end
|
||||
end
|
||||
|
||||
--- Return a @{Core.Point#POINT_VEC2} object representing a random 2D point at landheight within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
|
||||
--- Return a @{Core.Point#COORDINATE} object representing a random 2D point at landheight within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
|
||||
-- @param #ZONE_POLYGON_BASE self
|
||||
-- @return @{Core.Point#POINT_VEC2}
|
||||
-- @return @{Core.Point#COORDINATE}
|
||||
function ZONE_POLYGON_BASE:GetRandomPointVec2()
|
||||
--self:F2()
|
||||
|
||||
local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2() )
|
||||
local PointVec2 = COORDINATE:NewFromVec2( self:GetRandomVec2() )
|
||||
|
||||
--self:T2( PointVec2 )
|
||||
|
||||
return PointVec2
|
||||
end
|
||||
|
||||
--- Return a @{Core.Point#POINT_VEC3} object representing a random 3D point at landheight within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
|
||||
--- Return a @{Core.Point#COORDINATE} object representing a random 3D point at landheight within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
|
||||
-- @param #ZONE_POLYGON_BASE self
|
||||
-- @return @{Core.Point#POINT_VEC3}
|
||||
-- @return @{Core.Point#COORDINATE}
|
||||
function ZONE_POLYGON_BASE:GetRandomPointVec3()
|
||||
--self:F2()
|
||||
|
||||
local PointVec3 = POINT_VEC3:NewFromVec2( self:GetRandomVec2() )
|
||||
local PointVec3 = COORDINATE:NewFromVec2( self:GetRandomVec2() )
|
||||
|
||||
--self:T2( PointVec3 )
|
||||
|
||||
@@ -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
|
||||
@@ -3931,18 +4049,18 @@ function ZONE_OVAL:GetRandomVec2()
|
||||
return {x=rx, y=ry}
|
||||
end
|
||||
|
||||
--- Define a random @{Core.Point#POINT_VEC2} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
|
||||
--- Define a random @{Core.Point#COORDINATE} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
|
||||
-- @param #ZONE_OVAL self
|
||||
-- @return Core.Point#POINT_VEC2 The PointVec2 coordinates.
|
||||
-- @return Core.Point#COORDINATE The COORDINATE coordinates.
|
||||
function ZONE_OVAL:GetRandomPointVec2()
|
||||
return POINT_VEC2:NewFromVec2(self:GetRandomVec2())
|
||||
return COORDINATE:NewFromVec2(self:GetRandomVec2())
|
||||
end
|
||||
|
||||
--- Define a random @{Core.Point#POINT_VEC2} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
|
||||
--- Define a random @{Core.Point#COORDINATE} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
|
||||
-- @param #ZONE_OVAL self
|
||||
-- @return Core.Point#POINT_VEC2 The PointVec2 coordinates.
|
||||
-- @return Core.Point#COORDINATE The COORDINATE coordinates.
|
||||
function ZONE_OVAL:GetRandomPointVec3()
|
||||
return POINT_VEC3:NewFromVec3(self:GetRandomVec2())
|
||||
return COORDINATE:NewFromVec3(self:GetRandomVec2())
|
||||
end
|
||||
|
||||
--- Draw the zone on the F10 map.
|
||||
@@ -4082,15 +4200,15 @@ do -- ZONE_AIRBASE
|
||||
return ZoneVec2
|
||||
end
|
||||
|
||||
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
|
||||
--- Returns a @{Core.Point#COORDINATE} object reflecting a random 2D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
|
||||
-- @param #ZONE_AIRBASE self
|
||||
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
|
||||
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
|
||||
-- @return Core.Point#POINT_VEC2 The @{Core.Point#POINT_VEC2} object reflecting the random 3D location within the zone.
|
||||
-- @return Core.Point#COORDINATE The @{Core.Point#COORDINATE} object reflecting the random 3D location within the zone.
|
||||
function ZONE_AIRBASE:GetRandomPointVec2( inner, outer )
|
||||
--self:F( self.ZoneName, inner, outer )
|
||||
|
||||
local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2() )
|
||||
local PointVec2 = COORDINATE:NewFromVec2( self:GetRandomVec2() )
|
||||
|
||||
--self:T3( { PointVec2 } )
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ function ZONE_DETECTION:SmokeZone( SmokeColor, Points, AddHeight, AngleOffset )
|
||||
local Radial = ( Angle + AngleOffset ) * RadialBase / 360
|
||||
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
|
||||
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
|
||||
POINT_VEC2:New( Point.x, Point.y, AddHeight ):Smoke( SmokeColor )
|
||||
COORDINATE:New( Point.x, AddHeight, Point.y):Smoke( SmokeColor )
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -138,7 +138,7 @@ function ZONE_DETECTION:FlareZone( FlareColor, Points, Azimuth, AddHeight )
|
||||
local Radial = Angle * RadialBase / 360
|
||||
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
|
||||
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
|
||||
POINT_VEC2:New( Point.x, Point.y, AddHeight ):Flare( FlareColor, Azimuth )
|
||||
COORDINATE:New( Point.x, AddHeight, Point.y ):Flare( FlareColor, Azimuth )
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -202,4 +202,3 @@ function ZONE_DETECTION:IsVec3InZone( Vec3 )
|
||||
|
||||
return InZone
|
||||
end
|
||||
|
||||
|
||||
@@ -198,7 +198,7 @@ end -- env
|
||||
|
||||
do -- radio
|
||||
|
||||
---@type radio
|
||||
--@type radio
|
||||
-- @field #radio.modulation modulation
|
||||
|
||||
---
|
||||
|
||||
@@ -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 Max number of alive Ai Helos at the same time. Defaults to three.
|
||||
-- @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,10 +529,20 @@ 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 #string Path Path to your SRS Server External Audio Component, e.g. "C:\\\\Program Files\\\\DCS-SimpleRadio-Standalone\\\\ExternalAudio"
|
||||
-- @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.
|
||||
@@ -538,7 +553,7 @@ function AICSAR:SetSRSRadio(OnOff,Path,Frequency,Modulation,SoundPath,Port)
|
||||
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.
|
||||
@@ -570,7 +585,7 @@ function AICSAR:SetSRSTTSRadio(OnOff,Path,Frequency,Modulation,Port,Voice,Cultur
|
||||
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
|
||||
@@ -693,7 +708,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 +723,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 +817,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 +845,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 +896,7 @@ function AICSAR:_GetFlight()
|
||||
:InitUnControlled(true)
|
||||
:OnSpawnGroup(
|
||||
function(Group)
|
||||
Group:OptionPreferVerticalLanding()
|
||||
self:__HeloOnDuty(1,Group)
|
||||
end
|
||||
)
|
||||
@@ -892,7 +920,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 +962,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 +1016,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 +1059,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
|
||||
@@ -780,7 +780,7 @@ ARTY.db={
|
||||
|
||||
--- Arty script version.
|
||||
-- @field #string version
|
||||
ARTY.version="1.3.3"
|
||||
ARTY.version="1.3.4"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -2268,7 +2268,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)
|
||||
|
||||
@@ -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)})
|
||||
@@ -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
|
||||
@@ -508,7 +508,7 @@ end
|
||||
function AUTOLASE:SetUsingSRS(OnOff,Path,Frequency,Modulation,Label,Gender,Culture,Port,Voice,Volume,PathToGoogleKey)
|
||||
if OnOff then
|
||||
self.useSRS = true
|
||||
self.SRSPath = Path or 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"
|
||||
@@ -1020,7 +1020,7 @@ 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
|
||||
if static and static:GetCoalition() ~= self.coalition and static:GetCoordinate() then
|
||||
local IsLOS = position:IsLOS(static:GetCoordinate())
|
||||
if IsLOS then
|
||||
unit:KnowUnit(static,true,true)
|
||||
|
||||
@@ -0,0 +1,999 @@
|
||||
--- **Functional** - Build large airborne formations of aircraft.
|
||||
--
|
||||
-- **Features:**
|
||||
--
|
||||
-- * Build in-air formations consisting of more than 40 aircraft as one group.
|
||||
-- * Build different formation types.
|
||||
-- * Assign a group leader that will guide the large formation path.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Additional Material:
|
||||
--
|
||||
-- * **Demo Missions:** [GitHub](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/FORMATION)
|
||||
-- * **Guides:** None
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions: **Applevangelist** (refactor)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Functional.FORMATION
|
||||
-- @image AI_Large_Formations.JPG
|
||||
|
||||
--- FORMATION class
|
||||
-- @type FORMATION
|
||||
-- @extends Core.Fsm#FSM_SET
|
||||
-- @field Wrapper.Unit#UNIT FollowUnit
|
||||
-- @field Core.Set#SET_GROUP FollowGroupSet
|
||||
-- @field #string FollowName
|
||||
-- @field Core.Scheduler#SCHEDULER FollowScheduler The instance of the SCHEDULER class.
|
||||
-- @field #number FollowDistance The current follow distance.
|
||||
-- @field DCS#AI.Option.Air.val.ROE OptionROE Which ROE is set to the FollowGroup.
|
||||
-- @field DCS#AI.Option.Air.val.REACTION_ON_THREAT OptionReactionOnThreat Which REACTION_ON_THREAT is set to the FollowGroup.
|
||||
-- @field #number dtFollow Time step between position updates.
|
||||
|
||||
|
||||
--- Build (large) formations, make AI follow a @{Wrapper.Client#CLIENT} (player) leader or a @{Wrapper.Unit#UNIT} (AI) leader.
|
||||
--
|
||||
-- FORMATION makes AI @{Wrapper.Group#GROUP}s fly in formation of various compositions.
|
||||
-- The FORMATION class models formations in a different manner than the internal DCS formation logic!!!
|
||||
-- The purpose of the class is to:
|
||||
--
|
||||
-- * Make formation building a process that can be managed while in flight, rather than a task.
|
||||
-- * Human players can guide formations, consisting of larget planes.
|
||||
-- * Build large formations (like a large bomber field).
|
||||
-- * Form formations that DCS does not support off-the-shelve.
|
||||
--
|
||||
-- A few remarks:
|
||||
--
|
||||
-- * Depending on the type of plane, the change in direction by the leader may result in the formation getting disentangled while in flight and needs to be rebuild.
|
||||
-- * Formations are vulnerable to collissions, but is depending on the type of plane, the distance between the planes and the speed and angle executed by the leader.
|
||||
-- * Formations may take a while to build up.
|
||||
--
|
||||
-- As a result, the FORMATION is not perfect, but is very useful to:
|
||||
--
|
||||
-- * Model large formations when flying straight line. You can build close formations when doing this.
|
||||
-- * Make humans guide a large formation, when the planes are wide from each other.
|
||||
--
|
||||
-- ## FORMATION construction
|
||||
--
|
||||
-- Create a new SPAWN object with the @{#FORMATION.New} method:
|
||||
--
|
||||
-- * @{#FORMATION.New}(): Creates a new FORMATION object from a @{Wrapper.Group#GROUP} for a @{Wrapper.Client#CLIENT} or a @{Wrapper.Unit#UNIT}, with an optional briefing text.
|
||||
--
|
||||
-- ## Formation methods
|
||||
--
|
||||
-- The following methods can be used to set or change the formation:
|
||||
--
|
||||
-- * @{#FORMATION.FormationLine}(): Form a line formation (core formation function).
|
||||
-- * @{#FORMATION.FormationTrail}(): Form a trail formation.
|
||||
-- * @{#FORMATION.FormationLeftLine}(): Form a left line formation.
|
||||
-- * @{#FORMATION.FormationRightLine}(): Form a right line formation.
|
||||
-- * @{#FORMATION.FormationRightWing}(): Form a right wing formation.
|
||||
-- * @{#FORMATION.FormationLeftWing}(): Form a left wing formation.
|
||||
-- * @{#FORMATION.FormationCenterWing}(): Form a center wing formation.
|
||||
-- * @{#FORMATION.FormationCenterVic}(): Form a Vic formation (same as CenterWing).
|
||||
-- * @{#FORMATION.FormationCenterBoxed}(): Form a center boxed formation.
|
||||
--
|
||||
-- ## Randomization
|
||||
--
|
||||
-- Use the method @{FORMATION#FORMATION.SetFlightRandomization}() to simulate the formation flying errors that pilots make while in formation. Is a range set in meters.
|
||||
--
|
||||
-- @usage
|
||||
-- local FollowGroupSet = SET_GROUP:New():FilterCategories("plane"):FilterCoalitions("blue"):FilterPrefixes("Follow"):FilterStart()
|
||||
-- local LeaderUnit = UNIT:FindByName( "Leader" )
|
||||
-- local LargeFormation = FORMATION:New( LeaderUnit, FollowGroupSet, "Center Wing Formation", "Briefing" )
|
||||
-- LargeFormation:FormationCenterWing( 500, 50, 0, 250, 250 )
|
||||
-- LargeFormation:__Start( 1 )
|
||||
--
|
||||
-- @field #FORMATION
|
||||
FORMATION = {
|
||||
ClassName = "FORMATION",
|
||||
FollowName = nil, -- The Follow Name
|
||||
FollowUnit = nil,
|
||||
FollowGroupSet = nil,
|
||||
FollowScheduler = nil,
|
||||
OptionROE = AI.Option.Air.val.ROE.OPEN_FIRE,
|
||||
OptionReactionOnThreat = AI.Option.Air.val.REACTION_ON_THREAT.ALLOW_ABORT_MISSION,
|
||||
dtFollow = 0.5,
|
||||
}
|
||||
|
||||
--- @type FORMATION.Formation
|
||||
-- @field #number None
|
||||
-- @field #number Line
|
||||
-- @field #number Trail
|
||||
-- @field #number Stack
|
||||
-- @field #number LeftLine
|
||||
-- @field #number RightLine
|
||||
-- @field #number LeftWing
|
||||
-- @field #number RightWing
|
||||
-- @field #number Vic
|
||||
-- @field #number Box
|
||||
FORMATION.Formation = {
|
||||
None = 1,
|
||||
Line = 2,
|
||||
Trail = 3,
|
||||
Stack = 4,
|
||||
LeftLine = 5,
|
||||
RightLine = 6,
|
||||
LeftWing = 7,
|
||||
RightWing = 8,
|
||||
Vic = 9,
|
||||
Box = 10,
|
||||
}
|
||||
|
||||
--- FORMATION class constructor for an AI group
|
||||
-- @param #FORMATION self
|
||||
-- @param Wrapper.Unit#UNIT FollowUnit The UNIT leading the FolllowGroupSet.
|
||||
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
|
||||
-- @param #string FollowName Name of the escort.
|
||||
-- @return #FORMATION self
|
||||
function FORMATION:New( FollowUnit, FollowGroupSet, FollowName )
|
||||
local self = BASE:Inherit( self, FSM_SET:New( FollowGroupSet ) )
|
||||
self:F( { FollowUnit, FollowGroupSet, FollowName } )
|
||||
|
||||
self.FollowUnit = FollowUnit -- Wrapper.Unit#UNIT
|
||||
self.FollowGroupSet = FollowGroupSet -- Core.Set#SET_GROUP
|
||||
|
||||
self:SetFlightRandomization( 2 )
|
||||
|
||||
self:SetStartState( "None" )
|
||||
|
||||
self:AddTransition( "*", "Stop", "Stopped" )
|
||||
|
||||
self:AddTransition( {"None", "Stopped"}, "Start", "Following" )
|
||||
|
||||
self:AddTransition( "*", "FormationLine", "*" )
|
||||
|
||||
--- Start Trigger for FORMATION
|
||||
-- @function [parent=#FORMATION] Start
|
||||
-- @param #FORMATION self
|
||||
|
||||
--- Start Asynchronous Trigger for FORMATION
|
||||
-- @function [parent=#FORMATION] __Start
|
||||
-- @param #FORMATION self
|
||||
-- @param #number Delay
|
||||
|
||||
--- Stop Trigger for FORMATION
|
||||
-- @function [parent=#FORMATION] Stop
|
||||
-- @param #FORMATION self
|
||||
|
||||
--- Stop Asynchronous Trigger for FORMATION
|
||||
-- @function [parent=#FORMATION] __Stop
|
||||
-- @param #FORMATION self
|
||||
-- @param #number Delay
|
||||
|
||||
--- FormationLine Handler OnAfter for FORMATION
|
||||
-- @function [parent=#FORMATION] OnAfterFormationLine
|
||||
-- @param #FORMATION self
|
||||
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationLine Trigger for FORMATION
|
||||
-- @function [parent=#FORMATION] FormationLine
|
||||
-- @param #FORMATION self
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationLine Asynchronous Trigger for FORMATION
|
||||
-- @function [parent=#FORMATION] __FormationLine
|
||||
-- @param #FORMATION self
|
||||
-- @param #number Delay
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
self:AddTransition( "*", "FormationTrail", "*" )
|
||||
|
||||
--- FormationTrail Handler OnAfter for FORMATION
|
||||
-- @function [parent=#FORMATION] OnAfterFormationTrail
|
||||
-- @param #FORMATION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
|
||||
--- FormationTrail Trigger for FORMATION
|
||||
-- @function [parent=#FORMATION] FormationTrail
|
||||
-- @param #FORMATION self
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
|
||||
--- FormationTrail Asynchronous Trigger for FORMATION
|
||||
-- @function [parent=#FORMATION] __FormationTrail
|
||||
-- @param #FORMATION self
|
||||
-- @param #number Delay
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
|
||||
self:AddTransition( "*", "FormationStack", "*" )
|
||||
|
||||
--- FormationStack Handler OnAfter for FORMATION
|
||||
-- @function [parent=#FORMATION] OnAfterFormationStack
|
||||
-- @param #FORMATION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
|
||||
--- FormationStack Trigger for FORMATION
|
||||
-- @function [parent=#FORMATION] FormationStack
|
||||
-- @param #FORMATION self
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
|
||||
--- FormationStack Asynchronous Trigger for FORMATION
|
||||
-- @function [parent=#FORMATION] __FormationStack
|
||||
-- @param #FORMATION self
|
||||
-- @param #number Delay
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
|
||||
self:AddTransition( "*", "FormationLeftLine", "*" )
|
||||
|
||||
--- FormationLeftLine Handler OnAfter for FORMATION
|
||||
-- @function [parent=#FORMATION] OnAfterFormationLeftLine
|
||||
-- @param #FORMATION self
|
||||
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationLeftLine Trigger for FORMATION
|
||||
-- @function [parent=#FORMATION] FormationLeftLine
|
||||
-- @param #FORMATION self
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationLeftLine Asynchronous Trigger for FORMATION
|
||||
-- @function [parent=#FORMATION] __FormationLeftLine
|
||||
-- @param #FORMATION self
|
||||
-- @param #number Delay
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
self:AddTransition( "*", "FormationRightLine", "*" )
|
||||
|
||||
--- FormationRightLine Handler OnAfter for FORMATION
|
||||
-- @function [parent=#FORMATION] OnAfterFormationRightLine
|
||||
-- @param #FORMATION self
|
||||
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationRightLine Trigger for FORMATION
|
||||
-- @function [parent=#FORMATION] FormationRightLine
|
||||
-- @param #FORMATION self
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationRightLine Asynchronous Trigger for FORMATION
|
||||
-- @function [parent=#FORMATION] __FormationRightLine
|
||||
-- @param #FORMATION self
|
||||
-- @param #number Delay
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
self:AddTransition( "*", "FormationLeftWing", "*" )
|
||||
|
||||
--- FormationLeftWing Handler OnAfter for FORMATION
|
||||
-- @function [parent=#FORMATION] OnAfterFormationLeftWing
|
||||
-- @param #FORMATION self
|
||||
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationLeftWing Trigger for FORMATION
|
||||
-- @function [parent=#FORMATION] FormationLeftWing
|
||||
-- @param #FORMATION self
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationLeftWing Asynchronous Trigger for FORMATION
|
||||
-- @function [parent=#FORMATION] __FormationLeftWing
|
||||
-- @param #FORMATION self
|
||||
-- @param #number Delay
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
self:AddTransition( "*", "FormationRightWing", "*" )
|
||||
|
||||
--- FormationRightWing Handler OnAfter for FORMATION
|
||||
-- @function [parent=#FORMATION] OnAfterFormationRightWing
|
||||
-- @param #FORMATION self
|
||||
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationRightWing Trigger for FORMATION
|
||||
-- @function [parent=#FORMATION] FormationRightWing
|
||||
-- @param #FORMATION self
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationRightWing Asynchronous Trigger for FORMATION
|
||||
-- @function [parent=#FORMATION] __FormationRightWing
|
||||
-- @param #FORMATION self
|
||||
-- @param #number Delay
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
self:AddTransition( "*", "FormationCenterWing", "*" )
|
||||
|
||||
--- FormationCenterWing Handler OnAfter for FORMATION
|
||||
-- @function [parent=#FORMATION] OnAfterFormationCenterWing
|
||||
-- @param #FORMATION self
|
||||
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationCenterWing Trigger for FORMATION
|
||||
-- @function [parent=#FORMATION] FormationCenterWing
|
||||
-- @param #FORMATION self
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationCenterWing Asynchronous Trigger for FORMATION
|
||||
-- @function [parent=#FORMATION] __FormationCenterWing
|
||||
-- @param #FORMATION self
|
||||
-- @param #number Delay
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
self:AddTransition( "*", "FormationVic", "*" )
|
||||
|
||||
--- FormationVic Handler OnAfter for FORMATION
|
||||
-- @function [parent=#FORMATION] OnAfterFormationVic
|
||||
-- @param #FORMATION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationVic Trigger for FORMATION
|
||||
-- @function [parent=#FORMATION] FormationVic
|
||||
-- @param #FORMATION self
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationVic Asynchronous Trigger for FORMATION
|
||||
-- @function [parent=#FORMATION] __FormationVic
|
||||
-- @param #FORMATION self
|
||||
-- @param #number Delay
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
self:AddTransition( "*", "FormationBox", "*" )
|
||||
|
||||
--- FormationBox Handler OnAfter for FORMATION
|
||||
-- @function [parent=#FORMATION] OnAfterFormationBox
|
||||
-- @param #FORMATION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
-- @param #number ZLevels The amount of levels on the Z-axis.
|
||||
|
||||
--- FormationBox Trigger for FORMATION
|
||||
-- @function [parent=#FORMATION] FormationBox
|
||||
-- @param #FORMATION self
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
-- @param #number ZLevels The amount of levels on the Z-axis.
|
||||
|
||||
--- FormationBox Asynchronous Trigger for FORMATION
|
||||
-- @function [parent=#FORMATION] __FormationBox
|
||||
-- @param #FORMATION self
|
||||
-- @param #number Delay
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
-- @param #number ZLevels The amount of levels on the Z-axis.
|
||||
|
||||
self:AddTransition( "*", "Follow", "Following" )
|
||||
|
||||
self:FormationLeftLine( 500, 0, 250, 250 )
|
||||
|
||||
self.FollowName = FollowName
|
||||
|
||||
self.CT1 = 0
|
||||
self.GT1 = 0
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set time interval between updates of the formation.
|
||||
-- @param #FORMATION self
|
||||
-- @param #number dt Time step in seconds between formation updates. Default is every 0.5 seconds.
|
||||
-- @return #FORMATION
|
||||
function FORMATION:SetFollowTimeInterval(dt)
|
||||
self.dtFollow=dt or 0.5
|
||||
return self
|
||||
end
|
||||
|
||||
--- This function is for test, it will put on the frequency of the FollowScheduler a red smoke at the direction vector calculated for the escort to fly to.
|
||||
-- This allows to visualize where the escort is flying to.
|
||||
-- @param #FORMATION self
|
||||
-- @param #boolean SmokeDirection If true, then the direction vector will be smoked.
|
||||
-- @return #FORMATION
|
||||
function FORMATION:TestSmokeDirectionVector( SmokeDirection )
|
||||
self.SmokeDirectionVector = ( SmokeDirection == true ) and true or false
|
||||
return self
|
||||
end
|
||||
|
||||
--- FormationLine Handler OnAfter for FORMATION
|
||||
-- @param #FORMATION self
|
||||
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
-- @return #FORMATION
|
||||
function FORMATION:onafterFormationLine( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, Formation )
|
||||
self:F( { FollowGroupSet, From , Event ,To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, Formation } )
|
||||
|
||||
XStart = XStart or self.XStart
|
||||
XSpace = XSpace or self.XSpace
|
||||
YStart = YStart or self.YStart
|
||||
YSpace = YSpace or self.YSpace
|
||||
ZStart = ZStart or self.ZStart
|
||||
ZSpace = ZSpace or self.ZSpace
|
||||
|
||||
local FollowSet = FollowGroupSet:GetSet()
|
||||
|
||||
local i = 1 --FF i=0 caused first unit to have no XSpace! Probably needs further adjustments. This is just a quick work around.
|
||||
|
||||
for FollowID, FollowGroup in pairs( FollowSet ) do
|
||||
|
||||
local PointVec3 = COORDINATE:New()
|
||||
PointVec3:SetX( XStart + i * XSpace )
|
||||
PointVec3:SetY( YStart + i * YSpace )
|
||||
PointVec3:SetZ( ZStart + i * ZSpace )
|
||||
|
||||
local Vec3 = PointVec3:GetVec3()
|
||||
FollowGroup:SetState( self, "FormationVec3", Vec3 )
|
||||
i = i + 1
|
||||
|
||||
FollowGroup:SetState( FollowGroup, "Formation", Formation )
|
||||
end
|
||||
|
||||
return self
|
||||
|
||||
end
|
||||
|
||||
--- FormationTrail Handler OnAfter for FORMATION
|
||||
-- @param #FORMATION self
|
||||
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @return #FORMATION
|
||||
function FORMATION:onafterFormationTrail( FollowGroupSet, From , Event , To, XStart, XSpace, YStart )
|
||||
|
||||
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,0,0, self.Formation.Trail )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- FormationStack Handler OnAfter for FORMATION
|
||||
-- @param #FORMATION self
|
||||
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @return #FORMATION
|
||||
function FORMATION:onafterFormationStack( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace )
|
||||
|
||||
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,0,0, self.Formation.Stack )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- FormationLeftLine Handler OnAfter for FORMATION
|
||||
-- @param #FORMATION self
|
||||
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
-- @return #FORMATION
|
||||
function FORMATION:onafterFormationLeftLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace )
|
||||
|
||||
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,0,YStart,0,-ZStart,-ZSpace, self.Formation.LeftLine )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- FormationRightLine Handler OnAfter for FORMATION
|
||||
-- @param #FORMATION self
|
||||
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
-- @return #FORMATION
|
||||
function FORMATION:onafterFormationRightLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace )
|
||||
|
||||
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,0,YStart,0,ZStart,ZSpace,self.Formation.RightLine)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- FormationLeftWing Handler OnAfter for FORMATION
|
||||
-- @param #FORMATION self
|
||||
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
function FORMATION:onafterFormationLeftWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace )
|
||||
|
||||
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,-ZStart,-ZSpace,self.Formation.LeftWing)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- FormationRightWing Handler OnAfter for FORMATION
|
||||
-- @function [parent=#FORMATION] OnAfterFormationRightWing
|
||||
-- @param #FORMATION self
|
||||
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
function FORMATION:onafterFormationRightWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace )
|
||||
|
||||
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,ZStart,ZSpace,self.Formation.RightWing)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- FormationCenterWing Handler OnAfter for FORMATION
|
||||
-- @param #FORMATION self
|
||||
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
function FORMATION:onafterFormationCenterWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace )
|
||||
|
||||
local FollowSet = FollowGroupSet:GetSet()
|
||||
|
||||
local i = 0
|
||||
|
||||
for FollowID, FollowGroup in pairs( FollowSet ) do
|
||||
|
||||
local PointVec3 = COORDINATE:New()
|
||||
|
||||
local Side = ( i % 2 == 0 ) and 1 or -1
|
||||
local Row = i / 2 + 1
|
||||
|
||||
PointVec3:SetX( XStart + Row * XSpace )
|
||||
PointVec3:SetY( YStart )
|
||||
PointVec3:SetZ( Side * ( ZStart + i * ZSpace ) )
|
||||
|
||||
local Vec3 = PointVec3:GetVec3()
|
||||
FollowGroup:SetState( self, "FormationVec3", Vec3 )
|
||||
i = i + 1
|
||||
FollowGroup:SetState( FollowGroup, "Formation", self.Formation.Vic )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- FormationVic Handle for FORMATION
|
||||
-- @param #FORMATION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
-- @return #FORMATION
|
||||
function FORMATION:onafterFormationVic( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace )
|
||||
|
||||
self:onafterFormationCenterWing(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,ZStart,ZSpace)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- FormationBox Handler OnAfter for FORMATION
|
||||
-- @param #FORMATION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
-- @param #number ZLevels The amount of levels on the Z-axis.
|
||||
-- @return #FORMATION
|
||||
function FORMATION:onafterFormationBox( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, ZLevels )
|
||||
|
||||
local FollowSet = FollowGroupSet:GetSet()
|
||||
|
||||
local i = 0
|
||||
|
||||
for FollowID, FollowGroup in pairs( FollowSet ) do
|
||||
|
||||
local PointVec3 = COORDINATE:New()
|
||||
|
||||
local ZIndex = i % ZLevels
|
||||
local XIndex = math.floor( i / ZLevels )
|
||||
local YIndex = math.floor( i / ZLevels )
|
||||
|
||||
PointVec3:SetX( XStart + XIndex * XSpace )
|
||||
PointVec3:SetY( YStart + YIndex * YSpace )
|
||||
PointVec3:SetZ( -ZStart - (ZSpace * ZLevels / 2 ) + ZSpace * ZIndex )
|
||||
|
||||
local Vec3 = PointVec3:GetVec3()
|
||||
FollowGroup:SetState( self, "FormationVec3", Vec3 )
|
||||
i = i + 1
|
||||
FollowGroup:SetState( FollowGroup, "Formation", self.Formation.Box )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Use the method @{FORMATION#FORMATION.SetFlightRandomization}() to make the air units in your formation randomize their flight a bit while in formation.
|
||||
-- @param #FORMATION self
|
||||
-- @param #number FlightRandomization The formation flying errors that pilots can make while in formation. Is a range set in meters.
|
||||
-- @return #FORMATION
|
||||
function FORMATION:SetFlightRandomization( FlightRandomization )
|
||||
self.FlightRandomization = FlightRandomization
|
||||
return self
|
||||
end
|
||||
|
||||
--- Stop function. Formation will not be updated any more.
|
||||
-- @param #FORMATION self
|
||||
-- @param Core.Set#SET_GROUP FollowGroupSet The following set of groups.
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To The to state.
|
||||
function FORMATION:onafterStop(FollowGroupSet, From, Event, To)
|
||||
self:E("Stopping formation.")
|
||||
end
|
||||
|
||||
--- Follow event fuction. Check if coming from state "stopped". If so the transition is rejected.
|
||||
-- @param #FORMATION self
|
||||
-- @param Core.Set#SET_GROUP FollowGroupSet The following set of groups.
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To The to state.
|
||||
function FORMATION:onbeforeFollow( FollowGroupSet, From, Event, To )
|
||||
if From=="Stopped" then
|
||||
return false -- Deny transition.
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Enter following state.
|
||||
-- @param #FORMATION self
|
||||
-- @param Core.Set#SET_GROUP FollowGroupSet The following set of groups.
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To The to state.
|
||||
function FORMATION:onenterFollowing( FollowGroupSet )
|
||||
|
||||
if self.FollowUnit:IsAlive() then
|
||||
|
||||
local ClientUnit = self.FollowUnit
|
||||
|
||||
local CT1, CT2, CV1, CV2
|
||||
CT1 = ClientUnit:GetState( self, "CT1" )
|
||||
|
||||
local CuVec3=ClientUnit:GetVec3()
|
||||
|
||||
if CT1 == nil or CT1 == 0 then
|
||||
ClientUnit:SetState( self, "CV1", CuVec3)
|
||||
ClientUnit:SetState( self, "CT1", timer.getTime() )
|
||||
else
|
||||
CT1 = ClientUnit:GetState( self, "CT1" )
|
||||
CT2 = timer.getTime()
|
||||
CV1 = ClientUnit:GetState( self, "CV1" )
|
||||
CV2 = CuVec3
|
||||
|
||||
ClientUnit:SetState( self, "CT1", CT2 )
|
||||
ClientUnit:SetState( self, "CV1", CV2 )
|
||||
end
|
||||
|
||||
--FollowGroupSet:ForEachGroupAlive( bla, self, ClientUnit, CT1, CV1, CT2, CV2)
|
||||
|
||||
for _,_group in pairs(FollowGroupSet:GetSet()) do
|
||||
local group=_group --Wrapper.Group#GROUP
|
||||
if group and group:IsAlive() then
|
||||
self:FollowMe(group, ClientUnit, CT1, CV1, CT2, CV2)
|
||||
end
|
||||
end
|
||||
|
||||
self:__Follow( -self.dtFollow )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Follow me.
|
||||
-- @param #FORMATION self
|
||||
-- @param Wrapper.Group#GROUP FollowGroup Follow group.
|
||||
-- @param Wrapper.Unit#UNIT ClientUnit Client Unit.
|
||||
-- @param DCS#Time CT1 Time
|
||||
-- @param DCS#Vec3 CV1 Vec3
|
||||
-- @param DCS#Time CT2 Time
|
||||
-- @param DCS#Vec3 CV2 Vec3
|
||||
function FORMATION:FollowMe(FollowGroup, ClientUnit, CT1, CV1, CT2, CV2)
|
||||
|
||||
if not self:Is("Stopped") then
|
||||
|
||||
self:T({Mode=FollowGroup:GetState( FollowGroup, "Mode" )})
|
||||
|
||||
FollowGroup:OptionROTEvadeFire()
|
||||
FollowGroup:OptionROEReturnFire()
|
||||
|
||||
local GroupUnit = FollowGroup:GetUnit( 1 )
|
||||
|
||||
local GuVec3=GroupUnit:GetVec3()
|
||||
|
||||
local FollowFormation = FollowGroup:GetState( self, "FormationVec3" )
|
||||
|
||||
if FollowFormation then
|
||||
local FollowDistance = FollowFormation.x
|
||||
|
||||
local GT1 = GroupUnit:GetState( self, "GT1" )
|
||||
|
||||
if CT1 == nil or CT1 == 0 or GT1 == nil or GT1 == 0 then
|
||||
GroupUnit:SetState( self, "GV1", GuVec3)
|
||||
GroupUnit:SetState( self, "GT1", timer.getTime() )
|
||||
else
|
||||
local CD = ( ( CV2.x - CV1.x )^2 + ( CV2.y - CV1.y )^2 + ( CV2.z - CV1.z )^2 ) ^ 0.5
|
||||
local CT = CT2 - CT1
|
||||
|
||||
local CS = ( 3600 / CT ) * ( CD / 1000 ) / 3.6
|
||||
|
||||
local CDv = { x = CV2.x - CV1.x, y = CV2.y - CV1.y, z = CV2.z - CV1.z }
|
||||
local Ca = math.atan2( CDv.x, CDv.z )
|
||||
|
||||
local GT1 = GroupUnit:GetState( self, "GT1" )
|
||||
local GT2 = timer.getTime()
|
||||
|
||||
local GV1 = GroupUnit:GetState( self, "GV1" )
|
||||
local GV2 = GuVec3
|
||||
|
||||
GV2.x=GV2.x+math.random( -self.FlightRandomization / 2, self.FlightRandomization / 2 )
|
||||
GV2.y=GV2.y+math.random( -self.FlightRandomization / 2, self.FlightRandomization / 2 )
|
||||
GV2.z=GV2.z+math.random( -self.FlightRandomization / 2, self.FlightRandomization / 2 )
|
||||
|
||||
GroupUnit:SetState( self, "GT1", GT2 )
|
||||
GroupUnit:SetState( self, "GV1", GV2 )
|
||||
|
||||
local GD = ( ( GV2.x - GV1.x )^2 + ( GV2.y - GV1.y )^2 + ( GV2.z - GV1.z )^2 ) ^ 0.5
|
||||
local GT = GT2 - GT1
|
||||
|
||||
-- Calculate the distance
|
||||
local GDv = { x = GV2.x - CV1.x, y = GV2.y - CV1.y, z = GV2.z - CV1.z }
|
||||
local Alpha_T = math.atan2( GDv.x, GDv.z ) - math.atan2( CDv.x, CDv.z )
|
||||
local Alpha_R = ( Alpha_T < 0 ) and Alpha_T + 2 * math.pi or Alpha_T
|
||||
local Position = math.cos( Alpha_R )
|
||||
local GD = ( ( GDv.x )^2 + ( GDv.z )^2 ) ^ 0.5
|
||||
local Distance = GD * Position + - CS * 0.5
|
||||
|
||||
-- Calculate the group direction vector
|
||||
local GV = { x = GV2.x - CV2.x, y = GV2.y - CV2.y, z = GV2.z - CV2.z }
|
||||
|
||||
-- Calculate GH2, GH2 with the same height as CV2.
|
||||
local GH2 = { x = GV2.x, y = CV2.y + FollowFormation.y, z = GV2.z }
|
||||
|
||||
-- Calculate the angle of GV to the orthonormal plane
|
||||
local alpha = math.atan2( GV.x, GV.z )
|
||||
|
||||
local GVx = FollowFormation.z * math.cos( Ca ) + FollowFormation.x * math.sin( Ca )
|
||||
local GVz = FollowFormation.x * math.cos( Ca ) - FollowFormation.z * math.sin( Ca )
|
||||
|
||||
-- Now we calculate the intersecting vector between the circle around CV2 with radius FollowDistance and GH2.
|
||||
-- From the GeoGebra model: CVI = (x(CV2) + FollowDistance cos(alpha), y(GH2) + FollowDistance sin(alpha), z(CV2))
|
||||
local Inclination = ( Distance + FollowFormation.x ) / 10
|
||||
if Inclination < -30 then
|
||||
Inclination = - 30
|
||||
end
|
||||
|
||||
local CVI = {
|
||||
x = CV2.x + CS * 10 * math.sin(Ca),
|
||||
y = GH2.y + Inclination, -- + FollowFormation.y,
|
||||
z = CV2.z + CS * 10 * math.cos(Ca),
|
||||
}
|
||||
|
||||
-- Calculate the direction vector DV of the escort group. We use CVI as the base and CV2 as the direction.
|
||||
local DV = { x = CV2.x - CVI.x, y = CV2.y - CVI.y, z = CV2.z - CVI.z }
|
||||
|
||||
-- We now calculate the unary direction vector DVu, so that we can multiply DVu with the speed, which is expressed in meters / s.
|
||||
-- We need to calculate this vector to predict the point the escort group needs to fly to according its speed.
|
||||
-- The distance of the destination point should be far enough not to have the aircraft starting to swipe left to right...
|
||||
local DVu = { x = DV.x / FollowDistance, y = DV.y, z = DV.z / FollowDistance }
|
||||
|
||||
-- Now we can calculate the group destination vector GDV.
|
||||
local GDV = { x = CVI.x, y = CVI.y, z = CVI.z }
|
||||
|
||||
local ADDx = FollowFormation.x * math.cos(alpha) - FollowFormation.z * math.sin(alpha)
|
||||
local ADDz = FollowFormation.z * math.cos(alpha) + FollowFormation.x * math.sin(alpha)
|
||||
|
||||
local GDV_Formation = {
|
||||
x = GDV.x - GVx,
|
||||
y = GDV.y,
|
||||
z = GDV.z - GVz
|
||||
}
|
||||
|
||||
-- Debug smoke.
|
||||
if self.SmokeDirectionVector == true then
|
||||
trigger.action.smoke( GDV, trigger.smokeColor.Green )
|
||||
trigger.action.smoke( GDV_Formation, trigger.smokeColor.White )
|
||||
end
|
||||
|
||||
local Time = 120
|
||||
|
||||
local Speed = - ( Distance + FollowFormation.x ) / Time
|
||||
|
||||
if Distance > -10000 then
|
||||
Speed = - ( Distance + FollowFormation.x ) / 60
|
||||
end
|
||||
|
||||
if Distance > -2500 then
|
||||
Speed = - ( Distance + FollowFormation.x ) / 20
|
||||
end
|
||||
|
||||
local GS = Speed + CS
|
||||
|
||||
--self:F( { Distance = Distance, Speed = Speed, CS = CS, GS = GS } )
|
||||
|
||||
-- Now route the escort to the desired point with the desired speed.
|
||||
FollowGroup:RouteToVec3( GDV_Formation, GS ) -- DCS models speed in Mps (Miles per second)
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -22,7 +22,7 @@
|
||||
-- @module Functional.Mantis
|
||||
-- @image Functional.Mantis.jpg
|
||||
--
|
||||
-- Last Update: Mar 2025
|
||||
-- Last Update: December 2025
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
--- **MANTIS** class, extends Core.Base#BASE
|
||||
@@ -61,8 +61,13 @@
|
||||
-- @field #boolean checkforfriendlies If true, do not activate a SAM installation if a friendly aircraft is in firing range.
|
||||
-- @field #table FilterZones Table of Core.Zone#ZONE Zones Consider SAM groups in this zone(s) only for this MANTIS instance, must be handed as #table of Zone objects.
|
||||
-- @field #boolean SmokeDecoy If true, smoke short range SAM units as decoy if a plane is in firing range.
|
||||
-- @field #number SmokeDecoyColor Color to use, defaults to SMOKECOLOR.White
|
||||
-- @field #number checkcounter Counter for SAM Table refreshes
|
||||
-- @field #number SmokeDecoyColor Color to use, defaults to SMOKECOLOR.White.
|
||||
-- @field #number checkcounter Counter for SAM Table refreshes.
|
||||
-- @field #number DLinkCacheTime Seconds after which cached contacts in DLink will decay.
|
||||
-- @field #boolean logsamstatus Log SAM status in dcs.log every cycle if true.
|
||||
-- @field #boolean DetectAccoustic Set if we can also detect units accousticly.
|
||||
-- @field #number DetectAccousticRadius We can hear in this range.
|
||||
-- @field #table DetectAccousticCategories We can hear these categories.
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
@@ -74,10 +79,9 @@
|
||||
--
|
||||
-- * Moose derived Modular, Automatic and Network capable Targeting and Interception System.
|
||||
-- * Controls a network of SAM sites. Uses detection to switch on the SAM site closest to the enemy.
|
||||
-- * **Automatic mode** (default since 0.8) will set-up your SAM site network automatically for you
|
||||
-- * **Classic mode** behaves like before
|
||||
-- * Leverage evasiveness from SEAD, leverage attack range setting
|
||||
-- * Automatic setup of SHORAD based on groups of the class "short-range"
|
||||
-- * **Automatic mode** (default) will set-up your SAM site network automatically for you.
|
||||
-- * Leverage evasiveness from SEAD, leverage attack range setting.
|
||||
-- * Automatic setup of SHORAD based on groups of the class "short-range".
|
||||
--
|
||||
-- # 0. Base considerations and naming conventions
|
||||
--
|
||||
@@ -107,10 +111,15 @@
|
||||
-- * Patriot
|
||||
-- * Rapier
|
||||
-- * Roland
|
||||
-- * IRIS-T SLM
|
||||
-- * Pantsir S1
|
||||
-- * TOR M2
|
||||
-- * C-RAM
|
||||
-- * Silkworm (though strictly speaking this is a surface to ship missile)
|
||||
-- * SA-2, SA-3, SA-5, SA-6, SA-7, SA-8, SA-9, SA-10, SA-11, SA-13, SA-15, SA-19
|
||||
-- * SA-2, SA-3, SA-5, SA-6, SA-7, SA-8, SA-9, SA-10, SA-11, SA-13, SA-15, SA-19, SA-21, S-300VM, S-300V4, S-400
|
||||
-- * From IDF mod: STUNNER IDFA, TAMIR IDFA (Note all caps!)
|
||||
-- * From HDS (see note on HDS below): SA-2, SA-3, SA-10B, SA-10C, SA-12, SA-17, SA-20A, SA-20B, SA-23, HQ-2
|
||||
-- * From HDS (see note on HDS below): SA-2, SA-3, SA-10B, SA-10C, SA-12, SA-17, SA-20A, SA-20B, SA-23, HQ-2, SAMP/T Block 1, SAMP/T Block 1INT, SAMP/T Block2
|
||||
-- * Other Mods: Nike
|
||||
--
|
||||
-- * From SMA: RBS98M, RBS70, RBS90, RBS90M, RBS103A, RBS103B, RBS103AM, RBS103BM, Lvkv9040M
|
||||
-- **NOTE** If you are using the Swedish Military Assets (SMA), please note that the **group name** for RBS-SAM types also needs to contain the keyword "SMA"
|
||||
@@ -124,19 +133,20 @@
|
||||
-- * SA-2 (with V759 missile, e.g. "Red SAM SA-2 HDS")
|
||||
-- * SA-2 (with HQ-2 launcher, use HQ-2 in the group name, e.g. "Red SAM HQ-2" )
|
||||
-- * SA-3 (with V601P missile, e.g. "Red SAM SA-3 HDS")
|
||||
-- * SA-10B (overlap with other SA-10 types, e.g. "Red SAM SA-10B HDS")
|
||||
-- * SA-10C (overlap with other SA-10 types, e.g. "Red SAM SA-10C HDS")
|
||||
-- * SA-12 (launcher dependent range, e.g. "Red SAM SA-12 HDS")
|
||||
-- * SA-23 (launcher dependent range, e.g. "Red SAM SA-23 HDS")
|
||||
-- * SA-10B (overlap with other SA-10 types, e.g. "Red SAM SA-10B HDS" with 5P85CE launcher)
|
||||
-- * SA-10C (overlap with other SA-10 types, e.g. "Red SAM SA-10C HDS" with 5P85SE launcher)
|
||||
-- * SA-12 (launcher dependent range, e.g. "Red SAM SA-12 HDS 2" for the 9A82 variant and "Red SAM SA-12 HDS 1" for the 9A83 variant)
|
||||
-- * SA-23 (launcher dependent range, e.g. "Red SAM SA-23 HDS 2" for the 9A82ME variant and "Red SAM SA-23 HDS 1" for the 9A83ME variant)
|
||||
-- * SAMP/T (launcher dependent range, e.g. "Blue SAM SAMPT Block 1 HDS" for Block 1, "Blue SAM SAMPT Block 1INT HDS", "Blue SAM SAMPT Block 2 HDS")
|
||||
--
|
||||
-- The other HDS types work like the rest of the known SAM systems.
|
||||
--
|
||||
-- # 0.1 Set-up in the mission editor
|
||||
--
|
||||
-- Set up your SAM sites in the mission editor. Name the groups using a systematic approach like above.
|
||||
-- Set up your EWR system in the mission editor. Name the groups using a systematic approach like above. Can be e.g. AWACS or a combination of AWACS and Search Radars like e.g. EWR 1L13 etc.
|
||||
-- Set up your SAM sites in the mission editor. Name the groups using a systematic approach like above.Can be e.g. AWACS or a combination of AWACS and Search Radars like e.g. EWR 1L13 etc.
|
||||
-- Search Radars usually have "SR" or "STR" in their names. Use the encyclopedia in the mission editor to inform yourself.
|
||||
-- Set up your SHORAD systems. They need to be **close** to (i.e. around) the SAM sites to be effective. Use **one** group per SAM location. SA-15 TOR systems offer a good missile defense.
|
||||
-- Set up your SHORAD systems. They need to be **close** to (i.e. around) the SAM sites to be effective. Use **one unit ** per group (multiple groups) for the SAM location.
|
||||
-- Else, evasive manoevers might club up all defenders in one place. Red SA-15 TOR systems offer a good missile defense.
|
||||
--
|
||||
-- [optional] Set up your HQ. Can be any group, e.g. a command vehicle.
|
||||
--
|
||||
@@ -188,7 +198,7 @@
|
||||
--
|
||||
-- ## 2.1 Auto mode features
|
||||
--
|
||||
-- ### 2.1.1 You can now add Accept-, Reject- and Conflict-Zones to your setup, e.g. to consider borders or de-militarized zones:
|
||||
-- ### 2.1.1 You can add Accept-, Reject- and Conflict-Zones to your setup, e.g. to consider borders or de-militarized zones:
|
||||
--
|
||||
-- -- Parameters are tables of Core.Zone#ZONE objects!
|
||||
-- -- This is effectively a 3-stage filter allowing for zone overlap. A coordinate is accepted first when
|
||||
@@ -205,9 +215,6 @@
|
||||
-- ### 2.1.3 SHORAD/Point defense will automatically be added from SAM sites of type "point" or if the range is less than 5km or if the type is AAA.
|
||||
--
|
||||
-- ### 2.1.4 Advanced features
|
||||
--
|
||||
-- -- Option to switch off auto mode **before** you start MANTIS (not recommended)
|
||||
-- mybluemantis.automode = false
|
||||
--
|
||||
-- -- Option to set the scale of the activation range, i.e. don't activate at the fringes of max range, defaults below.
|
||||
-- -- also see engagerange below.
|
||||
@@ -220,6 +227,12 @@
|
||||
--
|
||||
-- -- For some scenarios, like Cold War, it might be useful not to activate SAMs if friendly aircraft are around to avoid death by friendly fire.
|
||||
-- mybluemantis.checkforfriendlies = true
|
||||
--
|
||||
-- ### 2.1.6 Shoot & Scoot
|
||||
--
|
||||
-- -- Option to make the (driveable) SHORAD units drive around and shuffle positions
|
||||
-- -- We use a SET_ZONE for that, number of zones to consider defaults to three, Random is true for random coordinates and Formation is e.g. "Vee".
|
||||
-- mybluemantis:AddScootZones(ZoneSet, Number, Random, Formation)
|
||||
--
|
||||
-- # 3. Default settings [both modes unless stated otherwise]
|
||||
--
|
||||
@@ -242,26 +255,8 @@
|
||||
-- E.g. mymantis:SetAdvancedMode( true, 90 )
|
||||
--
|
||||
-- Use this option if you want to make use of or allow advanced SEAD tactics.
|
||||
--
|
||||
-- # 5. Integrate SHORAD [classic mode, not necessary in automode, not recommended for manual setup]
|
||||
--
|
||||
-- You can also choose to integrate Mantis with @{Functional.Shorad#SHORAD} for protection against HARMs and AGMs manually. When SHORAD detects a missile fired at one of MANTIS' SAM sites, it will activate SHORAD systems in
|
||||
-- the given defense checkradius around that SAM site. Create a SHORAD object first, then integrate with MANTIS like so:
|
||||
--
|
||||
-- local SamSet = SET_GROUP:New():FilterPrefixes("Blue SAM"):FilterCoalitions("blue"):FilterStart()
|
||||
-- myshorad = SHORAD:New("BlueShorad", "Blue SHORAD", SamSet, 22000, 600, "blue")
|
||||
-- -- now set up MANTIS
|
||||
-- mymantis = MANTIS:New("BlueMantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")
|
||||
-- mymantis:AddShorad(myshorad,720)
|
||||
-- mymantis:Start()
|
||||
--
|
||||
-- If you systematically name your SHORAD groups starting with "Blue SHORAD" you'll need exactly **one** SHORAD instance to manage all SHORAD groups.
|
||||
--
|
||||
-- (Optionally) you can remove the link later on with
|
||||
--
|
||||
-- mymantis:RemoveShorad()
|
||||
--
|
||||
-- # 6. Integrated SEAD
|
||||
-- # 5. Integrated SEAD
|
||||
--
|
||||
-- MANTIS is using @{Functional.Sead#SEAD} internally to both detect and evade HARM attacks. No extra efforts needed to set this up!
|
||||
-- Once a HARM attack is detected, MANTIS (via SEAD) will shut down the radars of the attacked SAM site and take evasive action by moving the SAM
|
||||
@@ -288,6 +283,7 @@
|
||||
MANTIS = {
|
||||
ClassName = "MANTIS",
|
||||
name = "mymantis",
|
||||
version = "0.9.42",
|
||||
SAM_Templates_Prefix = "",
|
||||
SAM_Group = nil,
|
||||
EWR_Templates_Prefix = "",
|
||||
@@ -336,6 +332,11 @@ MANTIS = {
|
||||
SmokeDecoy = false,
|
||||
SmokeDecoyColor = SMOKECOLOR.White,
|
||||
checkcounter = 1,
|
||||
DLinkCacheTime = 120,
|
||||
logsamstatus = false,
|
||||
DetectAccoustic = false,
|
||||
DetectAccousticRadius = 2000,
|
||||
DetectAccousticCategories = {Unit.Category.HELICOPTER},
|
||||
}
|
||||
|
||||
--- Advanced state enumerator
|
||||
@@ -374,7 +375,7 @@ MANTIS.radiusscale[MANTIS.SamType.POINT] = 3
|
||||
MANTIS.SamData = {
|
||||
["Hawk"] = { Range=35, Blindspot=0, Height=12, Type="Medium", Radar="Hawk" }, -- measures in km
|
||||
["NASAMS"] = { Range=14, Blindspot=0, Height=7, Type="Short", Radar="NSAMS" }, -- AIM 120B
|
||||
["Patriot"] = { Range=99, Blindspot=0, Height=25, Type="Long", Radar="Patriot" },
|
||||
["Patriot"] = { Range=99, Blindspot=0, Height=25, Type="Long", Radar="Patriot str" },
|
||||
["Rapier"] = { Range=10, Blindspot=0, Height=3, Type="Short", Radar="rapier" },
|
||||
["SA-2"] = { Range=40, Blindspot=7, Height=25, Type="Medium", Radar="S_75M_Volhov" },
|
||||
["SA-3"] = { Range=18, Blindspot=6, Height=18, Type="Short", Radar="5p73 s-125 ln" },
|
||||
@@ -382,7 +383,8 @@ MANTIS.SamData = {
|
||||
["SA-6"] = { Range=25, Blindspot=0, Height=8, Type="Medium", Radar="1S91" },
|
||||
["SA-10"] = { Range=119, Blindspot=0, Height=18, Type="Long" , Radar="S-300PS 4"},
|
||||
["SA-11"] = { Range=35, Blindspot=0, Height=20, Type="Medium", Radar="SA-11" },
|
||||
["Roland"] = { Range=5, Blindspot=0, Height=5, Type="Point", Radar="Roland" },
|
||||
["Roland"] = { Range=6, Blindspot=0, Height=5, Type="Short", Radar="Roland" },
|
||||
["Gepard"] = { Range=5, Blindspot=0, Height=4, Type="Point", Radar="Gepard" },
|
||||
["HQ-7"] = { Range=12, Blindspot=0, Height=3, Type="Short", Radar="HQ-7" },
|
||||
["SA-9"] = { Range=4, Blindspot=0, Height=3, Type="Point", Radar="Strela", Point="true" },
|
||||
["SA-8"] = { Range=10, Blindspot=0, Height=5, Type="Short", Radar="Osa 9A33" },
|
||||
@@ -393,14 +395,25 @@ MANTIS.SamData = {
|
||||
["Chaparral"] = { Range=8, Blindspot=0, Height=3, Type="Short", Radar="Chaparral" },
|
||||
["Linebacker"] = { Range=4, Blindspot=0, Height=3, Type="Point", Radar="Linebacker", Point="true" },
|
||||
["Silkworm"] = { Range=90, Blindspot=1, Height=0.2, Type="Long", Radar="Silkworm" },
|
||||
["C-RAM"] = { Range=2, Blindspot=0, Height=2, Type="Point", Radar="HEMTT_C-RAM_Phalanx", Point="true" },
|
||||
-- units from HDS Mod, multi launcher options is tricky
|
||||
["SA-10B"] = { Range=75, Blindspot=0, Height=18, Type="Medium" , Radar="SA-10B"},
|
||||
["SA-17"] = { Range=50, Blindspot=3, Height=30, Type="Medium", Radar="SA-17" },
|
||||
["SA-17"] = { Range=50, Blindspot=3, Height=50, Type="Medium", Radar="SA-17" },
|
||||
["SA-20A"] = { Range=150, Blindspot=5, Height=27, Type="Long" , Radar="S-300PMU1"},
|
||||
["SA-20B"] = { Range=200, Blindspot=4, Height=27, Type="Long" , Radar="S-300PMU2"},
|
||||
["SA-21"] = { Range=380, Blindspot=5, Height=30, Type="Long" , Radar="92N6E"},
|
||||
["S-300VM"] = { Range=200, Blindspot=5, Height=30, Type="Long" , Radar="9S32M"},
|
||||
["S-300V4"] = { Range=380, Blindspot=5, Height=30, Type="Long" , Radar="9S32M"},
|
||||
["S-400"] = { Range=250, Blindspot=5, Height=27, Type="Long" , Radar="92N6E"},
|
||||
["HQ-2"] = { Range=50, Blindspot=6, Height=35, Type="Medium", Radar="HQ_2_Guideline_LN" },
|
||||
["TAMIR IDFA"] = { Range=20, Blindspot=0.6, Height=12.3, Type="Short", Radar="IRON_DOME_LN" },
|
||||
["STUNNER IDFA"] = { Range=250, Blindspot=1, Height=45, Type="Long", Radar="DAVID_SLING_LN" },
|
||||
["STUNNER IDFA"] = { Range=250, Blindspot=1, Height=45, Type="Long", Radar="DAVID_SLING_LN" },
|
||||
["Nike"] = { Range=155, Blindspot=6, Height=30, Type="Long", Radar="HIPAR" },
|
||||
["Dog Ear"] = { Range=11, Blindspot=0, Height=9, Type="Point", Radar="Dog Ear", Point="true" },
|
||||
-- CH Added to DCS core 2.9.19.x
|
||||
["Pantsir S1"] = { Range=20, Blindspot=1.2, Height=15, Type="Point", Radar="PantsirS1" , Point="true" },
|
||||
["Tor M2"] = { Range=12, Blindspot=1, Height=10, Type="Point", Radar="TorM2", Point="true" },
|
||||
["IRIS-T SLM"] = { Range=40, Blindspot=0.5, Height=20, Type="Medium", Radar="CH_IRIST_SLM" },
|
||||
}
|
||||
|
||||
--- SAM data HDS
|
||||
@@ -416,13 +429,17 @@ MANTIS.SamDataHDS = {
|
||||
-- group name MUST contain HDS to ID launcher type correctly!
|
||||
["SA-2 HDS"] = { Range=56, Blindspot=7, Height=30, Type="Medium", Radar="V759" },
|
||||
["SA-3 HDS"] = { Range=20, Blindspot=6, Height=30, Type="Short", Radar="V-601P" },
|
||||
["SA-10C HDS 2"] = { Range=90, Blindspot=5, Height=25, Type="Long" , Radar="5P85DE ln"}, -- V55RUD
|
||||
["SA-10C HDS 1"] = { Range=90, Blindspot=5, Height=25, Type="Long" , Radar="5P85CE ln"}, -- V55RUD
|
||||
["SA-12 HDS 2"] = { Range=100, Blindspot=10, Height=25, Type="Long" , Radar="S-300V 9A82 l"},
|
||||
["SA-12 HDS 1"] = { Range=75, Blindspot=1, Height=25, Type="Long" , Radar="S-300V 9A83 l"},
|
||||
["SA-10B HDS"] = { Range=90, Blindspot=5, Height=25, Type="Long" , Radar="5P85CE ln"}, -- V55RUD
|
||||
["SA-10C HDS"] = { Range=75, Blindspot=5, Height=25, Type="Long" , Radar="5P85SE ln"}, -- V55RUD
|
||||
["SA-17 HDS"] = { Range=50, Blindspot=3, Height=50, Type="Medium", Radar="SA-17 " },
|
||||
["SA-12 HDS 2"] = { Range=100, Blindspot=13, Height=30, Type="Long" , Radar="S-300V 9A82 l"},
|
||||
["SA-12 HDS 1"] = { Range=75, Blindspot=6, Height=25, Type="Long" , Radar="S-300V 9A83 l"},
|
||||
["SA-23 HDS 2"] = { Range=200, Blindspot=5, Height=37, Type="Long", Radar="S-300VM 9A82ME" },
|
||||
["SA-23 HDS 1"] = { Range=100, Blindspot=1, Height=50, Type="Long", Radar="S-300VM 9A83ME" },
|
||||
["HQ-2 HDS"] = { Range=50, Blindspot=6, Height=35, Type="Medium", Radar="HQ_2_Guideline_LN" },
|
||||
["SAMPT Block 1 HDS"] = { Range=120, Blindspot=1, Height=20, Type="long", Radar="SAMPT_MLT_Blk1" }, -- Block 1 Launcher
|
||||
["SAMPT Block 1INT HDS"] = { Range=150, Blindspot=1, Height=25, Type="long", Radar="SAMPT_MLT_Blk1NT" }, -- Block 1-INT Launcher
|
||||
["SAMPT Block 2 HDS"] = { Range=200, Blindspot=10, Height=70, Type="long", Radar="SAMPT_MLT_Blk2" }, -- Block 2 Launcher
|
||||
}
|
||||
|
||||
--- SAM data SMA
|
||||
@@ -437,16 +454,16 @@ MANTIS.SamDataSMA = {
|
||||
-- units from SMA Mod (Sweedish Military Assets)
|
||||
-- https://forum.dcs.world/topic/295202-swedish-military-assets-for-dcs-by-currenthill/
|
||||
-- group name MUST contain SMA to ID launcher type correctly!
|
||||
["RBS98M SMA"] = { Range=20, Blindspot=0, Height=8, Type="Short", Radar="RBS-98" },
|
||||
["RBS70 SMA"] = { Range=8, Blindspot=0, Height=5.5, Type="Short", Radar="RBS-70" },
|
||||
["RBS70M SMA"] = { Range=8, Blindspot=0, Height=5.5, Type="Short", Radar="BV410_RBS70" },
|
||||
["RBS90 SMA"] = { Range=8, Blindspot=0, Height=5.5, Type="Short", Radar="RBS-90" },
|
||||
["RBS90M SMA"] = { Range=8, Blindspot=0, Height=5.5, Type="Short", Radar="BV410_RBS90" },
|
||||
["RBS103A SMA"] = { Range=150, Blindspot=3, Height=24.5, Type="Long", Radar="LvS-103_Lavett103_Rb103A" },
|
||||
["RBS103B SMA"] = { Range=35, Blindspot=0, Height=36, Type="Medium", Radar="LvS-103_Lavett103_Rb103B" },
|
||||
["RBS103AM SMA"] = { Range=150, Blindspot=3, Height=24.5, Type="Long", Radar="LvS-103_Lavett103_HX_Rb103A" },
|
||||
["RBS103BM SMA"] = { Range=35, Blindspot=0, Height=36, Type="Medium", Radar="LvS-103_Lavett103_HX_Rb103B" },
|
||||
["Lvkv9040M SMA"] = { Range=4, Blindspot=0, Height=2.5, Type="Point", Radar="LvKv9040",Point="true" },
|
||||
["RBS98M SMA"] = { Range=20, Blindspot=0.2, Height=8, Type="Short", Radar="RBS-98" },
|
||||
["RBS70 SMA"] = { Range=8, Blindspot=0.25, Height=6, Type="Short", Radar="RBS-70" },
|
||||
["RBS70M SMA"] = { Range=8, Blindspot=0.25, Height=6, Type="Short", Radar="BV410_RBS70" },
|
||||
["RBS90 SMA"] = { Range=8, Blindspot=0.25, Height=6, Type="Short", Radar="RBS-90" },
|
||||
["RBS90M SMA"] = { Range=8, Blindspot=0.25, Height=6, Type="Short", Radar="BV410_RBS90" },
|
||||
["RBS103A SMA"] = { Range=160, Blindspot=1, Height=36, Type="Long", Radar="LvS-103_Lavett103_Rb103A" },
|
||||
["RBS103B SMA"] = { Range=120, Blindspot=3, Height=24.5, Type="Long", Radar="LvS-103_Lavett103_Rb103B" },
|
||||
["RBS103AM SMA"] = { Range=160, Blindspot=1, Height=36, Type="Long", Radar="LvS-103_Lavett103_HX_Rb103A" },
|
||||
["RBS103BM SMA"] = { Range=120, Blindspot=3, Height=24.5, Type="Long", Radar="LvS-103_Lavett103_HX_Rb103B" },
|
||||
["Lvkv9040M SMA"] = { Range=2, Blindspot=0.1, Height=1.2, Type="Point", Radar="LvKv9040",Point="true" },
|
||||
}
|
||||
|
||||
--- SAM data CH
|
||||
@@ -461,47 +478,49 @@ MANTIS.SamDataCH = {
|
||||
-- units from CH (Military Assets by Currenthill)
|
||||
-- https://www.currenthill.com/
|
||||
-- group name MUST contain CHM to ID launcher type correctly!
|
||||
["2S38 CHM"] = { Range=8, Blindspot=0.5, Height=6, Type="Short", Radar="2S38" },
|
||||
["PantsirS1 CHM"] = { Range=20, Blindspot=1.2, Height=15, Type="Short", Radar="PantsirS1" },
|
||||
["2S38 CHM"] = { Range=6, Blindspot=0.1, Height=4.5, Type="Short", Radar="2S38" },
|
||||
["PantsirS1 CHM"] = { Range=20, Blindspot=1.2, Height=15, Type="Point", Radar="PantsirS1", Point="true" },
|
||||
["PantsirS2 CHM"] = { Range=30, Blindspot=1.2, Height=18, Type="Medium", Radar="PantsirS2" },
|
||||
["PGL-625 CHM"] = { Range=10, Blindspot=0.5, Height=5, Type="Short", Radar="PGL_625" },
|
||||
["HQ-17A CHM"] = { Range=20, Blindspot=1.5, Height=10, Type="Short", Radar="HQ17A" },
|
||||
["M903PAC2 CHM"] = { Range=160, Blindspot=3, Height=24.5, Type="Long", Radar="MIM104_M903_PAC2" },
|
||||
["M903PAC3 CHM"] = { Range=120, Blindspot=1, Height=40, Type="Long", Radar="MIM104_M903_PAC3" },
|
||||
["TorM2 CHM"] = { Range=12, Blindspot=1, Height=10, Type="Short", Radar="TorM2" },
|
||||
["TorM2K CHM"] = { Range=12, Blindspot=1, Height=10, Type="Short", Radar="TorM2K" },
|
||||
["TorM2M CHM"] = { Range=16, Blindspot=1, Height=10, Type="Short", Radar="TorM2M" },
|
||||
["PGL-625 CHM"] = { Range=10, Blindspot=1, Height=5, Type="Short", Radar="PGL_625" },
|
||||
["HQ-17A CHM"] = { Range=15, Blindspot=1.5, Height=10, Type="Short", Radar="HQ17A" },
|
||||
["M903PAC2 CHM"] = { Range=120, Blindspot=3, Height=24.5, Type="Long", Radar="MIM104_M903_PAC2" },
|
||||
["M903PAC3 CHM"] = { Range=160, Blindspot=1, Height=40, Type="Long", Radar="MIM104_M903_PAC3" },
|
||||
["TorM2 CHM"] = { Range=12, Blindspot=1, Height=10, Type="Point", Radar="TorM2", Point="true" },
|
||||
["TorM2K CHM"] = { Range=12, Blindspot=1, Height=10, Type="Point", Radar="TorM2K", Point="true" },
|
||||
["TorM2M CHM"] = { Range=16, Blindspot=1, Height=10, Type="Point", Radar="TorM2M", Point="true" },
|
||||
["NASAMS3-AMRAAMER CHM"] = { Range=50, Blindspot=2, Height=35.7, Type="Medium", Radar="CH_NASAMS3_LN_AMRAAM_ER" },
|
||||
["NASAMS3-AIM9X2 CHM"] = { Range=20, Blindspot=0.2, Height=18, Type="Short", Radar="CH_NASAMS3_LN_AIM9X2" },
|
||||
["C-RAM CHM"] = { Range=2, Blindspot=0, Height=2, Type="Point", Radar="CH_Centurion_C_RAM", Point="true" },
|
||||
["PGZ-09 CHM"] = { Range=4, Blindspot=0, Height=3, Type="Point", Radar="CH_PGZ09", Point="true" },
|
||||
["S350-9M100 CHM"] = { Range=15, Blindspot=1.5, Height=8, Type="Short", Radar="CH_S350_50P6_9M100" },
|
||||
["PGZ-09 CHM"] = { Range=4, Blindspot=0.5, Height=3, Type="Point", Radar="CH_PGZ09", Point="true" },
|
||||
["S350-9M100 CHM"] = { Range=15, Blindspot=1, Height=8, Type="Short", Radar="CH_S350_50P6_9M100" },
|
||||
["S350-9M96D CHM"] = { Range=150, Blindspot=2.5, Height=30, Type="Long", Radar="CH_S350_50P6_9M96D" },
|
||||
["LAV-AD CHM"] = { Range=8, Blindspot=0.2, Height=4.8, Type="Short", Radar="CH_LAVAD" },
|
||||
["LAV-AD CHM"] = { Range=8, Blindspot=0.16, Height=4.8, Type="Short", Radar="CH_LAVAD" },
|
||||
["HQ-22 CHM"] = { Range=170, Blindspot=5, Height=27, Type="Long", Radar="CH_HQ22_LN" },
|
||||
["PGZ-95 CHM"] = { Range=2, Blindspot=0, Height=2, Type="Point", Radar="CH_PGZ95",Point="true" },
|
||||
["LD-3000 CHM"] = { Range=3, Blindspot=0, Height=3, Type="Point", Radar="CH_LD3000_stationary", Point="true" },
|
||||
["LD-3000M CHM"] = { Range=3, Blindspot=0, Height=3, Type="Point", Radar="CH_LD3000", Point="true" },
|
||||
["FlaRakRad CHM"] = { Range=8, Blindspot=1.5, Height=6, Type="Short", Radar="HQ17A" },
|
||||
["PGZ-95 CHM"] = { Range=2.5, Blindspot=0.5, Height=2, Type="Point", Radar="CH_PGZ95",Point="true" },
|
||||
["LD-3000 CHM"] = { Range=2.5, Blindspot=0.1, Height=3, Type="Point", Radar="CH_LD3000_stationary", Point="true" },
|
||||
["LD-3000M CHM"] = { Range=2.5, Blindspot=0.1, Height=3, Type="Point", Radar="CH_LD3000", Point="true" },
|
||||
["FlaRakRad CHM"] = { Range=8, Blindspot=1.5, Height=6, Type="Short", Radar="CH_FlaRakRad" },
|
||||
["IRIS-T SLM CHM"] = { Range=40, Blindspot=0.5, Height=20, Type="Medium", Radar="CH_IRIST_SLM" },
|
||||
["M903PAC2KAT1 CHM"] = { Range=160, Blindspot=3, Height=24.5, Type="Long", Radar="CH_MIM104_M903_PAC2_KAT1" },
|
||||
["Skynex CHM"] = { Range=3.5, Blindspot=0, Height=3.5, Type="Point", Radar="CH_SkynexHX", Point="true" },
|
||||
["Skyshield CHM"] = { Range=3.5, Blindspot=0, Height=3.5, Type="Point", Radar="CH_Skyshield_Gun", Point="true" },
|
||||
["WieselOzelot CHM"] = { Range=8, Blindspot=0.2, Height=4.8, Type="Short", Radar="CH_Wiesel2Ozelot" },
|
||||
["M903PAC2KAT1 CHM"] = { Range=120, Blindspot=3, Height=24.5, Type="Long", Radar="CH_MIM104_M903_PAC2_KAT1" },
|
||||
["Skynex CHM"] = { Range=3.5, Blindspot=0.1, Height=3.5, Type="Point", Radar="CH_SkynexHX", Point="true" },
|
||||
["Skyshield CHM"] = { Range=3.5, Blindspot=0.1, Height=3.5, Type="Point", Radar="CH_Skyshield_Gun", Point="true" },
|
||||
["WieselOzelot CHM"] = { Range=8, Blindspot=0.16, Height=4.8, Type="Short", Radar="CH_Wiesel2Ozelot" },
|
||||
["BukM3-9M317M CHM"] = { Range=70, Blindspot=0.25, Height=35, Type="Medium", Radar="CH_BukM3_9A317M" },
|
||||
["BukM3-9M317MA CHM"] = { Range=70, Blindspot=0.25, Height=35, Type="Medium", Radar="CH_BukM3_9A317MA" },
|
||||
["SkySabre CHM"] = { Range=30, Blindspot=0.5, Height=10, Type="Medium", Radar="CH_SkySabreLN" },
|
||||
["Stormer CHM"] = { Range=7.5, Blindspot=0.3, Height=7, Type="Short", Radar="CH_StormerHVM" },
|
||||
["THAAD CHM"] = { Range=200, Blindspot=40, Height=150, Type="Long", Radar="CH_THAAD_M1120" },
|
||||
["USInfantryFIM92K CHM"] = { Range=8, Blindspot=0.2, Height=4.8, Type="Short", Radar="CH_USInfantry_FIM92" },
|
||||
["RBS98M CHM"] = { Range=20, Blindspot=0, Height=8, Type="Short", Radar="RBS-98" },
|
||||
["RBS70 CHM"] = { Range=8, Blindspot=0, Height=5.5, Type="Short", Radar="RBS-70" },
|
||||
["RBS90 CHM"] = { Range=8, Blindspot=0, Height=5.5, Type="Short", Radar="RBS-90" },
|
||||
["RBS103A CHM"] = { Range=150, Blindspot=3, Height=24.5, Type="Long", Radar="LvS-103_Lavett103_Rb103A" },
|
||||
["RBS103B CHM"] = { Range=35, Blindspot=0, Height=36, Type="Medium", Radar="LvS-103_Lavett103_Rb103B" },
|
||||
["RBS103AM CHM"] = { Range=150, Blindspot=3, Height=24.5, Type="Long", Radar="LvS-103_Lavett103_HX_Rb103A" },
|
||||
["RBS103BM CHM"] = { Range=35, Blindspot=0, Height=36, Type="Medium", Radar="LvS-103_Lavett103_HX_Rb103B" },
|
||||
["Lvkv9040M CHM"] = { Range=4, Blindspot=0, Height=2.5, Type="Point", Radar="LvKv9040", Point="true" },
|
||||
["USInfantryFIM92K CHM"] = { Range=8, Blindspot=0.16, Height=4.8, Type="Short", Radar="CH_USInfantry_FIM92" },
|
||||
["RBS98M CHM"] = { Range=20, Blindspot=0.2, Height=8, Type="Short", Radar="RBS-98" },
|
||||
["RBS70 CHM"] = { Range=8, Blindspot=0.25, Height=6, Type="Short", Radar="RBS-70" },
|
||||
["RBS70M CHM"] = { Range=8, Blindspot=0.25, Height=6, Type="Short", Radar="BV410_RBS70" },
|
||||
["RBS90 CHM"] = { Range=8, Blindspot=0.25, Height=6, Type="Short", Radar="RBS-90" },
|
||||
["RBS90M CHM"] = { Range=8, Blindspot=0.25, Height=6, Type="Short", Radar="BV410_RBS90" },
|
||||
["RBS103A CHM"] = { Range=160, Blindspot=1, Height=36, Type="Long", Radar="LvS-103_Lavett103_Rb103A" },
|
||||
["RBS103B CHM"] = { Range=120, Blindspot=3, Height=24.5, Type="Long", Radar="LvS-103_Lavett103_Rb103B" },
|
||||
["RBS103AM CHM"] = { Range=160, Blindspot=1, Height=36, Type="Long", Radar="LvS-103_Lavett103_HX_Rb103A" },
|
||||
["RBS103BM CHM"] = { Range=120, Blindspot=3, Height=24.5, Type="Long", Radar="LvS-103_Lavett103_HX_Rb103B" },
|
||||
["Lvkv9040M CHM"] = { Range=2, Blindspot=0.1, Height=1.2, Type="Point", Radar="LvKv9040",Point="true" },
|
||||
}
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
@@ -515,7 +534,7 @@ do
|
||||
--@param #string samprefix Prefixes for the SAM groups from the ME, e.g. all groups starting with "Red Sam..."
|
||||
--@param #string ewrprefix Prefixes for the EWR groups from the ME, e.g. all groups starting with "Red EWR..."
|
||||
--@param #string hq Group name of your HQ (optional)
|
||||
--@param #string coalition Coalition side of your setup, e.g. "blue", "red" or "neutral"
|
||||
--@param #string Coalition Coalition side of your setup, e.g. "blue", "red" or "neutral"
|
||||
--@param #boolean dynamic Use constant (true) filtering or just filter once (false, default) (optional)
|
||||
--@param #string awacs Group name of your Awacs (optional)
|
||||
--@param #boolean EmOnOff Make MANTIS switch Emissions on and off instead of changing the alarm state between RED and GREEN (optional)
|
||||
@@ -539,7 +558,7 @@ do
|
||||
-- mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")
|
||||
-- mybluemantis:Start()
|
||||
--
|
||||
function MANTIS:New(name,samprefix,ewrprefix,hq,coalition,dynamic,awacs, EmOnOff, Padding, Zones)
|
||||
function MANTIS:New(name,samprefix,ewrprefix,hq,Coalition,dynamic,awacs, EmOnOff, Padding, Zones)
|
||||
|
||||
|
||||
-- Inherit everything from BASE class.
|
||||
@@ -552,12 +571,14 @@ do
|
||||
-- DONE: Treat Awacs separately, since they might be >80km off site
|
||||
-- DONE: Allow tables of prefixes for the setup
|
||||
-- DONE: Auto-Mode with range setups for various known SAM types.
|
||||
-- DONE: Added reaction on HIT and UNIT LOST events.
|
||||
|
||||
self.name = name or "mymantis"
|
||||
self.SAM_Templates_Prefix = samprefix or "Red SAM"
|
||||
self.EWR_Templates_Prefix = ewrprefix or "Red EWR"
|
||||
self.HQ_Template_CC = hq or nil
|
||||
self.Coalition = coalition or "red"
|
||||
self.Coalition = Coalition or "red"
|
||||
self.coalition = Coalition == "blue" and coalition.side.BLUE or coalition.side.RED
|
||||
self.SAM_Table = {}
|
||||
self.SAM_Table_Long = {}
|
||||
self.SAM_Table_Medium = {}
|
||||
@@ -623,7 +644,8 @@ do
|
||||
self.advAwacs = false
|
||||
end
|
||||
|
||||
|
||||
self:SetDLinkCacheTime()
|
||||
|
||||
-- Set the string id for output to DCS.log file.
|
||||
self.lid=string.format("MANTIS %s | ", self.name)
|
||||
|
||||
@@ -656,6 +678,8 @@ do
|
||||
table.insert(self.ewr_templates,awacs)
|
||||
end
|
||||
|
||||
self.logsamstatus = false
|
||||
|
||||
self:T({self.ewr_templates})
|
||||
|
||||
self.SAM_Group = SET_GROUP:New():FilterPrefixes(self.SAM_Templates_Prefix):FilterCoalitions(self.Coalition)
|
||||
@@ -685,9 +709,6 @@ do
|
||||
-- counter for SAM table updates
|
||||
self.checkcounter = 1
|
||||
|
||||
-- TODO Version
|
||||
-- @field #string version
|
||||
self.version="0.9.27"
|
||||
self:I(string.format("***** Starting MANTIS Version %s *****", self.version))
|
||||
|
||||
--- FSM Functions ---
|
||||
@@ -707,6 +728,8 @@ do
|
||||
self:AddTransition("*", "SeadSuppressionStart", "*") -- SEAD has switched off one group.
|
||||
self:AddTransition("*", "SeadSuppressionEnd", "*") -- SEAD has switched on one group.
|
||||
self:AddTransition("*", "SeadSuppressionPlanned", "*") -- SEAD has planned a suppression.
|
||||
self:AddTransition("*", "SAMUnitHit", "*") -- A SAM unit was hit
|
||||
self:AddTransition("*", "SAMUnitLost", "*") -- A SAM Unit was lost
|
||||
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
|
||||
|
||||
------------------------
|
||||
@@ -817,6 +840,24 @@ do
|
||||
-- @param Wrapper.Group#GROUP Group The suppressed GROUP object
|
||||
-- @param #string Name Name of the suppressed group
|
||||
|
||||
--- On After "SAMUnitHit" event. A SAM Unit was hit.
|
||||
-- @function [parent=#MANTIS] OnAfterSeadSuppressionEnd
|
||||
-- @param #MANTIS self
|
||||
-- @param #string From The From State
|
||||
-- @param #string Event The Event
|
||||
-- @param #string To The To State
|
||||
-- @param Wrapper.Group#GROUP Group The GROUP of the hit UNIT object
|
||||
-- @param #string Name Name of the suppressed group
|
||||
|
||||
--- On After "SAMUnitLoast" event. A SAM Unit was lost.
|
||||
-- @function [parent=#MANTIS] OnAfterSeadSuppressionEnd
|
||||
-- @param #MANTIS self
|
||||
-- @param #string From The From State
|
||||
-- @param #string Event The Event
|
||||
-- @param #string To The To State
|
||||
-- @param Wrapper.Group#GROUP Group The GROUP of the lost UNIT object
|
||||
-- @param #string Name Name of the suppressed group
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -824,6 +865,138 @@ do
|
||||
-- MANTIS helper functions
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
--- Set to accept accoustic detection. Set this *before* MANTIS starts!
|
||||
-- @param #MANTIS self
|
||||
-- @param #number Radius Radius in which we can "hear" units. Defaults to 2000 meters.
|
||||
-- @param #table UnitCategories Set what Unit Categories we can "hear". Defaults to `{Unit.Category.HELICOPTER}`
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:SetAccousticDetectionOn(Radius,UnitCategories)
|
||||
self.DetectAccoustic = true
|
||||
self.DetectAccousticRadius = Radius or 2000
|
||||
self.DetectAccousticCategories = UnitCategories or {Unit.Category.HELICOPTER}
|
||||
return self
|
||||
end
|
||||
|
||||
--- Switch off accoustic detection.
|
||||
-- @param #MANTIS self
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:SetAccousticDetectionOff()
|
||||
self.DetectAccoustic = false
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] Function to manage hits on SAM units
|
||||
-- @param #MANTIS self
|
||||
-- @param Core.Event#EVENTDATA EventData The EVENT data
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:_EventHandler(EventData)
|
||||
self:T(self.lid .. "_EventHandler")
|
||||
|
||||
local function IsOneOfOurs(name)
|
||||
for _,_name in pairs(self.ewr_templates) do
|
||||
if string.find(name,_name,1,true) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function SwitchSAMOn(Name,Group)
|
||||
local suppressed = self.SuppressedGroups[Name] or false
|
||||
if not suppressed and self.SamStateTracker[Name] == "GREEN" then
|
||||
self.SamStateTracker[Name] = "RED"
|
||||
if self.UseEmOnOff then
|
||||
-- DONE: add emissions on/off
|
||||
Group:EnableEmission(true)
|
||||
elseif (not self.UseEmOnOff) then
|
||||
Group:OptionAlarmStateRed()
|
||||
end
|
||||
self:__RedState(1,Group)
|
||||
if self.SmokeDecoy == true then
|
||||
self:_SmokeUnits(Group)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local coordinate -- Core.Point#COORDINATE
|
||||
local Name -- #string
|
||||
local Group -- Wrapper.Group#GROUP
|
||||
local lasthit = 0
|
||||
local firsthit = false
|
||||
local alerton = false
|
||||
|
||||
-- Check if we can get a location
|
||||
|
||||
local data = EventData -- Core.Event#EVENTDATA
|
||||
if data.id == EVENTS.Hit then
|
||||
-- Unit hit, one of ours?
|
||||
if data.TgtGroupName and IsOneOfOurs(data.TgtGroupName) then
|
||||
self:T("Unit hit in group: "..data.TgtGroupName)
|
||||
if data.TgtGroup then
|
||||
lasthit = data.TgtGroup:GetProperty("MANTIS_LASTHIT")
|
||||
firsthit = (lasthit==nil) and true or false
|
||||
if firsthit == true then alerton = true end
|
||||
if lasthit ~= nil and timer.getTime()-lasthit > self.ShoradTime then alerton = true end
|
||||
coordinate = data.TgtGroup:GetCoordinate()
|
||||
Name = data.TgtGroupName
|
||||
Group = data.TgtGroup
|
||||
if alerton == true then
|
||||
self:__SAMUnitHit(1,Group,Name)
|
||||
SwitchSAMOn(Name,Group)
|
||||
end
|
||||
if coordinate and self.debug then
|
||||
local text = coordinate:ToStringMGRS()
|
||||
self:I("Location: "..text)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if data.id == EVENTS.UnitLost then
|
||||
if data.IniGroupName and IsOneOfOurs(data.IniGroupName) then
|
||||
self:T("Unit lost in group: "..data.IniGroupName)
|
||||
if data.IniGroup then
|
||||
lasthit = data.IniGroup:GetProperty("MANTIS_LASTHIT")
|
||||
firsthit = (lasthit==nil) and true or false
|
||||
if firsthit == true then alerton = true end
|
||||
if lasthit ~= nil and timer.getTime()-lasthit > self.ShoradTime then alerton = true end
|
||||
coordinate = data.IniGroup:GetCoordinate()
|
||||
Name = data.IniGroupName
|
||||
Group = data.IniGroup
|
||||
alerton = true
|
||||
SwitchSAMOn(Name,Group)
|
||||
self:__SAMUnitLost(1,Group,Name)
|
||||
if coordinate and self.debug then
|
||||
local text = coordinate:ToStringMGRS()
|
||||
self:I("Location: "..text)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if firsthit == true or alerton == true then
|
||||
Group:SetProperty("MANTIS_LASTHIT",timer.getTime())
|
||||
end
|
||||
|
||||
|
||||
if coordinate ~= nil and Name ~= nil and Group ~=nil and alerton == true then
|
||||
if self.ShoradLink then
|
||||
self:T("Shorad activated for: "..Name)
|
||||
local Shorad = self.Shorad -- Functional.Shorad#SHORAD
|
||||
local radius = self.checkradius
|
||||
local ontime = self.ShoradTime
|
||||
Shorad:WakeUpShorad(Name, radius, ontime, nil, true)
|
||||
self:__ShoradActivated(1,Name, radius, ontime)
|
||||
end
|
||||
if self.autorelocate and Group then
|
||||
Group:RelocateGroundRandomInRadius(20,500,true,true,nil,true)
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- [Internal] Function to get the self.SAM_Table
|
||||
-- @param #MANTIS self
|
||||
-- @return #table table
|
||||
@@ -884,19 +1057,121 @@ do
|
||||
self.AcceptZones = AcceptZones or {}
|
||||
self.RejectZones = RejectZones or {}
|
||||
self.ConflictZones = ConflictZones or {}
|
||||
if #self.AcceptZones > 0 or #self.RejectZones > 0 or #self.ConflictZones > 0 then
|
||||
self.AcceptZonesNo = UTILS.TableLength(self.AcceptZones)
|
||||
self.RejectZonesNo = UTILS.TableLength(self.RejectZones)
|
||||
self.ConflictZonesNo = UTILS.TableLength(self.ConflictZones)
|
||||
self:T(string.format("AcceptZonesNo = %d | RejectZonesNo = %d | ConflictZonesNo = %d",self.AcceptZonesNo,self.RejectZonesNo,self.ConflictZonesNo))
|
||||
if self.AcceptZonesNo > 0 or self.RejectZonesNo > 0 or self.ConflictZonesNo > 0 then
|
||||
self.usezones = true
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add a single reject zone to MANTIS.
|
||||
-- @param #MANTIS self
|
||||
-- @param Core.Zone#ZONE Zone The zone to be added
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:AddRejectZone(Zone)
|
||||
if Zone and Zone:IsInstanceOf("ZONE_BASE") then
|
||||
table.insert(self.RejectZones,Zone)
|
||||
self.usezones = true
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add a single accept zone to MANTIS.
|
||||
-- @param #MANTIS self
|
||||
-- @param Core.Zone#ZONE Zone The zone to be added
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:AddAcceptZone(Zone)
|
||||
if Zone and Zone:IsInstanceOf("ZONE_BASE") then
|
||||
table.insert(self.AcceptZones,Zone)
|
||||
self.usezones = true
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add a single conflict zone to MANTIS.
|
||||
-- @param #MANTIS self
|
||||
-- @param Core.Zone#ZONE Zone The zone to be added
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:AddConflictZone(Zone)
|
||||
if Zone and Zone:IsInstanceOf("ZONE_BASE") then
|
||||
table.insert(self.ConflictZones,Zone)
|
||||
self.usezones = true
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Function to set corridor zones.
|
||||
-- @param #MANTIS self
|
||||
-- @param Core.Set#SET_ZONE CorridorZones Can be handed in as SET\_ZONE or single ZONE object.
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:SetCorridorZones(CorridorZones)
|
||||
self:T(self.lid .. "SetCorridorZones")
|
||||
if CorridorZones and CorridorZones:IsInstanceOf("SET_ZONE") then
|
||||
self.corridorzones = CorridorZones
|
||||
self.usecorridors = true
|
||||
elseif CorridorZones and CorridorZones:IsInstanceOf("ZONE_BASE") then
|
||||
if not self.corridorzones then self.corridorzones = SET_ZONE:New() end
|
||||
self.corridorzones:AddZone(CorridorZones)
|
||||
self.usecorridors = true
|
||||
end
|
||||
if self.intelset then
|
||||
for _,_intel in pairs(self.intelset) do
|
||||
_intel:SetCorridorZones(self.corridorzones)
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Function to add one corridor zone.
|
||||
-- @param #MANTIS self
|
||||
-- @param Core.Zone#ZONE CorridorZone The ZONE object to be added.
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:AddCorridorZone(CorridorZone)
|
||||
self:T(self.lid .. "AddCorridorZone")
|
||||
self:SetCorridorZones(CorridorZone)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Function to set corridor zone floor and ceiling in FEET.
|
||||
-- @param #MANTIS self
|
||||
-- @param #number Floor Floor altitude ASL in feet.
|
||||
-- @param #number Ceiling Ceiling altitude ASL in feet.
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:SetCorridorZoneFloorAndCeiling(Floor,Ceiling)
|
||||
self.corridorfloor = UTILS.FeetToMeters(Floor)
|
||||
self.corridorceiling = UTILS.FeetToMeters(Ceiling)
|
||||
if self.intelset then
|
||||
for _,_intel in pairs(self.intelset) do
|
||||
_intel:SetCorridorLimits(self.corridorfloor,self.corridorceiling)
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Function to set corridor zone floor and ceiling in METERS.
|
||||
-- @param #MANTIS self
|
||||
-- @param #number Floor Floor altitude ASL in meters.
|
||||
-- @param #number Ceiling Ceiling altitude ASL in meters.
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:SetCorridorZoneFloorAndCeilingMeters(Floor,Ceiling)
|
||||
self.corridorfloor = Floor
|
||||
self.corridorceiling = Ceiling
|
||||
if self.intelset then
|
||||
for _,_intel in pairs(self.intelset) do
|
||||
_intel:SetCorridorLimits(self.corridorfloor,self.corridorceiling)
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Function to set the detection radius of the EWR in meters. (Deprecated, SAM range is used)
|
||||
-- @param #MANTIS self
|
||||
-- @param #number radius Radius of the EWR detection zone
|
||||
function MANTIS:SetEWRRange(radius)
|
||||
self:T(self.lid .. "SetEWRRange")
|
||||
--local radius = radius or 80000
|
||||
-- self.acceptrange = radius
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1037,6 +1312,16 @@ do
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Function to set how long INTEL DLINK remembers contacts.
|
||||
-- @param #MANTIS self
|
||||
-- @param #number seconds Remember this many seconds, at least 5 seconds.
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:SetDLinkCacheTime(seconds)
|
||||
self.DLinkCacheTime = math.abs(seconds or 120)
|
||||
if self.DLinkCacheTime < 5 then self.DLinkCacheTime = 5 end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Function to set the detection interval
|
||||
-- @param #MANTIS self
|
||||
@@ -1266,7 +1551,8 @@ do
|
||||
self:T(self.lid.."_CheckCoordinateInZones")
|
||||
local inzone = false
|
||||
-- acceptzones
|
||||
if #self.AcceptZones > 0 then
|
||||
self:T(string.format("AcceptZonesNo = %d | RejectZonesNo = %d | ConflictZonesNo = %d",self.AcceptZonesNo,self.RejectZonesNo,self.ConflictZonesNo))
|
||||
if self.AcceptZonesNo > 0 then
|
||||
for _,_zone in pairs(self.AcceptZones) do
|
||||
local zone = _zone -- Core.Zone#ZONE
|
||||
if zone:IsCoordinateInZone(coord) then
|
||||
@@ -1277,7 +1563,7 @@ do
|
||||
end
|
||||
end
|
||||
-- rejectzones
|
||||
if #self.RejectZones > 0 and inzone then -- maybe in accept zone, but check the overlaps
|
||||
if self.RejectZonesNo > 0 then
|
||||
for _,_zone in pairs(self.RejectZones) do
|
||||
local zone = _zone -- Core.Zone#ZONE
|
||||
if zone:IsCoordinateInZone(coord) then
|
||||
@@ -1288,7 +1574,7 @@ do
|
||||
end
|
||||
end
|
||||
-- conflictzones
|
||||
if #self.ConflictZones > 0 and not inzone then -- if not already accepted, might be in conflict zones
|
||||
if self.ConflictZonesNo > 0 then
|
||||
for _,_zone in pairs(self.ConflictZones) do
|
||||
local zone = _zone -- Core.Zone#ZONE
|
||||
if zone:IsCoordinateInZone(coord) then
|
||||
@@ -1301,11 +1587,12 @@ do
|
||||
return inzone
|
||||
end
|
||||
|
||||
--- [Internal] Function to prefilter height based
|
||||
--- [Internal] Function to prefilter height based and check for Helo activity.
|
||||
-- @param #MANTIS self
|
||||
-- @param #number height
|
||||
-- @param Core.Point#COORDINATE SamCoordinate
|
||||
-- @return #table set
|
||||
function MANTIS:_PreFilterHeight(height)
|
||||
function MANTIS:_PreFilterHeight(height,SamCoordinate)
|
||||
self:T(self.lid.."_PreFilterHeight")
|
||||
local set = {}
|
||||
local dlink = self.Detection -- Ops.Intel#INTEL_DLINK
|
||||
@@ -1314,8 +1601,22 @@ do
|
||||
local contact = _contact -- Ops.Intel#INTEL.Contact
|
||||
local grp = contact.group -- Wrapper.Group#GROUP
|
||||
if grp:IsAlive() then
|
||||
if grp:GetHeight(true) < height then
|
||||
local coord = grp:GetCoordinate()
|
||||
local coord = grp:GetCoordinate()
|
||||
local dist = 0
|
||||
local include = true
|
||||
if grp:IsGround() then include = false end
|
||||
if grp:GetCoalition() == self.coalition then include = false end
|
||||
if coord and SamCoordinate and grp:IsHelicopter() then
|
||||
dist = coord:Get2DDistance(SamCoordinate) or 0
|
||||
if dist > self.ShoradActDistance then include = false end -- we do not want long range shooting at helos
|
||||
end
|
||||
if self.debug then
|
||||
local text = "Looking at Group: "..grp:GetName() or "N/A"
|
||||
text = text .. " Include = "..tostring(include)
|
||||
MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.verbose):ToLog()
|
||||
end
|
||||
local grpalt = grp:GetHeight(true)
|
||||
if grpalt < height and grpalt > 10 and include == true then
|
||||
table.insert(set,coord)
|
||||
end
|
||||
end
|
||||
@@ -1339,7 +1640,7 @@ do
|
||||
local set = dectset
|
||||
if dlink then
|
||||
-- DEBUG
|
||||
set = self:_PreFilterHeight(height)
|
||||
set = self:_PreFilterHeight(height,samcoordinate)
|
||||
end
|
||||
--self.friendlyset -- Core.Set#SET_GROUP
|
||||
if self.checkforfriendlies == true and self.friendlyset == nil then
|
||||
@@ -1354,6 +1655,7 @@ do
|
||||
end
|
||||
-- check accept/reject zones
|
||||
local zonecheck = true
|
||||
self:T("self.usezones = "..tostring(self.usezones))
|
||||
if self.usezones then
|
||||
-- DONE
|
||||
zonecheck = self:_CheckCoordinateInZones(coord)
|
||||
@@ -1419,17 +1721,35 @@ do
|
||||
|
||||
self.intelset = {}
|
||||
|
||||
local IntelOne = INTEL:New(groupset,self.Coalition,self.name.." IntelOne")
|
||||
--IntelOne:SetClusterAnalysis(true,true)
|
||||
--IntelOne:SetClusterRadius(5000)
|
||||
local IntelOne = INTEL:New(groupset,self.coalition,self.name.." IntelOne")
|
||||
IntelOne.DetectAccoustic = self.DetectAccoustic
|
||||
IntelOne.DetectAccousticRadius = self.DetectAccousticRadius or 2000
|
||||
IntelOne.DetectAccousticUnitTypes = self.DetectAccousticCategories or {Unit.Category.HELICOPTER}
|
||||
--IntelOne:SetClusterAnalysis(true,true,true)
|
||||
if self.usecorridors == true then
|
||||
IntelOne:SetCorridorZones(self.corridorzones)
|
||||
if self.corridorfloor or self.corridorceiling then
|
||||
IntelOne:SetCorridorLimits(self.corridorfloor,self.corridorceiling)
|
||||
end
|
||||
end
|
||||
IntelOne:Start()
|
||||
|
||||
local IntelTwo = INTEL:New(samset,self.Coalition,self.name.." IntelTwo")
|
||||
--IntelTwo:SetClusterAnalysis(true,true)
|
||||
--IntelTwo:SetClusterRadius(5000)
|
||||
local IntelTwo = INTEL:New(samset,self.coalition,self.name.." IntelTwo")
|
||||
IntelTwo.DetectAccoustic = self.DetectAccoustic
|
||||
IntelTwo.DetectAccousticRadius = self.DetectAccousticRadius or 2000
|
||||
IntelTwo.DetectAccousticUnitTypes = self.DetectAccousticCategories or {Unit.Category.HELICOPTER}
|
||||
--IntelTwo:SetClusterAnalysis(true,true,true)
|
||||
if self.usecorridors == true then
|
||||
IntelTwo:SetCorridorZones(self.corridorzones)
|
||||
if self.corridorfloor or self.corridorceiling then
|
||||
IntelTwo:SetCorridorLimits(self.corridorfloor,self.corridorceiling)
|
||||
end
|
||||
end
|
||||
IntelTwo:Start()
|
||||
|
||||
local IntelDlink = INTEL_DLINK:New({IntelOne,IntelTwo},self.name.." DLINK",22,300)
|
||||
local CacheTime = self.DLinkCacheTime or 120
|
||||
local IntelDlink = INTEL_DLINK:New({IntelOne,IntelTwo},self.name.." DLINK",22,CacheTime)
|
||||
|
||||
IntelDlink:__Start(1)
|
||||
|
||||
self:SetUsingDLink(IntelDlink)
|
||||
@@ -1491,7 +1811,7 @@ do
|
||||
elseif chm then
|
||||
SAMData = self.SamDataCH
|
||||
end
|
||||
--self:T("Looking to auto-match for "..grpname)
|
||||
--self:I("Looking to auto-match for "..grpname)
|
||||
for _,_unit in pairs(units) do
|
||||
local unit = _unit -- Wrapper.Unit#UNIT
|
||||
local type = string.lower(unit:GetTypeName())
|
||||
@@ -1692,7 +2012,9 @@ do
|
||||
local grpname = group:GetName()
|
||||
local grpcoord = group:GetCoordinate()
|
||||
local grprange, grpheight,type,blind = self:_GetSAMRange(grpname)
|
||||
local radaralive = group:IsSAM()
|
||||
-- TODO the below might stop working at some point after some hours, needs testing
|
||||
--local radaralive = group:IsSAM()
|
||||
local radaralive = true
|
||||
table.insert( SAM_Tbl, {grpname, grpcoord, grprange, grpheight, blind, type}) -- make the table lighter, as I don't really use the zone here
|
||||
table.insert( SEAD_Grps, grpname )
|
||||
if type == MANTIS.SamType.LONG and radaralive then
|
||||
@@ -1751,6 +2073,27 @@ do
|
||||
self.ShoradLink = false
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] Function to smoke a group in decoy.
|
||||
-- @param #MANTIS self
|
||||
-- @param Wrapper.Group#GROUP Group
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:_SmokeUnits(Group)
|
||||
self:T("Smoking")
|
||||
local LastSmoketime=Group:GetProperty("MANTIS_LASTSMOKE_TIME") or 0
|
||||
local TNow = timer.getTime()
|
||||
if TNow - LastSmoketime > 290 then -- Smoking lasts 5 minutes
|
||||
Group:SetProperty("MANTIS_LASTSMOKE_TIME",TNow)
|
||||
local units = Group:GetUnits() or {}
|
||||
local smoke = self.SmokeDecoyColor or SMOKECOLOR.White
|
||||
for _,unit in pairs(units) do
|
||||
if unit and unit:IsAlive() then
|
||||
unit:GetCoordinate():Smoke(smoke)
|
||||
end
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- MANTIS main functions
|
||||
@@ -1778,7 +2121,7 @@ do
|
||||
local radius = _data[3]
|
||||
local height = _data[4]
|
||||
local blind = _data[5] * 1.25 + 1
|
||||
local shortsam = (_data[6] == MANTIS.SamType.SHORT) and true or false
|
||||
local shortsam = (_data[6] ~= MANTIS.SamType.LONG) and true or false
|
||||
if not shortsam then
|
||||
shortsam = (_data[6] == MANTIS.SamType.POINT) and true or false
|
||||
end
|
||||
@@ -1789,7 +2132,7 @@ do
|
||||
if self.Shorad and self.Shorad.ActiveGroups and self.Shorad.ActiveGroups[name] then
|
||||
activeshorad = true
|
||||
end
|
||||
if IsInZone and not suppressed and not activeshorad then --check any target in zone and not currently managed by SEAD
|
||||
if IsInZone and (not suppressed) and (not activeshorad) then --check any target in zone and not currently managed by SEAD
|
||||
if samgroup:IsAlive() then
|
||||
-- switch on SAM
|
||||
local switch = false
|
||||
@@ -1805,23 +2148,16 @@ do
|
||||
end
|
||||
if self.SamStateTracker[name] ~= "RED" and switch then
|
||||
self:__RedState(1,samgroup)
|
||||
self.SamStateTracker[name] = "RED"
|
||||
end
|
||||
-- TODO doesn't work
|
||||
if shortsam == true and self.SmokeDecoy == true then
|
||||
-- DONE Restrict on Distance
|
||||
if shortsam == true and self.SmokeDecoy == true and Distance < self.DetectAccousticRadius*1.5 then
|
||||
self:T("Smoking")
|
||||
local units = samgroup:GetUnits() or {}
|
||||
local smoke = self.SmokeDecoyColor or SMOKECOLOR.White
|
||||
for _,unit in pairs(units) do
|
||||
if unit and unit:IsAlive() then
|
||||
unit:GetCoordinate():Smoke(smoke)
|
||||
end
|
||||
end
|
||||
self:_SmokeUnits(samgroup)
|
||||
end
|
||||
-- link in to SHORAD if available
|
||||
-- DONE: Test integration fully
|
||||
if self.ShoradLink and (Distance < self.ShoradActDistance or Distance < blind ) then -- don't give SHORAD position away too early
|
||||
local Shorad = self.Shorad
|
||||
local Shorad = self.Shorad --Functional.Shorad#SHORAD
|
||||
local radius = self.checkradius
|
||||
local ontime = self.ShoradTime
|
||||
Shorad:WakeUpShorad(name, radius, ontime)
|
||||
@@ -1854,7 +2190,7 @@ do
|
||||
end --end alive
|
||||
end --end check
|
||||
end --for loop
|
||||
if self.debug or self.verbose then
|
||||
if self.debug or self.verbose or self.logsamstatus then
|
||||
for _,_status in pairs(self.SamStateTracker) do
|
||||
if _status == "GREEN" then
|
||||
instatusgreen=instatusgreen+1
|
||||
@@ -1875,8 +2211,9 @@ do
|
||||
-- @param #MANTIS self
|
||||
-- @param Functional.Detection#DETECTION_AREAS detection Detection object
|
||||
-- @param #boolean dlink
|
||||
-- @param #boolean reporttolog
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:_Check(detection,dlink)
|
||||
function MANTIS:_Check(detection,dlink,reporttolog)
|
||||
self:T(self.lid .. "Check")
|
||||
--get detected set
|
||||
local detset = detection:GetDetectedItemCoordinates()
|
||||
@@ -1903,7 +2240,8 @@ do
|
||||
local samset = self:_GetSAMTable() -- table of i.1=names, i.2=coordinates, i.3=firing range, i.4=firing height
|
||||
instatusred, instatusgreen, activeshorads = self:_CheckLoop(samset,detset,dlink,self.maxclassic)
|
||||
end
|
||||
if self.debug or self.verbose then
|
||||
|
||||
local function GetReport()
|
||||
local statusreport = REPORT:New("\nMANTIS Status "..self.name)
|
||||
statusreport:Add("+-----------------------------+")
|
||||
statusreport:Add(string.format("+ SAM in RED State: %2d",instatusred))
|
||||
@@ -1912,7 +2250,15 @@ do
|
||||
statusreport:Add(string.format("+ SHORAD active: %2d",activeshorads))
|
||||
end
|
||||
statusreport:Add("+-----------------------------+")
|
||||
return statusreport
|
||||
end
|
||||
|
||||
if self.debug or self.verbose then
|
||||
local statusreport = GetReport()
|
||||
MESSAGE:New(statusreport:Text(),10):ToAll():ToLog()
|
||||
elseif reporttolog == true then
|
||||
local statusreport = GetReport()
|
||||
MESSAGE:New(statusreport:Text(),10):ToLog()
|
||||
end
|
||||
return self
|
||||
end
|
||||
@@ -1991,13 +2337,9 @@ do
|
||||
else
|
||||
self.Detection = self:StartIntelDetection()
|
||||
end
|
||||
--[[
|
||||
if self.advAwacs and not self.automode then
|
||||
self.AWACS_Detection = self:StartAwacsDetection()
|
||||
end
|
||||
--]]
|
||||
|
||||
if self.autoshorad then
|
||||
self.Shorad = SHORAD:New(self.name.."-SHORAD","SHORAD",self.SAM_Group,self.ShoradActDistance,self.ShoradTime,self.coalition,self.UseEmOnOff)
|
||||
self.Shorad = SHORAD:New(self.name.."-SHORAD","SHORAD",self.SAM_Group,self.ShoradActDistance,self.ShoradTime,self.Coalition,self.UseEmOnOff,self.SmokeDecoy,self.SmokeDecoyColor)
|
||||
self.Shorad:SetDefenseLimits(80,95)
|
||||
self.ShoradLink = true
|
||||
self.Shorad.Groupset=self.ShoradGroupSet
|
||||
@@ -2006,6 +2348,10 @@ do
|
||||
if self.shootandscoot and self.SkateZones and self.Shorad then
|
||||
self.Shorad:AddScootZones(self.SkateZones,self.SkateNumber or 3,self.ScootRandom,self.ScootFormation)
|
||||
end
|
||||
|
||||
self:HandleEvent(EVENTS.Hit,self._EventHandler)
|
||||
self:HandleEvent(EVENTS.UnitLost,self._EventHandler)
|
||||
|
||||
self:__Status(-math.random(1,10))
|
||||
return self
|
||||
end
|
||||
@@ -2020,7 +2366,7 @@ do
|
||||
self:T({From, Event, To})
|
||||
-- check detection
|
||||
if not self.state2flag then
|
||||
self:_Check(self.Detection,self.DLink)
|
||||
self:_Check(self.Detection,self.DLink,self.logsamstatus)
|
||||
end
|
||||
|
||||
local EWRAlive = self:_CheckAnyEWRAlive()
|
||||
@@ -2091,7 +2437,7 @@ do
|
||||
if self.debug and self.verbose then
|
||||
self:I(self.lid .. "Status Report")
|
||||
for _name,_state in pairs(self.SamStateTracker) do
|
||||
self:I(string.format("Site %s\tStatus %s",_name,_state))
|
||||
self:I(string.format("Site %s | Status %s",_name,_state))
|
||||
end
|
||||
end
|
||||
local interval = self.detectinterval * -1
|
||||
|
||||
@@ -53,6 +53,8 @@
|
||||
--
|
||||
-- # 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.
|
||||
--
|
||||
|
||||
@@ -100,7 +100,7 @@ PSEUDOATC.id="PseudoATC | "
|
||||
|
||||
--- PSEUDOATC version.
|
||||
-- @field #number version
|
||||
PSEUDOATC.version="0.10.5"
|
||||
PSEUDOATC.version="0.10.6"
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -5584,7 +5584,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 +5603,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 +5614,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 +5649,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 +5712,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 +5773,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 +5800,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 +5825,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,"^(.+)#")
|
||||
|
||||
@@ -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
|
||||
@@ -603,7 +602,7 @@ RANGE.MenuF10Root = nil
|
||||
|
||||
--- Range script version.
|
||||
-- @field #string version
|
||||
RANGE.version = "2.8.0"
|
||||
RANGE.version = "2.8.1"
|
||||
|
||||
-- TODO list:
|
||||
-- TODO: Verbosity level for messages.
|
||||
@@ -1271,6 +1270,9 @@ 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
|
||||
self.controlsrsQ = MSRSQUEUE:New("CONTROL")
|
||||
|
||||
self.instructmsrs=MSRS:New(PathToSRS or MSRS.path, Frequency or 305, Modulation or radio.modulation.AM)
|
||||
@@ -1278,6 +1280,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
|
||||
@@ -1316,8 +1321,13 @@ function RANGE:SetSRSRangeControl( frequency, modulation, voice, culture, gender
|
||||
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
|
||||
@@ -1345,9 +1355,13 @@ function RANGE:SetSRSRangeInstructor( frequency, modulation, voice, culture, gen
|
||||
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
|
||||
@@ -2032,10 +2046,10 @@ function RANGE._OnImpact(weapon, self, playerData, attackHdg, attackAlt, attackV
|
||||
|
||||
-- Smoke impact point of bomb.
|
||||
if playerData and playerData.smokebombimpact and insidezone then
|
||||
if playerData and playerData.delaysmoke then
|
||||
timer.scheduleFunction( self._DelayedSmoke, { coord = impactcoord, color = playerData.smokecolor }, timer.getTime() + self.TdelaySmoke )
|
||||
if playerData.delaysmoke then
|
||||
impactcoord:Smoke(playerData.smokecolor, 30, self.TdelaySmoke)
|
||||
else
|
||||
impactcoord:Smoke( playerData.smokecolor )
|
||||
impactcoord:Smoke(playerData.smokecolor, 30)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2102,7 +2116,12 @@ function RANGE._OnImpact(weapon, self, playerData, attackHdg, attackAlt, attackV
|
||||
result.attackHdg = attackHdg
|
||||
result.attackVel = attackVel
|
||||
result.attackAlt = attackAlt
|
||||
result.date=os and os.date() or "n/a"
|
||||
if os and os.date then
|
||||
result.date=os.date()
|
||||
else
|
||||
self:E(self.lid.."os or os.date() not available")
|
||||
result.date = "n/a"
|
||||
end
|
||||
|
||||
-- Add to table.
|
||||
table.insert( _results, result )
|
||||
@@ -2635,13 +2654,6 @@ end
|
||||
-- Display Messages
|
||||
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Start smoking a coordinate with a delay.
|
||||
-- @param #table _args Argements passed.
|
||||
function RANGE._DelayedSmoke( _args )
|
||||
_args.coord:Smoke(_args.color)
|
||||
--trigger.action.smoke( _args.coord:GetVec3(), _args.color )
|
||||
end
|
||||
|
||||
--- Display top 10 stafing results of a specific player.
|
||||
-- @param #RANGE self
|
||||
-- @param #string _unitName Name of the player unit.
|
||||
|
||||
@@ -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,13 +331,67 @@ 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.
|
||||
@@ -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,19 @@ 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
|
||||
text = text..string.format("%s;;%d;;%d\n")
|
||||
end
|
||||
UTILS.SaveToFile(path,filename,text)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Close CSV file
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
@@ -161,8 +165,10 @@ do
|
||||
-- @param #number ActiveTimer Determines how many seconds the systems stay on red alert after wake-up call
|
||||
-- @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 Throw smoke decoy when getting activated. Defaults to false.
|
||||
-- @param #number SmokeDecoyColor 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.5")
|
||||
-- Set the string id for output to DCS.log file.
|
||||
self.lid=string.format("SHORAD %s | ", self.name)
|
||||
self:_InitState()
|
||||
@@ -451,6 +465,32 @@ do
|
||||
return returnname
|
||||
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
|
||||
@@ -537,7 +577,7 @@ do
|
||||
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(1,_group)
|
||||
@@ -552,6 +592,7 @@ do
|
||||
_group:EnableEmission(true)
|
||||
end
|
||||
_group:OptionAlarmStateRed()
|
||||
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
|
||||
@@ -684,6 +725,7 @@ do
|
||||
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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1132,7 +1132,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 +1175,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 +1190,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 +1208,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)
|
||||
@@ -3153,7 +3153,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 +3340,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
|
||||
@@ -3918,7 +3917,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 +4112,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()) 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)
|
||||
@@ -4247,6 +4246,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 +4445,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())
|
||||
@@ -5965,6 +5973,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 +6132,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 +6152,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
|
||||
|
||||
@@ -6893,7 +6903,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 +8597,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
|
||||
_WAREHOUSEDB.Assets[stockitem.uid]=nil
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
--
|
||||
-- # 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
|
||||
--
|
||||
|
||||
@@ -20,6 +20,13 @@ _DATABASE:_RegisterCargos()
|
||||
_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' )
|
||||
@@ -84,6 +85,7 @@ __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/Ops/Airboss.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/RecoveryTanker.lua' )
|
||||
@@ -116,6 +118,7 @@ __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/Ops/EasyA2G.lua' )
|
||||
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Balancer.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Air.lua' )
|
||||
@@ -186,4 +189,9 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Cargo_Dispatcher
|
||||
__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' )
|
||||
|
||||
@@ -32,6 +32,7 @@ __Moose.Include( 'Core\\MarkerOps_Base.lua' )
|
||||
__Moose.Include( 'Core\\TextAndSound.lua' )
|
||||
__Moose.Include( 'Core\\Condition.lua' )
|
||||
__Moose.Include( 'Core\\ClientMenu.lua' )
|
||||
__Moose.Include( 'Core\\Vector.lua' )
|
||||
|
||||
__Moose.Include( 'Wrapper\\Object.lua' )
|
||||
__Moose.Include( 'Wrapper\\Identifiable.lua' )
|
||||
@@ -178,4 +179,9 @@ __Moose.Include( 'Tasking\\Task_Cargo_Dispatcher.lua' )
|
||||
__Moose.Include( 'Tasking\\Task_Capture_Zone.lua' )
|
||||
__Moose.Include( 'Tasking\\Task_Capture_Dispatcher.lua' )
|
||||
|
||||
__Moose.Include( 'Navigation\\Point.lua' )
|
||||
__Moose.Include( 'Navigation\\Beacons.lua' )
|
||||
__Moose.Include( 'Navigation\\Radios.lua' )
|
||||
__Moose.Include( 'Navigation\\Towns.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 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 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 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 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 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 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 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 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 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
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -501,6 +501,7 @@ ATIS.Alphabet = {
|
||||
-- @field #number Kola +15° (East).
|
||||
-- @field #number Afghanistan +3° (East).
|
||||
-- @field #number Iraq +4.4° (East).
|
||||
-- @field #number GermanyCW +0.1° (East).
|
||||
ATIS.RunwayM2T = {
|
||||
Caucasus = 0,
|
||||
Nevada = 12,
|
||||
@@ -513,7 +514,8 @@ ATIS.RunwayM2T = {
|
||||
SinaiMap = 5,
|
||||
Kola = 15,
|
||||
Afghanistan = 3,
|
||||
Iraq=4.4
|
||||
Iraq=4.4,
|
||||
GermanyCW=0.1,
|
||||
}
|
||||
|
||||
--- Whether ICAO phraseology is used for ATIS broadcasts.
|
||||
@@ -530,6 +532,7 @@ ATIS.RunwayM2T = {
|
||||
-- @field #boolean Kola true.
|
||||
-- @field #boolean Afghanistan true.
|
||||
-- @field #boolean Iraq true.
|
||||
-- @field #boolean GermanyCW true.
|
||||
ATIS.ICAOPhraseology = {
|
||||
Caucasus = true,
|
||||
Nevada = false,
|
||||
@@ -543,6 +546,7 @@ ATIS.ICAOPhraseology = {
|
||||
Kola = true,
|
||||
Afghanistan = true,
|
||||
Iraq = true,
|
||||
GermanyCW = true,
|
||||
}
|
||||
|
||||
--- Nav point data.
|
||||
@@ -2794,7 +2798,7 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
|
||||
end
|
||||
_RUNACT = subtitle
|
||||
alltext = alltext .. ";\n" .. subtitle
|
||||
--alltext = alltext .. ";\n" .. subtitle
|
||||
|
||||
-- Runway length.
|
||||
if self.rwylength then
|
||||
|
||||
@@ -159,6 +159,8 @@ AIRWING = {
|
||||
-- @field #number refuelsystem Refueling system type: `0=Unit.RefuelingSystem.BOOM_AND_RECEPTACLE`, `1=Unit.RefuelingSystem.PROBE_AND_DROGUE`.
|
||||
-- @field #number noccupied Number of flights on this patrol point.
|
||||
-- @field Wrapper.Marker#MARKER marker F10 marker.
|
||||
-- @field #boolean IsZonePoint flag for using a (moving) zone as point for patrol etc.
|
||||
-- @field Core.Zone#ZONE_BASE patrolzone in case Patrol coordinate was handed as zone, store here.
|
||||
|
||||
--- Patrol zone.
|
||||
-- @type AIRWING.PatrolZone
|
||||
@@ -187,13 +189,14 @@ AIRWING = {
|
||||
|
||||
--- AIRWING class version.
|
||||
-- @field #string version
|
||||
AIRWING.version="0.9.6"
|
||||
AIRWING.version="0.9.7"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- ToDo list
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: Check that airbase has enough parking spots if a request is BIG.
|
||||
-- DONE: Allow (moving) zones as base for patrol points.
|
||||
-- DONE: Spawn in air ==> Needs WAREHOUSE update.
|
||||
-- DONE: Spawn hot.
|
||||
-- DONE: Make special request to transfer squadrons to anther airwing (or warehouse).
|
||||
@@ -323,6 +326,27 @@ function AIRWING:AddSquadron(Squadron)
|
||||
if Squadron:IsStopped() then
|
||||
Squadron:Start()
|
||||
end
|
||||
|
||||
-- if storage is limited, add the amount of aircraft needed
|
||||
local airbasename = self:GetAirbaseName()
|
||||
|
||||
if airbasename then
|
||||
local group = GROUP:FindByName(Squadron.templategroup)
|
||||
local Nunits = 1
|
||||
local units
|
||||
if group then units = group:GetUnits() end
|
||||
if units then Nunits = #units end
|
||||
local typename = Squadron.aircrafttype or "none"
|
||||
local NAssets = Squadron.Ngroups * Nunits
|
||||
local storage = STORAGE:New(airbasename)
|
||||
--self:T(self.lid.."Adding "..typename.." #"..NAssets)
|
||||
if storage and storage:IsLimitedAircraft() and typename ~= "none" then
|
||||
local NInStore = storage:GetItemAmount(typename) or 0
|
||||
if NAssets > NInStore then
|
||||
storage:AddItem(typename,NAssets)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -807,13 +831,22 @@ function AIRWING:_PatrolPointMarkerText(point)
|
||||
end
|
||||
|
||||
--- Update marker of the patrol point.
|
||||
-- @param #AIRWING self
|
||||
-- @param #AIRWING.PatrolData point Patrol point table.
|
||||
function AIRWING:UpdatePatrolPointMarker(point)
|
||||
if self.markpoints then -- sometimes there's a direct call from #OPSGROUP
|
||||
|
||||
if self and self.markpoints then -- sometimes there's a direct call from #OPSGROUP
|
||||
local text=string.format("%s Occupied=%d\nheading=%03d, leg=%d NM, alt=%d ft, speed=%d kts",
|
||||
point.type, point.noccupied, point.heading, point.leg, point.altitude, point.speed)
|
||||
|
||||
point.marker:UpdateText(text, 1)
|
||||
|
||||
if point.IsZonePoint and point.IsZonePoint == true and point.patrolzone then
|
||||
-- update position
|
||||
local Coordinate = point.patrolzone:GetCoordinate()
|
||||
point.marker:UpdateCoordinate(Coordinate)
|
||||
point.marker:UpdateText(text, 1.5)
|
||||
else
|
||||
point.marker:UpdateText(text, 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -821,7 +854,7 @@ end
|
||||
--- Create a new generic patrol point.
|
||||
-- @param #AIRWING self
|
||||
-- @param #string Type Patrol point type, e.g. "CAP" or "AWACS". Default "Unknown".
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate of the patrol point. Default 10-15 NM away from the location of the airwing.
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate of the patrol point. Default 10-15 NM away from the location of the airwing. Can be handed as a Core.Zone#ZONE object (e.g. in case you want the point to align with a moving zone).
|
||||
-- @param #number Altitude Orbit altitude in feet. Default random between Angels 10 and 20.
|
||||
-- @param #number Heading Heading in degrees. Default random (0, 360] degrees.
|
||||
-- @param #number LegLength Length of race-track orbit in NM. Default 15 NM.
|
||||
@@ -830,14 +863,16 @@ end
|
||||
-- @return #AIRWING.PatrolData Patrol point table.
|
||||
function AIRWING:NewPatrolPoint(Type, Coordinate, Altitude, Speed, Heading, LegLength, RefuelSystem)
|
||||
|
||||
-- Check if a zone was passed instead of a coordinate.
|
||||
if Coordinate and Coordinate:IsInstanceOf("ZONE_BASE") then
|
||||
Coordinate=Coordinate:GetCoordinate()
|
||||
end
|
||||
|
||||
local patrolpoint={} --#AIRWING.PatrolData
|
||||
patrolpoint.type=Type or "Unknown"
|
||||
patrolpoint.coord=Coordinate or self:GetCoordinate():Translate(UTILS.NMToMeters(math.random(10, 15)), math.random(360))
|
||||
if Coordinate and Coordinate:IsInstanceOf("ZONE_BASE") then
|
||||
patrolpoint.IsZonePoint = true
|
||||
patrolpoint.patrolzone = Coordinate
|
||||
patrolpoint.coord = patrolpoint.patrolzone:GetCoordinate()
|
||||
else
|
||||
patrolpoint.IsZonePoint = false
|
||||
end
|
||||
patrolpoint.heading=Heading or math.random(360)
|
||||
patrolpoint.leg=LegLength or 15
|
||||
patrolpoint.altitude=Altitude or math.random(10,20)*1000
|
||||
@@ -847,7 +882,7 @@ function AIRWING:NewPatrolPoint(Type, Coordinate, Altitude, Speed, Heading, LegL
|
||||
|
||||
if self.markpoints then
|
||||
patrolpoint.marker=MARKER:New(Coordinate, "New Patrol Point"):ToAll()
|
||||
AIRWING.UpdatePatrolPointMarker(patrolpoint)
|
||||
self:UpdatePatrolPointMarker(patrolpoint)
|
||||
end
|
||||
|
||||
return patrolpoint
|
||||
@@ -855,7 +890,7 @@ end
|
||||
|
||||
--- Add a patrol Point for CAP missions.
|
||||
-- @param #AIRWING self
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate of the patrol point.
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate of the patrol point. Can be handed as a Core.Zone#ZONE object (e.g. in case you want the point to align with a moving zone).
|
||||
-- @param #number Altitude Orbit altitude in feet.
|
||||
-- @param #number Speed Orbit speed in knots.
|
||||
-- @param #number Heading Heading in degrees.
|
||||
@@ -872,7 +907,7 @@ end
|
||||
|
||||
--- Add a patrol Point for RECON missions.
|
||||
-- @param #AIRWING self
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate of the patrol point.
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate of the patrol point. Can be handed as a Core.Zone#ZONE object (e.g. in case you want the point to align with a moving zone).
|
||||
-- @param #number Altitude Orbit altitude in feet.
|
||||
-- @param #number Speed Orbit speed in knots.
|
||||
-- @param #number Heading Heading in degrees.
|
||||
@@ -889,7 +924,7 @@ end
|
||||
|
||||
--- Add a patrol Point for TANKER missions.
|
||||
-- @param #AIRWING self
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate of the patrol point.
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate of the patrol point. Can be handed as a Core.Zone#ZONE object (e.g. in case you want the point to align with a moving zone).
|
||||
-- @param #number Altitude Orbit altitude in feet.
|
||||
-- @param #number Speed Orbit speed in knots.
|
||||
-- @param #number Heading Heading in degrees.
|
||||
@@ -907,7 +942,7 @@ end
|
||||
|
||||
--- Add a patrol Point for AWACS missions.
|
||||
-- @param #AIRWING self
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate of the patrol point.
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate of the patrol point. Can be handed as a Core.Zone#ZONE object (e.g. in case you want the point to align with a moving zone).
|
||||
-- @param #number Altitude Orbit altitude in feet.
|
||||
-- @param #number Speed Orbit speed in knots.
|
||||
-- @param #number Heading Heading in degrees.
|
||||
@@ -974,6 +1009,46 @@ function AIRWING:SetTakeoffAir()
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the aircraft of the AirWing to land straight in.
|
||||
-- @param #AIRWING self
|
||||
-- @return #FLIGHTGROUP self
|
||||
function AIRWING:SetLandingStraightIn()
|
||||
self.OptionLandingStraightIn = true
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the aircraft of the AirWing to land in pairs for groups > 1 aircraft.
|
||||
-- @param #AIRWING self
|
||||
-- @return #AIRWING self
|
||||
function AIRWING:SetLandingForcePair()
|
||||
self.OptionLandingForcePair = true
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the aircraft of the AirWing to NOT land in pairs.
|
||||
-- @param #AIRWING self
|
||||
-- @return #AIRWING self
|
||||
function AIRWING:SetLandingRestrictPair()
|
||||
self.OptionLandingRestrictPair = true
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the aircraft of the AirWing to land after overhead break.
|
||||
-- @param #AIRWING self
|
||||
-- @return #AIRWING self
|
||||
function AIRWING:SetLandingOverheadBreak()
|
||||
self.OptionLandingOverheadBreak = true
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Helicopter] Set the aircraft of the AirWing to prefer vertical takeoff and landing.
|
||||
-- @param #AIRWING self
|
||||
-- @return #AIRWING self
|
||||
function AIRWING:SetOptionPreferVerticalLanding()
|
||||
self.OptionPreferVerticalLanding = true
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set despawn after landing. Aircraft will be despawned after the landing event.
|
||||
-- Can help to avoid DCS AI taxiing issues.
|
||||
-- @param #AIRWING self
|
||||
@@ -1136,6 +1211,10 @@ function AIRWING:_GetPatrolData(PatrolPoints, RefuelSystem)
|
||||
|
||||
for _,_patrolpoint in pairs(PatrolPoints) do
|
||||
local patrolpoint=_patrolpoint --#AIRWING.PatrolData
|
||||
if patrolpoint.IsZonePoint and patrolpoint.IsZonePoint == true and patrolpoint.patrolzone then
|
||||
-- update
|
||||
patrolpoint.coord = patrolpoint.patrolzone:GetCoordinate()
|
||||
end
|
||||
if (RefuelSystem and patrolpoint.refuelsystem and RefuelSystem==patrolpoint.refuelsystem) or RefuelSystem==nil or patrolpoint.refuelsystem==nil then
|
||||
return patrolpoint
|
||||
end
|
||||
@@ -1195,7 +1274,7 @@ function AIRWING:CheckCAP()
|
||||
|
||||
patrol.noccupied=patrol.noccupied+1
|
||||
|
||||
if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol) end
|
||||
if self.markpoints then self:UpdatePatrolPointMarker(patrol) end
|
||||
|
||||
self:AddMission(missionCAP)
|
||||
|
||||
@@ -1247,7 +1326,7 @@ function AIRWING:CheckRECON()
|
||||
|
||||
patrol.noccupied=patrol.noccupied+1
|
||||
|
||||
if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol) end
|
||||
if self.markpoints then self:UpdatePatrolPointMarker(patrol) end
|
||||
|
||||
self:AddMission(missionRECON)
|
||||
|
||||
@@ -1292,7 +1371,7 @@ function AIRWING:CheckTANKER()
|
||||
|
||||
patrol.noccupied=patrol.noccupied+1
|
||||
|
||||
if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol) end
|
||||
if self.markpoints then self:UpdatePatrolPointMarker(patrol) end
|
||||
|
||||
self:AddMission(mission)
|
||||
|
||||
@@ -1311,7 +1390,7 @@ function AIRWING:CheckTANKER()
|
||||
|
||||
patrol.noccupied=patrol.noccupied+1
|
||||
|
||||
if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol) end
|
||||
if self.markpoints then self:UpdatePatrolPointMarker(patrol) end
|
||||
|
||||
self:AddMission(mission)
|
||||
|
||||
@@ -1349,7 +1428,7 @@ function AIRWING:CheckAWACS()
|
||||
|
||||
patrol.noccupied=patrol.noccupied+1
|
||||
|
||||
if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol) end
|
||||
if self.markpoints then self:UpdatePatrolPointMarker(patrol) end
|
||||
|
||||
self:AddMission(mission)
|
||||
|
||||
@@ -1464,7 +1543,21 @@ function AIRWING:onafterFlightOnMission(From, Event, To, FlightGroup, Mission)
|
||||
self:T(self.lid..string.format("Group %s on %s mission %s", FlightGroup:GetName(), Mission:GetType(), Mission:GetName()))
|
||||
if self.UseConnectedOpsAwacs and self.ConnectedOpsAwacs then
|
||||
self.ConnectedOpsAwacs:__FlightOnMission(2,FlightGroup,Mission)
|
||||
end
|
||||
end
|
||||
-- Landing Options
|
||||
if self.OptionLandingForcePair then
|
||||
FlightGroup:SetOptionLandingForcePair()
|
||||
elseif self.OptionLandingOverheadBreak then
|
||||
FlightGroup:SetOptionLandingOverheadBreak()
|
||||
elseif self.OptionLandingRestrictPair then
|
||||
FlightGroup:SetOptionLandingRestrictPair()
|
||||
elseif self.OptionLandingStraightIn then
|
||||
FlightGroup:SetOptionLandingStraightIn()
|
||||
end
|
||||
-- Landing Options Helo
|
||||
if self.OptionPreferVerticalLanding then
|
||||
FlightGroup:SetOptionPreferVertical()
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -839,8 +839,8 @@ function ARMYGROUP:Status()
|
||||
local ammo=self:GetAmmoElement(element)
|
||||
|
||||
-- Output text for element.
|
||||
text=text..string.format("\n[%d] %s: status=%s, life=%.1f/%.1f, guns=%d, rockets=%d, bombs=%d, missiles=%d, cargo=%d/%d kg",
|
||||
i, name, status, life, life0, ammo.Guns, ammo.Rockets, ammo.Bombs, ammo.Missiles, element.weightCargo, element.weightMaxCargo)
|
||||
text=text..string.format("\n[%d] %s: status=%s, life=%.1f/%.1f, guns=%d, cannons=%d, rockets=%d, missiles=%d, cargo=%d/%d kg",
|
||||
i, name, status, life, life0, ammo.Guns, ammo.Cannons, ammo.Rockets, ammo.Missiles, element.weightCargo, element.weightMaxCargo)
|
||||
end
|
||||
if #self.elements==0 then
|
||||
text=text.." none!"
|
||||
@@ -1571,7 +1571,7 @@ end
|
||||
-- @param Core.Zone#ZONE Zone The zone to return to.
|
||||
-- @param #number Formation Formation of the group.
|
||||
function ARMYGROUP:onafterRTZ(From, Event, To, Zone, Formation)
|
||||
self:T2(self.lid.."onafterRTZ")
|
||||
self:T(self.lid.."onafterRTZ")
|
||||
|
||||
-- Zone.
|
||||
local zone=Zone or self.homezone
|
||||
@@ -1841,8 +1841,6 @@ function ARMYGROUP:_UpdateEngageTarget()
|
||||
-- Check if target moved more than 100 meters or we do not have line of sight.
|
||||
if dist>100 or los==false then
|
||||
|
||||
--env.info("FF Update Engage Target Moved "..self.engage.Target:GetName())
|
||||
|
||||
-- Update new position.
|
||||
self.engage.Coordinate:UpdateFromVec3(vec3)
|
||||
|
||||
@@ -1852,13 +1850,14 @@ function ARMYGROUP:_UpdateEngageTarget()
|
||||
-- Remove current waypoint
|
||||
self:RemoveWaypointByID(self.engage.Waypoint.uid)
|
||||
|
||||
local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.9)
|
||||
-- Get new coordinate where to go.
|
||||
local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.95)
|
||||
|
||||
-- Add waypoint after current.
|
||||
self.engage.Waypoint=self:AddWaypoint(intercoord, self.engage.Speed, uid, self.engage.Formation, true)
|
||||
|
||||
-- Set if we want to resume route after reaching the detour waypoint.
|
||||
self.engage.Waypoint.detour=0
|
||||
self.engage.Waypoint.detour=1
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -324,6 +324,10 @@
|
||||
--
|
||||
-- A ground attack mission can be created with the @{#AUFTRAG.NewGROUNDATTACK}() function.
|
||||
--
|
||||
-- ## NAVALENGAGEMENT
|
||||
--
|
||||
-- A naval engagement mission can be created with the @{#AUFTRAG.NewNAVALENGAGEMENT}() function.
|
||||
--
|
||||
-- # Assigning Missions
|
||||
--
|
||||
-- An AUFTRAG can be assigned to groups (FLIGHTGROUP, ARMYGROUP, NAVYGROUP), legions (AIRWING, BRIGADE, FLEET) or to a COMMANDER.
|
||||
@@ -397,6 +401,7 @@ AUFTRAG = {
|
||||
conditionPush = {},
|
||||
conditionSuccessSet = false,
|
||||
conditionFailureSet = false,
|
||||
repeatDelay = 1,
|
||||
}
|
||||
|
||||
--- Global mission counter.
|
||||
@@ -442,6 +447,7 @@ _AUFTRAGSNR=0
|
||||
-- @field #string HOVER Hover.
|
||||
-- @field #string LANDATCOORDINATE Land at coordinate.
|
||||
-- @field #string GROUNDATTACK Ground attack.
|
||||
-- @field #string NAVALENGAGEMENT Naval engagement (similar to GROUNDATTACK).
|
||||
-- @field #string CARGOTRANSPORT Cargo transport.
|
||||
-- @field #string RELOCATECOHORT Relocate a cohort from one legion to another.
|
||||
-- @field #string AIRDEFENSE Air defense.
|
||||
@@ -490,6 +496,7 @@ AUFTRAG.Type={
|
||||
HOVER="Hover",
|
||||
LANDATCOORDINATE="Land at Coordinate",
|
||||
GROUNDATTACK="Ground Attack",
|
||||
NAVALENGAGEMENT="Naval Engagement",
|
||||
CARGOTRANSPORT="Cargo Transport",
|
||||
RELOCATECOHORT="Relocate Cohort",
|
||||
AIRDEFENSE="Air Defence",
|
||||
@@ -514,6 +521,7 @@ AUFTRAG.Type={
|
||||
-- @field #string BARRAGE Barrage.
|
||||
-- @field #string HOVER Hover.
|
||||
-- @field #string GROUNDATTACK Ground attack.
|
||||
-- @field #string NAVALENGAGEMENT Naval engagement.
|
||||
-- @field #string FERRY Ferry mission.
|
||||
-- @field #string RELOCATECOHORT Relocate cohort.
|
||||
-- @field #string AIRDEFENSE Air defense.
|
||||
@@ -536,6 +544,7 @@ AUFTRAG.SpecialTask={
|
||||
ARMORATTACK="AmorAttack",
|
||||
HOVER="Hover",
|
||||
GROUNDATTACK="Ground Attack",
|
||||
NAVALENGAGEMENT="Naval Engagement",
|
||||
FERRY="Ferry",
|
||||
RELOCATECOHORT="Relocate Cohort",
|
||||
AIRDEFENSE="Air Defense",
|
||||
@@ -665,7 +674,7 @@ AUFTRAG.Category={
|
||||
|
||||
--- AUFTRAG class version.
|
||||
-- @field #string version
|
||||
AUFTRAG.version="1.2.1"
|
||||
AUFTRAG.version="1.3.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -1320,13 +1329,19 @@ end
|
||||
-- @param #number Altitude Orbit altitude in feet. Default is y component of `Coordinate`.
|
||||
-- @param #number Speed Orbit indicated airspeed in knots at the set altitude ASL. Default 350 KIAS.
|
||||
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
|
||||
-- @param #number Leg Length of race-track in NM. Default 10 NM.
|
||||
-- @param #number Leg Length of race-track in NM. Default 10 NM. Set to 0 for a simple circular orbit.
|
||||
-- @param #number RefuelSystem Refueling system (0=boom, 1=probe). This info is *only* for AIRWINGs so they launch the right tanker type.
|
||||
-- @return #AUFTRAG self
|
||||
function AUFTRAG:NewTANKER(Coordinate, Altitude, Speed, Heading, Leg, RefuelSystem)
|
||||
|
||||
|
||||
local mission
|
||||
if Leg == 0 then
|
||||
mission=AUFTRAG:NewORBIT_CIRCLE(Coordinate,Altitude,Speed)
|
||||
else
|
||||
mission=AUFTRAG:NewORBIT_RACETRACK(Coordinate,Altitude,Speed,Heading,Leg)
|
||||
end
|
||||
-- Create ORBIT first.
|
||||
local mission=AUFTRAG:NewORBIT_RACETRACK(Coordinate, Altitude, Speed, Heading, Leg)
|
||||
--local mission=AUFTRAG:NewORBIT_RACETRACK(Coordinate, Altitude, Speed, Heading, Leg)
|
||||
|
||||
-- Mission type TANKER.
|
||||
mission.type=AUFTRAG.Type.TANKER
|
||||
@@ -1428,7 +1443,7 @@ function AUFTRAG:NewCAP(ZoneCAP, Altitude, Speed, Coordinate, Heading, Leg, Targ
|
||||
mission:_SetLogID()
|
||||
|
||||
-- DCS task parameters:
|
||||
mission.engageZone=ZoneCAP
|
||||
mission.engageZone=ZoneCAP or Coordinate
|
||||
mission.engageTargetTypes=TargetTypes or {"Air"}
|
||||
|
||||
-- Mission options:
|
||||
@@ -1715,9 +1730,45 @@ function AUFTRAG:NewSEAD(Target, Altitude)
|
||||
return mission
|
||||
end
|
||||
|
||||
--- **[AIR]** Create a SEAD in Zone mission.
|
||||
-- @param #AUFTRAG self
|
||||
-- @param Core.Zone#ZONE TargetZone The target zone to attack.
|
||||
-- @param #number Altitude Engage altitude in feet. Default 25000 ft.
|
||||
-- @param #table TargetTypes Table of string of DCS known target types, defaults to {"Air Defence"}. See [DCS Target Attributes](https://wiki.hoggitworld.com/view/DCS_enum_attributes)
|
||||
-- @param #number Duration Engage this much time when the AUFTRAG starts executing.
|
||||
-- @return #AUFTRAG self
|
||||
function AUFTRAG:NewSEADInZone(TargetZone, Altitude, TargetTypes, Duration)
|
||||
|
||||
local mission=AUFTRAG:New(AUFTRAG.Type.SEAD)
|
||||
|
||||
--mission:_TargetFromObject(TargetZone)
|
||||
|
||||
-- DCS Task options:
|
||||
mission.engageWeaponType=ENUMS.WeaponFlag.Auto
|
||||
mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL
|
||||
mission.engageAltitude=UTILS.FeetToMeters(Altitude or 25000)
|
||||
mission.engageZone = TargetZone
|
||||
mission.engageTargetTypes = TargetTypes or {"Air Defence"}
|
||||
|
||||
-- Mission options:
|
||||
mission.missionTask=ENUMS.MissionTask.SEAD
|
||||
mission.missionAltitude=mission.engageAltitude
|
||||
mission.missionFraction=0.2
|
||||
mission.optionROE=ENUMS.ROE.OpenFire
|
||||
mission.optionROT=ENUMS.ROT.EvadeFire
|
||||
|
||||
mission.categories={AUFTRAG.Category.AIRCRAFT}
|
||||
|
||||
mission.DCStask=mission:GetDCSMissionTask()
|
||||
|
||||
mission:SetDuration(Duration or 1800)
|
||||
|
||||
return mission
|
||||
end
|
||||
|
||||
--- **[AIR]** Create a STRIKE mission. Flight will attack the closest map object to the specified coordinate.
|
||||
-- @param #AUFTRAG self
|
||||
-- @param Core.Point#COORDINATE Target The target coordinate. Can also be given as a GROUP, UNIT, STATIC or TARGET object.
|
||||
-- @param Core.Point#COORDINATE Target The target coordinate. Can also be given as a GROUP, UNIT, STATIC, SET_GROUP, SET_UNIT, SET_STATIC or TARGET object.
|
||||
-- @param #number Altitude Engage altitude in feet. Default 2000 ft.
|
||||
-- @param #number EngageWeaponType Which weapon to use. Defaults to auto, ie ENUMS.WeaponFlag.Auto. See ENUMS.WeaponFlag for options.
|
||||
-- @return #AUFTRAG self
|
||||
@@ -1749,11 +1800,12 @@ end
|
||||
--- **[AIR]** Create a BOMBING mission. Flight will drop bombs a specified coordinate.
|
||||
-- See [DCS task bombing](https://wiki.hoggitworld.com/view/DCS_task_bombing).
|
||||
-- @param #AUFTRAG self
|
||||
-- @param Core.Point#COORDINATE Target Target coordinate. Can also be specified as a GROUP, UNIT, STATIC or TARGET object.
|
||||
-- @param Core.Point#COORDINATE Target Target coordinate. Can also be specified as a GROUP, UNIT, STATIC, SET_GROUP, SET_UNIT, SET_STATIC or TARGET object.
|
||||
-- @param #number Altitude Engage altitude in feet. Default 25000 ft.
|
||||
-- @param #number EngageWeaponType Which weapon to use. Defaults to auto, ie ENUMS.WeaponFlag.Auto. See ENUMS.WeaponFlag for options.
|
||||
-- @param #boolean Divebomb If true, use a dive bombing attack approach.
|
||||
-- @return #AUFTRAG self
|
||||
function AUFTRAG:NewBOMBING(Target, Altitude, EngageWeaponType)
|
||||
function AUFTRAG:NewBOMBING(Target, Altitude, EngageWeaponType, Divebomb)
|
||||
|
||||
local mission=AUFTRAG:New(AUFTRAG.Type.BOMBING)
|
||||
|
||||
@@ -1770,6 +1822,7 @@ function AUFTRAG:NewBOMBING(Target, Altitude, EngageWeaponType)
|
||||
mission.missionFraction=0.5
|
||||
mission.optionROE=ENUMS.ROE.OpenFire
|
||||
mission.optionROT=ENUMS.ROT.NoReaction -- No reaction is better.
|
||||
mission.optionDivebomb = Divebomb or nil
|
||||
|
||||
-- Evaluate result after 5 min. We might need time until the bombs have dropped and targets have been detroyed.
|
||||
mission.dTevaluate=5*60
|
||||
@@ -1984,7 +2037,7 @@ function AUFTRAG:NewRESCUEHELO(Carrier)
|
||||
|
||||
-- Mission options:
|
||||
mission.missionTask=ENUMS.MissionTask.NOTHING
|
||||
mission.missionFraction=0.5
|
||||
mission.missionFraction=0.9
|
||||
mission.optionROE=ENUMS.ROE.WeaponHold
|
||||
mission.optionROT=ENUMS.ROT.NoReaction
|
||||
|
||||
@@ -2165,7 +2218,7 @@ end
|
||||
-- **Note** that it is recommended to set the weapon range via the `OPSGROUP:AddWeaponRange()` function as this cannot be retrieved from the DCS API.
|
||||
-- @param #AUFTRAG self
|
||||
-- @param Core.Point#COORDINATE Target Center of the firing solution.
|
||||
-- @param #number Nshots Number of shots to be fired. Default `#nil`.
|
||||
-- @param #number Nshots Number of shots to be fired. Default `#nil`. If value is in (0,1), it is interpreted as per cent of available ammo.
|
||||
-- @param #number Radius Radius of the shells in meters. Default 100 meters.
|
||||
-- @param #number Altitude Altitude in meters. Can be used to setup a Barrage. Default `#nil`.
|
||||
-- @return #AUFTRAG self
|
||||
@@ -2279,8 +2332,9 @@ end
|
||||
-- @param #number Speed Speed in knots.
|
||||
-- @param #number Altitude Altitude in feet. Only for airborne units. Default 2000 feet ASL.
|
||||
-- @param #string Formation Formation used by ground units during patrol. Default "Off Road".
|
||||
-- @param #number StayInZoneTime Stay this many seconds in the zone when done, only then drive back.
|
||||
-- @return #AUFTRAG self
|
||||
function AUFTRAG:NewCAPTUREZONE(OpsZone, Coalition, Speed, Altitude, Formation)
|
||||
function AUFTRAG:NewCAPTUREZONE(OpsZone, Coalition, Speed, Altitude, Formation, StayInZoneTime)
|
||||
|
||||
local mission=AUFTRAG:New(AUFTRAG.Type.CAPTUREZONE)
|
||||
|
||||
@@ -2294,6 +2348,7 @@ function AUFTRAG:NewCAPTUREZONE(OpsZone, Coalition, Speed, Altitude, Formation)
|
||||
mission.optionROE=ENUMS.ROE.ReturnFire
|
||||
mission.optionROT=ENUMS.ROT.PassiveDefense
|
||||
mission.optionAlarm=ENUMS.AlarmState.Auto
|
||||
mission.StayInZoneTime = StayInZoneTime
|
||||
|
||||
mission.missionFraction=0.1
|
||||
mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or nil
|
||||
@@ -2335,11 +2390,13 @@ function AUFTRAG:NewARMORATTACK(Target, Speed, Formation)
|
||||
return mission
|
||||
end
|
||||
|
||||
--- **[GROUND]** Create a GROUNDATTACK mission. Ground group(s) will go to a target object and attack.
|
||||
--- **[GROUND]** Create a GROUNDATTACK mission. Ground group(s) will go to a target object and attack at their own discretion.
|
||||
-- Unfortunately, the "Attack Group" and "Attack Unit" tasks do not work for ground and naval groups (only for aircraft).
|
||||
-- Therefore, we resort to this workaround, which guides the attacking group to the vicinity of the target. Then they start shooting on their own, once they detect the target.
|
||||
-- @param #AUFTRAG self
|
||||
-- @param Wrapper.Positionable#POSITIONABLE Target The target to attack. Can be a GROUP, UNIT or STATIC object.
|
||||
-- @param #number Speed Speed in knots. Default max.
|
||||
-- @param #string Formation The attack formation, e.g. "Wedge", "Vee" etc. Default `ENUMS.Formation.Vehicle.Vee`.
|
||||
-- @param #string Formation The attack formation, e.g. "Wedge", "Vee" etc. Default `ENUMS.Formation.Vehicle.Vee`. Only working for ground, not naval!
|
||||
-- @return #AUFTRAG self
|
||||
function AUFTRAG:NewGROUNDATTACK(Target, Speed, Formation)
|
||||
|
||||
@@ -2366,6 +2423,38 @@ function AUFTRAG:NewGROUNDATTACK(Target, Speed, Formation)
|
||||
return mission
|
||||
end
|
||||
|
||||
--- **[NAVAL]** Create a NAVALENGAGEMENT mission. Naval group(s) will go to a target object and attack at their own discretion.
|
||||
-- Unfortunately, the "Attack Group" and "Attack Unit" tasks do not work for ground and naval groups (only for aircraft).
|
||||
-- Therefore, we resort to this workaround, which guides the attacking group to the vicinity of the target. Then they start shooting on their own, once they detect the target.
|
||||
-- @param #AUFTRAG self
|
||||
-- @param Wrapper.Positionable#POSITIONABLE Target The target to attack. Can be a GROUP, UNIT or STATIC object.
|
||||
-- @param #number Speed Speed in knots. Default max.
|
||||
-- @param #number Depth The attack depth in meters. Only for submarines!
|
||||
-- @return #AUFTRAG self
|
||||
function AUFTRAG:NewNAVALENGAGEMENT(Target, Speed, Depth)
|
||||
|
||||
local mission=AUFTRAG:New(AUFTRAG.Type.NAVALENGAGEMENT)
|
||||
|
||||
mission:_TargetFromObject(Target)
|
||||
|
||||
mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.NAVALENGAGEMENT)
|
||||
|
||||
-- Defaults.
|
||||
mission.optionROE=ENUMS.ROE.OpenFire
|
||||
mission.optionAlarm=ENUMS.AlarmState.Auto
|
||||
mission.missionFraction=0.70
|
||||
mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or nil
|
||||
mission.missionAltitude=Depth or 0
|
||||
|
||||
mission.categories={AUFTRAG.Category.NAVAL}
|
||||
|
||||
mission.DCStask=mission:GetDCSMissionTask()
|
||||
|
||||
mission.DCStask.params.speed=mission.missionSpeed and UTILS.KmphToMps(mission.missionSpeed) or nil
|
||||
|
||||
return mission
|
||||
end
|
||||
|
||||
--- **[AIR, GROUND, NAVAL]** Create a RECON mission.
|
||||
-- @param #AUFTRAG self
|
||||
-- @param Core.Set#SET_ZONE ZoneSet The recon zones.
|
||||
@@ -2658,33 +2747,35 @@ function AUFTRAG:NewFromTarget(Target, MissionType)
|
||||
local mission=nil --#AUFTRAG
|
||||
|
||||
if MissionType==AUFTRAG.Type.ANTISHIP then
|
||||
mission=self:NewANTISHIP(Target, Altitude)
|
||||
mission=self:NewANTISHIP(Target)
|
||||
elseif MissionType==AUFTRAG.Type.ARTY then
|
||||
mission=self:NewARTY(Target, Nshots, Radius)
|
||||
mission=self:NewARTY(Target, 0.3) -- use 30% of the available ammo
|
||||
elseif MissionType==AUFTRAG.Type.BAI then
|
||||
mission=self:NewBAI(Target, Altitude)
|
||||
mission=self:NewBAI(Target)
|
||||
elseif MissionType==AUFTRAG.Type.BOMBCARPET then
|
||||
mission=self:NewBOMBCARPET(Target, Altitude, CarpetLength)
|
||||
mission=self:NewBOMBCARPET(Target)
|
||||
elseif MissionType==AUFTRAG.Type.BOMBING then
|
||||
mission=self:NewBOMBING(Target, Altitude)
|
||||
mission=self:NewBOMBING(Target)
|
||||
elseif MissionType==AUFTRAG.Type.BOMBRUNWAY then
|
||||
mission=self:NewBOMBRUNWAY(Target, Altitude)
|
||||
mission=self:NewBOMBRUNWAY(Target)
|
||||
elseif MissionType==AUFTRAG.Type.STRAFING then
|
||||
mission=self:NewSTRAFING(Target, Altitude)
|
||||
mission=self:NewSTRAFING(Target)
|
||||
elseif MissionType==AUFTRAG.Type.CAS then
|
||||
mission=self:NewCAS(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000), Altitude, Speed, Target:GetAverageCoordinate(), Heading, Leg, TargetTypes)
|
||||
mission=self:NewCAS(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000), nil, nil, Target:GetAverageCoordinate())
|
||||
elseif MissionType==AUFTRAG.Type.CASENHANCED then
|
||||
mission=self:NewCASENHANCED(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000), Altitude, Speed, RangeMax, NoEngageZoneSet, TargetTypes)
|
||||
mission=self:NewCASENHANCED(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000))
|
||||
elseif MissionType==AUFTRAG.Type.INTERCEPT then
|
||||
mission=self:NewINTERCEPT(Target)
|
||||
elseif MissionType==AUFTRAG.Type.SEAD then
|
||||
mission=self:NewSEAD(Target, Altitude)
|
||||
mission=self:NewSEAD(Target)
|
||||
elseif MissionType==AUFTRAG.Type.STRIKE then
|
||||
mission=self:NewSTRIKE(Target, Altitude)
|
||||
mission=self:NewSTRIKE(Target)
|
||||
elseif MissionType==AUFTRAG.Type.ARMORATTACK then
|
||||
mission=self:NewARMORATTACK(Target, Speed)
|
||||
mission=self:NewARMORATTACK(Target)
|
||||
elseif MissionType==AUFTRAG.Type.GROUNDATTACK then
|
||||
mission=self:NewGROUNDATTACK(Target, Speed, Formation)
|
||||
mission=self:NewGROUNDATTACK(Target)
|
||||
elseif MissionType==AUFTRAG.Type.NAVALENGAGEMENT then
|
||||
mission=self:NewNAVALENGAGEMENT(Target)
|
||||
else
|
||||
return nil
|
||||
end
|
||||
@@ -2801,7 +2892,7 @@ function AUFTRAG:NewAUTO(EngageGroup)
|
||||
if auftrag==AUFTRAG.Type.ANTISHIP then
|
||||
mission=AUFTRAG:NewANTISHIP(Target)
|
||||
elseif auftrag==AUFTRAG.Type.ARTY then
|
||||
mission=AUFTRAG:NewARTY(Target)
|
||||
mission=AUFTRAG:NewARTY(Target, 0.2)
|
||||
elseif auftrag==AUFTRAG.Type.AWACS then
|
||||
mission=AUFTRAG:NewAWACS(Coordinate, Altitude,Speed,Heading,Leg)
|
||||
elseif auftrag==AUFTRAG.Type.BAI then
|
||||
@@ -2966,6 +3057,16 @@ function AUFTRAG:SetRepeat(Nrepeat)
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- **[LEGION, COMMANDER, CHIEF]** Set the repeat delay in seconds after a mission is successful/failed. Only valid if the mission is handled by a LEGION (AIRWING, BRIGADE, FLEET) or higher level.
|
||||
-- @param #AUFTRAG self
|
||||
-- @param #number RepeatDelay Repeat delay in seconds. Default 1.
|
||||
-- @return #AUFTRAG self
|
||||
function AUFTRAG:SetRepeatDelay(RepeatDelay)
|
||||
self.repeatDelay = RepeatDelay
|
||||
return self
|
||||
end
|
||||
|
||||
--- **[LEGION, COMMANDER, CHIEF]** Set how many times the mission is repeated if it fails. Only valid if the mission is handled by a LEGION (AIRWING, BRIGADE, FLEET) or higher level.
|
||||
-- @param #AUFTRAG self
|
||||
-- @param #number Nrepeat Number of repeats. Default 0.
|
||||
@@ -3961,6 +4062,23 @@ function AUFTRAG:IsOver()
|
||||
return over
|
||||
end
|
||||
|
||||
--- Check if mission is repeatable.
|
||||
-- @param #AUFTRAG self
|
||||
-- @return #boolean If true, mission is repeatable.
|
||||
function AUFTRAG:IsRepeatable()
|
||||
local repeatmeS=self.repeatedSuccess<self.NrepeatSuccess or self.repeated<self.Nrepeat
|
||||
local repeatmeF=self.repeatedFailure<self.NrepeatFailure or self.repeated<self.Nrepeat
|
||||
if repeatmeS==true or repeatmeF==true then return true else return false end
|
||||
return false
|
||||
end
|
||||
|
||||
--- Check if mission is NOT repeatable.
|
||||
-- @param #AUFTRAG self
|
||||
-- @return #boolean If true, mission is NOT repeatable.
|
||||
function AUFTRAG:IsNotRepeatable()
|
||||
return not self:IsRepeatable()
|
||||
end
|
||||
|
||||
--- Check if mission is NOT over.
|
||||
-- @param #AUFTRAG self
|
||||
-- @return #boolean If true, mission is NOT over yet.
|
||||
@@ -4325,7 +4443,7 @@ function AUFTRAG:onafterStatus(From, Event, To)
|
||||
-- Group info.
|
||||
if self.verbose>=3 then
|
||||
-- Data on assigned groups.
|
||||
local text=string.format("Assets [N=%d,Nassigned=%s, Ndead=%s]:", self.Nassets or 0, self.Nassigned or 0, self.Ndead or 0)
|
||||
local text=string.format("Assets [N=%d, Nassigned=%s, Ndead=%s]:", self.Nassets or 0, self.Nassigned or 0, self.Ndead or 0)
|
||||
for i,_asset in pairs(self.assets or {}) do
|
||||
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
|
||||
text=text..string.format("\n[%d] %s: spawned=%s, requested=%s, reserved=%s", i, asset.spawngroupname, tostring(asset.spawned), tostring(asset.requested), tostring(asset.reserved))
|
||||
@@ -4366,7 +4484,7 @@ function AUFTRAG:Evaluate()
|
||||
local owndamage=self.Ncasualties/self.Nelements*100
|
||||
|
||||
-- Current number of mission targets.
|
||||
local Ntargets=self:CountMissionTargets()
|
||||
local Ntargets=self:CountMissionTargets(true)
|
||||
local Ntargets0=self:GetTargetInitialNumber()
|
||||
|
||||
local Life=self:GetTargetLife()
|
||||
@@ -4765,6 +4883,8 @@ end
|
||||
-- @return #boolean If `true`, all groups are done with the mission.
|
||||
function AUFTRAG:CheckGroupsDone()
|
||||
|
||||
local fsmState = self:GetState()
|
||||
|
||||
-- Check status of all OPS groups.
|
||||
for groupname,data in pairs(self.groupdata) do
|
||||
local groupdata=data --#AUFTRAG.GroupData
|
||||
@@ -4815,13 +4935,27 @@ function AUFTRAG:CheckGroupsDone()
|
||||
self:T2(self.lid..string.format("CheckGroupsDone: Mission is still in state %s [FSM=%s] and reinfoce=%d. Mission NOT DONE!", self.status, self:GetState(), self.reinforce))
|
||||
return false
|
||||
end
|
||||
|
||||
local NopsgroupsAlive=self:CountOpsGroups()
|
||||
local NopsgroupsDone=self:CountOpsGroupsInStatus(AUFTRAG.GroupStatus.DONE)+self:CountOpsGroupsInStatus(AUFTRAG.GroupStatus.CANCELLED)
|
||||
|
||||
-- It could be that all groups were destroyed on the way to the mission execution waypoint.
|
||||
-- TODO: would be better to check if everybody is dead by now.
|
||||
if self:IsStarted() and self:CountOpsGroups()==0 then
|
||||
if self:IsStarted() and NopsgroupsAlive==0 then
|
||||
self:T(self.lid..string.format("CheckGroupsDone: Mission is STARTED state %s [FSM=%s] but count of alive OPSGROUP is zero. Mission DONE!", self.status, self:GetState()))
|
||||
return true
|
||||
end
|
||||
|
||||
-- Every group alive is done or cancelled
|
||||
if NopsgroupsAlive==NopsgroupsDone then
|
||||
self:T(self.lid..string.format("CheckGroupsDone: Mission is in state %s [FSM=%s] but all groups [=%d] are done or cancelled. Mission DONE!", self.status, self:GetState(), NopsgroupsAlive))
|
||||
return true
|
||||
end
|
||||
|
||||
if (self:IsStarted() or self:IsExecuting()) and (fsmState == AUFTRAG.Status.STARTED or fsmState == AUFTRAG.Status.EXECUTING) and NopsgroupsAlive>0 then
|
||||
self:T(self.lid..string.format("CheckGroupsDone: Mission is in state %s [FSM=%s] and count of alive OPSGROUP > zero. Mission NOT DONE!", self.status, self:GetState()))
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
@@ -5160,7 +5294,7 @@ function AUFTRAG:onafterSuccess(From, Event, To)
|
||||
|
||||
-- Repeat mission.
|
||||
self:T(self.lid..string.format("Mission SUCCESS! Repeating mission for the %d time (max %d times) ==> Repeat mission!", self.repeated+1, N))
|
||||
self:Repeat()
|
||||
self:__Repeat(self.repeatDelay)
|
||||
|
||||
else
|
||||
|
||||
@@ -5202,7 +5336,7 @@ function AUFTRAG:onafterFailed(From, Event, To)
|
||||
|
||||
-- Repeat mission.
|
||||
self:T(self.lid..string.format("Mission FAILED! Repeating mission for the %d time (max %d times) ==> Repeat mission!", self.repeated+1, N))
|
||||
self:Repeat()
|
||||
self:__Repeat(self.repeatDelay)
|
||||
|
||||
else
|
||||
|
||||
@@ -5413,8 +5547,9 @@ end
|
||||
|
||||
--- Count alive mission targets.
|
||||
-- @param #AUFTRAG self
|
||||
-- @param #boolean OnlyReallyAlive (Optional) If `true`, count only really alive targets (units, groups) but not coordinates or zones.
|
||||
-- @return #number Number of alive target units.
|
||||
function AUFTRAG:CountMissionTargets()
|
||||
function AUFTRAG:CountMissionTargets(OnlyReallyAlive)
|
||||
|
||||
local N=0
|
||||
|
||||
@@ -5422,7 +5557,7 @@ function AUFTRAG:CountMissionTargets()
|
||||
local Coalitions=self.coalition and UTILS.GetCoalitionEnemy(self.coalition, true) or nil
|
||||
|
||||
if self.engageTarget then
|
||||
N=self.engageTarget:CountTargets(Coalitions)
|
||||
N=self.engageTarget:CountTargets(Coalitions, OnlyReallyAlive)
|
||||
end
|
||||
|
||||
return N
|
||||
@@ -6108,10 +6243,13 @@ function AUFTRAG:GetDCSMissionTask()
|
||||
-- BOMBING Mission --
|
||||
---------------------
|
||||
|
||||
local DCStask=CONTROLLABLE.TaskBombing(nil, self:GetTargetVec2(), self.engageAsGroup, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType, Divebomb)
|
||||
local coords = self.engageTarget:GetCoordinates()
|
||||
for _, coord in pairs(coords) do
|
||||
local DCStask = CONTROLLABLE.TaskBombing(nil, coord:GetVec2(), self.engageAsGroup, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType, self.optionDivebomb)
|
||||
|
||||
table.insert(DCStasks, DCStask)
|
||||
end
|
||||
|
||||
table.insert(DCStasks, DCStask)
|
||||
|
||||
elseif self.type==AUFTRAG.Type.STRAFING then
|
||||
|
||||
----------------------
|
||||
@@ -6147,8 +6285,16 @@ function AUFTRAG:GetDCSMissionTask()
|
||||
-----------------
|
||||
-- CAP Mission --
|
||||
-----------------
|
||||
|
||||
local DCStask=CONTROLLABLE.EnRouteTaskEngageTargetsInZone(nil, self.engageZone:GetVec2(), self.engageZone:GetRadius(), self.engageTargetTypes, Priority)
|
||||
|
||||
local Vec2 = self.engageZone:GetVec2()
|
||||
local Radius
|
||||
if self.engageZone:IsInstanceOf("COORDINATE") then
|
||||
Radius = UTILS.NMToMeters(20)
|
||||
else
|
||||
Radius = self.engageZone:GetRadius()
|
||||
end
|
||||
|
||||
local DCStask=CONTROLLABLE.EnRouteTaskEngageTargetsInZone(nil, Vec2, Radius, self.engageTargetTypes, Priority)
|
||||
|
||||
table.insert(self.enrouteTasks, DCStask)
|
||||
|
||||
@@ -6302,18 +6448,47 @@ function AUFTRAG:GetDCSMissionTask()
|
||||
-- Add enroute task SEAD. Disabled that here because the group enganges everything on its route.
|
||||
--local DCStask=CONTROLLABLE.EnRouteTaskSEAD(nil, self.TargetType)
|
||||
--table.insert(self.enrouteTasks, DCStask)
|
||||
|
||||
self:_GetDCSAttackTask(self.engageTarget, DCStasks)
|
||||
|
||||
|
||||
if self.engageZone then
|
||||
|
||||
--local DCStask=CONTROLLABLE.EnRouteTaskSEAD(nil, self.engageTargetTypes)
|
||||
--table.insert(self.enrouteTasks, DCStask)
|
||||
self.engageZone:Scan({Object.Category.UNIT},{Unit.Category.GROUND_UNIT})
|
||||
local ScanUnitSet = self.engageZone:GetScannedSetUnit()
|
||||
local SeadUnitSet = SET_UNIT:New()
|
||||
for _,_unit in pairs (ScanUnitSet.Set) do
|
||||
local unit = _unit -- Wrapper.Unit#UNTI
|
||||
if unit and unit:IsAlive() and unit:HasSEAD() then
|
||||
self:T("Adding UNIT for SEAD: "..unit:GetName())
|
||||
local task = CONTROLLABLE.TaskAttackUnit(nil,unit,GroupAttack,AI.Task.WeaponExpend.ALL,1,Direction,self.engageAltitude,2956984318)
|
||||
table.insert(DCStasks, task)
|
||||
SeadUnitSet:AddUnit(unit)
|
||||
end
|
||||
end
|
||||
self.engageTarget = TARGET:New(SeadUnitSet)
|
||||
--local OrbitTask = CONTROLLABLE.TaskOrbitCircle(nil,self.engageAltitude,self.missionSpeed,self.engageZone:GetCoordinate())
|
||||
--local Point = self.engageZone:GetVec2()
|
||||
--local OrbitTask = CONTROLLABLE.TaskOrbitCircleAtVec2(nil,Point,self.engageAltitude,self.missionSpeed)
|
||||
--table.insert(DCStasks, OrbitTask)
|
||||
|
||||
else
|
||||
|
||||
self:_GetDCSAttackTask(self.engageTarget, DCStasks)
|
||||
|
||||
end
|
||||
|
||||
elseif self.type==AUFTRAG.Type.STRIKE then
|
||||
|
||||
--------------------
|
||||
-- STRIKE Mission --
|
||||
--------------------
|
||||
|
||||
local DCStask=CONTROLLABLE.TaskAttackMapObject(nil, self:GetTargetVec2(), self.engageAsGroup, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType)
|
||||
local coords = self.engageTarget:GetCoordinates()
|
||||
for _, coord in pairs(coords) do
|
||||
local DCStask=CONTROLLABLE.TaskAttackMapObject(nil, coord:GetVec2(), self.engageAsGroup, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType)
|
||||
|
||||
table.insert(DCStasks, DCStask)
|
||||
table.insert(DCStasks, DCStask)
|
||||
end
|
||||
|
||||
elseif self.type==AUFTRAG.Type.TANKER or self.type==AUFTRAG.Type.RECOVERYTANKER then
|
||||
|
||||
@@ -6521,6 +6696,26 @@ function AUFTRAG:GetDCSMissionTask()
|
||||
|
||||
table.insert(DCStasks, DCStask)
|
||||
|
||||
elseif self.type==AUFTRAG.Type.NAVALENGAGEMENT then
|
||||
|
||||
---------------------------
|
||||
-- NAVAL ENGAGEMENT Mission --
|
||||
---------------------------
|
||||
|
||||
local DCStask={}
|
||||
|
||||
DCStask.id=AUFTRAG.SpecialTask.NAVALENGAGEMENT
|
||||
|
||||
-- We create a "fake" DCS task and pass the parameters to the NAVYGROUP.
|
||||
local param={}
|
||||
param.target=self:GetTargetData()
|
||||
param.speed=self.missionSpeed and UTILS.KmphToMps(self.missionSpeed) or nil
|
||||
param.altitude=self.missionAltitude or 0
|
||||
|
||||
DCStask.params=param
|
||||
|
||||
table.insert(DCStasks, DCStask)
|
||||
|
||||
elseif self.type==AUFTRAG.Type.AMMOSUPPLY then
|
||||
|
||||
-------------------------
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **applevangelist**
|
||||
-- @date Last Update Jan 2025
|
||||
-- @date Last Update July 2025
|
||||
-- @module Ops.AWACS
|
||||
-- @image OPS_AWACS.jpg
|
||||
|
||||
@@ -237,7 +237,7 @@ do
|
||||
-- -- Callsign will be "Focus". We'll be a Angels 30, doing 300 knots, orbit leg to 88deg with a length of 25nm.
|
||||
-- testawacs:SetAwacsDetails(CALLSIGN.AWACS.Focus,1,30,300,88,25)
|
||||
-- -- Set up SRS on port 5010 - change the below to your path and port
|
||||
-- testawacs:SetSRS("C:\\Program Files\\DCS-SimpleRadio-Standalone","female","en-GB",5010)
|
||||
-- testawacs:SetSRS("C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio","female","en-GB",5010)
|
||||
-- -- Add a "red" border we don't want to cross, set up in the mission editor with a late activated helo named "Red Border#ZONE_POLYGON"
|
||||
-- testawacs:SetRejectionZone(ZONE:FindByName("Red Border"))
|
||||
-- -- Our CAP flight will have the callsign "Ford", we want 4 AI planes, Time-On-Station is four hours, doing 300 kn IAS.
|
||||
@@ -255,7 +255,7 @@ do
|
||||
-- -- The CAP station zone is called "Fremont". We will be on 255 AM. Note the Orbit Zone is given as *nil* in the `New()`-Statement
|
||||
-- local testawacs = AWACS:New("GCI Senaki",AwacsAW,"blue",AIRBASE.Caucasus.Senaki_Kolkhi,nil,ZONE:FindByName("Rock"),"Fremont",255,radio.modulation.AM )
|
||||
-- -- Set up SRS on port 5010 - change the below to your path and port
|
||||
-- testawacs:SetSRS("C:\\Program Files\\DCS-SimpleRadio-Standalone","female","en-GB",5010)
|
||||
-- testawacs:SetSRS("C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio","female","en-GB",5010)
|
||||
-- -- Add a "red" border we don't want to cross, set up in the mission editor with a late activated helo named "Red Border#ZONE_POLYGON"
|
||||
-- testawacs:SetRejectionZone(ZONE:FindByName("Red Border"))
|
||||
-- -- Our CAP flight will have the callsign "Ford", we want 4 AI planes, Time-On-Station is four hours, doing 300 kn IAS.
|
||||
@@ -509,7 +509,7 @@ do
|
||||
-- @field #AWACS
|
||||
AWACS = {
|
||||
ClassName = "AWACS", -- #string
|
||||
version = "0.2.71", -- #string
|
||||
version = "0.2.74", -- #string
|
||||
lid = "", -- #string
|
||||
coalition = coalition.side.BLUE, -- #number
|
||||
coalitiontxt = "blue", -- #string
|
||||
@@ -1123,7 +1123,7 @@ function AWACS:New(Name,AirWing,Coalition,AirbaseName,AwacsOrbit,OpsZone,Station
|
||||
self.EscortMissionReplacement = {}
|
||||
|
||||
-- SRS
|
||||
self.PathToSRS = "C:\\Program Files\\DCS-SimpleRadio-Standalone"
|
||||
self.PathToSRS = "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio"
|
||||
self.Gender = "female"
|
||||
self.Culture = "en-GB"
|
||||
self.Voice = nil
|
||||
@@ -1242,6 +1242,8 @@ function AWACS:New(Name,AirWing,Coalition,AirbaseName,AwacsOrbit,OpsZone,Station
|
||||
self:AddTransition("*", "Intercept", "*")
|
||||
self:AddTransition("*", "InterceptSuccess", "*")
|
||||
self:AddTransition("*", "InterceptFailure", "*")
|
||||
self:AddTransition("*", "VIDSuccess", "*")
|
||||
self:AddTransition("*", "VIDFailure", "*")
|
||||
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
|
||||
|
||||
|
||||
@@ -1365,18 +1367,38 @@ function AWACS:New(Name,AirWing,Coalition,AirbaseName,AwacsOrbit,OpsZone,Station
|
||||
-- @param #string To To state.
|
||||
|
||||
--- On After "InterceptSuccess" event. Intercept successful.
|
||||
-- @function [parent=#AWACS] OnAfterIntercept
|
||||
-- @function [parent=#AWACS] OnAfterInterceptSuccess
|
||||
-- @param #AWACS self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
|
||||
--- On After "InterceptFailure" event. Intercept failure.
|
||||
-- @function [parent=#AWACS] OnAfterIntercept
|
||||
-- @function [parent=#AWACS] OnAfterInterceptFailure
|
||||
-- @param #AWACS self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
|
||||
--- On After "VIDSuccess" event. Intercept successful.
|
||||
-- @function [parent=#AWACS] OnAfterVIDSuccess
|
||||
-- @param #AWACS self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param #number GID Managed group ID (Player)
|
||||
-- @param Wrapper.Group#GROUP Group (Player) Group done the VID
|
||||
-- @param #AWACS.ManagedContact Contact The contact that was VID'd
|
||||
|
||||
--- On After "VIDFailure" event. Intercept failure.
|
||||
-- @function [parent=#AWACS] OnAfterVIDFailure
|
||||
-- @param #AWACS self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param #number GID Managed group ID (Player)
|
||||
-- @param Wrapper.Group#GROUP Group (Player) Group done the VID
|
||||
-- @param #AWACS.ManagedContact Contact The contact that was VID'd
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -1574,6 +1596,16 @@ function AWACS:SetLocale(Locale)
|
||||
return self
|
||||
end
|
||||
|
||||
--- [User] Set own coordinate for BullsEye.
|
||||
-- @param #AWACS self
|
||||
-- @param Core.Point#COORDINATE
|
||||
-- @return #AWACS self
|
||||
function AWACS:SetBullsCoordinate(Coordinate)
|
||||
self:T(self.lid.."SetBullsCoordinate")
|
||||
self.AOCoordinate = Coordinate
|
||||
return self
|
||||
end
|
||||
|
||||
--- [User] Set the max mission range flights can be away from their home base.
|
||||
-- @param #AWACS self
|
||||
-- @param #number NM Distance in nautical miles
|
||||
@@ -1999,7 +2031,9 @@ function AWACS:SetAdditionalZone(Zone, Draw)
|
||||
self.BorderZone = Zone
|
||||
if self.debug then
|
||||
Zone:DrawZone(self.coalition,{1,0.64,0},1,{1,0.64,0},0.2,1,true)
|
||||
MARKER:New(Zone:GetCoordinate(),"Defensive Zone"):ToCoalition(self.coalition)
|
||||
if self.AllowMarkers then
|
||||
MARKER:New(Zone:GetCoordinate(),"Defensive Zone"):ToCoalition(self.coalition)
|
||||
end
|
||||
elseif Draw then
|
||||
Zone:DrawZone(self.coalition,{1,0.64,0},1,{1,0.64,0},0.2,1,true)
|
||||
end
|
||||
@@ -2019,11 +2053,62 @@ function AWACS:SetRejectionZone(Zone,Draw)
|
||||
--MARKER:New(Zone:GetCoordinate(),"Rejection Zone"):ToAll()
|
||||
elseif self.debug then
|
||||
Zone:DrawZone(self.coalition,{1,0.64,0},1,{1,0.64,0},0.2,1,true)
|
||||
MARKER:New(Zone:GetCoordinate(),"Rejection Zone"):ToCoalition(self.coalition)
|
||||
if self.AllowMarkers then
|
||||
MARKER:New(Zone:GetCoordinate(),"Rejection Zone"):ToCoalition(self.coalition)
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Function to set corridor zones.
|
||||
-- @param #AWACS self
|
||||
-- @param Core.Set#SET_ZONE CorridorZones Can be handed in as SET\_ZONE or single ZONE object.
|
||||
-- @return #AWACS self
|
||||
function AWACS:SetCorridorZones(CorridorZones)
|
||||
self:T(self.lid .. "SetCorridorZones")
|
||||
if CorridorZones and CorridorZones:IsInstanceOf("SET_ZONE") then
|
||||
self.corridorzones = CorridorZones
|
||||
self.usecorridors = true
|
||||
elseif CorridorZones and CorridorZones:IsInstanceOf("ZONE_BASE") then
|
||||
if not self.corridorzones then self.corridorzones = SET_ZONE:New() end
|
||||
self.corridorzones:AddZone(CorridorZones)
|
||||
self.usecorridors = true
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Function to add one corridor zone.
|
||||
-- @param #AWACS self
|
||||
-- @param Core.Zone#ZONE CorridorZone The ZONE object to be added.
|
||||
-- @return #AWACS self
|
||||
function AWACS:AddCorridorZone(CorridorZone)
|
||||
self:T(self.lid .. "AddCorridorZone")
|
||||
self:SetCorridorZones(CorridorZone)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Function to set corridor zone floor and ceiling in FEET.
|
||||
-- @param #AWACS self
|
||||
-- @param #number Floor Floor altitude ASL in feet.
|
||||
-- @param #number Ceiling Ceiling altitude ASL in feet.
|
||||
-- @return #AWACS self
|
||||
function AWACS:SetCorridorZoneFloorAndCeiling(Floor,Ceiling)
|
||||
self.corridorfloor = UTILS.FeetToMeters(Floor)
|
||||
self.corridorceiling = UTILS.FeetToMeters(Ceiling)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Function to set corridor zone floor and ceiling in METERS.
|
||||
-- @param #AWACS self
|
||||
-- @param #number Floor Floor altitude ASL in meters.
|
||||
-- @param #number Ceiling Ceiling altitude ASL in meters.
|
||||
-- @return #AWACS self
|
||||
function AWACS:SetCorridorZoneFloorAndCeilingMeters(Floor,Ceiling)
|
||||
self.corridorfloor = Floor
|
||||
self.corridorceiling = Ceiling
|
||||
return self
|
||||
end
|
||||
|
||||
--- [User] Draw a line around the FEZ on the F10 map.
|
||||
-- @param #AWACS self
|
||||
-- @return #AWACS self
|
||||
@@ -2091,7 +2176,7 @@ end
|
||||
|
||||
--- [User] Set AWACS SRS TTS details - see @{Sound.SRS} for details. `SetSRS()` will try to use as many attributes configured with @{Sound.SRS#MSRS.LoadConfigFile}() as possible.
|
||||
-- @param #AWACS self
|
||||
-- @param #string PathToSRS Defaults to "C:\\Program Files\\DCS-SimpleRadio-Standalone"
|
||||
-- @param #string PathToSRS Defaults to "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio"
|
||||
-- @param #string Gender Defaults to "male"
|
||||
-- @param #string Culture Defaults to "en-US"
|
||||
-- @param #number Port Defaults to 5002
|
||||
@@ -2104,7 +2189,7 @@ end
|
||||
-- @return #AWACS self
|
||||
function AWACS:SetSRS(PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,AccessKey,Backend)
|
||||
self:T(self.lid.."SetSRS")
|
||||
self.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone"
|
||||
self.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio"
|
||||
self.Gender = Gender or MSRS.gender or "male"
|
||||
self.Culture = Culture or MSRS.culture or "en-US"
|
||||
self.Port = Port or MSRS.port or 5002
|
||||
@@ -3263,12 +3348,14 @@ function AWACS:_VID(Group,Declaration)
|
||||
local vidpos = self.gettext:GetEntry("VIDPOS",self.locale)
|
||||
text = string.format(vidpos,Callsign,self.callsigntxt, Declaration)
|
||||
self:T(text)
|
||||
self:__VIDSuccess(3,GID,group,cluster)
|
||||
else
|
||||
-- too far away
|
||||
self:T("Contact VID not close enough")
|
||||
local vidneg = self.gettext:GetEntry("VIDNEG",self.locale)
|
||||
text = string.format(vidneg,Callsign,self.callsigntxt)
|
||||
self:T(text)
|
||||
self:__VIDFailure(3,GID,group,cluster)
|
||||
end
|
||||
self:_NewRadioEntry(text,text,GID,Outcome,true,true,false,true)
|
||||
end
|
||||
@@ -4070,10 +4157,14 @@ function AWACS:_CreateAnchorStackFromMarker(Name,Coord)
|
||||
if self.debug then
|
||||
AnchorStackOne.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true)
|
||||
local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
|
||||
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
|
||||
if self.AllowMarkers then
|
||||
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
|
||||
end
|
||||
else
|
||||
local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
|
||||
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
|
||||
if self.AllowMarkers then
|
||||
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
|
||||
end
|
||||
end
|
||||
|
||||
self.AnchorStacks:Push(AnchorStackOne,newname)
|
||||
@@ -4116,10 +4207,14 @@ function AWACS:_CreateAnchorStack()
|
||||
--self.AnchorStacks:Flush()
|
||||
AnchorStackOne.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true)
|
||||
local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
|
||||
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
|
||||
if self.AllowMarkers then
|
||||
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
|
||||
end
|
||||
else
|
||||
local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
|
||||
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
|
||||
if self.AllowMarkers then
|
||||
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
|
||||
end
|
||||
end
|
||||
self.AnchorStacks:Push(AnchorStackOne,newname)
|
||||
else
|
||||
@@ -4143,10 +4238,14 @@ function AWACS:_CreateAnchorStack()
|
||||
if self.debug then
|
||||
AnchorStackOne.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true)
|
||||
local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
|
||||
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
|
||||
if self.AllowMarkers then
|
||||
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
|
||||
end
|
||||
else
|
||||
local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
|
||||
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
|
||||
if self.AllowMarkers then
|
||||
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
|
||||
end
|
||||
end
|
||||
self.AnchorStacks:Push(AnchorStackOne,newname)
|
||||
end
|
||||
@@ -4286,6 +4385,14 @@ function AWACS:_StartIntel(awacs)
|
||||
intel:SetFilterCategory({Unit.Category.AIRPLANE,Unit.Category.HELICOPTER})
|
||||
end
|
||||
|
||||
-- Corridors
|
||||
if self.usecorridors == true then
|
||||
intel:SetCorridorZones(self.corridorzones)
|
||||
if self.corridorceiling or self.corridorfloor then
|
||||
intel:SetCorridorLimits(self.corridorfloor,self.corridorceiling)
|
||||
end
|
||||
end
|
||||
|
||||
-- Callbacks
|
||||
local function NewCluster(Cluster)
|
||||
self:__NewCluster(5,Cluster)
|
||||
@@ -5078,10 +5185,14 @@ function AWACS:AddCAPAirWing(AirWing,Zone)
|
||||
if self.debug then
|
||||
AnchorStackOne.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true)
|
||||
local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
|
||||
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
|
||||
if self.AllowMarkers then
|
||||
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
|
||||
end
|
||||
else
|
||||
local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
|
||||
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
|
||||
if self.AllowMarkers then
|
||||
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
|
||||
end
|
||||
end
|
||||
self.AnchorStacks:Push(AnchorStackOne,newname)
|
||||
AirWing.HasOwnStation = true
|
||||
@@ -5924,23 +6035,35 @@ function AWACS:onafterStart(From, Event, To)
|
||||
self.OpsZone:DrawZone(self.coalition,{1,0,0},1,{1,0,0},0.2,5,true)
|
||||
local AOCoordString = self.AOCoordinate:ToStringLLDDM()
|
||||
local Rocktag = string.format("FEZ: %s\nBulls Coordinate: %s",self.AOName,AOCoordString)
|
||||
MARKER:New(self.AOCoordinate,Rocktag):ToCoalition(self.coalition)
|
||||
if self.AllowMarkers then
|
||||
MARKER:New(self.AOCoordinate,Rocktag):ToCoalition(self.coalition)
|
||||
end
|
||||
self.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true)
|
||||
local stationtag = string.format("Station: %s\nCoordinate: %s",self.StationZoneName,self.StationZone:GetCoordinate():ToStringLLDDM())
|
||||
if not self.GCI then
|
||||
MARKER:New(self.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
|
||||
if self.AllowMarkers then
|
||||
MARKER:New(self.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
|
||||
end
|
||||
self.OrbitZone:DrawZone(self.coalition,{0,1,0},1,{0,1,0},0.2,5,true)
|
||||
MARKER:New(self.OrbitZone:GetCoordinate(),"AIC Orbit Zone"):ToCoalition(self.coalition)
|
||||
if self.AllowMarkers then
|
||||
MARKER:New(self.OrbitZone:GetCoordinate(),"AIC Orbit Zone"):ToCoalition(self.coalition)
|
||||
end
|
||||
end
|
||||
else
|
||||
local AOCoordString = self.AOCoordinate:ToStringLLDDM()
|
||||
local Rocktag = string.format("FEZ: %s\nBulls Coordinate: %s",self.AOName,AOCoordString)
|
||||
MARKER:New(self.AOCoordinate,Rocktag):ToCoalition(self.coalition)
|
||||
if self.AllowMarkers then
|
||||
MARKER:New(self.AOCoordinate,Rocktag):ToCoalition(self.coalition)
|
||||
end
|
||||
if not self.GCI then
|
||||
MARKER:New(self.OrbitZone:GetCoordinate(),"AIC Orbit Zone"):ToCoalition(self.coalition)
|
||||
if self.AllowMarkers then
|
||||
MARKER:New(self.OrbitZone:GetCoordinate(),"AIC Orbit Zone"):ToCoalition(self.coalition)
|
||||
end
|
||||
end
|
||||
local stationtag = string.format("Station: %s\nCoordinate: %s",self.StationZoneName,self.StationZone:GetCoordinate():ToStringLLDDM())
|
||||
MARKER:New(self.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
|
||||
if self.AllowMarkers then
|
||||
MARKER:New(self.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
|
||||
end
|
||||
end
|
||||
|
||||
if not self.GCI then
|
||||
|
||||
@@ -599,7 +599,80 @@ function BRIGADE:onafterStatus(From, Event, To)
|
||||
text=text..string.format("\n* %s: spawned=%s", asset.spawngroupname, tostring(asset.spawned))
|
||||
end
|
||||
self:I(self.lid..text)
|
||||
end
|
||||
end
|
||||
|
||||
if self.verbose>=3 then
|
||||
|
||||
-- Count numbers
|
||||
local Ntotal=0
|
||||
local Nspawned=0
|
||||
local Nrequested=0
|
||||
local Nreserved=0
|
||||
local Nstock=0
|
||||
|
||||
local text="\n===========================================\n"
|
||||
text=text.."Assets:"
|
||||
local legion=self --Ops.Legion#LEGION
|
||||
|
||||
for _,_cohort in pairs(legion.cohorts) do
|
||||
local cohort=_cohort --Ops.Cohort#COHORT
|
||||
|
||||
for _,_asset in pairs(cohort.assets) do
|
||||
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
|
||||
|
||||
local state="In Stock"
|
||||
if asset.flightgroup then
|
||||
state=asset.flightgroup:GetState()
|
||||
local mission=legion:GetAssetCurrentMission(asset)
|
||||
if mission then
|
||||
state=state..string.format(", Mission \"%s\" [%s]", mission:GetName(), mission:GetType())
|
||||
end
|
||||
else
|
||||
if asset.spawned then
|
||||
env.info("FF ERROR: asset has opsgroup but is NOT spawned!")
|
||||
end
|
||||
if asset.requested and asset.isReserved then
|
||||
env.info("FF ERROR: asset is requested and reserved. Should not be both!")
|
||||
state="Reserved+Requested!"
|
||||
elseif asset.isReserved then
|
||||
state="Reserved"
|
||||
elseif asset.requested then
|
||||
state="Requested"
|
||||
end
|
||||
end
|
||||
|
||||
-- Text.
|
||||
text=text..string.format("\n[UID=%03d] %s Legion=%s [%s]: State=%s [RID=%s]",
|
||||
asset.uid, asset.spawngroupname, legion.alias, cohort.name, state, tostring(asset.rid))
|
||||
|
||||
|
||||
if asset.spawned then
|
||||
Nspawned=Nspawned+1
|
||||
end
|
||||
if asset.requested then
|
||||
Nrequested=Nrequested+1
|
||||
end
|
||||
if asset.isReserved then
|
||||
Nreserved=Nreserved+1
|
||||
end
|
||||
if not (asset.spawned or asset.requested or asset.isReserved) then
|
||||
Nstock=Nstock+1
|
||||
end
|
||||
|
||||
Ntotal=Ntotal+1
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
text=text.."\n-------------------------------------------"
|
||||
text=text..string.format("\nNstock = %d", Nstock)
|
||||
text=text..string.format("\nNreserved = %d", Nreserved)
|
||||
text=text..string.format("\nNrequested = %d", Nrequested)
|
||||
text=text..string.format("\nNspawned = %d", Nspawned)
|
||||
text=text..string.format("\nNtotal = %d (=%d)", Ntotal, Nstock+Nspawned+Nrequested+Nreserved)
|
||||
text=text.."\n==========================================="
|
||||
self:I(self.lid..text)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+2598
-553
File diff suppressed because it is too large
Load Diff
@@ -35,7 +35,7 @@
|
||||
-- @field #number Nsuccess Number of successful missions.
|
||||
-- @field #number Nfailure Number of failed mission.
|
||||
-- @field #table assetNumbers Asset numbers. Each entry is a table of data type `#CHIEF.AssetNumber`.
|
||||
-- @extends Ops.Intel#INTEL
|
||||
-- @extends Ops.Intelligence#INTEL
|
||||
|
||||
--- *In preparing for battle I have always found that plans are useless, but planning is indispensable* -- Dwight D Eisenhower
|
||||
--
|
||||
@@ -48,7 +48,7 @@
|
||||
--
|
||||
-- # Territory
|
||||
--
|
||||
-- The chief class allows you to define boarder zones, conflict zones and attack zones.
|
||||
-- The chief class allows you to define border zones, conflict zones and attack zones.
|
||||
--
|
||||
-- ## Border Zones
|
||||
--
|
||||
@@ -332,7 +332,7 @@ CHIEF.Strategy = {
|
||||
|
||||
--- CHIEF class version.
|
||||
-- @field #string version
|
||||
CHIEF.version="0.6.1"
|
||||
CHIEF.version="0.7.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -2897,6 +2897,8 @@ function CHIEF:_GetMissionPerformanceFromTarget(Target)
|
||||
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ARTY, 30))
|
||||
|
||||
else
|
||||
|
||||
-- Everything else
|
||||
|
||||
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.BAI, 100))
|
||||
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.GROUNDATTACK, 50))
|
||||
@@ -2912,6 +2914,8 @@ function CHIEF:_GetMissionPerformanceFromTarget(Target)
|
||||
---
|
||||
|
||||
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ANTISHIP, 100))
|
||||
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.NAVALENGAGEMENT, 50))
|
||||
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ARTY, 30))
|
||||
|
||||
else
|
||||
self:E(self.lid.."ERROR: Unknown Group category!")
|
||||
|
||||
@@ -88,7 +88,7 @@ COHORT = {
|
||||
|
||||
--- COHORT class version.
|
||||
-- @field #string version
|
||||
COHORT.version="0.3.6"
|
||||
COHORT.version="0.3.7"
|
||||
|
||||
--- Global variable to store the unique(!) cohort names
|
||||
_COHORTNAMES={}
|
||||
@@ -100,6 +100,7 @@ _COHORTNAMES={}
|
||||
-- DONE: Create FLOTILLA class.
|
||||
-- DONE: Added check for properties.
|
||||
-- DONE: Make general so that PLATOON and SQUADRON can inherit this class.
|
||||
-- DONE: Better setting of call signs.
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Constructor
|
||||
@@ -515,10 +516,12 @@ end
|
||||
-- @param #COHORT self
|
||||
-- @param #number Callsign Callsign from CALLSIGN.Aircraft, e.g. "Chevy" for CALLSIGN.Aircraft.CHEVY.
|
||||
-- @param #number Index Callsign index, Chevy-**1**.
|
||||
-- @param #string CallsignString (optional) Set this for tasks like TANKER, AWACS or KIOWA and the like, which have special names. E.g. "Darkstar" or "Roughneck".
|
||||
-- @return #COHORT self
|
||||
function COHORT:SetCallsign(Callsign, Index)
|
||||
function COHORT:SetCallsign(Callsign, Index, CallsignString)
|
||||
self.callsignName=Callsign
|
||||
self.callsignIndex=Index
|
||||
self.callsignClearName=CallsignString
|
||||
self.callsign={}
|
||||
self.callsign.NumberSquad=Callsign
|
||||
self.callsign.NumberGroup=Index
|
||||
@@ -626,32 +629,42 @@ end
|
||||
--- Remove assets from pool. Not that assets must not be spawned or already reserved or requested.
|
||||
-- @param #COHORT self
|
||||
-- @param #number N Number of assets to be removed. Default 1.
|
||||
-- @param #number Delay Delay in seconds before assets are removed.
|
||||
-- @return #COHORT self
|
||||
function COHORT:RemoveAssets(N)
|
||||
function COHORT:RemoveAssets(N, Delay)
|
||||
self:T2(self.lid..string.format("Remove %d assets of Cohort", N))
|
||||
|
||||
N=N or 1
|
||||
|
||||
local n=0
|
||||
for i=#self.assets,1,-1 do
|
||||
local asset=self.assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem
|
||||
if Delay and Delay>0 then
|
||||
-- Delayed call
|
||||
self:ScheduleOnce(Delay, COHORT.RemoveAssets, self, N, 0)
|
||||
else
|
||||
|
||||
self:T2(self.lid..string.format("Checking removing asset %s", asset.spawngroupname))
|
||||
if not (asset.requested or asset.spawned or asset.isReserved) then
|
||||
self:T2(self.lid..string.format("Removing asset %s", asset.spawngroupname))
|
||||
table.remove(self.assets, i)
|
||||
n=n+1
|
||||
else
|
||||
self:T2(self.lid..string.format("Could NOT Remove asset %s", asset.spawngroupname))
|
||||
N=N or 1
|
||||
|
||||
local n=0
|
||||
for i=#self.assets,1,-1 do
|
||||
local asset=self.assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem
|
||||
|
||||
self:T2(self.lid..string.format("Checking removing asset %s", asset.spawngroupname))
|
||||
if not (asset.requested or asset.spawned or asset.isReserved) then
|
||||
self:T2(self.lid..string.format("Removing asset %s", asset.spawngroupname))
|
||||
-- Remove from warehouse and warehouse DB
|
||||
asset.legion:_DeleteStockItem(asset)
|
||||
table.remove(self.assets, i)
|
||||
n=n+1
|
||||
else
|
||||
self:T2(self.lid..string.format("Could NOT Remove asset %s", asset.spawngroupname))
|
||||
end
|
||||
|
||||
if n>=N then
|
||||
break
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if n>=N then
|
||||
break
|
||||
end
|
||||
self:T(self.lid..string.format("Removed %d/%d assets. New asset count=%d", n, N, #self.assets))
|
||||
|
||||
end
|
||||
|
||||
self:T(self.lid..string.format("Removed %d/%d assets. New asset count=%d", n, N, #self.assets))
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -679,7 +692,16 @@ end
|
||||
function COHORT:GetCallsign(Asset)
|
||||
|
||||
if self.callsignName then
|
||||
|
||||
--[[
|
||||
["callsign"] =
|
||||
{
|
||||
[2] = 1,
|
||||
["name"] = "Darkstar11",
|
||||
[3] = 1,
|
||||
[1] = 5,
|
||||
[4] = "Darkstar11",
|
||||
}, -- end of ["callsign"]
|
||||
]]
|
||||
Asset.callsign={}
|
||||
|
||||
for i=1,Asset.nunits do
|
||||
@@ -695,12 +717,16 @@ function COHORT:GetCallsign(Asset)
|
||||
else
|
||||
self.callsigncounter=self.callsigncounter+1
|
||||
end
|
||||
callsign["name"] = self.callsignClearName or UTILS.GetCallsignName(self.callsignName) or "None"
|
||||
callsign["name"] = string.format("%s%d%d",callsign["name"],callsign[2],callsign[3])
|
||||
callsign[4] = callsign["name"]
|
||||
|
||||
Asset.callsign[i]=callsign
|
||||
|
||||
self:T3({callsign=callsign})
|
||||
|
||||
--TODO: there is also a table entry .name, which is a string.
|
||||
--DONE: there is also a table entry .name, which is a string.
|
||||
--UTILS.PrintTableToLog(callsign)
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -136,6 +136,7 @@ COMMANDER = {
|
||||
awacsZones = {},
|
||||
tankerZones = {},
|
||||
limitMission = {},
|
||||
maxMissionsAssignPerCycle = 1,
|
||||
}
|
||||
|
||||
--- COMMANDER class version.
|
||||
@@ -1535,6 +1536,8 @@ function COMMANDER:CheckMissionQueue()
|
||||
end
|
||||
end
|
||||
|
||||
local missionsAssigned = 0
|
||||
|
||||
-- Loop over missions in queue.
|
||||
for _,_mission in pairs(self.missionqueue) do
|
||||
local mission=_mission --Ops.Auftrag#AUFTRAG
|
||||
@@ -1594,9 +1597,12 @@ function COMMANDER:CheckMissionQueue()
|
||||
-- Recruited assets but no requested escort available. Unrecruit assets!
|
||||
LEGION.UnRecruitAssets(assets, mission)
|
||||
end
|
||||
|
||||
-- Only ONE mission is assigned.
|
||||
return
|
||||
|
||||
missionsAssigned = missionsAssigned + 1
|
||||
if missionsAssigned >= (self.maxMissionsAssignPerCycle or 1) then
|
||||
return
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
else
|
||||
@@ -1611,6 +1617,16 @@ function COMMANDER:CheckMissionQueue()
|
||||
|
||||
end
|
||||
|
||||
--- Set how many missions can be assigned in a single status iteration. (eg. This is useful for persistent missions where you need to load all AUFTRAGs on mission start and then change it back to default)
|
||||
--- Warning: Increasing this value will increase the number of missions started per iteration and thus may lead to performance issues if too many missions are started at once.
|
||||
-- @param #COMMANDER self
|
||||
-- @param #number Number of missions assigned per status iteration. Default is 1.
|
||||
-- @return #COMMANDER self.
|
||||
function COMMANDER:SetMaxMissionsAssignPerCycle(MaxMissionsAssignPerCycle)
|
||||
self.maxMissionsAssignPerCycle = MaxMissionsAssignPerCycle or 1
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get cohorts.
|
||||
-- @param #COMMANDER self
|
||||
-- @param #table Legions Special legions.
|
||||
@@ -1670,9 +1686,12 @@ function COMMANDER:_GetCohorts(Legions, Cohorts, Operation)
|
||||
for _,_legion in pairs(Legions or {}) do
|
||||
local legion=_legion --Ops.Legion#LEGION
|
||||
|
||||
-- Check that runway is operational.
|
||||
local Runway=legion:IsAirwing() and legion:IsRunwayOperational() or true
|
||||
|
||||
-- Check that runway is operational.
|
||||
local Runway=true
|
||||
if legion:IsAirwing() then
|
||||
Runway=legion:IsRunwayOperational() and legion.airbase and legion.airbase:GetCoalition() == legion:GetCoalition()
|
||||
end
|
||||
|
||||
-- Legion has to be running.
|
||||
if legion:IsRunning() and Runway then
|
||||
|
||||
@@ -1703,9 +1722,12 @@ function COMMANDER:_GetCohorts(Legions, Cohorts, Operation)
|
||||
for _,_legion in pairs(self.legions) do
|
||||
local legion=_legion --Ops.Legion#LEGION
|
||||
|
||||
-- Check that runway is operational.
|
||||
local Runway=legion:IsAirwing() and legion:IsRunwayOperational() or true
|
||||
|
||||
-- Check that runway is operational.
|
||||
local Runway=true
|
||||
if legion:IsAirwing() then
|
||||
Runway=legion:IsRunwayOperational() and legion.airbase and legion.airbase:GetCoalition() == legion:GetCoalition()
|
||||
end
|
||||
|
||||
-- Legion has to be running.
|
||||
if legion:IsRunning() and Runway then
|
||||
|
||||
|
||||
@@ -0,0 +1,948 @@
|
||||
--- **Ops** - Create your A2G Defenses.
|
||||
--
|
||||
-- **Main Features:**
|
||||
--
|
||||
-- * Automatically create and manage A2G defenses using an AirWing and Squadrons for one coalition
|
||||
-- * Easy set-up
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-------------------------------------------------------------------------
|
||||
-- Easy A2G Engagement Class, based on OPS classes
|
||||
-------------------------------------------------------------------------
|
||||
--
|
||||
-- ## Documentation:
|
||||
--
|
||||
-- https://flightcontrol-master.github.io/MOOSE_DOCS_DEVELOP/Documentation/Ops.EasyAG.html
|
||||
--
|
||||
-- ## Example Missions:
|
||||
--
|
||||
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Ops/EasyAG).
|
||||
--
|
||||
-------------------------------------------------------------------------
|
||||
-- Date: Dec 2025
|
||||
-- Last Update: Dec 2025
|
||||
-------------------------------------------------------------------------
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- **Main Features:**
|
||||
--
|
||||
-- * Automatically create and manage A2G defenses using an AirWing and Squadrons for one coalition
|
||||
-- * Easy set-up
|
||||
-- * Add additional AirWings on other airbases
|
||||
-- * Each wing can have more than one Squadron - tasking to Squadrons is done on a random basis per AirWing
|
||||
-- * Create borders and zones of engagement
|
||||
-- * Detection can be ground based and/or via AWACS
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### AUTHOR: **applevangelist**
|
||||
--
|
||||
-- @module Ops.EasyAG
|
||||
-- @image AI_Air_To_Ground_Dispatching.JPG
|
||||
|
||||
|
||||
--- EASYA2G Class
|
||||
-- @type EASYA2G
|
||||
-- @field #string ClassName
|
||||
-- @field #number overhead
|
||||
-- @field #number engagerange
|
||||
-- @field #number capgrouping
|
||||
-- @field #string airbasename
|
||||
-- @field Wrapper.Airbase#AIRBASE airbase
|
||||
-- @field #number coalition
|
||||
-- @field #string alias
|
||||
-- @field #table wings
|
||||
-- @field Ops.Intel#INTEL Intel
|
||||
-- @field #number resurrection
|
||||
-- @field #number capspeed
|
||||
-- @field #number capalt
|
||||
-- @field #number capdir
|
||||
-- @field #number capleg
|
||||
-- @field #number maxinterceptsize
|
||||
-- @field #number missionrange
|
||||
-- @field #number noalert5
|
||||
-- @field #table ManagedAW
|
||||
-- @field #table ManagedSQ
|
||||
-- @field #table ManagedCP
|
||||
-- @field #table ManagedTK
|
||||
-- @field #table ManagedEWR
|
||||
-- @field #table ManagedREC
|
||||
-- @field #number MaxAliveMissions
|
||||
-- @field #boolean debug
|
||||
-- @field #number repeatsonfailure
|
||||
-- @field Core.Set#SET_ZONE GoZoneSet
|
||||
-- @field Core.Set#SET_ZONE NoGoZoneSet
|
||||
-- @field Core.Set#SET_ZONE ConflictZoneSet
|
||||
-- @field #boolean Monitor
|
||||
-- @field #boolean TankerInvisible
|
||||
-- @field #number CapFormation
|
||||
-- @field #table ReadyFlightGroups
|
||||
-- @field #boolean DespawnAfterLanding
|
||||
-- @field #boolean DespawnAfterHolding
|
||||
-- @field #list<Ops.Auftrag#AUFTRAG> ListOfAuftrag
|
||||
-- @field #string defaulttakeofftype Take off type
|
||||
-- @field #number FuelLowThreshold
|
||||
-- @field #number FuelCriticalThreshold
|
||||
-- @field #boolean showpatrolpointmarks
|
||||
-- @field #table EngageTargetTypes
|
||||
-- @extends Ops.EasyGCICAP#EASYGCICAP
|
||||
|
||||
--- *“High-Threat Close-Air-Support is a Myth.”* -- Mike “Starbaby” Pietrucha.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # The EasyAG Concept
|
||||
--
|
||||
-- The idea of this class is partially to make the OPS classes easier operational for an A2G defense network, and to replace the legacy AI_A2G_Dispatcher system - not to it's
|
||||
-- full extent, but make a basic system work very quickly.
|
||||
--
|
||||
-- # Setup
|
||||
--
|
||||
-- ## Basic understanding
|
||||
--
|
||||
-- The basics are, there is **one** and only **one** AirWing per airbase. Each AirWing has **at least** one Squadron, who will do A2G tasks. Squadrons will be randomly chosen for the task at hand.
|
||||
-- Each AirWing has **at least** one Conflict Zone that it manages. COnflict Zones will be covered by the AirWing automatically as long as airframes are available. Detected enemy ground forces will be assigned to **one**
|
||||
-- AirWing based on proximity (that is, if you have more than one).
|
||||
--
|
||||
-- ## Assignment of tasks for enemies
|
||||
--
|
||||
-- An exisiting plane or a newly spawned plane will take care of the intruders. Standard overhead is 0.1, i.e. a group of 10 intrudes will
|
||||
-- be managed by one planes from the assigned AirWing. There is an maximum missions limitation per AirWing, so we do not spam the skies.
|
||||
--
|
||||
-- ## Basic set-up code
|
||||
--
|
||||
-- ### Prerequisites
|
||||
--
|
||||
-- You have to put a **STATIC WAREHOUSE** object on the airbase with the UNIT name according to the name of the airbase. **Do not put any other static type or it creates a conflict with the airbase name!**
|
||||
-- E.g. for Kutaisi this has to have the unit name Kutaisi. This object symbolizes the AirWing HQ.
|
||||
-- Next put a late activated template group for your A2G Squadron on the map. Last, put a zone on the map for the Defense operations, let's name it "Blue Zone 1". Size of the zone plays no role.
|
||||
-- Put a scout system on the map and name it aptly, like "Blue SCOUT".
|
||||
--
|
||||
-- ### Zones
|
||||
--
|
||||
-- For our example, you create a RED and a BLUE border, as a closed polygonal zone representing the borderlines. You can also have conflict zone, where - for our example - BLUE will attack
|
||||
-- RED groups, despite being on or close to RED territory. Think of a no-fly zone or an limited area of engagement. Conflict zones take precedence over borders, i.e. they can overlap all borders.
|
||||
--
|
||||
-- ### Code it
|
||||
--
|
||||
-- -- Set up a basic system for the blue side, we'll reside on Kutaisi, and use GROUP objects with "Blue SCOUT" in the name as Detecting Systems.
|
||||
-- local mywing = EASYA2G:New("A2G",AIRBASE.Caucasus.Kutaisi,"blue","SCOUT")
|
||||
--
|
||||
-- -- Add a holding/ingress point belonging to our airbase, we'll be at 5k ft doing 250 kn, initial direction 225 degrees (West), leg 5NM
|
||||
-- -- This will effectively be the ingress coordinate into the cnflict zone
|
||||
-- local Coordinate = ZONE:New("A2G Loitering"):GetCoordinate()
|
||||
-- mywing:AddHoldingPointA2G(AIRBASE.Caucasus.Kutaisi,Coordinate,5000,250,225,5)
|
||||
--
|
||||
-- -- Add a recon point over the conflict zone, we'll use a reaper for recon
|
||||
-- local Coordinate2 = ZONE:New("A2G Recon"):GetCoordinate()
|
||||
-- mywing:AddPatrolPointRecon(AIRBASE.Caucasus.Kutaisi,Coordinate2,15000,225,225,5)
|
||||
--
|
||||
-- -- Add three Squadrons with templates "Hero 1" and "Hero 2", 20 airframes, skill as set
|
||||
-- mywing:AddSquadron("A2G Flight", "Hero 1", AIRBASE.Caucasus.Kutaisi, 5, AI.Skill.GOOD, Modex, Livery)
|
||||
-- mywing:AddSquadron("A2G Helo", "Hero 2", AIRBASE.Caucasus.Kutaisi, 5, AI.Skill.HIGH, Modex, Livery)
|
||||
-- mywing:AddReconSquadron("Recon Drone", "SpyInTheSky SCOUT", AIRBASE.Caucasus.Kutaisi, 5, AI.Skill.EXCELLENT, Modex, Livery)
|
||||
--
|
||||
-- -- Ensure our reaper doesn't get immediately killed
|
||||
-- mywing:SetTankerAndScoutsInvisible(true)
|
||||
--
|
||||
-- -- Add a couple of zones
|
||||
-- -- We'll defend our own border
|
||||
-- mywing:AddAcceptZone(ZONE_POLYGON:New( "Blue Border", GROUP:FindByName( "Blue Border" ) ))
|
||||
-- -- We'll attack intruders also here - conflictzones can overlap borders(!) - limited zone of engagement
|
||||
-- mywing:AddConflictZone(ZONE_POLYGON:New("Red Defense Zone", GROUP:FindByName( "Red Defense Zone" )))
|
||||
-- -- We'll leave the reds alone on their turf
|
||||
-- mywing:AddRejectZone(ZONE_POLYGON:New( "Red Border", GROUP:FindByName( "Red Border" ) ))
|
||||
--
|
||||
-- -- Optional - Draw the borders on the map so we see what's going on
|
||||
-- -- Set up borders on map
|
||||
-- local BlueBorder = ZONE_POLYGON:New( "Blue Border", GROUP:FindByName( "Blue Border" ) )
|
||||
-- BlueBorder:DrawZone(-1,{0,0,1},1,FillColor,FillAlpha,1,true)
|
||||
-- local ConflictZone = ZONE_POLYGON:New("Red Defense Zone", GROUP:FindByName( "Red Defense Zone" ))
|
||||
-- ConflictZone:DrawZone(-1,{1,1,0},1,FillColor,FillAlpha,2,true)
|
||||
-- local BlueNoGoZone = ZONE_POLYGON:New( "Red Border", GROUP:FindByName( "Red Border" ) )
|
||||
-- BlueNoGoZone:DrawZone(-1,{1,0,0},1,FillColor,FillAlpha,4,true)
|
||||
--
|
||||
-- ### Add a second airwing with squads and own patrol point (optional)
|
||||
--
|
||||
-- -- Set this up at Sukhumi
|
||||
-- mywing:AddAirwing(AIRBASE.Caucasus.Sukhumi_Babushara,"Blue A2G Sukhumi")
|
||||
-- -- A2G Point "Blue Zone 2"
|
||||
-- mywing:AddPatrolPointA2G(AIRBASE.Caucasus.Sukhumi_Babushara,ZONE:FindByName("Blue Zone 2"):GetCoordinate(),30000,400,90,20)
|
||||
--
|
||||
-- -- This one has two squadrons to choose from
|
||||
-- mywing:AddSquadron("Blue Sq3 F16","A2G Sukhumi II",AIRBASE.Caucasus.Sukhumi_Babushara,20,AI.Skill.GOOD,402,"JASDF 6th TFS 43-8526 Skull Riders")
|
||||
-- mywing:AddSquadron("Blue Sq2 F15","A2G Sukhumi I",AIRBASE.Caucasus.Sukhumi_Babushara,20,AI.Skill.GOOD,202,"390th Fighter SQN")
|
||||
--
|
||||
-- ### Add a tanker (optional)
|
||||
--
|
||||
-- -- **Note** If you need different tanker types, i.e. Boom and Drogue, set them up at different AirWings!
|
||||
-- -- Add a tanker point
|
||||
-- mywing:AddPatrolPointTanker(AIRBASE.Caucasus.Kutaisi,ZONE:FindByName("Blue Zone Tanker"):GetCoordinate(),20000,280,270,50)
|
||||
-- -- Add a tanker squad - Radio 251 AM, TACAN 51Y
|
||||
-- mywing:AddTankerSquadron("Blue Tanker","Tanker Ops Kutaisi",AIRBASE.Caucasus.Kutaisi,20,AI.Skill.EXCELLENT,602,nil,251,radio.modulation.AM,51)
|
||||
--
|
||||
-- ### Add an AWACS (optional)
|
||||
--
|
||||
-- -- Add an AWACS point
|
||||
-- mywing:AddPatrolPointAwacs(AIRBASE.Caucasus.Kutaisi,ZONE:FindByName("Blue Zone AWACS"):GetCoordinate(),25000,300,270,50)
|
||||
-- -- Add an AWACS squad - Radio 251 AM, TACAN 51Y
|
||||
-- mywing:AddAWACSSquadron("Blue AWACS","AWACS Ops Kutaisi",AIRBASE.Caucasus.Kutaisi,20,AI.Skill.AVERAGE,702,nil,271,radio.modulation.AM)
|
||||
--
|
||||
-- # Fine-Tuning
|
||||
--
|
||||
-- ## Change Defaults
|
||||
--
|
||||
-- * @{#EASYA2G.SetDefaultResurrection}: Set how many seconds the AirWing stays inoperable after the AirWing STATIC HQ ist destroyed, default 900 secs.
|
||||
-- * @{#EASYA2G.SetDefaultA2GSpeed}: Set how many knots the A2G flights should do (will be altitude corrected), default 225 kn.
|
||||
-- * @{#EASYA2G.SetDefaultA2GAlt}: Set at which altitude (ASL) the A2G planes will fly, default 10,000 ft.
|
||||
-- * @{#EASYA2G.SetDefaultA2GDirection}: Set the initial direction from the A2G point the planes will fly in degrees, default is 90°.
|
||||
-- * @{#EASYA2G.SetDefaultA2GLeg}: Set the length of the A2G leg, default is 5 NM.
|
||||
-- * @{#EASYA2G.SetDefaultA2GGrouping}: Set how many planes will be spawned per mission (CVAP/GCI), defaults to 1.
|
||||
-- * @{#EASYA2G.SetDefaultMissionRange}: Set how many NM the planes can go from the home base, defaults to 100.
|
||||
-- * @{#EASYA2G.SetDefaultNumberAlert5Standby}: Set how many planes will be spawned on cold standby (Alert5), default 2.
|
||||
-- * @{#EASYA2G.SetDefaultEngageRange}: Set max engage range for A2G flights if they detect intruders, defaults to 50.
|
||||
-- * @{#EASYA2G.SetMaxAliveMissions}: Set max parallel missions can be done (A2G+GCI+Alert5+Tanker+AWACS), defaults to 8.
|
||||
-- * @{#EASYA2G.SetDefaultRepeatOnFailure}: Set max repeats on failure for intercepting/killing intruders, defaults to 3.
|
||||
-- * @{#EASYA2G.SetTankerAndScoutsInvisible}: Set Tanker and Scouts to be invisible to enemy AI eyes. Is set to `true` by default.
|
||||
--
|
||||
-- ## Debug and Monitor
|
||||
--
|
||||
-- mywing.debug = true -- log information
|
||||
-- mywing.Monitor = true -- show some statistics on screen
|
||||
--
|
||||
--
|
||||
-- @field #EASYA2G
|
||||
EASYA2G = {
|
||||
ClassName = "EASYA2G",
|
||||
overhead = 0.2,
|
||||
capgrouping = 1,
|
||||
airbasename = nil,
|
||||
airbase = nil,
|
||||
coalition = "blue",
|
||||
alias = nil,
|
||||
wings = {},
|
||||
Intel = nil,
|
||||
resurrection = 900,
|
||||
capspeed = 300,
|
||||
capalt = 25000,
|
||||
capdir = 45,
|
||||
capleg = 5,
|
||||
maxinterceptsize = 2,
|
||||
missionrange = 100,
|
||||
noalert5 = 2,
|
||||
ManagedAW = {},
|
||||
ManagedSQ = {},
|
||||
ManagedCP = {},
|
||||
ManagedTK = {},
|
||||
ManagedEWR = {},
|
||||
ManagedREC = {},
|
||||
MaxAliveMissions = 8,
|
||||
debug = false,
|
||||
engagerange = 50,
|
||||
repeatsonfailure = 3,
|
||||
GoZoneSet = nil,
|
||||
NoGoZoneSet = nil,
|
||||
ConflictZoneSet = nil,
|
||||
Monitor = false,
|
||||
TankerInvisible = true,
|
||||
CapFormation = nil,
|
||||
ReadyFlightGroups = {},
|
||||
DespawnAfterLanding = false,
|
||||
DespawnAfterHolding = true,
|
||||
ListOfAuftrag = {},
|
||||
defaulttakeofftype = "hot",
|
||||
FuelLowThreshold = 25,
|
||||
FuelCriticalThreshold = 10,
|
||||
showpatrolpointmarks = false,
|
||||
EngageTargetTypes = {"Ground"},
|
||||
}
|
||||
|
||||
--- Internal Squadron data type
|
||||
-- @type EASYA2G.Squad
|
||||
-- @field #string TemplateName
|
||||
-- @field #string SquadName
|
||||
-- @field #string AirbaseName
|
||||
-- @field #number AirFrames
|
||||
-- @field #string Skill
|
||||
-- @field #string Modex
|
||||
-- @field #string Livery
|
||||
-- @field #boolean Tanker
|
||||
-- @field #boolean AWACS
|
||||
-- @field #boolean RECON
|
||||
-- @field #number Frequency
|
||||
-- @field #number Modulation
|
||||
-- @field #number TACAN
|
||||
|
||||
--- Internal Wing data type
|
||||
-- @type EASYA2G.Wing
|
||||
-- @field #string AirbaseName
|
||||
-- @field #string Alias
|
||||
-- @field #string CapZoneName
|
||||
|
||||
--- Internal CapPoint data type
|
||||
-- @type EASYA2G.CapPoint
|
||||
-- @field #string AirbaseName
|
||||
-- @field Core.Point#COORDINATE Coordinate
|
||||
-- @field #number Altitude
|
||||
-- @field #number Speed
|
||||
-- @field #number Heading
|
||||
-- @field #number LegLength
|
||||
-- @field Core.Zone#ZONE_BASE Zone
|
||||
|
||||
--- EASYA2G class version.
|
||||
-- @field #string version
|
||||
EASYA2G.version="0.1.3"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: TBD
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Constructor
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Create a new GCIA2G Manager
|
||||
-- @param #EASYA2G self
|
||||
-- @param #string Alias A Name for this A2G Setup
|
||||
-- @param #string AirbaseName Name of the Home Airbase
|
||||
-- @param #string Coalition Coalition, e.g. "blue" or "red"
|
||||
-- @param #string ScoutName (Partial) group name of the detection system of the coalition, e.g. "Red SCOUT", can be handed in as table of names, e.g.{"SCOUT","DRONE","SAM"}
|
||||
-- @return #EASYA2G self
|
||||
function EASYA2G:New(Alias, AirbaseName, Coalition, ScoutName)
|
||||
-- Inherit everything from FSM class.
|
||||
|
||||
local self=BASE:Inherit(self, EASYGCICAP:New(Alias, AirbaseName, Coalition, ScoutName)) -- #EASYA2G
|
||||
|
||||
-- defaults
|
||||
self.alias = Alias or AirbaseName.." A2G Wing"
|
||||
self.coalitionname = string.lower(Coalition) or "blue"
|
||||
self.coalition = self.coalitionname == "blue" and coalition.side.BLUE or coalition.side.RED
|
||||
self.wings = {}
|
||||
if type(ScoutName) == "string" then ScoutName = {ScoutName} end
|
||||
self.EWRName = ScoutName --or self.coalitionname.." EWR"
|
||||
--self.CapZoneName = CapZoneName
|
||||
self.airbasename = AirbaseName
|
||||
self.airbase = AIRBASE:FindByName(self.airbasename)
|
||||
self.GoZoneSet = SET_ZONE:New()
|
||||
self.NoGoZoneSet = SET_ZONE:New()
|
||||
self.ConflictZoneSet = SET_ZONE:New()
|
||||
self.resurrection = 900
|
||||
self.capspeed = 225
|
||||
self.capalt = 5000
|
||||
self.capdir = 90
|
||||
self.capleg = 5
|
||||
self.capgrouping = 2
|
||||
self.missionrange = 100
|
||||
self.noalert5 = 2
|
||||
self.MaxAliveMissions = 8
|
||||
self.engagerange = 50
|
||||
self.repeatsonfailure = 3
|
||||
self.Monitor = false
|
||||
self.TankerInvisible = true
|
||||
self.CapFormation = ENUMS.Formation.FixedWing.FingerFour.Group
|
||||
self.DespawnAfterLanding = false
|
||||
self.DespawnAfterHolding = true
|
||||
self.ListOfAuftrag = {}
|
||||
self.defaulttakeofftype = "hot"
|
||||
self.FuelLowThreshold = 25
|
||||
self.FuelCriticalThreshold = 10
|
||||
self.showpatrolpointmarks = false
|
||||
self.EngageTargetTypes = {"Ground"}
|
||||
|
||||
-- Set some string id for output to DCS.log file.
|
||||
self.lid=string.format("EASYA2G %s | ", self.alias)
|
||||
|
||||
-- Add FSM transitions.
|
||||
-- From State --> Event --> To State
|
||||
self:SetStartState("Stopped")
|
||||
self:AddTransition("Stopped", "Start", "Running")
|
||||
self:AddTransition("Running", "Stop", "Stopped")
|
||||
self:AddTransition("*", "Status", "*")
|
||||
|
||||
--- On Before "Start" event.
|
||||
-- @function [parent=#EASYA2G] OnBeforeStart
|
||||
-- @param #EASYA2G self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
|
||||
--- On After "Start" event.
|
||||
-- @function [parent=#EASYA2G] OnAfterStart
|
||||
-- @param #EASYA2G self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
|
||||
--- On Before "Status" event.
|
||||
-- @function [parent=#EASYA2G] OnBeforeStatus
|
||||
-- @param #EASYA2G self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
|
||||
--- On After "Status" event.
|
||||
-- @function [parent=#EASYA2G] OnAfterStatus
|
||||
-- @param #EASYA2G self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
|
||||
self:AddAirwing(self.airbasename,self.alias,self.CapZoneName)
|
||||
|
||||
self:I(self.lid.."Created new instance (v"..self.version..")")
|
||||
|
||||
self:__Start(math.random(6,12))
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
-- Functions
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
--- Set Tanker and Scouts to be invisible to enemy AI eyes
|
||||
-- @param #EASYA2G self
|
||||
-- @param #boolean Switch Set to true or false, by default this is set to true already
|
||||
-- @return #EASYA2G self
|
||||
function EASYA2G:SetTankerAndScoutsInvisible(Switch)
|
||||
self:T(self.lid.."SetTankerAndScoutsInvisible")
|
||||
self.TankerInvisible = Switch
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set default A2G Speed in knots
|
||||
-- @param #EASYA2G self
|
||||
-- @param #number Speed Speed defaults to 300
|
||||
-- @return #EASYA2G self
|
||||
function EASYA2G:SetDefaultA2GSpeed(Speed)
|
||||
self:T(self.lid.."SetDefaultSpeed")
|
||||
self.capspeed = Speed or 300
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set A2G Flight formation.
|
||||
-- @param #EASYA2G self
|
||||
-- @param #number Formation Formation to fly, defaults to ENUMS.Formation.FixedWing.FingerFour.Group
|
||||
-- @return #EASYA2G self
|
||||
function EASYA2G:SetA2GFormation(Formation)
|
||||
self:T(self.lid.."SetA2GFormation")
|
||||
self.CapFormation = Formation
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set default A2G Altitude in feet
|
||||
-- @param #EASYA2G self
|
||||
-- @param #number Altitude Altitude defaults to 25000
|
||||
-- @return #EASYA2G self
|
||||
function EASYA2G:SetDefaultA2GAlt(Altitude)
|
||||
self:T(self.lid.."SetDefaultAltitude")
|
||||
self.capalt = Altitude or 25000
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set default A2G lieg initial direction in degrees
|
||||
-- @param #EASYA2G self
|
||||
-- @param #number Direction Direction defaults to 90 (East)
|
||||
-- @return #EASYA2G self
|
||||
function EASYA2G:SetDefaultA2GDirection(Direction)
|
||||
self:T(self.lid.."SetDefaultDirection")
|
||||
self.capdir = Direction or 90
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set default leg length in NM
|
||||
-- @param #EASYA2G self
|
||||
-- @param #number Leg Leg defaults to 5
|
||||
-- @return #EASYA2G self
|
||||
function EASYA2G:SetDefaultA2GLeg(Leg)
|
||||
self:T(self.lid.."SetDefaultLeg")
|
||||
self.capleg = Leg or 5
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set default grouping, i.e. how many airplanes per A2G point
|
||||
-- @param #EASYA2G self
|
||||
-- @param #number Grouping Grouping defaults to 2
|
||||
-- @return #EASYA2G self
|
||||
function EASYA2G:SetDefaultA2GGrouping(Grouping)
|
||||
self:T(self.lid.."SetDefaultA2GGrouping")
|
||||
self.capgrouping = Grouping or 2
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set A2G mission start to vary randomly between Start end End seconds.
|
||||
-- @param #EASYA2G self
|
||||
-- @param #number Start
|
||||
-- @param #number End
|
||||
-- @return #EASYA2G self
|
||||
function EASYA2G:SetA2GStartTimeVariation(Start, End)
|
||||
self.capOptionVaryStartTime = Start or 5
|
||||
self.capOptionVaryEndTime = End or 60
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set which target types A2G flights will prefer to engage, defaults to {"Ground"}
|
||||
-- @param #EASYA2G self
|
||||
-- @param #table types Table of comma separated #string entries, defaults to {"Ground"} (everything that is ground and is not a weapon). Useful other options are e.g. {"Armored vehicles"}, {"Tanks"},
|
||||
-- or {"APC"} or combinations like {"APC", "Tanks", "Artillery"}. See [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_enum_attributes).
|
||||
-- @return #EASYA2G self
|
||||
function EASYA2G:SetA2GEngageTargetTypes(types)
|
||||
self.EngageTargetTypes = types or {"Ground"}
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add a A2G patrol/holding point to a Wing
|
||||
-- @param #EASYA2G self
|
||||
-- @param #string AirbaseName Name of the Wing's airbase
|
||||
-- @param Core.Point#COORDINATE Coordinate. Can be handed as a Core.Zone#ZONE object (e.g. in case you want the point to align with a moving zone).
|
||||
-- @param #number Altitude Defaults to 25000 feet ASL.
|
||||
-- @param #number Speed Defaults to 300 knots TAS.
|
||||
-- @param #number Heading Defaults to 90 degrees (East).
|
||||
-- @param #number LegLength Defaults to 15 NM.
|
||||
-- @return #EASYA2G self
|
||||
function EASYA2G:AddHoldingPointA2G(AirbaseName,Coordinate,Altitude,Speed,Heading,LegLength)
|
||||
self:T(self.lid.."AddHoldingPointA2G")--..Coordinate:ToStringLLDDM())
|
||||
local coordinate = Coordinate
|
||||
local EntryCAP = {} -- #EASYGCICAP.CapPoint
|
||||
if Coordinate:IsInstanceOf("ZONE_BASE") then
|
||||
-- adjust coordinate and get the coordinate from the zone
|
||||
coordinate = Coordinate:GetCoordinate()
|
||||
EntryCAP.Zone = Coordinate
|
||||
end
|
||||
EntryCAP.AirbaseName = AirbaseName
|
||||
EntryCAP.Coordinate = coordinate
|
||||
EntryCAP.Altitude = Altitude or 25000
|
||||
EntryCAP.Speed = Speed or 300
|
||||
EntryCAP.Heading = Heading or 90
|
||||
EntryCAP.LegLength = LegLength or 5
|
||||
self.ManagedCP[#self.ManagedCP+1] = EntryCAP
|
||||
if self.debug then
|
||||
local mark = MARKER:New(coordinate,self.lid.."Holding Point"):ToAll()
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- (Internal) Add a Squadron to an Airwing of the manager
|
||||
-- @param #EASYA2G self
|
||||
-- @param #string TemplateName Name of the group template.
|
||||
-- @param #string SquadName Squadron name - must be unique!
|
||||
-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi
|
||||
-- @param #number AirFrames Number of available airframes, e.g. 20.
|
||||
-- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE
|
||||
-- @param #string Modex (optional) Modex to be used,e.g. 402.
|
||||
-- @param #string Livery (optional) Livery name to be used.
|
||||
-- @param #number Frequency (optional) Radio Frequency to be used.
|
||||
-- @param #number Modulation (optional) Radio Modulation to be used, e.g. radio.modulation.AM or radio.modulation.FM
|
||||
-- @return #EASYA2G self
|
||||
function EASYA2G:_AddSquadron(TemplateName, SquadName, AirbaseName, AirFrames, Skill, Modex, Livery, Frequency, Modulation)
|
||||
self:T(self.lid.."_AddSquadron "..SquadName)
|
||||
-- Add Squadrons
|
||||
local Squadron_One = SQUADRON:New(TemplateName,AirFrames,SquadName)
|
||||
Squadron_One:AddMissionCapability({AUFTRAG.Type.CAS, AUFTRAG.Type.CASENHANCED, AUFTRAG.Type.BAI, AUFTRAG.Type.ALERT5, AUFTRAG.Type.BOMBING, AUFTRAG.Type.STRIKE})
|
||||
--Squadron_One:SetFuelLowRefuel(true)
|
||||
Squadron_One:SetFuelLowThreshold(0.3)
|
||||
Squadron_One:SetTurnoverTime(10,20)
|
||||
Squadron_One:SetModex(Modex)
|
||||
Squadron_One:SetLivery(Livery)
|
||||
Squadron_One:SetSkill(Skill or AI.Skill.AVERAGE)
|
||||
Squadron_One:SetMissionRange(self.missionrange)
|
||||
|
||||
local wing = self.wings[AirbaseName][1] -- Ops.Airwing#AIRWING
|
||||
|
||||
wing:AddSquadron(Squadron_One)
|
||||
wing:NewPayload(TemplateName,-1,{AUFTRAG.Type.CAS, AUFTRAG.Type.CASENHANCED, AUFTRAG.Type.BAI, AUFTRAG.Type.ALERT5, AUFTRAG.Type.BOMBING, AUFTRAG.Type.STRIKE},75)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- (Internal) Try to assign the intercept to a FlightGroup already in air and ready.
|
||||
-- @param #EASYA2G self
|
||||
-- @param #table ReadyFlightGroups ReadyFlightGroups
|
||||
-- @param Ops.Auftrag#AUFTRAG Auftrag The Auftrag
|
||||
-- @param Wrapper.Group#GROUP Group The Target
|
||||
-- @param #number WingSize Calculated number of Flights
|
||||
-- @return #boolean assigned
|
||||
-- @return #number leftover
|
||||
function EASYA2G:_TryAssignMission(ReadyFlightGroups,Auftrag,Group,WingSize)
|
||||
self:T("_TryAssignMission for size "..WingSize or 1)
|
||||
local assigned = false
|
||||
local wingsize = WingSize or 1
|
||||
local mindist = 0
|
||||
local disttable = {}
|
||||
if Group and Group:IsAlive() then
|
||||
local gcoord = Group:GetCoordinate() or COORDINATE:New(0,0,0)
|
||||
self:T(self.lid..string.format("Assignment for %s",Group:GetName()))
|
||||
for _name,_FG in pairs(ReadyFlightGroups or {}) do
|
||||
local FG = _FG -- Ops.FlightGroup#FLIGHTGROUP
|
||||
local fcoord = FG:GetCoordinate()
|
||||
local dist = math.floor(UTILS.Round(fcoord:Get2DDistance(gcoord)/1000,1))
|
||||
self:T(self.lid..string.format("FG %s Distance %dkm",_name,dist))
|
||||
disttable[#disttable+1] = { FG=FG, dist=dist}
|
||||
if dist>mindist then mindist=dist end
|
||||
end
|
||||
|
||||
local function sortDistance(a, b)
|
||||
return a.dist < b.dist
|
||||
end
|
||||
|
||||
table.sort(disttable, sortDistance)
|
||||
|
||||
for _,_entry in ipairs(disttable) do
|
||||
local FG = _entry.FG -- Ops.FlightGroup#FLIGHTGROUP
|
||||
FG:AddMission(Auftrag)
|
||||
local cm = FG:GetMissionCurrent()
|
||||
if cm then cm:Cancel() end
|
||||
wingsize = wingsize - 1
|
||||
self:T(self.lid..string.format("Assigned to FG %s Distance %dkm",FG:GetName(),_entry.dist))
|
||||
if wingsize == 0 then
|
||||
assigned = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return assigned, wingsize
|
||||
end
|
||||
|
||||
--- Find a holding point closest to the group to be attacked (if any set)
|
||||
-- @param #EASYA2G self
|
||||
-- @param Wrapper.Group#GROUP Group
|
||||
-- @return Core.Point#COORDINATE Point (can be nil!)
|
||||
function EASYA2G:_GetClosestHoldingPoint(Group)
|
||||
local point = nil
|
||||
local mindist = 0
|
||||
if Group and Group:IsAlive() then
|
||||
local gcoord = Group:GetCoordinate() or COORDINATE:New(0,0,0)
|
||||
for _,_data in pairs(self.ManagedCP or {}) do
|
||||
local data = _data -- #EASYGCICAP.CapPoint
|
||||
--data.Coordinate
|
||||
local dist = math.floor(UTILS.Round(data.Coordinate:Get2DDistance(gcoord)/1000,1))
|
||||
self:T(self.lid..string.format("Holding Point Distance %dkm",dist))
|
||||
if dist>mindist then
|
||||
mindist=dist
|
||||
point=data.Coordinate
|
||||
end
|
||||
end
|
||||
end
|
||||
return point
|
||||
end
|
||||
|
||||
--- Here, we'll decide if we need to launch an attacking flight, and from where
|
||||
-- @param #EASYA2G self
|
||||
-- @param Ops.Intel#INTEL.Cluster Cluster
|
||||
-- @return #EASYA2G self
|
||||
function EASYA2G:_AssignMission(Cluster)
|
||||
self:I(self.lid.."_AssignMission")
|
||||
-- Here, we'll decide if we need to launch an attacking flight, and from where
|
||||
local overhead = self.overhead
|
||||
local capspeed = self.capspeed + 100
|
||||
local capalt = self.capalt or 5000
|
||||
local maxsize = self.maxinterceptsize
|
||||
local repeatsonfailure = self.repeatsonfailure
|
||||
|
||||
local wings = self.wings
|
||||
local ctlpts = self.ManagedCP
|
||||
local MaxAliveMissions = self.MaxAliveMissions --* self.capgrouping
|
||||
local nogozoneset = self.NoGoZoneSet
|
||||
local conflictzoneset = self.ConflictZoneSet
|
||||
local ReadyFlightGroups = self.ReadyFlightGroups
|
||||
|
||||
-- Aircraft?
|
||||
if Cluster.ctype == INTEL.Ctype.AIRCRAFT then return end
|
||||
-- Threatlevel 0..10
|
||||
local contact = self.Intel:GetHighestThreatContact(Cluster)
|
||||
local name = contact.groupname --#string
|
||||
local threat = contact.threatlevel --#number
|
||||
local position = self.Intel:CalcClusterFuturePosition(Cluster,300)
|
||||
-- calculate closest zone
|
||||
local bestdistance = 2000*1000 -- 2000km
|
||||
local targetairwing = nil -- Ops.Airwing#AIRWING
|
||||
local targetawname = "" -- #string
|
||||
local clustersize = self.Intel:ClusterCountUnits(Cluster) or 1
|
||||
local wingsize = math.abs(overhead * (clustersize+1))
|
||||
if wingsize > maxsize then wingsize = maxsize end
|
||||
-- existing mission, and if so - done?
|
||||
local retrymission = true
|
||||
if Cluster.mission and (not Cluster.mission:IsOver()) then
|
||||
retrymission = false
|
||||
end
|
||||
if (retrymission) and (wingsize >= 1) then
|
||||
MESSAGE:New(string.format("**** %s Attackers need wingsize %d", UTILS.GetCoalitionName(self.coalition), wingsize),15,"A2G"):ToAllIf(self.debug):ToLog()
|
||||
for _,_data in pairs (wings) do
|
||||
local airwing = _data[1] -- Ops.Airwing#AIRWING
|
||||
local zone = _data[2] -- Core.Zone#ZONE
|
||||
local zonecoord = zone:GetCoordinate()
|
||||
local name = _data[3] -- #string
|
||||
local coa = AIRBASE:FindByName(name):GetCoalition()
|
||||
local distance = position:DistanceFromPointVec2(zonecoord)
|
||||
local airframes = airwing:CountAssets(true)
|
||||
local samecoalitionab = coa == self.coalition and true or false
|
||||
if distance < bestdistance and airframes >= wingsize and samecoalitionab == true then
|
||||
bestdistance = distance
|
||||
targetairwing = airwing
|
||||
targetawname = name
|
||||
end
|
||||
end
|
||||
for _,_data in pairs (ctlpts) do
|
||||
--local airwing = _data[1] -- Ops.Airwing#AIRWING
|
||||
--local zone = _data[2] -- Core.Zone#ZONE
|
||||
--local zonecoord = zone:GetCoordinate()
|
||||
--local name = _data[3] -- #string
|
||||
|
||||
local data = _data -- #EASYGCICAP.CapPoint
|
||||
local name = data.AirbaseName
|
||||
local zonecoord = data.Coordinate
|
||||
if data.Zone then
|
||||
-- refresh coordinate in case we have a (moving) zone
|
||||
zonecoord = data.Zone:GetCoordinate()
|
||||
end
|
||||
local airwing = wings[name][1]
|
||||
local coa = AIRBASE:FindByName(name):GetCoalition()
|
||||
local samecoalitionab = coa == self.coalition and true or false
|
||||
local distance = position:DistanceFromPointVec2(zonecoord)
|
||||
local airframes = airwing:CountAssets(true)
|
||||
if distance < bestdistance and airframes >= wingsize and samecoalitionab == true then
|
||||
bestdistance = distance
|
||||
targetairwing = airwing -- Ops.Airwing#AIRWING
|
||||
targetawname = name
|
||||
end
|
||||
end
|
||||
local text = string.format("Closest Airwing is %s", targetawname)
|
||||
local m = MESSAGE:New(text,10,"EasyA2G"):ToAllIf(self.debug):ToLog()
|
||||
-- Do we have a matching airwing?
|
||||
if targetairwing then
|
||||
local AssetCount = targetairwing:CountAssetsOnMission(MissionTypes,Cohort)
|
||||
local missioncount = self:_CountAliveAuftrags()
|
||||
-- Enough airframes on mission already?
|
||||
self:T(self.lid.." Assets on Mission "..AssetCount)
|
||||
if missioncount < MaxAliveMissions then
|
||||
local repeats = repeatsonfailure
|
||||
local Vec1 = contact.group:GetVec2()
|
||||
local Vec2 = targetairwing:GetVec2()
|
||||
--local HoldingVec2 = UTILS.FindNearestPointOnCircle(Vec1,UTILS.NMToMeters(10),Vec2)
|
||||
local IngressCoordinate = self:_GetClosestHoldingPoint(contact.group)
|
||||
if IngressCoordinate == nil then
|
||||
local IngressVec2 = UTILS.FindNearestPointOnCircle(Vec1,UTILS.NMToMeters(10),Vec2)
|
||||
IngressCoordinate = COORDINATE:NewFromVec2(IngressVec2)
|
||||
end
|
||||
local InterceptAuftrag = AUFTRAG:NewBAI(contact.group,capalt)
|
||||
:SetMissionRange(150)
|
||||
:SetPriority(1,true,1)
|
||||
:SetRepeatDelay(300)
|
||||
--:SetRequiredAssets(wingsize)
|
||||
:SetRepeatOnFailure(repeats)
|
||||
:SetMissionSpeed(UTILS.KnotsToAltKIAS(capspeed,capalt))
|
||||
:SetMissionAltitude(capalt)
|
||||
-- TODO: Refine this
|
||||
--:SetMissionHoldingCoord(COORDINATE:NewFromVec2(HoldingVec2),capalt,capspeed,120)
|
||||
:SetMissionIngressCoord(IngressCoordinate,capalt,capspeed)
|
||||
--:SetMissionEgressCoord(COORDINATE:NewFromVec2(HoldingVec2),capalt,capspeed)
|
||||
|
||||
if nogozoneset:Count() > 0 then
|
||||
InterceptAuftrag:AddConditionSuccess(
|
||||
function(group,zoneset,conflictset)
|
||||
local success = false
|
||||
if group and group:IsAlive() then
|
||||
local coord = group:GetCoordinate()
|
||||
if coord and zoneset:Count() > 0 and zoneset:IsCoordinateInZone(coord) then
|
||||
success = true
|
||||
end
|
||||
if coord and conflictset:Count() > 0 and conflictset:IsCoordinateInZone(coord) then
|
||||
success = false
|
||||
end
|
||||
else
|
||||
success = true -- target dead
|
||||
end
|
||||
return success
|
||||
end,
|
||||
contact.group,
|
||||
nogozoneset,
|
||||
conflictzoneset
|
||||
)
|
||||
end
|
||||
|
||||
InterceptAuftrag:AddConditionFailure(
|
||||
function()
|
||||
local failure = false
|
||||
if InterceptAuftrag:CountOpsGroups()==0 and InterceptAuftrag:IsExecuting() then failure = true end
|
||||
return failure
|
||||
end
|
||||
)
|
||||
|
||||
table.insert(self.ListOfAuftrag,InterceptAuftrag)
|
||||
local assigned, rest = self:_TryAssignMission(ReadyFlightGroups,InterceptAuftrag,contact.group,wingsize)
|
||||
if not assigned then
|
||||
InterceptAuftrag:SetRequiredAssets(rest)
|
||||
targetairwing:AddMission(InterceptAuftrag)
|
||||
end
|
||||
Cluster.mission = InterceptAuftrag
|
||||
end
|
||||
else
|
||||
MESSAGE:New("**** Not enough airframes available or max mission limit reached!",15,"EasyA2G"):ToAllIf(self.debug):ToLog()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- (Internal) Start detection.
|
||||
-- @param #EASYA2G self
|
||||
-- @return #EASYA2G self
|
||||
function EASYA2G:_StartIntel()
|
||||
self:T(self.lid.."_StartIntel")
|
||||
-- Border GCI Detection
|
||||
local BlueAir_DetectionSetGroup = SET_GROUP:New()
|
||||
BlueAir_DetectionSetGroup:FilterPrefixes( self.EWRName )
|
||||
BlueAir_DetectionSetGroup:FilterStart()
|
||||
|
||||
-- Intel type detection
|
||||
local BlueIntel = INTEL:New(BlueAir_DetectionSetGroup,self.coalitionname, self.alias)
|
||||
BlueIntel:SetClusterAnalysis(true,false,false)
|
||||
BlueIntel:SetForgetTime(300)
|
||||
BlueIntel:SetAcceptZones(self.GoZoneSet)
|
||||
BlueIntel:SetRejectZones(self.NoGoZoneSet)
|
||||
BlueIntel:SetConflictZones(self.ConflictZoneSet)
|
||||
BlueIntel:SetVerbosity(0)
|
||||
|
||||
if self.usecorridors == true then
|
||||
BlueIntel:SetCorridorZones(self.corridorzones)
|
||||
if self.corridorfloor or self.corridorceiling then
|
||||
BlueIntel:SetCorridorLimitsFeet(self.corridorfloor,self.corridorceiling)
|
||||
end
|
||||
end
|
||||
|
||||
BlueIntel:Start()
|
||||
|
||||
if self.debug then
|
||||
BlueIntel.debug = true
|
||||
end
|
||||
|
||||
local function AssignCluster(Cluster)
|
||||
self:_AssignMission(Cluster)
|
||||
end
|
||||
|
||||
function BlueIntel:onbeforeNewCluster(From,Event,To,Cluster)
|
||||
AssignCluster(Cluster)
|
||||
end
|
||||
|
||||
self.Intel = BlueIntel
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
-- TODO FSM Functions
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
--- (Internal) FSM Function onafterStart
|
||||
-- @param #EASYA2G self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #EASYA2G self
|
||||
function EASYA2G:onafterStart(From,Event,To)
|
||||
self:T({From,Event,To})
|
||||
self:_StartIntel()
|
||||
self:_CreateAirwings()
|
||||
self:_CreateSquads()
|
||||
--self:_SetCAPPatrolPoints()
|
||||
self:_SetTankerPatrolPoints()
|
||||
self:_SetAwacsPatrolPoints()
|
||||
self:_SetReconPatrolPoints()
|
||||
self:__Status(-10)
|
||||
return self
|
||||
end
|
||||
|
||||
--- (Internal) FSM Function onafterStatus
|
||||
-- @param #EASYA2G self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #EASYA2G self
|
||||
function EASYA2G:onafterStatus(From,Event,To)
|
||||
self:T({From,Event,To})
|
||||
-- cleanup
|
||||
local cleaned = false
|
||||
local cleanlist = {}
|
||||
for _,_auftrag in pairs(self.ListOfAuftrag) do
|
||||
local auftrag = _auftrag -- Ops.Auftrag#AUFTRAG
|
||||
if auftrag and (not (auftrag:IsCancelled() or auftrag:IsDone() or auftrag:IsOver())) then
|
||||
table.insert(cleanlist,auftrag)
|
||||
cleaned = true
|
||||
end
|
||||
end
|
||||
if cleaned == true then
|
||||
self.ListOfAuftrag = nil
|
||||
self.ListOfAuftrag = cleanlist
|
||||
end
|
||||
-- Gather Some Stats
|
||||
local function counttable(tbl)
|
||||
local count = 0
|
||||
for _,_data in pairs(tbl) do
|
||||
count = count + 1
|
||||
end
|
||||
return count
|
||||
end
|
||||
local wings = counttable(self.ManagedAW)
|
||||
local squads = counttable(self.ManagedSQ)
|
||||
local caps = counttable(self.ManagedCP)
|
||||
local assets = 0
|
||||
local instock = 0
|
||||
local capmission = 0
|
||||
local interceptmission = 0
|
||||
local reconmission = 0
|
||||
local awacsmission = 0
|
||||
local tankermission = 0
|
||||
local alert5mission = 0
|
||||
for _,_wing in pairs(self.wings) do
|
||||
local count = _wing[1]:CountAssetsOnMission(MissionTypes,Cohort)
|
||||
local count2 = _wing[1]:CountAssets(true,MissionTypes,Attributes)
|
||||
--capmission = capmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.PATROLRACETRACK})
|
||||
interceptmission = interceptmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.BAI})
|
||||
reconmission = reconmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.RECON})
|
||||
awacsmission = awacsmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.AWACS})
|
||||
tankermission = tankermission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.TANKER})
|
||||
alert5mission = alert5mission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.ALERT5})
|
||||
assets = assets + count
|
||||
instock = instock + count2
|
||||
local assetsonmission = _wing[1]:GetAssetsOnMission({AUFTRAG.Type.BAI,AUFTRAG.Type.ALERT5})
|
||||
-- update ready groups
|
||||
self.ReadyFlightGroups = nil
|
||||
self.ReadyFlightGroups = {}
|
||||
for _,_asset in pairs(assetsonmission or {}) do
|
||||
local asset = _asset -- Functional.Warehouse#WAREHOUSE.Assetitem
|
||||
local FG = asset.flightgroup -- Ops.FlightGroup#FLIGHTGROUP
|
||||
if FG then
|
||||
local name = FG:GetName()
|
||||
local engage = FG:IsEngaging()
|
||||
local hasmissiles = FG:CanAirToGround()
|
||||
--self:T("Is Alert5? "..tostring(FG:GetMissionCurrent().type))
|
||||
local isalert5 = (FG:GetMissionCurrent() ~= nil and FG:GetMissionCurrent().type == AUFTRAG.Type.ALERT5) and true or false
|
||||
local ready = hasmissiles and FG:IsFuelGood() and (FG:IsAirborne() or isalert5)
|
||||
self:T(string.format("Flightgroup %s Engaging = %s Ready = %s (HasAmmo = %s HasFuel = %s Alert5 = %s)",tostring(name),tostring(engage),tostring(ready),tostring(hasmissiles),tostring(FG:IsFuelGood()), tostring(isalert5)))
|
||||
if ready then
|
||||
self.ReadyFlightGroups[name] = FG
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if self.Monitor then
|
||||
local threatcount = #self.Intel.Clusters or 0
|
||||
local text = self.alias
|
||||
text = text.."\nWings: "..wings.."\nSquads: "..squads.."\nHoldPoints: "..caps.."\nAssets on Mission: "..assets.."\nAssets in Stock: "..instock
|
||||
text = text.."\nThreats: "..threatcount
|
||||
text = text.."\nAirWing alive Missions: "..capmission+awacsmission+tankermission+reconmission+interceptmission+alert5mission
|
||||
--text = text.."\n - A2G Holding: "..capmission
|
||||
text = text.."\n - A2G Attack: "..interceptmission
|
||||
text = text.."\n - AWACS: "..awacsmission
|
||||
text = text.."\n - TANKER: "..tankermission
|
||||
text = text.."\n - Recon: "..reconmission
|
||||
text = text.."\n - Alert5 "..alert5mission
|
||||
text = text.."\nMission Limit: "..self.MaxAliveMissions
|
||||
MESSAGE:New(text,15,"A2G"):ToAll():ToLogIf(self.debug)
|
||||
end
|
||||
self:__Status(30)
|
||||
return self
|
||||
end
|
||||
@@ -1,13 +1,27 @@
|
||||
--- **Ops** - Create your A2A Defenses.
|
||||
--
|
||||
-- **Main Features:**
|
||||
--
|
||||
-- * Automatically create and manage A2A defenses using an AirWing and Squadrons for one coalition
|
||||
-- * Easy set-up
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-------------------------------------------------------------------------
|
||||
-- Easy CAP/GCI Class, based on OPS classes
|
||||
-------------------------------------------------------------------------
|
||||
-- Documentation
|
||||
--
|
||||
-- ## Documentation:
|
||||
--
|
||||
-- https://flightcontrol-master.github.io/MOOSE_DOCS_DEVELOP/Documentation/Ops.EasyGCICAP.html
|
||||
--
|
||||
-- ## Example Missions:
|
||||
--
|
||||
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Ops/EasyGCICAP).
|
||||
--
|
||||
-------------------------------------------------------------------------
|
||||
-- Date: September 2023
|
||||
-- Last Update: July 2024
|
||||
-- Last Update: Aug 2025
|
||||
-------------------------------------------------------------------------
|
||||
--
|
||||
--- **Ops** - Easy GCI & CAP Manager
|
||||
@@ -62,6 +76,7 @@
|
||||
-- @field #number repeatsonfailure
|
||||
-- @field Core.Set#SET_ZONE GoZoneSet
|
||||
-- @field Core.Set#SET_ZONE NoGoZoneSet
|
||||
-- @field Core.Set#SET_ZONE ConflictZoneSet
|
||||
-- @field #boolean Monitor
|
||||
-- @field #boolean TankerInvisible
|
||||
-- @field #number CapFormation
|
||||
@@ -69,6 +84,11 @@
|
||||
-- @field #boolean DespawnAfterLanding
|
||||
-- @field #boolean DespawnAfterHolding
|
||||
-- @field #list<Ops.Auftrag#AUFTRAG> ListOfAuftrag
|
||||
-- @field #string defaulttakeofftype Take off type
|
||||
-- @field #number FuelLowThreshold
|
||||
-- @field #number FuelCriticalThreshold
|
||||
-- @field #boolean showpatrolpointmarks
|
||||
-- @field #table EngageTargetTypes
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
--- *“Airspeed, altitude, and brains. Two are always needed to successfully complete the flight.”* -- Unknown.
|
||||
@@ -98,10 +118,15 @@
|
||||
-- ### Prerequisites
|
||||
--
|
||||
-- You have to put a **STATIC WAREHOUSE** object on the airbase with the UNIT name according to the name of the airbase. **Do not put any other static type or it creates a conflict with the airbase name!**
|
||||
-- E.g. for Kuitaisi this has to have the unit name Kutaisi. This object symbolizes the AirWing HQ.
|
||||
-- E.g. for Kutaisi this has to have the unit name Kutaisi. This object symbolizes the AirWing HQ.
|
||||
-- Next put a late activated template group for your CAP/GCI Squadron on the map. Last, put a zone on the map for the CAP operations, let's name it "Blue Zone 1". Size of the zone plays no role.
|
||||
-- Put an EW radar system on the map and name it aptly, like "Blue EWR".
|
||||
--
|
||||
-- ### Zones
|
||||
--
|
||||
-- For our example, you create a RED and a BLUE border, as a closed polygonal zone representing the borderlines. You can also have conflict zone, where - for our example - BLUE will attack
|
||||
-- RED planes, despite being on RED territory. Think of a no-fly zone or an limited area of engagement. Conflict zones take precedence over borders, i.e. they can overlap all borders.
|
||||
--
|
||||
-- ### Code it
|
||||
--
|
||||
-- -- Set up a basic system for the blue side, we'll reside on Kutaisi, and use GROUP objects with "Blue EWR" in the name as EW Radar Systems.
|
||||
@@ -114,10 +139,10 @@
|
||||
-- mywing:AddSquadron("Blue Sq1 M2000c","CAP Kutaisi",AIRBASE.Caucasus.Kutaisi,20,AI.Skill.GOOD,102,"ec1.5_Vendee_Jeanne_clean")
|
||||
--
|
||||
-- -- Add a couple of zones
|
||||
-- -- We'll defend our border
|
||||
-- -- We'll defend our own border
|
||||
-- mywing:AddAcceptZone(ZONE_POLYGON:New( "Blue Border", GROUP:FindByName( "Blue Border" ) ))
|
||||
-- -- We'll attack intruders also here
|
||||
-- mywing:AddAcceptZone(ZONE_POLYGON:New("Red Defense Zone", GROUP:FindByName( "Red Defense Zone" )))
|
||||
-- -- We'll attack intruders also here - conflictzones can overlap borders(!) - limited zone of engagement
|
||||
-- mywing:AddConflictZone(ZONE_POLYGON:New("Red Defense Zone", GROUP:FindByName( "Red Defense Zone" )))
|
||||
-- -- We'll leave the reds alone on their turf
|
||||
-- mywing:AddRejectZone(ZONE_POLYGON:New( "Red Border", GROUP:FindByName( "Red Border" ) ))
|
||||
--
|
||||
@@ -125,10 +150,10 @@
|
||||
-- -- Set up borders on map
|
||||
-- local BlueBorder = ZONE_POLYGON:New( "Blue Border", GROUP:FindByName( "Blue Border" ) )
|
||||
-- BlueBorder:DrawZone(-1,{0,0,1},1,FillColor,FillAlpha,1,true)
|
||||
-- local BlueNoGoZone = ZONE_POLYGON:New("Red Defense Zone", GROUP:FindByName( "Red Defense Zone" ))
|
||||
-- BlueNoGoZone:DrawZone(-1,{1,1,0},1,FillColor,FillAlpha,2,true)
|
||||
-- local BlueNoGoZone2 = ZONE_POLYGON:New( "Red Border", GROUP:FindByName( "Red Border" ) )
|
||||
-- BlueNoGoZone2:DrawZone(-1,{1,0,0},1,FillColor,FillAlpha,4,true)
|
||||
-- local ConflictZone = ZONE_POLYGON:New("Red Defense Zone", GROUP:FindByName( "Red Defense Zone" ))
|
||||
-- ConflictZone:DrawZone(-1,{1,1,0},1,FillColor,FillAlpha,2,true)
|
||||
-- local BlueNoGoZone = ZONE_POLYGON:New( "Red Border", GROUP:FindByName( "Red Border" ) )
|
||||
-- BlueNoGoZone:DrawZone(-1,{1,0,0},1,FillColor,FillAlpha,4,true)
|
||||
--
|
||||
-- ### Add a second airwing with squads and own CAP point (optional)
|
||||
--
|
||||
@@ -189,7 +214,7 @@ EASYGCICAP = {
|
||||
coalition = "blue",
|
||||
alias = nil,
|
||||
wings = {},
|
||||
Intel = nil,
|
||||
Intel = nil, -- Ops.Intel#INTEL
|
||||
resurrection = 900,
|
||||
capspeed = 300,
|
||||
capalt = 25000,
|
||||
@@ -210,13 +235,19 @@ EASYGCICAP = {
|
||||
repeatsonfailure = 3,
|
||||
GoZoneSet = nil,
|
||||
NoGoZoneSet = nil,
|
||||
ConflictZoneSet = nil,
|
||||
Monitor = false,
|
||||
TankerInvisible = true,
|
||||
CapFormation = nil,
|
||||
ReadyFlightGroups = {},
|
||||
DespawnAfterLanding = false,
|
||||
DespawnAfterHolding = true,
|
||||
ListOfAuftrag = {}
|
||||
ListOfAuftrag = {},
|
||||
defaulttakeofftype = "hot",
|
||||
FuelLowThreshold = 25,
|
||||
FuelCriticalThreshold = 10,
|
||||
showpatrolpointmarks = false,
|
||||
EngageTargetTypes = {"Air"},
|
||||
}
|
||||
|
||||
--- Internal Squadron data type
|
||||
@@ -249,13 +280,15 @@ EASYGCICAP = {
|
||||
-- @field #number Speed
|
||||
-- @field #number Heading
|
||||
-- @field #number LegLength
|
||||
-- @field Core.Zone#ZONE_BASE Zone
|
||||
|
||||
--- EASYGCICAP class version.
|
||||
-- @field #string version
|
||||
EASYGCICAP.version="0.1.17"
|
||||
EASYGCICAP.version="0.1.33"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
--
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: TBD
|
||||
@@ -287,6 +320,7 @@ function EASYGCICAP:New(Alias, AirbaseName, Coalition, EWRName)
|
||||
self.airbase = AIRBASE:FindByName(self.airbasename)
|
||||
self.GoZoneSet = SET_ZONE:New()
|
||||
self.NoGoZoneSet = SET_ZONE:New()
|
||||
self.ConflictZoneSet = SET_ZONE:New()
|
||||
self.resurrection = 900
|
||||
self.capspeed = 300
|
||||
self.capalt = 25000
|
||||
@@ -304,6 +338,11 @@ function EASYGCICAP:New(Alias, AirbaseName, Coalition, EWRName)
|
||||
self.DespawnAfterLanding = false
|
||||
self.DespawnAfterHolding = true
|
||||
self.ListOfAuftrag = {}
|
||||
self.defaulttakeofftype = "hot"
|
||||
self.FuelLowThreshold = 25
|
||||
self.FuelCriticalThreshold = 10
|
||||
self.showpatrolpointmarks = false
|
||||
self.EngageTargetTypes = {"Air"}
|
||||
|
||||
-- Set some string id for output to DCS.log file.
|
||||
self.lid=string.format("EASYGCICAP %s | ", self.alias)
|
||||
@@ -321,6 +360,34 @@ function EASYGCICAP:New(Alias, AirbaseName, Coalition, EWRName)
|
||||
|
||||
self:__Start(math.random(6,12))
|
||||
|
||||
--- On Before "Start" event.
|
||||
-- @function [parent=#EASYGCICAP] OnBeforeStart
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
|
||||
--- On After "Start" event.
|
||||
-- @function [parent=#EASYGCICAP] OnAfterStart
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
|
||||
--- On Before "Status" event.
|
||||
-- @function [parent=#EASYGCICAP] OnBeforeStatus
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
|
||||
--- On After "Status" event.
|
||||
-- @function [parent=#EASYGCICAP] OnAfterStatus
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -328,6 +395,85 @@ end
|
||||
-- Functions
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
--- Get a specific managed AirWing by name
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param #string AirbaseName Airbase name of the home of this wing.
|
||||
-- @return Ops.AirWing#AIRWING Airwing or nil if not found
|
||||
function EASYGCICAP:GetAirwing(AirbaseName)
|
||||
self:T(self.lid.."GetAirwing")
|
||||
if self.wings[AirbaseName] then
|
||||
return self.wings[AirbaseName][1]
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Add an agent to the underlying INTEL detection - caution, we need to be started first for this to work!
|
||||
-- Normally this isn't necessary when the Group name is correctly filled (see EWRName in `New()`).
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param Wrapper.Group#GROUP Group The group object to be added as Intel Agent.
|
||||
-- @return #EASYGCICAP self
|
||||
function EASYGCICAP:AddAgent(Group)
|
||||
self:T(self.lid.."AddAgent")
|
||||
if Group:IsInstanceOf("GROUP") and self.Intel ~= nil then
|
||||
self.Intel:AddAgent(Group)
|
||||
if self.TankerInvisible == true then
|
||||
Group:SetCommandInvisible(true)
|
||||
Group:OptionROEHoldFire()
|
||||
if Group:IsAir() then
|
||||
Group:OptionROTEvadeFire()
|
||||
else
|
||||
Group:OptionDisperseOnAttack(30)
|
||||
end
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get a table of all managed AirWings
|
||||
-- @param #EASYGCICAP self
|
||||
-- @return #table Table of Ops.AirWing#AIRWING Airwings
|
||||
function EASYGCICAP:GetAirwingTable()
|
||||
self:T(self.lid.."GetAirwingTable")
|
||||
local Wingtable = {}
|
||||
for _,_object in pairs(self.wings or {}) do
|
||||
table.insert(Wingtable,_object[1])
|
||||
end
|
||||
return Wingtable
|
||||
end
|
||||
|
||||
--- Set "fuel low" threshold for CAP and INTERCEPT flights.
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param #number Percent RTB if fuel at this percent. Values: 1..100, defaults to 25.
|
||||
-- @return #EASYGCICAP self
|
||||
function EASYGCICAP:SetFuelLow(Percent)
|
||||
self:T(self.lid.."SetFuelLow")
|
||||
self.FuelLowThreshold = Percent or 25
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set markers on the map for Patrol Points.
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param #boolean onoff Set to true to switch markers on.
|
||||
-- @return #EASYGCICAP self
|
||||
function EASYGCICAP:ShowPatrolPointMarkers(onoff)
|
||||
if onoff then
|
||||
self.showpatrolpointmarks = true
|
||||
else
|
||||
self.showpatrolpointmarks = false
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set "fuel critical" threshold for CAP and INTERCEPT flights.
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param #number Percent RTB if fuel at this percent. Values: 1..100, defaults to 10.
|
||||
-- @return #EASYGCICAP self
|
||||
function EASYGCICAP:SetFuelCritical(Percent)
|
||||
self:T(self.lid.."SetFuelCritical")
|
||||
self.FuelCriticalThreshold = Percent or 10
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set CAP formation.
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param #number Formation Formation to fly, defaults to ENUMS.Formation.FixedWing.FingerFour.Group
|
||||
@@ -348,7 +494,7 @@ function EASYGCICAP:SetTankerAndAWACSInvisible(Switch)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Count alive missions in our internal stack.
|
||||
--- (internal) Count alive missions in our internal stack.
|
||||
-- @param #EASYGCICAP self
|
||||
-- @return #number count
|
||||
function EASYGCICAP:_CountAliveAuftrags()
|
||||
@@ -392,6 +538,16 @@ function EASYGCICAP:SetDefaultRepeatOnFailure(Retries)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add default take off type for the airwings.
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param #string Takeoff Can be "hot", "cold", or "air" - default is "hot".
|
||||
-- @return #EASYGCICAP self
|
||||
function EASYGCICAP:SetDefaultTakeOffType(Takeoff)
|
||||
self:T(self.lid.."SetDefaultTakeOffType")
|
||||
self.defaulttakeofftype = Takeoff or "hot"
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set default CAP Speed in knots
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param #number Speed Speed defaults to 300
|
||||
@@ -515,6 +671,16 @@ function EASYGCICAP:SetCapStartTimeVariation(Start, End)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set which target types CAP flights will prefer to engage, defaults to {"Air"}
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param #table types Table of comma separated #string entries, defaults to {"Air"} (everything that flies and is not a weapon). Useful other options are e.g. {"Bombers"}, {"Fighters"},
|
||||
-- or {"Helicopters"} or combinations like {"Bombers", "Fighters", "UAVs"}. See [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_enum_attributes).
|
||||
-- @return #EASYGCICAP self
|
||||
function EASYGCICAP:SetCAPEngageTargetTypes(types)
|
||||
self.EngageTargetTypes = types or {"Air"}
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add an AirWing to the manager
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param #string Airbasename
|
||||
@@ -549,7 +715,7 @@ function EASYGCICAP:_CreateAirwings()
|
||||
return self
|
||||
end
|
||||
|
||||
--- (internal) Create and add another AirWing to the manager
|
||||
--- (Internal) Create and add another AirWing to the manager
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param #string Airbasename
|
||||
-- @param #string Alias
|
||||
@@ -561,6 +727,13 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias)
|
||||
local DespawnAfterLanding = self.DespawnAfterLanding
|
||||
local DespawnAfterHolding = self.DespawnAfterHolding
|
||||
|
||||
-- Check STATIC name
|
||||
local check = STATIC:FindByName(Airbasename,false) or UNIT:FindByName(Airbasename)
|
||||
if check == nil then
|
||||
MESSAGE:New(self.lid.."There's no warehouse static on the map (wrong naming?) for airbase "..tostring(Airbasename).."!",30,"CHECK"):ToAllIf(self.debug):ToLog()
|
||||
return
|
||||
end
|
||||
|
||||
-- Create Airwing
|
||||
local CAP_Wing = AIRWING:New(Airbasename,Alias)
|
||||
CAP_Wing:SetVerbosityLevel(0)
|
||||
@@ -570,6 +743,10 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias)
|
||||
CAP_Wing:SetRespawnAfterDestroyed()
|
||||
CAP_Wing:SetNumberCAP(self.capgrouping)
|
||||
CAP_Wing:SetCapCloseRaceTrack(true)
|
||||
|
||||
if self.showpatrolpointmarks then
|
||||
CAP_Wing:ShowPatrolPointMarkers(true)
|
||||
end
|
||||
|
||||
if self.capOptionVaryStartTime then
|
||||
CAP_Wing:SetCapStartTimeVariation(self.capOptionVaryStartTime,self.capOptionVaryEndTime)
|
||||
@@ -588,9 +765,8 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias)
|
||||
if #self.ManagedREC > 0 then
|
||||
CAP_Wing:SetNumberRecon(1)
|
||||
end
|
||||
--local PatrolCoordinateKutaisi = ZONE:New(CapZoneName):GetCoordinate()
|
||||
--CAP_Wing:AddPatrolPointCAP(PatrolCoordinateKutaisi,self.capalt,UTILS.KnotsToAltKIAS(self.capspeed,self.capalt),self.capdir,self.capleg)
|
||||
CAP_Wing:SetTakeoffHot()
|
||||
|
||||
CAP_Wing:SetTakeoffType(self.defaulttakeofftype)
|
||||
CAP_Wing:SetLowFuelThreshold(0.3)
|
||||
CAP_Wing.RandomAssetScore = math.random(50,100)
|
||||
CAP_Wing:Start()
|
||||
@@ -598,6 +774,12 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias)
|
||||
local Intel = self.Intel
|
||||
|
||||
local TankerInvisible = self.TankerInvisible
|
||||
local engagerange = self.engagerange
|
||||
local GoZoneSet = self.GoZoneSet
|
||||
local NoGoZoneSet = self.NoGoZoneSet
|
||||
local FuelLow = self.FuelLowThreshold or 25
|
||||
local FuelCritical = self.FuelCriticalThreshold or 10
|
||||
local EngageTypes = self.EngageTargetTypes or {"Air"}
|
||||
|
||||
function CAP_Wing:onbeforeFlightOnMission(From, Event, To, Flightgroup, Mission)
|
||||
local flightgroup = Flightgroup -- Ops.FlightGroup#FLIGHTGROUP
|
||||
@@ -609,10 +791,15 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias)
|
||||
flightgroup:SetDestinationbase(AIRBASE:FindByName(Airbasename))
|
||||
flightgroup:GetGroup():CommandEPLRS(true,5)
|
||||
flightgroup:GetGroup():SetOptionRadarUsingForContinousSearch()
|
||||
flightgroup:GetGroup():SetOptionLandingOverheadBreak()
|
||||
if Mission.type ~= AUFTRAG.Type.TANKER and Mission.type ~= AUFTRAG.Type.AWACS and Mission.type ~= AUFTRAG.Type.RECON then
|
||||
flightgroup:SetDetection(true)
|
||||
flightgroup:SetEngageDetectedOn(self.engagerange,{"Air"},self.GoZoneSet,self.NoGoZoneSet)
|
||||
flightgroup:SetEngageDetectedOn(engagerange,EngageTypes,GoZoneSet,NoGoZoneSet)
|
||||
flightgroup:SetOutOfAAMRTB()
|
||||
flightgroup:SetFuelLowRTB(true)
|
||||
flightgroup:SetFuelLowThreshold(FuelLow)
|
||||
flightgroup:SetFuelCriticalRTB(true)
|
||||
flightgroup:SetFuelCriticalThreshold(FuelCritical)
|
||||
if CapFormation then
|
||||
flightgroup:GetGroup():SetOption(AI.Option.Air.id.FORMATION,CapFormation)
|
||||
end
|
||||
@@ -635,8 +822,13 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias)
|
||||
end
|
||||
end
|
||||
|
||||
if self.noalert5 > 0 then
|
||||
local alert = AUFTRAG:NewALERT5(AUFTRAG.Type.INTERCEPT)
|
||||
if self.noalert5 > 0 then
|
||||
local alert
|
||||
if self.ClassName == "EASYGCICAP" then
|
||||
alert = AUFTRAG:NewALERT5(AUFTRAG.Type.INTERCEPT)
|
||||
elseif self.ClassName == "EASYA2G" then
|
||||
alert = AUFTRAG:NewALERT5(AUFTRAG.Type.BAI)
|
||||
end
|
||||
alert:SetRequiredAssets(self.noalert5)
|
||||
alert:SetRepeat(99)
|
||||
CAP_Wing:AddMission(alert)
|
||||
@@ -651,24 +843,30 @@ end
|
||||
--- Add a CAP patrol point to a Wing
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param #string AirbaseName Name of the Wing's airbase
|
||||
-- @param Core.Point#COORDINATE Coordinate.
|
||||
-- @param Core.Point#COORDINATE Coordinate. Can be handed as a Core.Zone#ZONE object (e.g. in case you want the point to align with a moving zone).
|
||||
-- @param #number Altitude Defaults to 25000 feet ASL.
|
||||
-- @param #number Speed Defaults to 300 knots TAS.
|
||||
-- @param #number Heading Defaults to 90 degrees (East).
|
||||
-- @param #number LegLength Defaults to 15 NM.
|
||||
-- @return #EASYGCICAP self
|
||||
function EASYGCICAP:AddPatrolPointCAP(AirbaseName,Coordinate,Altitude,Speed,Heading,LegLength)
|
||||
self:T(self.lid.."AddPatrolPointCAP "..Coordinate:ToStringLLDDM())
|
||||
local EntryCAP = {} -- #EASYGCICAP.CapPoint
|
||||
self:T(self.lid.."AddPatrolPointCAP")--..Coordinate:ToStringLLDDM())
|
||||
local coordinate = Coordinate
|
||||
local EntryCAP = {} -- #EASYGCICAP.CapPoint
|
||||
if Coordinate:IsInstanceOf("ZONE_BASE") then
|
||||
-- adjust coordinate and get the coordinate from the zone
|
||||
coordinate = Coordinate:GetCoordinate()
|
||||
EntryCAP.Zone = Coordinate
|
||||
end
|
||||
EntryCAP.AirbaseName = AirbaseName
|
||||
EntryCAP.Coordinate = Coordinate
|
||||
EntryCAP.Coordinate = coordinate
|
||||
EntryCAP.Altitude = Altitude or 25000
|
||||
EntryCAP.Speed = Speed or 300
|
||||
EntryCAP.Heading = Heading or 90
|
||||
EntryCAP.LegLength = LegLength or 15
|
||||
self.ManagedCP[#self.ManagedCP+1] = EntryCAP
|
||||
if self.debug then
|
||||
local mark = MARKER:New(Coordinate,self.lid.."Patrol Point"):ToAll()
|
||||
local mark = MARKER:New(coordinate,self.lid.."Patrol Point"):ToAll()
|
||||
end
|
||||
return self
|
||||
end
|
||||
@@ -676,7 +874,7 @@ end
|
||||
--- Add a RECON patrol point to a Wing
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param #string AirbaseName Name of the Wing's airbase
|
||||
-- @param Core.Point#COORDINATE Coordinate.
|
||||
-- @param Core.Point#COORDINATE Coordinate. Can be handed as a Core.Zone#ZONE object (e.g. in case you want the point to align with a moving zone).
|
||||
-- @param #number Altitude Defaults to 25000 feet.
|
||||
-- @param #number Speed Defaults to 300 knots.
|
||||
-- @param #number Heading Defaults to 90 degrees (East).
|
||||
@@ -701,7 +899,7 @@ end
|
||||
--- Add a TANKER patrol point to a Wing
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param #string AirbaseName Name of the Wing's airbase
|
||||
-- @param Core.Point#COORDINATE Coordinate.
|
||||
-- @param Core.Point#COORDINATE Coordinate. Can be handed as a Core.Zone#ZONE object (e.g. in case you want the point to align with a moving zone).
|
||||
-- @param #number Altitude Defaults to 25000 feet.
|
||||
-- @param #number Speed Defaults to 300 knots.
|
||||
-- @param #number Heading Defaults to 90 degrees (East).
|
||||
@@ -726,7 +924,7 @@ end
|
||||
--- Add an AWACS patrol point to a Wing
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param #string AirbaseName Name of the Wing's airbase
|
||||
-- @param Core.Point#COORDINATE Coordinate.
|
||||
-- @param Core.Point#COORDINATE Coordinate. Can be handed as a Core.Zone#ZONE object (e.g. in case you want the point to align with a moving zone).
|
||||
-- @param #number Altitude Defaults to 25000 feet.
|
||||
-- @param #number Speed Defaults to 300 knots.
|
||||
-- @param #number Heading Defaults to 90 degrees (East).
|
||||
@@ -755,6 +953,11 @@ function EASYGCICAP:_SetTankerPatrolPoints()
|
||||
self:T(self.lid.."_SetTankerPatrolPoints")
|
||||
for _,_data in pairs(self.ManagedTK) do
|
||||
local data = _data --#EASYGCICAP.CapPoint
|
||||
self:T("Airbasename = "..data.AirbaseName)
|
||||
if not self.wings[data.AirbaseName] then
|
||||
MESSAGE:New(self.lid.."You are trying to create a TANKER point for which there is no wing! "..tostring(data.AirbaseName),30,"CHECK"):ToAllIf(self.debug):ToLog()
|
||||
return
|
||||
end
|
||||
local Wing = self.wings[data.AirbaseName][1] -- Ops.Airwing#AIRWING
|
||||
local Coordinate = data.Coordinate
|
||||
local Altitude = data.Altitude
|
||||
@@ -774,6 +977,11 @@ function EASYGCICAP:_SetAwacsPatrolPoints()
|
||||
self:T(self.lid.."_SetAwacsPatrolPoints")
|
||||
for _,_data in pairs(self.ManagedEWR) do
|
||||
local data = _data --#EASYGCICAP.CapPoint
|
||||
self:T("Airbasename = "..data.AirbaseName)
|
||||
if not self.wings[data.AirbaseName] then
|
||||
MESSAGE:New(self.lid.."You are trying to create an AWACS point for which there is no wing! "..tostring(data.AirbaseName),30,"CHECK"):ToAllIf(self.debug):ToLog()
|
||||
return
|
||||
end
|
||||
local Wing = self.wings[data.AirbaseName][1] -- Ops.Airwing#AIRWING
|
||||
local Coordinate = data.Coordinate
|
||||
local Altitude = data.Altitude
|
||||
@@ -793,13 +1001,23 @@ function EASYGCICAP:_SetCAPPatrolPoints()
|
||||
self:T(self.lid.."_SetCAPPatrolPoints")
|
||||
for _,_data in pairs(self.ManagedCP) do
|
||||
local data = _data --#EASYGCICAP.CapPoint
|
||||
self:T("Airbasename = "..data.AirbaseName)
|
||||
if not self.wings[data.AirbaseName] then
|
||||
MESSAGE:New(self.lid.."You are trying to create a CAP point for which there is no wing! "..tostring(data.AirbaseName),30,"CHECK"):ToAllIf(self.debug):ToLog()
|
||||
return
|
||||
end
|
||||
local Wing = self.wings[data.AirbaseName][1] -- Ops.Airwing#AIRWING
|
||||
local Coordinate = data.Coordinate
|
||||
local Altitude = data.Altitude
|
||||
local Speed = data.Speed
|
||||
local Heading = data.Heading
|
||||
local LegLength = data.LegLength
|
||||
Wing:AddPatrolPointCAP(Coordinate,Altitude,Speed,Heading,LegLength)
|
||||
local Zone = _data.Zone
|
||||
if Zone then
|
||||
Wing:AddPatrolPointCAP(Zone,Altitude,Speed,Heading,LegLength)
|
||||
else
|
||||
Wing:AddPatrolPointCAP(Coordinate,Altitude,Speed,Heading,LegLength)
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -812,6 +1030,11 @@ function EASYGCICAP:_SetReconPatrolPoints()
|
||||
self:T(self.lid.."_SetReconPatrolPoints")
|
||||
for _,_data in pairs(self.ManagedREC) do
|
||||
local data = _data --#EASYGCICAP.CapPoint
|
||||
self:T("Airbasename = "..data.AirbaseName)
|
||||
if not self.wings[data.AirbaseName] then
|
||||
MESSAGE:New(self.lid.."You are trying to create a RECON point for which there is no wing! "..tostring(data.AirbaseName),30,"CHECK"):ToAllIf(self.debug):ToLog()
|
||||
return
|
||||
end
|
||||
local Wing = self.wings[data.AirbaseName][1] -- Ops.Airwing#AIRWING
|
||||
local Coordinate = data.Coordinate
|
||||
local Altitude = data.Altitude
|
||||
@@ -860,7 +1083,7 @@ end
|
||||
-- @param #string SquadName Squadron name - must be unique!
|
||||
-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi
|
||||
-- @param #number AirFrames Number of available airframes, e.g. 20.
|
||||
-- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE
|
||||
-- @param #string Skill (optional) Skill level, e.g. AI.Skill.AVERAGE
|
||||
-- @param #string Modex (optional) Modex to be used,e.g. 402.
|
||||
-- @param #string Livery (optional) Livery name to be used.
|
||||
-- @return #EASYGCICAP self
|
||||
@@ -1065,7 +1288,9 @@ function EASYGCICAP:_AddTankerSquadron(TemplateName, SquadName, AirbaseName, Air
|
||||
Squadron_One:SetSkill(Skill or AI.Skill.AVERAGE)
|
||||
Squadron_One:SetMissionRange(self.missionrange)
|
||||
Squadron_One:SetRadio(Frequency,Modulation)
|
||||
Squadron_One:AddTacanChannel(TACAN,TACAN)
|
||||
if TACAN then
|
||||
Squadron_One:AddTacanChannel(TACAN,TACAN)
|
||||
end
|
||||
|
||||
local wing = self.wings[AirbaseName][1] -- Ops.Airwing#AIRWING
|
||||
|
||||
@@ -1113,7 +1338,7 @@ end
|
||||
-- @param Core.Zone#ZONE_BASE Zone
|
||||
-- @return #EASYGCICAP self
|
||||
function EASYGCICAP:AddAcceptZone(Zone)
|
||||
self:T(self.lid.."AddAcceptZone0")
|
||||
self:T(self.lid.."AddAcceptZone")
|
||||
self.GoZoneSet:AddZone(Zone)
|
||||
return self
|
||||
end
|
||||
@@ -1128,6 +1353,66 @@ function EASYGCICAP:AddRejectZone(Zone)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add a zone to the conflict zones set.
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param Core.Zone#ZONE_BASE Zone
|
||||
-- @return #EASYGCICAP self
|
||||
function EASYGCICAP:AddConflictZone(Zone)
|
||||
self:T(self.lid.."AddConflictZone")
|
||||
self.ConflictZoneSet:AddZone(Zone)
|
||||
self.GoZoneSet:AddZone(Zone)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Function to set corridor zones.
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param Core.Set#SET_ZONE CorridorZones Can be handed in as SET\_ZONE or single ZONE object.
|
||||
-- @return #EASYGCICAP self
|
||||
function EASYGCICAP:SetCorridorZones(CorridorZones)
|
||||
self:T(self.lid .. "SetCorridorZones")
|
||||
if CorridorZones and CorridorZones:IsInstanceOf("SET_ZONE") then
|
||||
self.corridorzones = CorridorZones
|
||||
self.usecorridors = true
|
||||
elseif CorridorZones and CorridorZones:IsInstanceOf("ZONE_BASE") then
|
||||
if not self.corridorzones then self.corridorzones = SET_ZONE:New() end
|
||||
self.corridorzones:AddZone(CorridorZones)
|
||||
self.usecorridors = true
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Function to add one corridor zone.
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param Core.Zone#ZONE CorridorZone The ZONE object to be added.
|
||||
-- @return #EASYGCICAP self
|
||||
function EASYGCICAP:AddCorridorZone(CorridorZone)
|
||||
self:T(self.lid .. "AddCorridorZone")
|
||||
self:SetCorridorZones(CorridorZone)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Function to set corridor zone floor and ceiling in FEET.
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param #number Floor Floor altitude ASL in feet.
|
||||
-- @param #number Ceiling Ceiling altitude ASL in feet.
|
||||
-- @return #EASYGCICAP self
|
||||
function EASYGCICAP:SetCorridorZoneFloorAndCeiling(Floor,Ceiling)
|
||||
self.corridorfloor = UTILS.FeetToMeters(Floor)
|
||||
self.corridorceiling = UTILS.FeetToMeters(Ceiling)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Function to set corridor zone floor and ceiling in METERS.
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param #number Floor Floor altitude ASL in meters.
|
||||
-- @param #number Ceiling Ceiling altitude ASL in meters.
|
||||
-- @return #EASYGCICAP self
|
||||
function EASYGCICAP:SetCorridorZoneFloorAndCeilingMeters(Floor,Ceiling)
|
||||
self.corridorfloor = Floor
|
||||
self.corridorceiling = Ceiling
|
||||
return self
|
||||
end
|
||||
|
||||
--- (Internal) Try to assign the intercept to a FlightGroup already in air and ready.
|
||||
-- @param #EASYGCICAP self
|
||||
-- @param #table ReadyFlightGroups ReadyFlightGroups
|
||||
@@ -1137,19 +1422,19 @@ end
|
||||
-- @return #boolean assigned
|
||||
-- @return #number leftover
|
||||
function EASYGCICAP:_TryAssignIntercept(ReadyFlightGroups,InterceptAuftrag,Group,WingSize)
|
||||
self:I("_TryAssignIntercept for size "..WingSize or 1)
|
||||
self:T("_TryAssignIntercept for size "..WingSize or 1)
|
||||
local assigned = false
|
||||
local wingsize = WingSize or 1
|
||||
local mindist = 0
|
||||
local disttable = {}
|
||||
if Group and Group:IsAlive() then
|
||||
local gcoord = Group:GetCoordinate() or COORDINATE:New(0,0,0)
|
||||
self:I(self.lid..string.format("Assignment for %s",Group:GetName()))
|
||||
self:T(self.lid..string.format("Assignment for %s",Group:GetName()))
|
||||
for _name,_FG in pairs(ReadyFlightGroups or {}) do
|
||||
local FG = _FG -- Ops.FlightGroup#FLIGHTGROUP
|
||||
local fcoord = FG:GetCoordinate()
|
||||
local dist = math.floor(UTILS.Round(fcoord:Get2DDistance(gcoord)/1000,1))
|
||||
self:I(self.lid..string.format("FG %s Distance %dkm",_name,dist))
|
||||
self:T(self.lid..string.format("FG %s Distance %dkm",_name,dist))
|
||||
disttable[#disttable+1] = { FG=FG, dist=dist}
|
||||
if dist>mindist then mindist=dist end
|
||||
end
|
||||
@@ -1166,7 +1451,7 @@ function EASYGCICAP:_TryAssignIntercept(ReadyFlightGroups,InterceptAuftrag,Group
|
||||
local cm = FG:GetMissionCurrent()
|
||||
if cm then cm:Cancel() end
|
||||
wingsize = wingsize - 1
|
||||
self:I(self.lid..string.format("Assigned to FG %s Distance %dkm",FG:GetName(),_entry.dist))
|
||||
self:T(self.lid..string.format("Assigned to FG %s Distance %dkm",FG:GetName(),_entry.dist))
|
||||
if wingsize == 0 then
|
||||
assigned = true
|
||||
break
|
||||
@@ -1193,9 +1478,10 @@ function EASYGCICAP:_AssignIntercept(Cluster)
|
||||
local ctlpts = self.ManagedCP
|
||||
local MaxAliveMissions = self.MaxAliveMissions --* self.capgrouping
|
||||
local nogozoneset = self.NoGoZoneSet
|
||||
local conflictzoneset = self.ConflictZoneSet
|
||||
local ReadyFlightGroups = self.ReadyFlightGroups
|
||||
|
||||
-- Aircraft?
|
||||
-- Aircraft?
|
||||
if Cluster.ctype ~= INTEL.Ctype.AIRCRAFT then return end
|
||||
-- Threatlevel 0..10
|
||||
local contact = self.Intel:GetHighestThreatContact(Cluster)
|
||||
@@ -1240,6 +1526,10 @@ function EASYGCICAP:_AssignIntercept(Cluster)
|
||||
local data = _data -- #EASYGCICAP.CapPoint
|
||||
local name = data.AirbaseName
|
||||
local zonecoord = data.Coordinate
|
||||
if data.Zone then
|
||||
-- refresh coordinate in case we have a (moving) zone
|
||||
zonecoord = data.Zone:GetCoordinate()
|
||||
end
|
||||
local airwing = wings[name][1]
|
||||
local coa = AIRBASE:FindByName(name):GetCoalition()
|
||||
local samecoalitionab = coa == self.coalition and true or false
|
||||
@@ -1271,21 +1561,35 @@ function EASYGCICAP:_AssignIntercept(Cluster)
|
||||
|
||||
if nogozoneset:Count() > 0 then
|
||||
InterceptAuftrag:AddConditionSuccess(
|
||||
function(group,zoneset)
|
||||
function(group,zoneset,conflictset)
|
||||
local success = false
|
||||
if group and group:IsAlive() then
|
||||
local coord = group:GetCoordinate()
|
||||
if coord and zoneset:IsCoordinateInZone(coord) then
|
||||
if coord and zoneset:Count() > 0 and zoneset:IsCoordinateInZone(coord) then
|
||||
success = true
|
||||
end
|
||||
if coord and conflictset:Count() > 0 and conflictset:IsCoordinateInZone(coord) then
|
||||
success = false
|
||||
end
|
||||
else
|
||||
success = true
|
||||
end
|
||||
return success
|
||||
end,
|
||||
contact.group,
|
||||
nogozoneset
|
||||
nogozoneset,
|
||||
conflictzoneset
|
||||
)
|
||||
end
|
||||
|
||||
InterceptAuftrag:AddConditionFailure(
|
||||
function()
|
||||
local failure = false
|
||||
if InterceptAuftrag:CountOpsGroups()==0 and InterceptAuftrag:IsExecuting() then failure = true end
|
||||
return failure
|
||||
end
|
||||
)
|
||||
|
||||
table.insert(self.ListOfAuftrag,InterceptAuftrag)
|
||||
local assigned, rest = self:_TryAssignIntercept(ReadyFlightGroups,InterceptAuftrag,contact.group,wingsize)
|
||||
if not assigned then
|
||||
@@ -1316,7 +1620,16 @@ function EASYGCICAP:_StartIntel()
|
||||
BlueIntel:SetForgetTime(300)
|
||||
BlueIntel:SetAcceptZones(self.GoZoneSet)
|
||||
BlueIntel:SetRejectZones(self.NoGoZoneSet)
|
||||
BlueIntel:SetConflictZones(self.ConflictZoneSet)
|
||||
BlueIntel:SetVerbosity(0)
|
||||
|
||||
if self.usecorridors == true then
|
||||
BlueIntel:SetCorridorZones(self.corridorzones)
|
||||
if self.corridorfloor or self.corridorceiling then
|
||||
BlueIntel:SetCorridorLimitsFeet(self.corridorfloor,self.corridorceiling)
|
||||
end
|
||||
end
|
||||
|
||||
BlueIntel:Start()
|
||||
|
||||
if self.debug then
|
||||
@@ -1336,7 +1649,7 @@ function EASYGCICAP:_StartIntel()
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
-- FSM Functions
|
||||
-- TODO FSM Functions
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
--- (Internal) FSM Function onafterStart
|
||||
@@ -1420,7 +1733,7 @@ function EASYGCICAP:onafterStatus(From,Event,To)
|
||||
tankermission = tankermission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.TANKER})
|
||||
assets = assets + count
|
||||
instock = instock + count2
|
||||
local assetsonmission = _wing[1]:GetAssetsOnMission({AUFTRAG.Type.GCICAP,AUFTRAG.Type.PATROLRACETRACK})
|
||||
local assetsonmission = _wing[1]:GetAssetsOnMission({AUFTRAG.Type.ALERT5, AUFTRAG.Type.GCICAP,AUFTRAG.Type.PATROLRACETRACK})
|
||||
-- update ready groups
|
||||
self.ReadyFlightGroups = nil
|
||||
self.ReadyFlightGroups = {}
|
||||
@@ -1431,8 +1744,9 @@ function EASYGCICAP:onafterStatus(From,Event,To)
|
||||
local name = FG:GetName()
|
||||
local engage = FG:IsEngaging()
|
||||
local hasmissiles = FG:IsOutOfMissiles() == nil and true or false
|
||||
local ready = hasmissiles and FG:IsFuelGood() and FG:IsAirborne()
|
||||
--self:I(string.format("Flightgroup %s Engaging = %s Ready = %s",tostring(name),tostring(engage),tostring(ready)))
|
||||
local isalert5 = (FG:GetMissionCurrent() ~= nil and FG:GetMissionCurrent().type == AUFTRAG.Type.ALERT5) and true or false
|
||||
local ready = hasmissiles and FG:IsFuelGood() and (FG:IsAirborne() or isalert5)
|
||||
--self:T(string.format("Flightgroup %s Engaging = %s Ready = %s",tostring(name),tostring(engage),tostring(ready)))
|
||||
if ready then
|
||||
self.ReadyFlightGroups[name] = FG
|
||||
end
|
||||
@@ -1467,5 +1781,8 @@ end
|
||||
function EASYGCICAP:onafterStop(From,Event,To)
|
||||
self:T({From,Event,To})
|
||||
self.Intel:Stop()
|
||||
for _,_wing in pairs(self.wings or {}) do
|
||||
_wing:Stop()
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -2464,7 +2464,7 @@ end
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Payer Menu
|
||||
-- Player Menu
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Create player menu.
|
||||
|
||||
@@ -259,7 +259,7 @@ function FLIGHTGROUP:New(group)
|
||||
local self=BASE:Inherit(self, OPSGROUP:New(group)) -- #FLIGHTGROUP
|
||||
|
||||
-- Set some string id for output to DCS.log file.
|
||||
self.lid=string.format("FLIGHTGROUP %s | ", self.groupname)
|
||||
self.lid=string.format("FLIGHTGROUP %s | ", self.groupname or "N/A")
|
||||
|
||||
-- Defaults
|
||||
self:SetDefaultROE()
|
||||
@@ -779,6 +779,61 @@ function FLIGHTGROUP:SetJettisonWeapons(Switch)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the aircraft to land straight in.
|
||||
-- @param #FLIGHTGROUP self
|
||||
-- @return #FLIGHTGROUP self
|
||||
function FLIGHTGROUP:SetOptionLandingStraightIn()
|
||||
self.OptionLandingStraightIn = true
|
||||
if self:GetGroup():IsAlive() then
|
||||
self:GetGroup():SetOptionLandingStraightIn()
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the aircraft to land in pairs.
|
||||
-- @param #FLIGHTGROUP self
|
||||
-- @return #FLIGHTGROUP self
|
||||
function FLIGHTGROUP:SetOptionLandingForcePair()
|
||||
self.OptionLandingForcePair = true
|
||||
if self:GetGroup():IsAlive() then
|
||||
self:GetGroup():SetOptionLandingForcePair()
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the aircraft to NOT land in pairs.
|
||||
-- @param #FLIGHTGROUP self
|
||||
-- @return #FLIGHTGROUP self
|
||||
function FLIGHTGROUP:SetOptionLandingRestrictPair()
|
||||
self.OptionLandingRestrictPair = true
|
||||
if self:GetGroup():IsAlive() then
|
||||
self:GetGroup():SetOptionLandingRestrictPair()
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the aircraft to land after overhead break.
|
||||
-- @param #FLIGHTGROUP self
|
||||
-- @return #FLIGHTGROUP self
|
||||
function FLIGHTGROUP:SetOptionLandingOverheadBreak()
|
||||
self.OptionLandingOverheadBreak = true
|
||||
if self:GetGroup():IsAlive() then
|
||||
self:GetGroup():SetOptionLandingOverheadBreak()
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- [HELICOPTER] Set the aircraft to prefer takeoff and landing vertically.
|
||||
-- @param #FLIGHTGROUP self
|
||||
-- @return #FLIGHTGROUP self
|
||||
function FLIGHTGROUP:SetOptionPreferVertical()
|
||||
self.OptionPreferVertical = true
|
||||
if self:GetGroup():IsAlive() then
|
||||
self:GetGroup():OptionPreferVerticalLanding()
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set if group is ready for taxi/takeoff if controlled by a `FLIGHTCONTROL`.
|
||||
-- @param #FLIGHTGROUP self
|
||||
-- @param #boolean ReadyTO If `true`, flight is ready for takeoff.
|
||||
@@ -2002,6 +2057,9 @@ function FLIGHTGROUP:onafterElementAirborne(From, Event, To, Element)
|
||||
|
||||
-- Debug info.
|
||||
self:T2(self.lid..string.format("Element airborne %s", Element.name))
|
||||
|
||||
-- Set parking spot to free. Also for FC. This is usually done after taxiing but doing it here in case the group is teleported.
|
||||
self:_SetElementParkingFree(Element)
|
||||
|
||||
-- Set element status.
|
||||
self:_UpdateStatus(Element, OPSGROUP.ElementStatus.AIRBORNE)
|
||||
@@ -3076,7 +3134,7 @@ function FLIGHTGROUP:onbeforeLandAtAirbase(From, Event, To, airbase)
|
||||
local Tsuspend=nil
|
||||
|
||||
if airbase==nil then
|
||||
self:T(self.lid.."ERROR: Airbase is nil in LandAtAirase() call!")
|
||||
self:T(self.lid.."ERROR: Airbase is nil in LandAtAirbase() call!")
|
||||
allowed=false
|
||||
end
|
||||
|
||||
@@ -4494,6 +4552,11 @@ function FLIGHTGROUP:GetParkingSpot(element, maxdist, airbase)
|
||||
-- Airbase.
|
||||
airbase=airbase or self:GetClosestAirbase()
|
||||
|
||||
if airbase == nil then
|
||||
self:T(self.lid.."No airbase found for element "..element.name)
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Parking table of airbase.
|
||||
local parking=airbase.parking --:GetParkingSpotsTable()
|
||||
|
||||
@@ -4604,10 +4667,12 @@ function FLIGHTGROUP:GetParking(airbase)
|
||||
local coords={}
|
||||
for clientname, client in pairs(clients) do
|
||||
local template=_DATABASE:GetGroupTemplateFromUnitName(clientname)
|
||||
local units=template.units
|
||||
for i,unit in pairs(units) do
|
||||
local coord=COORDINATE:New(unit.x, unit.alt, unit.y)
|
||||
coords[unit.name]=coord
|
||||
if template then
|
||||
local units=template.units
|
||||
for i,unit in pairs(units) do
|
||||
local coord=COORDINATE:New(unit.x, unit.alt, unit.y)
|
||||
coords[unit.name]=coord
|
||||
end
|
||||
end
|
||||
end
|
||||
return coords
|
||||
@@ -4964,7 +5029,7 @@ function FLIGHTGROUP:_UpdateMenu(delay)
|
||||
|
||||
-- Message to group.
|
||||
MESSAGE:New(text, 5):ToGroup(self.group)
|
||||
self:I(self.lid..text)
|
||||
self:T(self.lid..text)
|
||||
end
|
||||
|
||||
-- Get current position of player.
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
-- @field Core.Set#SET_ZONE acceptzoneset Set of accept zones. If defined, only contacts in these zones are considered.
|
||||
-- @field Core.Set#SET_ZONE rejectzoneset Set of reject zones. Contacts in these zones are not considered, even if they are in accept zones.
|
||||
-- @field Core.Set#SET_ZONE conflictzoneset Set of conflict zones. Contacts in these zones are considered, even if they are not in accept zones or if they are in reject zones.
|
||||
-- @field Core.Set#SET_ZONE corridorzoneset Set of corridor zones. Contacts in these zones are never considered. Also see corridorfloorheight and corridorfloorceiling.
|
||||
-- @field #number corridorfloor [Air] Contacts below this height (ASL!) are considered, even if they are in a corridor zone.
|
||||
-- @field #number corridorceiling [Air] Contacts above this height (ASL!) are considered, even if they are in a corridor zone.
|
||||
-- @field #table Contacts Table of detected items.
|
||||
-- @field #table ContactsLost Table of lost detected items.
|
||||
-- @field #table ContactsUnknown Table of new detected items.
|
||||
@@ -39,6 +42,9 @@
|
||||
-- @field #number prediction Seconds default to be used with CalcClusterFuturePosition.
|
||||
-- @field #boolean detectStatics If `true`, detect STATIC objects. Default `false`.
|
||||
-- @field #number statusupdate Time interval in seconds after which the status is refreshed. Default 60 sec. Should be negative.
|
||||
-- @field #boolean DetectAccoustic If true, also detect by sound (ie proximity).
|
||||
-- @field #number DetectAccousticRadius Radius dfor accoustic detection, defaults to 2000 meters.
|
||||
-- @field #table DetectAccousticUnitTypes Types of units we can detect accousticly. Defaults to {Unit.Category.HELICOPTER}
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
--- Top Secret!
|
||||
@@ -102,6 +108,9 @@ INTEL = {
|
||||
clusterarrows = false,
|
||||
prediction = 300,
|
||||
detectStatics = false,
|
||||
DetectAccoustic = false,
|
||||
DetectAccousticRadius = 1000,
|
||||
DetectAccousticUnitTypes = {Unit.Category.HELICOPTER},
|
||||
}
|
||||
|
||||
--- Detected item info.
|
||||
@@ -160,14 +169,14 @@ INTEL.Ctype={
|
||||
|
||||
--- INTEL class version.
|
||||
-- @field #string version
|
||||
INTEL.version="0.3.6"
|
||||
INTEL.version="0.3.10"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- ToDo list
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: Add min cluster size. Only create new clusters if they have a certain group size.
|
||||
-- TODO: process detected set asynchroniously for better performance.
|
||||
-- NODO: process detected set asynchroniously for better performance.
|
||||
-- DONE: Add statics.
|
||||
-- DONE: Filter detection methods.
|
||||
-- DONE: Accept zones.
|
||||
@@ -175,6 +184,7 @@ INTEL.version="0.3.6"
|
||||
-- NOGO: SetAttributeZone --> return groups of generalized attributes in a zone.
|
||||
-- DONE: Loose units only if they remain undetected for a given time interval. We want to avoid fast oscillation between detected/lost states. Maybe 1-5 min would be a good time interval?!
|
||||
-- DONE: Combine units to groups for all, new and lost.
|
||||
-- DONE: Add corridor zones.
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Constructor
|
||||
@@ -266,6 +276,7 @@ function INTEL:New(DetectionSet, Coalition, Alias)
|
||||
self:SetForgetTime()
|
||||
self:SetAcceptZones()
|
||||
self:SetRejectZones()
|
||||
self:SetCorridorZones()
|
||||
self:SetConflictZones()
|
||||
|
||||
------------------------
|
||||
@@ -398,6 +409,26 @@ function INTEL:SetAcceptZones(AcceptZoneSet)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set to accept accoustic detection.
|
||||
-- @param #INTEL self
|
||||
-- @param #number Radius Radius in which we can "hear" units. Defaults to 1000 meters.
|
||||
-- @param #table UnitCategories Set what Unit Categories we can "hear". Defaults to `{Unit.Category.GROUND_UNIT,Unit.Category.HELICOPTER}`
|
||||
-- @return #INTEL self
|
||||
function INTEL:SetAccousticDetectionOn(Radius,UnitCategories)
|
||||
self.DetectAccoustic = true
|
||||
self.DetectAccousticRadius = Radius or 1000
|
||||
self.DetectAccousticUnitTypes = UnitCategories or {Unit.Category.HELICOPTER}
|
||||
return self
|
||||
end
|
||||
|
||||
--- Switch off accoustic detection.
|
||||
-- @param #INTEL self
|
||||
-- @return #INTEL self
|
||||
function INTEL:SetAccousticDetectionOff()
|
||||
self.DetectAccoustic = false
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add an accept zone. Only contacts detected in this zone are considered.
|
||||
-- @param #INTEL self
|
||||
-- @param Core.Zone#ZONE AcceptZone Add a zone to the accept zone set.
|
||||
@@ -475,7 +506,69 @@ function INTEL:RemoveConflictZone(ConflictZone)
|
||||
return self
|
||||
end
|
||||
|
||||
--- **OBSOLETE, will be removed in next version!** Set forget contacts time interval.
|
||||
--- Set corrdidor zones. Contacts detected in this/these zone(s) are never reported by the detection.
|
||||
-- Note that corrdidor zones overrule all other zones, for exceptions see corridor floor and corridor ceiling heights.
|
||||
-- @param #INTEL self
|
||||
-- @param Core.Set#SET_ZONE CorridorZoneSet Set of corrdidor zone(s).
|
||||
-- @return #INTEL self
|
||||
function INTEL:SetCorridorZones(CorridorZoneSet)
|
||||
self.corridorzoneset=CorridorZoneSet or SET_ZONE:New()
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add a corrdidor zone. Contacts detected in this zone are corrdidored and not reported by the detection.
|
||||
-- Note that corrdidor zones overrule all other zones, for exceptions see corridor floor and corridor ceiling heights.
|
||||
-- @param #INTEL self
|
||||
-- @param Core.Zone#ZONE CorridorZone Add a zone to the corrdidor zone set.
|
||||
-- @return #INTEL self
|
||||
function INTEL:AddCorridorZone(CorridorZone)
|
||||
self.corridorzoneset:AddZone(CorridorZone)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove a corrdidor zone from the corrdidor zone set.
|
||||
-- Note that corrdidor zones overrule all other zones, for exceptions see corridor floor and corridor ceiling heights.
|
||||
-- @param #INTEL self
|
||||
-- @param Core.Zone#ZONE CorridorZone Remove a zone from the corrdidor zone set.
|
||||
-- @return #INTEL self
|
||||
function INTEL:RemoveCorridorZone(CorridorZone)
|
||||
self.corridorzoneset:Remove(CorridorZone:GetName(), true)
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Air] Add corrdidor zone floor and height. This is generally applicable to all(!) corridor zones. Considered as ASL (above sea level or barometric) values.
|
||||
-- Overrides corridor exception for objects flying outside this limitations.
|
||||
-- To set an individual ceiling/floor on any Core.Zone#ZONE you wish to use, set these properties on the Core.Zone#ZONE object:
|
||||
-- `mycorridorzone:SetProperty("CorridorFloor",500)` -- meters, case sensitivity matters!
|
||||
-- `mycorridorzone:SetProperty("CorridorCeiling",10000)` -- meters, case sensitivity matters!
|
||||
-- @param #INTEL self
|
||||
-- @param #number Floor Floor altitude in meters.
|
||||
-- @param #number Ceiling Ceiling altitude in meters.
|
||||
-- @return #INTEL self
|
||||
function INTEL:SetCorridorLimits(Floor,Ceiling)
|
||||
self.corridorceiling = Ceiling or 10000
|
||||
self.corridorfloor = Floor or 1
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Air] Add corrdidor zone floor and height. This is generally applicable to all(!) corridor zones. Considered as ASL (above sea level or barometric) values.
|
||||
-- Overrides corridor exception for objects flying outside this limitations.
|
||||
-- To set an individual ceiling/floor on any Core.Zone#ZONE you wish to use, set these properties on the Core.Zone#ZONE object:
|
||||
-- `mycorridorzone:SetProperty("CorridorFloor",UTILS.FeetToMeters(5000))` -- feet, case sensitivity matters!
|
||||
-- `mycorridorzone:SetProperty("CorridorCeiling",UTILS.FeetToMeters(20000))` -- feet, case sensitivity matters!
|
||||
-- @param #INTEL self
|
||||
-- @param #number Floor Floor altitude in feet.
|
||||
-- @param #number Ceiling Ceiling altitude in feet.
|
||||
-- @return #INTEL self
|
||||
function INTEL:SetCorridorLimitsFeet(Floor,Ceiling)
|
||||
local Ceiling = Ceiling or 25000
|
||||
local Floor = Floor or 15000
|
||||
self.corridorceiling = UTILS.FeetToMeters(Ceiling)
|
||||
self.corridorfloor = UTILS.FeetToMeters(Floor)
|
||||
return self
|
||||
end
|
||||
|
||||
--- **OBSOLETE, not functional!** Set forget contacts time interval.
|
||||
-- Previously known contacts that are not detected any more, are "lost" after this time.
|
||||
-- This avoids fast oscillations between a contact being detected and undetected.
|
||||
-- @param #INTEL self
|
||||
@@ -831,6 +924,18 @@ function INTEL:UpdateIntel()
|
||||
self:GetDetectedUnits(recce, DetectedUnits, RecceDetecting, self.DetectVisual, self.DetectOptical, self.DetectRadar, self.DetectIRST, self.DetectRWR, self.DetectDLINK)
|
||||
|
||||
end
|
||||
|
||||
if self.DetectAccoustic then
|
||||
local recce = group:GetFirstUnitAlive()
|
||||
local detectionzone = group:GetProperty("INTEL_DETECT_ACCZONE")
|
||||
if not detectionzone then
|
||||
detectionzone = ZONE_GROUP:New(group.IdentifiableName.."INTEL_DETECT_ACCZONE",group,self.DetectAccousticRadius or 2000)
|
||||
group:SetProperty("INTEL_DETECT_ACCZONE",detectionzone)
|
||||
end
|
||||
if recce and recce:IsGround() then
|
||||
self:GetDetectedUnitsAccoustic(recce,DetectedUnits,RecceDetecting,detectionzone)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -884,6 +989,36 @@ function INTEL:UpdateIntel()
|
||||
table.insert(remove, unitname)
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if unit is in any of the corridor zones.
|
||||
if self.corridorzoneset:Count()>0 then
|
||||
self:T("Corridorzone Check for unit "..unit:GetName())
|
||||
local inzone = false
|
||||
for _,_zone in pairs(self.corridorzoneset.Set) do
|
||||
local zone=_zone --Core.Zone#ZONE
|
||||
if unit:IsInZone(zone) then
|
||||
local corridorfloor = zone:GetProperty("CorridorFloor") or self.corridorfloor
|
||||
local corridorceiling = zone:GetProperty("CorridorCeiling") or self.corridorceiling
|
||||
local debugtext = "Corridorzone Check for unit "..unit:GetName().."\n"
|
||||
debugtext = debugtext .. string.format("IsAir %s | Alt %dft | Floor %dft | Ceil %dft",tostring(unit:IsAir()),tonumber(UTILS.MetersToFeet(unit:GetAltitude())),
|
||||
tonumber(UTILS.MetersToFeet(corridorfloor)),tonumber(UTILS.MetersToFeet(corridorceiling)))
|
||||
MESSAGE:New(debugtext,15,"INTEL"):ToAllIf(self.verbose>1):ToLogIf(self.verbose>1)
|
||||
if unit:IsAir() and (corridorfloor ~= nil or corridorceiling ~= nil) then
|
||||
local alt = unit:GetAltitude()
|
||||
if corridorfloor and alt > corridorfloor then inzone = true end
|
||||
if corridorceiling and (inzone == true or corridorfloor == nil) and alt < corridorceiling then inzone = true else inzone = false end
|
||||
if inzone == true then break end
|
||||
else
|
||||
inzone=true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Unit is inside a corridor zone ==> remove!
|
||||
if inzone then
|
||||
table.insert(remove, unitname)
|
||||
end
|
||||
end
|
||||
|
||||
-- Filter unit categories. Added check that we have a UNIT and not a STATIC object because :GetUnitCategory() is only available for units.
|
||||
if #self.filterCategory>0 and unit:IsInstanceOf("UNIT") then
|
||||
@@ -1106,7 +1241,7 @@ function INTEL:CreateDetectedItems(DetectedGroups, DetectedStatics, RecceDetecti
|
||||
return self
|
||||
end
|
||||
|
||||
--- (Internal) Return the detected target groups of the controllable as a @{Core.Set#SET_GROUP}.
|
||||
--- (Internal) Return the detected target groups of the controllable as a table.
|
||||
-- The optional parameters specify the detection methods that can be applied.
|
||||
-- If no detection method is given, the detection will use all the available methods by default.
|
||||
-- @param #INTEL self
|
||||
@@ -1202,6 +1337,33 @@ function INTEL:GetDetectedUnits(Unit, DetectedUnits, RecceDetecting, DetectVisua
|
||||
end
|
||||
end
|
||||
|
||||
--- (Internal) Return the detected target groups of the controllable as a @{Core.Set#SET_GROUP}.
|
||||
-- @param #INTEL self
|
||||
-- @param Wrapper.Unit#UNIT Recce The unit detecting.
|
||||
-- @param #table DetectedUnits Table of detected units to be filled.
|
||||
-- @param #table RecceDetecting Table of recce per unit to be filled.
|
||||
-- @param Core.Zone#ZONE_GROUP detectionzone The zone where to look.
|
||||
function INTEL:GetDetectedUnitsAccoustic(Recce,DetectedUnits,RecceDetecting,detectionzone)
|
||||
local othercoalition = self.coalition == coalition.side.BLUE and coalition.side.RED or coalition.side.BLUE
|
||||
self:T("Other coalition = "..othercoalition)
|
||||
if detectionzone then
|
||||
-- Get detected units
|
||||
local reccename = Recce:GetName()
|
||||
local DetectAccousticUnitTypes = self.DetectAccousticUnitTypes or {Unit.Category.HELICOPTER}
|
||||
detectionzone:Scan({Object.Category.UNIT},DetectAccousticUnitTypes)
|
||||
local unitset = detectionzone:GetScannedSetUnit(othercoalition) -- Core.Set#SET_UNIT
|
||||
self:T("Accoustic detection found #Units "..unitset:CountAlive())
|
||||
for _,_unit in pairs(unitset.Set or {}) do
|
||||
if _unit and _unit:IsAlive() and _unit:GetCoalition() ~= self.coalition then
|
||||
local name = _unit:GetName() or "none"
|
||||
DetectedUnits[name]=_unit
|
||||
RecceDetecting[name]=reccename
|
||||
self:T("Unit name = "..name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- FSM Events
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -2144,7 +2306,7 @@ function INTEL:GetClusterCoordinate(Cluster, Update)
|
||||
return Cluster.coordinate
|
||||
end
|
||||
|
||||
--- Check if the coorindate of the cluster changed.
|
||||
--- Check if the coordindate of the cluster changed.
|
||||
-- @param #INTEL self
|
||||
-- @param #INTEL.Cluster Cluster The cluster.
|
||||
-- @param #number Threshold in meters. Default 100 m.
|
||||
@@ -2324,7 +2486,7 @@ INTEL_DLINK = {
|
||||
verbose = 0,
|
||||
lid = nil,
|
||||
alias = nil,
|
||||
cachetime = 300,
|
||||
cachetime = 120,
|
||||
interval = 20,
|
||||
contacts = {},
|
||||
clusters = {},
|
||||
@@ -2333,7 +2495,7 @@ INTEL_DLINK = {
|
||||
|
||||
--- Version string
|
||||
-- @field #string version
|
||||
INTEL_DLINK.version = "0.0.1"
|
||||
INTEL_DLINK.version = "0.0.2"
|
||||
|
||||
--- Function to instantiate a new object
|
||||
-- @param #INTEL_DLINK self
|
||||
@@ -2384,15 +2546,15 @@ function INTEL_DLINK:New(Intels, Alias, Interval, Cachetime)
|
||||
self.alias="SPECTRE"
|
||||
end
|
||||
|
||||
-- Cache time
|
||||
self.cachetime = Cachetime or 300
|
||||
|
||||
-- Interval
|
||||
self.interval = Interval or 20
|
||||
|
||||
-- Set some string id for output to DCS.log file.
|
||||
self.lid=string.format("INTEL_DLINK %s | ", self.alias)
|
||||
|
||||
-- Cache time
|
||||
self:SetDLinkCacheTime(Cachetime or 120)
|
||||
|
||||
-- Start State.
|
||||
self:SetStartState("Stopped")
|
||||
|
||||
@@ -2477,6 +2639,16 @@ function INTEL_DLINK:onafterStart(From, Event, To)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Function to set how long INTEL DLINK remembers contacts.
|
||||
-- @param #INTEL_DLINK self
|
||||
-- @param #number seconds Remember this many seconds. Defaults to 180.
|
||||
-- @return #INTEL_DLINK self
|
||||
function INTEL_DLINK:SetDLinkCacheTime(seconds)
|
||||
self.cachetime = math.abs(seconds or 120)
|
||||
self:I(self.lid.."Caching for "..self.cachetime.." seconds.")
|
||||
return self
|
||||
end
|
||||
|
||||
--- Function to collect data from the various #INTEL
|
||||
-- @param #INTEL_DLINK self
|
||||
-- @param #string From The From state
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user