mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2026-07-17 18:22:19 +00:00
Compare commits
1355 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a3d77a5a71 | |||
| 394f3ffd88 | |||
| bff5f79282 | |||
| a3d7b4eba9 | |||
| 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 | |||
| dd0b2ace65 | |||
| dc83af4d02 | |||
| 342e901dd1 | |||
| 0a38700edb | |||
| 659615114a | |||
| bfd26522d6 | |||
| 00d14c7c0a | |||
| 47c9e1ba1f | |||
| 92e680a276 | |||
| 4955fe4d92 | |||
| e439bcdd0f | |||
| 792aa73832 | |||
| d375e0ce29 | |||
| e3670219ed | |||
| a915452e6e | |||
| 6662a1cf97 | |||
| be8405b72b | |||
| 9b24695377 | |||
| 5ca3e3b2b8 | |||
| 8bbef34b74 | |||
| 618a8744a2 | |||
| 5329da32eb | |||
| df86c3f2a1 | |||
| 784fcb7882 | |||
| 23aeef7a20 | |||
| 0ee24b86dc | |||
| 9ac4f136aa | |||
| ca9b2d79cc | |||
| c9a09c2fc9 | |||
| 813286f7f3 | |||
| 6028c91f81 | |||
| 11b6bb2638 | |||
| 3c57928f46 | |||
| e091e659a2 | |||
| 32f0bb33c3 | |||
| dea64751c3 | |||
| 31d0410284 | |||
| 9cdf550432 | |||
| 09525029ab | |||
| 87c436ba34 | |||
| b1002017e5 | |||
| 6e9727e265 | |||
| 383e953ba1 | |||
| 94ee76fe62 | |||
| 9b52b640e6 | |||
| ea23162ca9 | |||
| 68d0220912 | |||
| 683388faee | |||
| 56ec3920c5 | |||
| 88842d1c67 | |||
| f335ffc4ec | |||
| f76aefa976 | |||
| 4976cd86f2 | |||
| 2cd66ae1d4 | |||
| c00eff8b23 | |||
| 7f0376561b | |||
| 3c710613a8 | |||
| 3585208547 | |||
| 29c0017e80 | |||
| 45ebf9a3c7 | |||
| 908e505ce3 | |||
| c808e4a4e2 | |||
| e129eb97a7 | |||
| e2612b97d7 | |||
| 70e9d91bb5 | |||
| 0b8810f8b3 | |||
| 3d39ccbdce | |||
| 41b867a4ca | |||
| 427baa43d7 | |||
| d4e141f3c5 | |||
| 1c0a8d9380 | |||
| f05680f23d | |||
| 2b0b9d44eb | |||
| 2fc7a3b542 | |||
| 820ae2759b | |||
| 12b596a47f | |||
| a06d099917 | |||
| 7552309a28 | |||
| b9ad5b5ba7 | |||
| 8ef781a9ac | |||
| 5e24e8658b | |||
| 43eeaede65 | |||
| 749c5f87de | |||
| a520daeb56 | |||
| 9bb713db10 | |||
| f9ba96f228 | |||
| a49bd23a2a | |||
| 913b2c6d3f | |||
| cea2f18228 | |||
| fd2d8a5119 | |||
| 24b3200777 | |||
| fa4e0447dd | |||
| 31aa604fc4 | |||
| 3518c89791 | |||
| f44db27565 | |||
| acaab0c6a9 | |||
| 9b1abab73a | |||
| cad8f15b61 | |||
| aafbd8c297 | |||
| 1d08bcf2e0 | |||
| 6d38346eb8 | |||
| 9487a5ae91 | |||
| d2bb6a4fc0 | |||
| c3ccecdd4a | |||
| 5ad54648ab | |||
| 96337cc5df | |||
| fa0ce0927b | |||
| 693c2a730f | |||
| 0d438a9452 | |||
| bc9eee22b7 | |||
| f74d25b31c | |||
| 66af360ce6 | |||
| ad9893c3ab | |||
| 1156971d94 | |||
| c79b5c37c4 | |||
| b4e6201b68 | |||
| f26d8d6822 | |||
| 3d0b7b9267 | |||
| a2b650a9e3 | |||
| 905b442e9b | |||
| 7cd95377f9 | |||
| 18fe3112bd | |||
| 84e85dd0b5 | |||
| 2744befbab | |||
| e87dc525b9 | |||
| cc057744ba | |||
| db1779e1db | |||
| ed31b87b2f | |||
| d1e31a3652 | |||
| d25a723fc7 | |||
| 3fb4cae7b8 | |||
| f0fe1b431d | |||
| 8e286edd25 | |||
| 74520b1359 | |||
| 0ffcdf2f66 | |||
| fe0edeb011 | |||
| d06e44d37b | |||
| 35348d9b81 | |||
| d06a618582 | |||
| 4b16e94eaf | |||
| d5253f0420 | |||
| d983676330 | |||
| 03c30f3cce | |||
| 99b93266ad | |||
| 4530b74367 | |||
| e307b57e67 | |||
| 5ef9bb2acd | |||
| 4aacdc1567 | |||
| 57552f4300 | |||
| 09d53f7d8c | |||
| 161bdc0413 | |||
| 44ad362dbc | |||
| ea776aeacc | |||
| e30479329a | |||
| d9948d1a19 | |||
| b962096661 | |||
| 725b55a505 | |||
| 5f7a4f2bbb | |||
| f555399e2f | |||
| 1b6945e0b0 | |||
| 4fd55b1bd6 | |||
| fb276cf812 | |||
| 41dfaab82a | |||
| 20b8deb6de | |||
| 6af836c118 | |||
| f87b8a2c2a | |||
| daffd7412a | |||
| bfb60b318e | |||
| a55959dfbb | |||
| 4d24eb82be | |||
| 4656d3e019 | |||
| 86b76ba216 | |||
| ff561410ad | |||
| e26caa2f74 | |||
| b32d17c2ab | |||
| 29111e1018 | |||
| 3433ebd665 | |||
| b75fff60c8 | |||
| a47fa3f9fc | |||
| 4ac57fce7a | |||
| 6218b94c99 | |||
| 74a6c89801 | |||
| 25a9a0120a | |||
| deb0747e66 | |||
| 00f5fde5a8 | |||
| 03cd354f9e | |||
| 66a1fa8af5 | |||
| c2096c8dfd | |||
| 1b4033cfce | |||
| 750edf1144 | |||
| 48bc41873a | |||
| 2e54f51229 | |||
| e39e414e0d | |||
| 068a1ab99c | |||
| e35d9eb07f | |||
| 6551383070 | |||
| b03978cc3d | |||
| 3a7233b594 | |||
| 82f4c5790a | |||
| 5957124e9e | |||
| 7c91b9847b | |||
| 92a05ca74a | |||
| a3c13c8cea | |||
| d02b5db6dd | |||
| bd074728fe | |||
| 8d87531464 | |||
| 5fbd3d9525 | |||
| 61b7b3ead6 | |||
| 8f2178a79c | |||
| 72bb23ed0d | |||
| 7f7999e3e5 | |||
| 0a08e3fdac | |||
| b522b38d31 | |||
| 646a2aec66 | |||
| 7392cb9bf3 | |||
| 2e77988473 | |||
| b2dc7bc232 | |||
| 1b25220729 | |||
| 4953000565 | |||
| 01b7575bca | |||
| f6e6dcac9a | |||
| 93c307d9dd | |||
| 852c18cef8 | |||
| def622a02c | |||
| acfe1a856e | |||
| 9f1f6af647 | |||
| 30bedb39f1 | |||
| fbcc4ee32b | |||
| 27571cc22f | |||
| 1182a5eb1e | |||
| 5f57c71c62 | |||
| c0d60b1bcf | |||
| 63c68d729b | |||
| 5fe29e2ba1 | |||
| 16ff1ac3e7 | |||
| 0e4f805e1c | |||
| 5f29bdc7a7 | |||
| 04f8f6d512 | |||
| f4768aff07 | |||
| b74d46f762 | |||
| 02decc3901 | |||
| dcdea16624 | |||
| 2635cf6345 | |||
| 578c65196c | |||
| 64fb24ce96 | |||
| 45dd7117c7 | |||
| e86069d39c | |||
| e9db714937 | |||
| fdcf153b0b | |||
| 8523b7e20a | |||
| 29b992bf81 | |||
| 4c52509d6d | |||
| 8d603a0cef | |||
| 9bc067f2e8 | |||
| 5fbe0d9a70 | |||
| 4c1c36ef46 | |||
| 067fbcaeaf | |||
| f0aad83d53 | |||
| 474f767e56 | |||
| be062061d5 | |||
| eeca95c77f | |||
| ac5dfab82f | |||
| 13d5f4ac99 | |||
| f9030be843 | |||
| 3cea1cc78c | |||
| 37c1423c9f | |||
| 538e35d8f0 | |||
| d23f029953 | |||
| aafa37f80d | |||
| 203f0c8abc | |||
| 1c0e6362a1 | |||
| 68165cee75 | |||
| 8ef5f0b3f6 | |||
| d0736b0b56 | |||
| 2fbcd9d2b9 | |||
| 15b1ed028e | |||
| a611a38181 | |||
| 008617a35c | |||
| 81a403f02d | |||
| 6144a61a2e | |||
| df102fba6c | |||
| 5d192abd25 | |||
| 902f6dae11 | |||
| 62337f445a | |||
| b38c1c5827 | |||
| 8309768735 | |||
| 42e6b57296 | |||
| 2835ec812f | |||
| 990161729b | |||
| c08cee2317 | |||
| e406fb0c88 | |||
| e1ea4322a1 | |||
| 0f7759d070 | |||
| 6523c4473f | |||
| 4a0842bea6 | |||
| 3745e6a8d8 | |||
| 3b3666c5f7 | |||
| 9e1a28f7ca | |||
| 82c7e921eb | |||
| 67ccf532ea | |||
| 45912911ee | |||
| c7fe42d919 | |||
| 9916afaa49 | |||
| 55c5a23616 | |||
| 7b6df08268 | |||
| 9300050573 | |||
| 6bee1cc88e | |||
| c94767a715 | |||
| 7aa3169523 | |||
| cf86eacb16 | |||
| 43ab4d5f38 | |||
| aa064a1d0e | |||
| 21240a60de | |||
| 0427c0d3a7 | |||
| 3f632b92e2 | |||
| 014750ea7f | |||
| 548b80969a | |||
| e4bbfce314 | |||
| 278f1db9a9 | |||
| 359429b17e | |||
| f151271cb1 | |||
| 6001f6abda | |||
| 18e5012546 | |||
| fd9b5d8d16 | |||
| da3ee13582 | |||
| b6f184388a | |||
| 903c065b74 | |||
| d8471698ab | |||
| 6204cecbbd | |||
| ddeca49916 | |||
| d8dcf37886 | |||
| 5ae41a208b | |||
| 0462d900e6 | |||
| 4fb2ad88bc | |||
| ea55e90e62 | |||
| 6025c05f33 | |||
| 64f854e646 | |||
| 0f962461e1 | |||
| 62dfb5b5ff | |||
| 171af5a3c3 | |||
| 874548d1c1 | |||
| aadc03c38d | |||
| fb0aeafaa2 | |||
| 683fa13bb2 | |||
| d3bd55a290 | |||
| e4408a964d | |||
| 1fbe78b667 | |||
| 4902e0f597 | |||
| 34e248b1c3 | |||
| 5a05917bc2 | |||
| 98a613261c | |||
| ee880a893e | |||
| 7e8555d6b7 | |||
| 0a6bdd6e04 | |||
| f97f33ab59 | |||
| f59102ee09 | |||
| de304d6bb9 | |||
| 73181e3f45 | |||
| e42e4e1ddf | |||
| 86e899f39b | |||
| a30079c45b | |||
| f644d49e71 | |||
| c0b09b03d9 | |||
| 50ffd9aba6 | |||
| 4d4138c1e6 | |||
| 936fec1f49 | |||
| 9625d87dd5 | |||
| c4d0319bb2 | |||
| 802139205c | |||
| 6022a3905f | |||
| 6f724c62fb | |||
| fb918cb2a4 | |||
| 1a74f112ef | |||
| abb4de46d7 | |||
| 5747c49abf | |||
| b87930738a | |||
| d2c78516f5 | |||
| 93cff96794 | |||
| 75e80a0091 | |||
| 46d2c9c196 | |||
| 6f61d160af | |||
| 13419171a9 | |||
| 147cfea587 | |||
| c27da8e54e | |||
| f3c8b4c1cd | |||
| 09e1883488 | |||
| dce9631399 | |||
| fb2ba1dea0 | |||
| 19073534dd | |||
| 23080e3cc4 | |||
| b50d3fa809 | |||
| 1f191cf9bb | |||
| 6c773786d2 | |||
| 8939963187 | |||
| e3fb693cb7 | |||
| 7cc11f55bb | |||
| f9747d1c4c | |||
| 60a3d3409e | |||
| cd8cbc54c8 | |||
| b72124c0d9 | |||
| 0d6a1644e2 | |||
| d51e761b26 | |||
| 1ebe8b82ec | |||
| 1445ef61a0 | |||
| f82490f0d5 | |||
| dfe2ed2a98 | |||
| bb5c044a25 | |||
| be87103b53 | |||
| 54f7627f9c | |||
| 2fb460c4bb | |||
| 1b3c94cc57 | |||
| 216ea230a8 | |||
| 7e727620a4 | |||
| 13ec9bcdd4 | |||
| 90096163ee | |||
| d2564d4a54 | |||
| cd178d6a8c | |||
| 2aa0b5ddfb | |||
| cf6e026392 | |||
| 37f819458a | |||
| 16fb0c3081 | |||
| 168f4301d2 | |||
| 9fb03d9d8a | |||
| d89ed535b7 | |||
| 08b4b890ce | |||
| d5a406c60f | |||
| cb16210577 | |||
| 8c0e0de45f | |||
| 3524cba4ef | |||
| df6a37a972 | |||
| 13600ff6fd | |||
| a957bb4968 | |||
| 5f8d1cf5b0 | |||
| a115267e08 | |||
| 37998fbf1d | |||
| 846aa823d4 | |||
| 0d877853eb | |||
| 68298fc585 | |||
| b97a7cf387 | |||
| e9d75f6d94 | |||
| 1dce2eb747 | |||
| 7c110d90cd | |||
| 2ecc02ce4d | |||
| 8fa5277417 | |||
| 7cce5745af | |||
| 231f1f236d | |||
| ece0a46f97 | |||
| 2c9b0b8376 | |||
| 76577c03e7 | |||
| 48d30250a0 | |||
| a798f2d61c | |||
| d0f8b1436b | |||
| ddc5e0f86e | |||
| ae08c87822 | |||
| ae880e9d1c | |||
| 101d2e1de5 | |||
| f6b7708567 | |||
| 051286acd1 | |||
| d6d9c9d8cf | |||
| 7f572a1a9b | |||
| 68007306b3 | |||
| 0c3f97370c | |||
| d62025dfe0 | |||
| 6bc766e95f | |||
| 07009630c6 | |||
| c2aeb1ebcd | |||
| 865042a843 | |||
| 6243137d4c | |||
| f00d0dc871 | |||
| 8695953d5a | |||
| 6afb68390b | |||
| d6adcdf8bd | |||
| 8151700ad7 | |||
| 2c192fba30 | |||
| 97f11c93bb | |||
| a3102a7381 | |||
| d41b5e8ede | |||
| a45856c925 | |||
| f531fdaa70 | |||
| f4b2f07656 | |||
| 81f8e84ca4 | |||
| f7e64d98bc | |||
| d74de11b8b | |||
| 4380c1be2e | |||
| 30f2097d7a | |||
| db2d4e97a6 | |||
| f903844059 | |||
| b4653be54e | |||
| 68b2b452cc | |||
| e2b6efde7a | |||
| 668d120d60 | |||
| 3eea17bc76 | |||
| 2894bcc833 | |||
| 7543f31c85 | |||
| ff52197707 | |||
| 56d84e7b25 | |||
| b025abaa88 | |||
| ed2d3d856b | |||
| dac637f360 | |||
| 1e5c3a3c21 | |||
| 861588205a | |||
| 37d5b6a0fc | |||
| 4d3fb2990e | |||
| c58a954d18 | |||
| cce878e759 | |||
| 0d481afa16 | |||
| 2dbad7cb1b | |||
| 016875d724 | |||
| 7a0fa629b5 | |||
| 5df0d60135 | |||
| aee7bd8bb6 | |||
| e77d61c4cb | |||
| b1a40407b3 | |||
| 504142c585 | |||
| 0496571c92 | |||
| 62a0adce5a | |||
| d1a1be1a9e | |||
| 80011f4e4e | |||
| 80df849d18 | |||
| c2933b6bed | |||
| fc4ba9f21d | |||
| e74c79b5d6 | |||
| f738ddfca8 | |||
| 5bbd122fa4 | |||
| af45b0d709 | |||
| 928c20c867 | |||
| 4bd36a4786 | |||
| e746617139 | |||
| 3105f7407d | |||
| 2f568bca17 | |||
| ad7dba6fac | |||
| 9efe0fe243 | |||
| 2836d7a40f | |||
| f1bf62e3c1 | |||
| a560d65ce7 | |||
| 9de3cb73f7 | |||
| 757cc86a68 | |||
| aeb1664134 | |||
| 2af1483724 | |||
| b7702ab933 | |||
| cc14f82e85 | |||
| f23dd0a5dd | |||
| 5aea17e20e | |||
| 8336b744bb | |||
| ad9eaea010 | |||
| 9c47e35d54 | |||
| 284a770daa | |||
| 16030d4fa1 | |||
| 37d2dd17d7 | |||
| ccd190a8b1 | |||
| 36bf2436b6 | |||
| 19f6a8d8f6 | |||
| b8e09afcce | |||
| 24264bd885 | |||
| ed690ee6e9 | |||
| 872bb3d775 | |||
| c0dcd12ada | |||
| d36cd8e284 | |||
| 5d98672de5 | |||
| eab643268f | |||
| e2fa1f992b | |||
| 9356794112 | |||
| bb865aef38 | |||
| 35c24810f9 | |||
| 875b5fb34d | |||
| 19187bcb14 | |||
| ebe6e9fb9f | |||
| a54944b021 | |||
| 5f7f75b1c9 | |||
| bc9938d08a | |||
| 59963e152d | |||
| b77f179acc |
@@ -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.
|
||||
@@ -3895,10 +3897,14 @@ do -- AI_A2G_DISPATCHER
|
||||
|
||||
if Squadron then
|
||||
local FirstUnit = AttackSetUnit:GetRandomSurely()
|
||||
if FirstUnit then
|
||||
local Coordinate = FirstUnit:GetCoordinate() -- Core.Point#COORDINATE
|
||||
if self.SetSendPlayerMessages then
|
||||
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", on route to ground target at " .. Coordinate:ToStringA2G( DefenderGroup ), DefenderGroup )
|
||||
end
|
||||
else
|
||||
return
|
||||
end
|
||||
end
|
||||
self:GetParent(self).onafterEngageRoute( self, DefenderGroup, From, Event, To, AttackSetUnit )
|
||||
end
|
||||
@@ -4784,4 +4790,5 @@ end
|
||||
Squadron.ResourceCount = Squadron.ResourceCount - Amount
|
||||
end
|
||||
self:T({Squadron = Squadron.Name,SquadronResourceCount = Squadron.ResourceCount})
|
||||
end
|
||||
end
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -9,11 +9,13 @@
|
||||
-- @module AI.AI_Air
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
---
|
||||
-- @type AI_AIR
|
||||
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
||||
|
||||
--- The AI_AIR class implements the core functions to operate an AI @{Wrapper.Group}.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- # 1) AI_AIR constructor
|
||||
--
|
||||
@@ -656,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
|
||||
)
|
||||
@@ -665,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
|
||||
)
|
||||
@@ -760,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.
|
||||
--
|
||||
-- 
|
||||
@@ -174,8 +176,7 @@ function AI_BAI_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
-- @param #string To The To State string.
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Engage.
|
||||
@@ -522,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
|
||||
)
|
||||
@@ -578,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.
|
||||
--
|
||||
-- 
|
||||
@@ -162,7 +165,6 @@ function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Engage.
|
||||
@@ -466,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
|
||||
)
|
||||
@@ -508,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
|
||||
)
|
||||
@@ -846,7 +848,6 @@ function AI_PATROL_ZONE:onafterStatus()
|
||||
OldAIControllable:SetTask( TimedOrbitTask, 10 )
|
||||
|
||||
RTB = true
|
||||
else
|
||||
end
|
||||
|
||||
-- TODO: Check GROUP damage function.
|
||||
@@ -856,6 +857,16 @@ function AI_PATROL_ZONE:onafterStatus()
|
||||
RTB = true
|
||||
end
|
||||
|
||||
if self:IsInstanceOf("AI_CAS") or self:IsInstanceOf("AI_BAI") then
|
||||
local atotal,shells,rockets,bombs,missiles = self.Controllable:GetAmmunition()
|
||||
local arelevant = rockets+bombs
|
||||
if arelevant == 0 or missiles == 0 then
|
||||
RTB = true
|
||||
self:T({total=atotal,shells=shells,rockets=rockets,bombs=bombs,missiles=missiles})
|
||||
self:T( self.Controllable:GetName() .. " is out of ammo, RTB!" )
|
||||
end
|
||||
end
|
||||
|
||||
if RTB == true then
|
||||
self:RTB()
|
||||
else
|
||||
@@ -881,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 } )
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
-- @module Core.Base
|
||||
-- @image Core_Base.JPG
|
||||
|
||||
local _TraceOnOff = true
|
||||
local _TraceOnOff = false -- default to no tracing
|
||||
local _TraceLevel = 1
|
||||
local _TraceAll = false
|
||||
local _TraceClass = {}
|
||||
@@ -34,11 +34,12 @@ local _TraceClassMethod = {}
|
||||
|
||||
local _ClassID = 0
|
||||
|
||||
---
|
||||
--- Base class of everything
|
||||
-- @type BASE
|
||||
-- @field ClassName The name of the class.
|
||||
-- @field ClassID The ID number of the class.
|
||||
-- @field ClassNameAndID The name of the class concatenated with the ID number of the class.
|
||||
-- @field #string ClassName The name of the class.
|
||||
-- @field #number ClassID The ID number of the class.
|
||||
-- @field #string ClassNameAndID The name of the class concatenated with the ID number of the class.
|
||||
-- @field Core.Scheduler#SCHEDULER Scheduler The scheduler object.
|
||||
|
||||
--- BASE class
|
||||
--
|
||||
@@ -200,6 +201,7 @@ BASE = {
|
||||
States = {},
|
||||
Debug = debug,
|
||||
Scheduler = nil,
|
||||
Properties = {},
|
||||
}
|
||||
|
||||
-- @field #BASE.__
|
||||
@@ -210,14 +212,6 @@ BASE._ = {
|
||||
Schedules = {}, --- Contains the Schedulers Active
|
||||
}
|
||||
|
||||
--- The Formation Class
|
||||
-- @type FORMATION
|
||||
-- @field Cone A cone formation.
|
||||
FORMATION = {
|
||||
Cone = "Cone",
|
||||
Vee = "Vee",
|
||||
}
|
||||
|
||||
--- BASE constructor.
|
||||
--
|
||||
-- This is an example how to use the BASE:New() constructor in a new class definition when inheriting from BASE.
|
||||
@@ -741,7 +735,31 @@ do -- Event Handling
|
||||
-- @function [parent=#BASE] OnEventPlayerEnterAircraft
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
|
||||
--- Occurs when a player creates a dynamic cargo object from the F8 ground crew menu.
|
||||
-- *** NOTE *** this is a workarounf for DCS not creating these events as of Aug 2024.
|
||||
-- @function [parent=#BASE] OnEventNewDynamicCargo
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
--- Occurs when a player loads a dynamic cargo object with the F8 ground crew menu into a helo.
|
||||
-- *** NOTE *** this is a workarounf for DCS not creating these events as of Aug 2024.
|
||||
-- @function [parent=#BASE] OnEventDynamicCargoLoaded
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
--- Occurs when a player unloads a dynamic cargo object with the F8 ground crew menu from a helo.
|
||||
-- *** NOTE *** this is a workarounf for DCS not creating these events as of Aug 2024.
|
||||
-- @function [parent=#BASE] OnEventDynamicCargoUnloaded
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
--- Occurs when a dynamic cargo crate is removed.
|
||||
-- *** NOTE *** this is a workarounf for DCS not creating these events as of Aug 2024.
|
||||
-- @function [parent=#BASE] OnEventDynamicCargoRemoved
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
end
|
||||
|
||||
--- Creation of a Birth Event.
|
||||
@@ -862,6 +880,62 @@ end
|
||||
|
||||
world.onEvent(Event)
|
||||
end
|
||||
|
||||
--- Creation of a S_EVENT_NEW_DYNAMIC_CARGO event.
|
||||
-- @param #BASE self
|
||||
-- @param Wrapper.DynamicCargo#DYNAMICCARGO DynamicCargo the dynamic cargo object
|
||||
function BASE:CreateEventNewDynamicCargo(DynamicCargo)
|
||||
self:F({DynamicCargo})
|
||||
local Event = {
|
||||
id = EVENTS.NewDynamicCargo,
|
||||
time = timer.getTime(),
|
||||
dynamiccargo = DynamicCargo,
|
||||
initiator = DynamicCargo:GetDCSObject(),
|
||||
}
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
--- Creation of a S_EVENT_DYNAMIC_CARGO_LOADED event.
|
||||
-- @param #BASE self
|
||||
-- @param Wrapper.DynamicCargo#DYNAMICCARGO DynamicCargo the dynamic cargo object
|
||||
function BASE:CreateEventDynamicCargoLoaded(DynamicCargo)
|
||||
self:F({DynamicCargo})
|
||||
local Event = {
|
||||
id = EVENTS.DynamicCargoLoaded,
|
||||
time = timer.getTime(),
|
||||
dynamiccargo = DynamicCargo,
|
||||
initiator = DynamicCargo:GetDCSObject(),
|
||||
}
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
--- Creation of a S_EVENT_DYNAMIC_CARGO_UNLOADED event.
|
||||
-- @param #BASE self
|
||||
-- @param Wrapper.DynamicCargo#DYNAMICCARGO DynamicCargo the dynamic cargo object
|
||||
function BASE:CreateEventDynamicCargoUnloaded(DynamicCargo)
|
||||
self:F({DynamicCargo})
|
||||
local Event = {
|
||||
id = EVENTS.DynamicCargoUnloaded,
|
||||
time = timer.getTime(),
|
||||
dynamiccargo = DynamicCargo,
|
||||
initiator = DynamicCargo:GetDCSObject(),
|
||||
}
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
--- Creation of a S_EVENT_DYNAMIC_CARGO_REMOVED event.
|
||||
-- @param #BASE self
|
||||
-- @param Wrapper.DynamicCargo#DYNAMICCARGO DynamicCargo the dynamic cargo object
|
||||
function BASE:CreateEventDynamicCargoRemoved(DynamicCargo)
|
||||
self:F({DynamicCargo})
|
||||
local Event = {
|
||||
id = EVENTS.DynamicCargoRemoved,
|
||||
time = timer.getTime(),
|
||||
dynamiccargo = DynamicCargo,
|
||||
initiator = DynamicCargo:GetDCSObject(),
|
||||
}
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
--- The main event handling function... This function captures all events generated for the class.
|
||||
-- @param #BASE self
|
||||
@@ -900,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, ... )
|
||||
|
||||
@@ -1036,6 +1110,31 @@ function BASE:ClearState( Object, StateName )
|
||||
end
|
||||
end
|
||||
|
||||
--- Set one property of an object.
|
||||
-- @param #BASE self
|
||||
-- @param Key The key that is used as a reference of the value. Note that the key can be a #string, but it can also be any other type!
|
||||
-- @param Value The value that is stored. Note that the value can be a #string, but it can also be any other type!
|
||||
function BASE:SetProperty(Key,Value)
|
||||
self.Properties = self.Properties or {}
|
||||
self.Properties[Key] = Value
|
||||
end
|
||||
|
||||
--- Get one property of an object by the key.
|
||||
-- @param #BASE self
|
||||
-- @param Key The key that is used as a reference of the value. Note that the key can be a #string, but it can also be any other type!
|
||||
-- @return Value The value that is stored. Note that the value can be a #string, but it can also be any other type! Nil if not found.
|
||||
function BASE:GetProperty(Key)
|
||||
self.Properties = self.Properties or {}
|
||||
return self.Properties[Key]
|
||||
end
|
||||
|
||||
--- Get all of the properties of an object in a table.
|
||||
-- @param #BASE self
|
||||
-- @return #table of values, indexed by keys.
|
||||
function BASE:GetProperties()
|
||||
return self.Properties
|
||||
end
|
||||
|
||||
-- Trace section
|
||||
|
||||
-- Log a trace (only shown when trace is on)
|
||||
@@ -1200,7 +1299,7 @@ end
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:F( Arguments )
|
||||
|
||||
if BASE.Debug and _TraceOnOff then
|
||||
if BASE.Debug and _TraceOnOff == true then
|
||||
local DebugInfoCurrent = BASE.Debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = BASE.Debug.getinfo( 3, "l" )
|
||||
|
||||
@@ -1215,7 +1314,7 @@ end
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:F2( Arguments )
|
||||
|
||||
if BASE.Debug and _TraceOnOff then
|
||||
if BASE.Debug and _TraceOnOff == true and _TraceLevel >= 2 then
|
||||
local DebugInfoCurrent = BASE.Debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = BASE.Debug.getinfo( 3, "l" )
|
||||
|
||||
@@ -1230,7 +1329,7 @@ end
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:F3( Arguments )
|
||||
|
||||
if BASE.Debug and _TraceOnOff then
|
||||
if BASE.Debug and _TraceOnOff == true and _TraceLevel >= 3 then
|
||||
local DebugInfoCurrent = BASE.Debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = BASE.Debug.getinfo( 3, "l" )
|
||||
|
||||
@@ -1274,7 +1373,7 @@ end
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:T( Arguments )
|
||||
|
||||
if BASE.Debug and _TraceOnOff then
|
||||
if BASE.Debug and _TraceOnOff == true then
|
||||
local DebugInfoCurrent = BASE.Debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = BASE.Debug.getinfo( 3, "l" )
|
||||
|
||||
@@ -1289,7 +1388,7 @@ end
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:T2( Arguments )
|
||||
|
||||
if BASE.Debug and _TraceOnOff then
|
||||
if BASE.Debug and _TraceOnOff == true and _TraceLevel >= 2 then
|
||||
local DebugInfoCurrent = BASE.Debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = BASE.Debug.getinfo( 3, "l" )
|
||||
|
||||
@@ -1304,7 +1403,7 @@ end
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:T3( Arguments )
|
||||
|
||||
if BASE.Debug and _TraceOnOff then
|
||||
if BASE.Debug and _TraceOnOff == true and _TraceLevel >= 3 then
|
||||
local DebugInfoCurrent = BASE.Debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = BASE.Debug.getinfo( 3, "l" )
|
||||
|
||||
@@ -1336,7 +1435,7 @@ function BASE:E( Arguments )
|
||||
|
||||
env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "E", self.ClassName, self.ClassID, Function, UTILS.BasicSerialize( Arguments ) ) )
|
||||
else
|
||||
env.info( string.format( "%1s:%30s%05d(%s)", "E", self.ClassName, self.ClassID, BASE:_Serialize(Arguments) ) )
|
||||
env.info( string.format( "%1s:%30s%05d(%s)", "E", self.ClassName, self.ClassID, UTILS.BasicSerialize(Arguments) ) )
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1363,8 +1462,7 @@ function BASE:I( Arguments )
|
||||
|
||||
env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "I", self.ClassName, self.ClassID, Function, UTILS.BasicSerialize( Arguments ) ) )
|
||||
else
|
||||
env.info( string.format( "%1s:%30s%05d(%s)", "I", self.ClassName, self.ClassID, BASE:_Serialize(Arguments)) )
|
||||
env.info( string.format( "%1s:%30s%05d(%s)", "I", self.ClassName, self.ClassID, UTILS.BasicSerialize(Arguments)) )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
--
|
||||
-- @module Core.ClientMenu
|
||||
-- @image Core_Menu.JPG
|
||||
-- last change: May 2024
|
||||
-- last change: Sept 2025
|
||||
|
||||
-- TODO
|
||||
----------------------------------------------------------------------------------------------------------------
|
||||
@@ -57,9 +57,9 @@
|
||||
---
|
||||
-- @field #CLIENTMENU
|
||||
CLIENTMENU = {
|
||||
ClassName = "CLIENTMENUE",
|
||||
ClassName = "CLIENTMENU",
|
||||
lid = "",
|
||||
version = "0.1.2",
|
||||
version = "0.1.3",
|
||||
name = nil,
|
||||
path = nil,
|
||||
group = nil,
|
||||
@@ -417,7 +417,7 @@ end
|
||||
CLIENTMENUMANAGER = {
|
||||
ClassName = "CLIENTMENUMANAGER",
|
||||
lid = "",
|
||||
version = "0.1.6",
|
||||
version = "0.1.7",
|
||||
name = nil,
|
||||
clientset = nil,
|
||||
menutree = {},
|
||||
@@ -455,7 +455,7 @@ end
|
||||
-- @param #CLIENTMENUMANAGER self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
-- @return #CLIENTMENUMANAGER self
|
||||
function CLIENTMENUMANAGER:_EventHandler(EventData)
|
||||
function CLIENTMENUMANAGER:_EventHandler(EventData,Retry)
|
||||
self:T(self.lid.."_EventHandler: "..EventData.id)
|
||||
--self:I(self.lid.."_EventHandler: "..tostring(EventData.IniPlayerName))
|
||||
if EventData.id == EVENTS.PlayerLeaveUnit or EventData.id == EVENTS.Ejection or EventData.id == EVENTS.Crash or EventData.id == EVENTS.PilotDead then
|
||||
@@ -468,6 +468,10 @@ function CLIENTMENUMANAGER:_EventHandler(EventData)
|
||||
if EventData.IniPlayerName and EventData.IniGroup then
|
||||
if (not self.clientset:IsIncludeObject(_DATABASE:FindClient( EventData.IniUnitName ))) then
|
||||
self:T(self.lid.."Client not in SET: "..EventData.IniPlayerName)
|
||||
if not Retry then
|
||||
-- try again in 2 secs
|
||||
self:ScheduleOnce(2,CLIENTMENUMANAGER._EventHandler,self,EventData,true)
|
||||
end
|
||||
return self
|
||||
end
|
||||
--self:I(self.lid.."Join event for player: "..EventData.IniPlayerName)
|
||||
@@ -524,7 +528,7 @@ function CLIENTMENUMANAGER:InitAutoPropagation()
|
||||
self:HandleEvent(EVENTS.PilotDead, self._EventHandler)
|
||||
self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler)
|
||||
self:HandleEvent(EVENTS.PlayerEnterUnit, self._EventHandler)
|
||||
self:SetEventPriority(5)
|
||||
self:SetEventPriority(6)
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -802,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
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
-- * Manage database of hits to units and statics.
|
||||
-- * Manage database of destroys of units and statics.
|
||||
-- * Manage database of @{Core.Zone#ZONE_BASE} objects.
|
||||
-- * Manage database of @{Wrapper.DynamicCargo#DYNAMICCARGO} objects alive in the mission.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -39,6 +40,7 @@
|
||||
-- @field #table STORAGES DCS warehouse storages.
|
||||
-- @field #table STNS Used Link16 octal numbers for F16/15/18/AWACS planes.
|
||||
-- @field #table SADL Used Link16 octal numbers for A10/C-II planes.
|
||||
-- @field #table DYNAMICCARGO Dynamic Cargo objects.
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- Contains collections of wrapper objects defined within MOOSE that reflect objects within the simulator.
|
||||
@@ -54,6 +56,7 @@
|
||||
-- * PLAYERS
|
||||
-- * CARGOS
|
||||
-- * STORAGES (DCS warehouses)
|
||||
-- * DYNAMICCARGO
|
||||
--
|
||||
-- On top, for internal MOOSE administration purposes, the DATABASE administers the Unit and Group TEMPLATES as defined within the Mission Editor.
|
||||
--
|
||||
@@ -97,6 +100,7 @@ DATABASE = {
|
||||
STORAGES = {},
|
||||
STNS={},
|
||||
SADL={},
|
||||
DYNAMICCARGO={},
|
||||
}
|
||||
|
||||
local _DATABASECoalition =
|
||||
@@ -143,6 +147,8 @@ function DATABASE:New()
|
||||
self:HandleEvent( EVENTS.DeleteZone )
|
||||
--self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit ) -- This is not working anymore!, handling this through the birth event.
|
||||
self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnPlayerLeaveUnit )
|
||||
-- DCS 2.9.7 Moose own dynamic cargo events
|
||||
self:HandleEvent( EVENTS.DynamicCargoRemoved, self._EventOnDynamicCargoRemoved)
|
||||
|
||||
self:_RegisterTemplates()
|
||||
self:_RegisterGroupsAndUnits()
|
||||
@@ -173,16 +179,20 @@ end
|
||||
-- @param #boolean force
|
||||
-- @return Wrapper.Unit#UNIT The added unit.
|
||||
function DATABASE:AddUnit( DCSUnitName, force )
|
||||
|
||||
if not self.UNITS[DCSUnitName] or force == true then
|
||||
|
||||
local DCSunitName = DCSUnitName
|
||||
|
||||
if type(DCSunitName) == "number" then DCSunitName = string.format("%d",DCSUnitName) end
|
||||
|
||||
if not self.UNITS[DCSunitName] or force == true then
|
||||
-- Debug info.
|
||||
self:T( { "Add UNIT:", DCSUnitName } )
|
||||
self:T( { "Add UNIT:", DCSunitName } )
|
||||
|
||||
-- Register unit
|
||||
self.UNITS[DCSUnitName]=UNIT:Register(DCSUnitName)
|
||||
self.UNITS[DCSunitName]=UNIT:Register(DCSunitName)
|
||||
end
|
||||
|
||||
return self.UNITS[DCSUnitName]
|
||||
return self.UNITS[DCSunitName]
|
||||
end
|
||||
|
||||
|
||||
@@ -201,10 +211,9 @@ function DATABASE:AddStatic( DCSStaticName )
|
||||
|
||||
if not self.STATICS[DCSStaticName] then
|
||||
self.STATICS[DCSStaticName] = STATIC:Register( DCSStaticName )
|
||||
return self.STATICS[DCSStaticName]
|
||||
end
|
||||
|
||||
return nil
|
||||
return self.STATICS[DCSStaticName]
|
||||
end
|
||||
|
||||
|
||||
@@ -214,16 +223,42 @@ function DATABASE:DeleteStatic( DCSStaticName )
|
||||
self.STATICS[DCSStaticName] = nil
|
||||
end
|
||||
|
||||
--- Finds a STATIC based on the StaticName.
|
||||
--- Finds a STATIC based on the Static Name.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string StaticName
|
||||
-- @param #string StaticName Name of the static object.
|
||||
-- @return Wrapper.Static#STATIC The found STATIC.
|
||||
function DATABASE:FindStatic( StaticName )
|
||||
|
||||
local StaticFound = self.STATICS[StaticName]
|
||||
return StaticFound
|
||||
end
|
||||
|
||||
--- Add a DynamicCargo to the database.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string Name Name of the dynamic cargo.
|
||||
-- @return Wrapper.DynamicCargo#DYNAMICCARGO The dynamic cargo object.
|
||||
function DATABASE:AddDynamicCargo( Name )
|
||||
if not self.DYNAMICCARGO[Name] then
|
||||
self.DYNAMICCARGO[Name] = DYNAMICCARGO:Register(Name)
|
||||
end
|
||||
return self.DYNAMICCARGO[Name]
|
||||
end
|
||||
|
||||
--- Finds a DYNAMICCARGO based on the Dynamic Cargo Name.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string DynamicCargoName
|
||||
-- @return Wrapper.DynamicCargo#DYNAMICCARGO The found DYNAMICCARGO.
|
||||
function DATABASE:FindDynamicCargo( DynamicCargoName )
|
||||
local StaticFound = self.DYNAMICCARGO[DynamicCargoName]
|
||||
return StaticFound
|
||||
end
|
||||
|
||||
--- Deletes a DYNAMICCARGO from the DATABASE based on the Dynamic Cargo Name.
|
||||
-- @param #DATABASE self
|
||||
function DATABASE:DeleteDynamicCargo( DynamicCargoName )
|
||||
self.DYNAMICCARGO[DynamicCargoName] = nil
|
||||
return self
|
||||
end
|
||||
|
||||
--- Adds a Airbase based on the Airbase Name in the DATABASE.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string AirbaseName The name of the airbase.
|
||||
@@ -542,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")
|
||||
|
||||
@@ -818,12 +859,16 @@ end
|
||||
-- @param #boolean Force (optional) Force registration of client.
|
||||
-- @return Wrapper.Client#CLIENT The client object.
|
||||
function DATABASE:AddClient( ClientName, Force )
|
||||
|
||||
if not self.CLIENTS[ClientName] or Force == true then
|
||||
self.CLIENTS[ClientName] = CLIENT:Register( ClientName )
|
||||
|
||||
local DCSUnitName = ClientName
|
||||
|
||||
if type(DCSUnitName) == "number" then DCSUnitName = string.format("%d",ClientName) end
|
||||
|
||||
if not self.CLIENTS[DCSUnitName] or Force == true then
|
||||
self.CLIENTS[DCSUnitName] = CLIENT:Register( DCSUnitName )
|
||||
end
|
||||
|
||||
return self.CLIENTS[ClientName]
|
||||
return self.CLIENTS[DCSUnitName]
|
||||
end
|
||||
|
||||
|
||||
@@ -833,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
|
||||
@@ -863,9 +910,11 @@ end
|
||||
--- Adds a player based on the Player Name in the DATABASE.
|
||||
-- @param #DATABASE self
|
||||
function DATABASE:AddPlayer( UnitName, PlayerName )
|
||||
|
||||
|
||||
if type(UnitName) == "number" then UnitName = string.format("%d",UnitName) end
|
||||
|
||||
if PlayerName then
|
||||
self:T( { "Add player for unit:", UnitName, PlayerName } )
|
||||
self:I( { "Add player for unit:", UnitName, PlayerName } )
|
||||
self.PLAYERS[PlayerName] = UnitName
|
||||
self.PLAYERUNITS[PlayerName] = self:FindUnit( UnitName )
|
||||
self.PLAYERSJOINED[PlayerName] = PlayerName
|
||||
@@ -873,6 +922,21 @@ function DATABASE:AddPlayer( UnitName, PlayerName )
|
||||
|
||||
end
|
||||
|
||||
--- Get a PlayerName by UnitName from PLAYERS in DATABASE.
|
||||
-- @param #DATABASE self
|
||||
-- @return #string PlayerName
|
||||
-- @return Wrapper.Unit#UNIT PlayerUnit
|
||||
function DATABASE:_FindPlayerNameByUnitName(UnitName)
|
||||
if UnitName then
|
||||
for playername,unitname in pairs(self.PLAYERS) do
|
||||
if unitname == UnitName and self.PLAYERUNITS[playername] and self.PLAYERUNITS[playername]:IsAlive() then
|
||||
return playername, self.PLAYERUNITS[playername]
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Deletes a player from the DATABASE based on the Player Name.
|
||||
-- @param #DATABASE self
|
||||
function DATABASE:DeletePlayer( UnitName, PlayerName )
|
||||
@@ -1054,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
|
||||
@@ -1063,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
|
||||
@@ -1244,7 +1308,7 @@ function DATABASE:_GetGenericStaticCargoGroupTemplate(Name,Typename,Mass,Coaliti
|
||||
StaticTemplate.CategoryID = "static"
|
||||
StaticTemplate.CoalitionID = Coalition or coalition.side.BLUE
|
||||
StaticTemplate.CountryID = Country or country.id.GERMANY
|
||||
UTILS.PrintTableToLog(StaticTemplate)
|
||||
--UTILS.PrintTableToLog(StaticTemplate)
|
||||
return StaticTemplate
|
||||
end
|
||||
|
||||
@@ -1324,7 +1388,7 @@ function DATABASE:GetCoalitionFromClientTemplate( ClientName )
|
||||
if self.Templates.ClientsByName[ClientName] then
|
||||
return self.Templates.ClientsByName[ClientName].CoalitionID
|
||||
end
|
||||
self:E("ERROR: Template does not exist for client "..tostring(ClientName))
|
||||
self:T("WARNING: Template does not exist for client "..tostring(ClientName))
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -1336,7 +1400,7 @@ function DATABASE:GetCategoryFromClientTemplate( ClientName )
|
||||
if self.Templates.ClientsByName[ClientName] then
|
||||
return self.Templates.ClientsByName[ClientName].CategoryID
|
||||
end
|
||||
self:E("ERROR: Template does not exist for client "..tostring(ClientName))
|
||||
self:T("WARNING: Template does not exist for client "..tostring(ClientName))
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -1348,7 +1412,7 @@ function DATABASE:GetCountryFromClientTemplate( ClientName )
|
||||
if self.Templates.ClientsByName[ClientName] then
|
||||
return self.Templates.ClientsByName[ClientName].CountryID
|
||||
end
|
||||
self:E("ERROR: Template does not exist for client "..tostring(ClientName))
|
||||
self:T("WARNING: Template does not exist for client "..tostring(ClientName))
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -1417,7 +1481,7 @@ function DATABASE:_RegisterDynamicGroup(Groupname)
|
||||
|
||||
-- Add unit.
|
||||
self:I(string.format("Register Unit: %s", tostring(DCSUnitName)))
|
||||
self:AddUnit( DCSUnitName, true )
|
||||
self:AddUnit( tostring(DCSUnitName), true )
|
||||
|
||||
end
|
||||
else
|
||||
@@ -1523,12 +1587,29 @@ end
|
||||
-- @param DCS#Airbase airbase Airbase.
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:_RegisterAirbase(airbase)
|
||||
|
||||
|
||||
local IsSyria = UTILS.GetDCSMap() == "Syria" and true or false
|
||||
local countHSyria = 0
|
||||
|
||||
if airbase then
|
||||
|
||||
-- Get the airbase name.
|
||||
local DCSAirbaseName = airbase:getName()
|
||||
|
||||
|
||||
-- DCS 2.9.8.1107 added 143 helipads all named H with the same object ID ..
|
||||
if IsSyria and DCSAirbaseName == "H" and countHSyria > 0 then
|
||||
--[[
|
||||
local p = airbase:getPosition().p
|
||||
local mgrs = COORDINATE:New(p.x,p.z,p.y):ToStringMGRS()
|
||||
self:I("Airbase on Syria map named H @ "..mgrs)
|
||||
countHSyria = countHSyria + 1
|
||||
if countHSyria > 1 then return self end
|
||||
--]]
|
||||
return self
|
||||
elseif IsSyria and DCSAirbaseName == "H" and countHSyria == 0 then
|
||||
countHSyria = countHSyria + 1
|
||||
end
|
||||
|
||||
-- This gave the incorrect value to be inserted into the airdromeID for DCS 2.5.6. Is fixed now.
|
||||
local airbaseID=airbase:getID()
|
||||
|
||||
@@ -1568,7 +1649,7 @@ end
|
||||
-- @param #DATABASE self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
function DATABASE:_EventOnBirth( Event )
|
||||
self:F( { Event } )
|
||||
self:T( { Event } )
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
|
||||
@@ -1576,7 +1657,17 @@ function DATABASE:_EventOnBirth( Event )
|
||||
|
||||
-- Add static object to DB.
|
||||
self:AddStatic( Event.IniDCSUnitName )
|
||||
|
||||
elseif Event.IniObjectCategory == Object.Category.CARGO and string.match(Event.IniUnitName,".+|%d%d:%d%d|PKG%d+") then
|
||||
|
||||
-- Add dynamic cargo object to DB
|
||||
|
||||
local cargo = self:AddDynamicCargo(Event.IniDCSUnitName)
|
||||
|
||||
self:I(string.format("Adding dynamic cargo %s", tostring(Event.IniDCSUnitName)))
|
||||
|
||||
self:CreateEventNewDynamicCargo( cargo )
|
||||
|
||||
else
|
||||
|
||||
if Event.IniObjectCategory == Object.Category.UNIT then
|
||||
@@ -1614,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
|
||||
@@ -1729,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
|
||||
@@ -1762,6 +1853,15 @@ function DATABASE:_EventOnPlayerEnterUnit( Event )
|
||||
end
|
||||
end
|
||||
|
||||
--- Handles the OnDynamicCargoRemoved event to clean the active dynamic cargo table.
|
||||
-- @param #DATABASE self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
function DATABASE:_EventOnDynamicCargoRemoved( Event )
|
||||
self:T( { Event } )
|
||||
if Event.IniDynamicCargoName then
|
||||
self:DeleteDynamicCargo(Event.IniDynamicCargoName)
|
||||
end
|
||||
end
|
||||
|
||||
--- Handles the OnPlayerLeaveUnit event to clean the active players table.
|
||||
-- @param #DATABASE self
|
||||
|
||||
@@ -194,6 +194,11 @@ world.event.S_EVENT_NEW_ZONE_GOAL = world.event.S_EVENT_MAX + 1004
|
||||
world.event.S_EVENT_DELETE_ZONE_GOAL = world.event.S_EVENT_MAX + 1005
|
||||
world.event.S_EVENT_REMOVE_UNIT = world.event.S_EVENT_MAX + 1006
|
||||
world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT = world.event.S_EVENT_MAX + 1007
|
||||
-- dynamic cargo
|
||||
world.event.S_EVENT_NEW_DYNAMIC_CARGO = world.event.S_EVENT_MAX + 1008
|
||||
world.event.S_EVENT_DYNAMIC_CARGO_LOADED = world.event.S_EVENT_MAX + 1009
|
||||
world.event.S_EVENT_DYNAMIC_CARGO_UNLOADED = world.event.S_EVENT_MAX + 1010
|
||||
world.event.S_EVENT_DYNAMIC_CARGO_REMOVED = world.event.S_EVENT_MAX + 1011
|
||||
|
||||
|
||||
--- The different types of events supported by MOOSE.
|
||||
@@ -275,7 +280,12 @@ EVENTS = {
|
||||
SimulationFreeze = world.event.S_EVENT_SIMULATION_FREEZE or -1,
|
||||
SimulationUnfreeze = world.event.S_EVENT_SIMULATION_UNFREEZE or -1,
|
||||
HumanAircraftRepairStart = world.event.S_EVENT_HUMAN_AIRCRAFT_REPAIR_START or -1,
|
||||
HumanAircraftRepairFinish = world.event.S_EVENT_HUMAN_AIRCRAFT_REPAIR_FINISH or -1,
|
||||
HumanAircraftRepairFinish = world.event.S_EVENT_HUMAN_AIRCRAFT_REPAIR_FINISH or -1,
|
||||
-- dynamic cargo
|
||||
NewDynamicCargo = world.event.S_EVENT_NEW_DYNAMIC_CARGO or -1,
|
||||
DynamicCargoLoaded = world.event.S_EVENT_DYNAMIC_CARGO_LOADED or -1,
|
||||
DynamicCargoUnloaded = world.event.S_EVENT_DYNAMIC_CARGO_UNLOADED or -1,
|
||||
DynamicCargoRemoved = world.event.S_EVENT_DYNAMIC_CARGO_REMOVED or -1,
|
||||
}
|
||||
|
||||
|
||||
@@ -334,6 +344,9 @@ EVENTS = {
|
||||
--
|
||||
-- @field Core.Zone#ZONE Zone The zone object.
|
||||
-- @field #string ZoneName The name of the zone.
|
||||
--
|
||||
-- @field Wrapper.DynamicCargo#DYNAMICCARGO IniDynamicCargo The dynamic cargo object.
|
||||
-- @field #string IniDynamicCargoName The dynamic cargo unit name.
|
||||
|
||||
|
||||
|
||||
@@ -730,6 +743,31 @@ local _EVENTMETA = {
|
||||
Side = "I",
|
||||
Event = "OnEventHumanAircraftRepairFinish",
|
||||
Text = "S_EVENT_HUMAN_AIRCRAFT_REPAIR_FINISH"
|
||||
},
|
||||
-- dynamic cargo
|
||||
[EVENTS.NewDynamicCargo] = {
|
||||
Order = 1,
|
||||
Side = "I",
|
||||
Event = "OnEventNewDynamicCargo",
|
||||
Text = "S_EVENT_NEW_DYNAMIC_CARGO"
|
||||
},
|
||||
[EVENTS.DynamicCargoLoaded] = {
|
||||
Order = 1,
|
||||
Side = "I",
|
||||
Event = "OnEventDynamicCargoLoaded",
|
||||
Text = "S_EVENT_DYNAMIC_CARGO_LOADED"
|
||||
},
|
||||
[EVENTS.DynamicCargoUnloaded] = {
|
||||
Order = 1,
|
||||
Side = "I",
|
||||
Event = "OnEventDynamicCargoUnloaded",
|
||||
Text = "S_EVENT_DYNAMIC_CARGO_UNLOADED"
|
||||
},
|
||||
[EVENTS.DynamicCargoRemoved] = {
|
||||
Order = 1,
|
||||
Side = "I",
|
||||
Event = "OnEventDynamicCargoRemoved",
|
||||
Text = "S_EVENT_DYNAMIC_CARGO_REMOVED"
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1146,7 +1184,63 @@ do -- Event Creation
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
|
||||
--- Creation of a S_EVENT_NEW_DYNAMIC_CARGO event.
|
||||
-- @param #EVENT self
|
||||
-- @param Wrapper.DynamicCargo#DYNAMICCARGO DynamicCargo the dynamic cargo object
|
||||
function EVENT:CreateEventNewDynamicCargo(DynamicCargo)
|
||||
self:F({DynamicCargo})
|
||||
local Event = {
|
||||
id = EVENTS.NewDynamicCargo,
|
||||
time = timer.getTime(),
|
||||
dynamiccargo = DynamicCargo,
|
||||
initiator = DynamicCargo:GetDCSObject(),
|
||||
}
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
--- Creation of a S_EVENT_DYNAMIC_CARGO_LOADED event.
|
||||
-- @param #EVENT self
|
||||
-- @param Wrapper.DynamicCargo#DYNAMICCARGO DynamicCargo the dynamic cargo object
|
||||
function EVENT:CreateEventDynamicCargoLoaded(DynamicCargo)
|
||||
self:F({DynamicCargo})
|
||||
local Event = {
|
||||
id = EVENTS.DynamicCargoLoaded,
|
||||
time = timer.getTime(),
|
||||
dynamiccargo = DynamicCargo,
|
||||
initiator = DynamicCargo:GetDCSObject(),
|
||||
}
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
--- Creation of a S_EVENT_DYNAMIC_CARGO_UNLOADED event.
|
||||
-- @param #EVENT self
|
||||
-- @param Wrapper.DynamicCargo#DYNAMICCARGO DynamicCargo the dynamic cargo object
|
||||
function EVENT:CreateEventDynamicCargoUnloaded(DynamicCargo)
|
||||
self:F({DynamicCargo})
|
||||
local Event = {
|
||||
id = EVENTS.DynamicCargoUnloaded,
|
||||
time = timer.getTime(),
|
||||
dynamiccargo = DynamicCargo,
|
||||
initiator = DynamicCargo:GetDCSObject(),
|
||||
}
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
--- Creation of a S_EVENT_DYNAMIC_CARGO_REMOVED event.
|
||||
-- @param #EVENT self
|
||||
-- @param Wrapper.DynamicCargo#DYNAMICCARGO DynamicCargo the dynamic cargo object
|
||||
function EVENT:CreateEventDynamicCargoRemoved(DynamicCargo)
|
||||
self:F({DynamicCargo})
|
||||
local Event = {
|
||||
id = EVENTS.DynamicCargoRemoved,
|
||||
time = timer.getTime(),
|
||||
dynamiccargo = DynamicCargo,
|
||||
initiator = DynamicCargo:GetDCSObject(),
|
||||
}
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Main event function.
|
||||
@@ -1235,6 +1329,7 @@ function EVENT:onEvent( Event )
|
||||
end
|
||||
|
||||
Event.IniDCSGroupName = Event.IniUnit and Event.IniUnit.GroupName or ""
|
||||
Event.IniGroupName=Event.IniDCSGroupName --At least set the group name because group might not exist any more
|
||||
if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then
|
||||
Event.IniDCSGroupName = Event.IniDCSGroup:getName()
|
||||
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
|
||||
@@ -1261,7 +1356,13 @@ function EVENT:onEvent( Event )
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniUnit = CARGO:FindByName( Event.IniDCSUnitName )
|
||||
if string.match(Event.IniUnitName,".+|%d%d:%d%d|PKG%d+") then
|
||||
Event.IniDynamicCargo = DYNAMICCARGO:FindByName(Event.IniUnitName)
|
||||
Event.IniDynamicCargoName = Event.IniUnitName
|
||||
Event.IniPlayerName = string.match(Event.IniUnitName,"^(.+)|%d%d:%d%d|PKG%d+")
|
||||
else
|
||||
Event.IniUnit = CARGO:FindByName( Event.IniDCSUnitName )
|
||||
end
|
||||
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
||||
@@ -1271,11 +1372,12 @@ function EVENT:onEvent( Event )
|
||||
-- Scenery
|
||||
---
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||
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().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
|
||||
---
|
||||
@@ -1373,16 +1475,18 @@ function EVENT:onEvent( Event )
|
||||
-- SCENERY
|
||||
---
|
||||
Event.TgtDCSUnit = Event.target
|
||||
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
||||
Event.TgtUnitName = Event.TgtDCSUnitName
|
||||
Event.TgtUnit = SCENERY:Register( Event.TgtDCSUnitName, Event.target )
|
||||
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
|
||||
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
|
||||
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 )
|
||||
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
|
||||
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Weapon.
|
||||
if Event.weapon and type(Event.weapon) == "table" then
|
||||
if Event.weapon and type(Event.weapon) == "table" and Event.weapon.isExist and Event.weapon:isExist() then
|
||||
Event.Weapon = Event.weapon
|
||||
Event.WeaponName = Event.weapon:isExist() and Event.weapon:getTypeName() or "Unknown Weapon"
|
||||
Event.WeaponUNIT = CLIENT:Find( Event.Weapon, '', true ) -- Sometimes, the weapon is a player unit!
|
||||
@@ -1404,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
|
||||
@@ -1425,6 +1531,15 @@ function EVENT:onEvent( Event )
|
||||
Event.Cargo = Event.cargo
|
||||
Event.CargoName = Event.cargo.Name
|
||||
end
|
||||
|
||||
-- Dynamic cargo Object
|
||||
if Event.dynamiccargo then
|
||||
Event.IniDynamicCargo = Event.dynamiccargo
|
||||
Event.IniDynamicCargoName = Event.IniDynamicCargo.StaticName
|
||||
if Event.IniDynamicCargo.Owner or Event.IniUnitName then
|
||||
Event.IniPlayerName = Event.IniDynamicCargo.Owner or string.match(Event.IniUnitName or "None|00:00|PKG00","^(.+)|%d%d:%d%d|PKG%d+")
|
||||
end
|
||||
end
|
||||
|
||||
-- Zone object.
|
||||
if Event.zone then
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
|
||||
do -- FSM
|
||||
|
||||
--- FSM class
|
||||
-- @type FSM
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field Core.Scheduler#SCHEDULER CallScheduler Call scheduler.
|
||||
@@ -948,7 +949,8 @@ do -- FSM
|
||||
end
|
||||
|
||||
do -- FSM_CONTROLLABLE
|
||||
|
||||
|
||||
---
|
||||
-- @type FSM_CONTROLLABLE
|
||||
-- @field Wrapper.Controllable#CONTROLLABLE Controllable
|
||||
-- @extends Core.Fsm#FSM
|
||||
@@ -1081,7 +1083,8 @@ do -- FSM_CONTROLLABLE
|
||||
end
|
||||
|
||||
do -- FSM_PROCESS
|
||||
|
||||
|
||||
---
|
||||
-- @type FSM_PROCESS
|
||||
-- @field Tasking.Task#TASK Task
|
||||
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
||||
|
||||
@@ -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 ---
|
||||
-----------------------
|
||||
@@ -108,26 +103,30 @@ function MARKEROPS_BASE:New(Tagname,Keywords,Casesensitive)
|
||||
--- On after "MarkAdded" event. Triggered when a Marker is added to the F10 map.
|
||||
-- @function [parent=#MARKEROPS_BASE] OnAfterMarkAdded
|
||||
-- @param #MARKEROPS_BASE self
|
||||
-- @param #string From The From state
|
||||
-- @param #string Event The Event called
|
||||
-- @param #string To The To state
|
||||
-- @param #string Text The text on the marker
|
||||
-- @param #table Keywords Table of matching keywords found in the Event text
|
||||
-- @param #string From The From state.
|
||||
-- @param #string Event The Event called.
|
||||
-- @param #string To The To state.
|
||||
-- @param #string Text The text on the marker.
|
||||
-- @param #table Keywords Table of matching keywords found in the Event text.
|
||||
-- @param Core.Point#COORDINATE Coord Coordinate of the marker.
|
||||
-- @param #number MarkerID Id of this marker
|
||||
-- @param #number CoalitionNumber Coalition of the marker creator
|
||||
-- @param #number MarkerID Id of this marker.
|
||||
-- @param #number CoalitionNumber Coalition of the marker creator.
|
||||
-- @param #string PlayerName Name of the player creating/changing the mark. nil if it cannot be obtained.
|
||||
-- @param Core.Event#EVENTDATA EventData the event data table.
|
||||
|
||||
--- On after "MarkChanged" event. Triggered when a Marker is changed on the F10 map.
|
||||
-- @function [parent=#MARKEROPS_BASE] OnAfterMarkChanged
|
||||
-- @param #MARKEROPS_BASE self
|
||||
-- @param #string From The From state
|
||||
-- @param #string Event The Event called
|
||||
-- @param #string To The To state
|
||||
-- @param #string Text The text on the marker
|
||||
-- @param #table Keywords Table of matching keywords found in the Event text
|
||||
-- @param #string From The From state.
|
||||
-- @param #string Event The Event called.
|
||||
-- @param #string To The To state.
|
||||
-- @param #string Text The text on the marker.
|
||||
-- @param #table Keywords Table of matching keywords found in the Event text.
|
||||
-- @param Core.Point#COORDINATE Coord Coordinate of the marker.
|
||||
-- @param #number MarkerID Id of this marker
|
||||
-- @param #number CoalitionNumber Coalition of the marker creator
|
||||
-- @param #number MarkerID Id of this marker.
|
||||
-- @param #number CoalitionNumber Coalition of the marker creator.
|
||||
-- @param #string PlayerName Name of the player creating/changing the mark. nil if it cannot be obtained.
|
||||
-- @param Core.Event#EVENTDATA EventData the event data table
|
||||
|
||||
--- On after "MarkDeleted" event. Triggered when a Marker is deleted from the F10 map.
|
||||
-- @function [parent=#MARKEROPS_BASE] OnAfterMarkDeleted
|
||||
@@ -141,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)
|
||||
@@ -150,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
|
||||
@@ -166,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)
|
||||
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
|
||||
@@ -176,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)
|
||||
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
|
||||
@@ -192,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
|
||||
@@ -212,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
|
||||
@@ -277,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.
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
@@ -105,6 +105,7 @@ function MENU_INDEX:PrepareCoalition( CoalitionSide )
|
||||
self.Coalition[CoalitionSide] = self.Coalition[CoalitionSide] or {}
|
||||
self.Coalition[CoalitionSide].Menus = self.Coalition[CoalitionSide].Menus or {}
|
||||
end
|
||||
|
||||
---
|
||||
-- @param Wrapper.Group#GROUP Group
|
||||
function MENU_INDEX:PrepareGroup( Group )
|
||||
@@ -118,9 +119,11 @@ end
|
||||
function MENU_INDEX:HasMissionMenu( Path )
|
||||
return self.MenuMission.Menus[Path]
|
||||
end
|
||||
|
||||
function MENU_INDEX:SetMissionMenu( Path, Menu )
|
||||
self.MenuMission.Menus[Path] = Menu
|
||||
end
|
||||
|
||||
function MENU_INDEX:ClearMissionMenu( Path )
|
||||
self.MenuMission.Menus[Path] = nil
|
||||
end
|
||||
@@ -128,9 +131,11 @@ end
|
||||
function MENU_INDEX:HasCoalitionMenu( Coalition, Path )
|
||||
return self.Coalition[Coalition].Menus[Path]
|
||||
end
|
||||
|
||||
function MENU_INDEX:SetCoalitionMenu( Coalition, Path, Menu )
|
||||
self.Coalition[Coalition].Menus[Path] = Menu
|
||||
end
|
||||
|
||||
function MENU_INDEX:ClearCoalitionMenu( Coalition, Path )
|
||||
self.Coalition[Coalition].Menus[Path] = nil
|
||||
end
|
||||
@@ -138,19 +143,24 @@ end
|
||||
function MENU_INDEX:HasGroupMenu( Group, Path )
|
||||
if Group and Group:IsAlive() then
|
||||
local MenuGroupName = Group:GetName()
|
||||
return self.Group[MenuGroupName].Menus[Path]
|
||||
if self.Group[MenuGroupName] and self.Group[MenuGroupName].Menus and self.Group[MenuGroupName].Menus[Path] then
|
||||
return self.Group[MenuGroupName].Menus[Path]
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function MENU_INDEX:SetGroupMenu( Group, Path, Menu )
|
||||
local MenuGroupName = Group:GetName()
|
||||
Group:F({MenuGroupName=MenuGroupName,Path=Path})
|
||||
--Group:F({MenuGroupName=MenuGroupName,Path=Path})
|
||||
self.Group[MenuGroupName].Menus[Path] = Menu
|
||||
end
|
||||
|
||||
function MENU_INDEX:ClearGroupMenu( Group, Path )
|
||||
local MenuGroupName = Group:GetName()
|
||||
self.Group[MenuGroupName].Menus[Path] = nil
|
||||
end
|
||||
|
||||
function MENU_INDEX:Refresh( Group )
|
||||
for MenuID, Menu in pairs( self.MenuMission.Menus ) do
|
||||
Menu:Refresh()
|
||||
|
||||
@@ -75,35 +75,37 @@ MESSAGE.Type = {
|
||||
|
||||
--- Creates a new MESSAGE object. Note that these MESSAGE objects are not yet displayed on the display panel. You must use the functions @{#MESSAGE.ToClient} or @{#MESSAGE.ToCoalition} or @{#MESSAGE.ToAll} to send these Messages to the respective recipients.
|
||||
-- @param self
|
||||
-- @param #string MessageText is the text of the Message.
|
||||
-- @param #number MessageDuration is a number in seconds of how long the MESSAGE should be shown on the display panel.
|
||||
-- @param #string MessageCategory (optional) is a string expressing the "category" of the Message. The category will be shown as the first text in the message followed by a ": ".
|
||||
-- @param #string Text is the text of the Message.
|
||||
-- @param #number Duration Duration in seconds how long the message text is shown.
|
||||
-- @param #string Category (Optional) String expressing the "category" of the Message. The category will be shown as the first text in the message followed by a ": ".
|
||||
-- @param #boolean ClearScreen (optional) Clear all previous messages if true.
|
||||
-- @return #MESSAGE
|
||||
-- @return #MESSAGE self
|
||||
-- @usage
|
||||
--
|
||||
-- -- Create a series of new Messages.
|
||||
-- -- MessageAll is meant to be sent to all players, for 25 seconds, and is classified as "Score".
|
||||
-- -- MessageRED is meant to be sent to the RED players only, for 10 seconds, and is classified as "End of Mission", with ID "Win".
|
||||
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
|
||||
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
|
||||
-- -- Create a series of new Messages.
|
||||
-- -- MessageAll is meant to be sent to all players, for 25 seconds, and is classified as "Score".
|
||||
-- -- MessageRED is meant to be sent to the RED players only, for 10 seconds, and is classified as "End of Mission", with ID "Win".
|
||||
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
|
||||
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
|
||||
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", 25, "End of Mission" )
|
||||
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", 25, "Penalty" )
|
||||
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", 25, "Score" )
|
||||
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", 25, "Score")
|
||||
--
|
||||
function MESSAGE:New( MessageText, MessageDuration, MessageCategory, ClearScreen )
|
||||
function MESSAGE:New( Text, Duration, Category, ClearScreen )
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( { MessageText, MessageDuration, MessageCategory } )
|
||||
|
||||
self:F( { Text, Duration, Category } )
|
||||
|
||||
self.MessageType = nil
|
||||
|
||||
-- When no MessageCategory is given, we don't show it as a title...
|
||||
if MessageCategory and MessageCategory ~= "" then
|
||||
if MessageCategory:sub( -1 ) ~= "\n" then
|
||||
self.MessageCategory = MessageCategory .. ": "
|
||||
if Category and Category ~= "" then
|
||||
if Category:sub( -1 ) ~= "\n" then
|
||||
self.MessageCategory = Category .. ": "
|
||||
else
|
||||
self.MessageCategory = MessageCategory:sub( 1, -2 ) .. ":\n"
|
||||
self.MessageCategory = Category:sub( 1, -2 ) .. ":\n"
|
||||
end
|
||||
else
|
||||
self.MessageCategory = ""
|
||||
@@ -114,9 +116,9 @@ function MESSAGE:New( MessageText, MessageDuration, MessageCategory, ClearScreen
|
||||
self.ClearScreen = ClearScreen
|
||||
end
|
||||
|
||||
self.MessageDuration = MessageDuration or 5
|
||||
self.MessageDuration = Duration or 5
|
||||
self.MessageTime = timer.getTime()
|
||||
self.MessageText = MessageText:gsub( "^\n", "", 1 ):gsub( "\n$", "", 1 )
|
||||
self.MessageText = Text:gsub( "^\n", "", 1 ):gsub( "\n$", "", 1 )
|
||||
|
||||
self.MessageSent = false
|
||||
self.MessageGroup = false
|
||||
@@ -204,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
|
||||
@@ -229,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
|
||||
@@ -450,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.
|
||||
@@ -462,16 +464,17 @@ _MESSAGESRS = {}
|
||||
-- @param #number Volume (optional) Volume, can be between 0.0 and 1.0 (loudest).
|
||||
-- @param #string Label (optional) Label, defaults to "MESSAGE" or the Message Category set.
|
||||
-- @param Core.Point#COORDINATE Coordinate (optional) Coordinate this messages originates from.
|
||||
-- @param #string Backend (optional) Backend to be used, can be MSRS.Backend.SRSEXE or MSRS.Backend.GRPC
|
||||
-- @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)
|
||||
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
|
||||
@@ -487,6 +490,10 @@ function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,G
|
||||
_MESSAGESRS.MSRS:SetCoordinate(Coordinate)
|
||||
end
|
||||
|
||||
if Backend then
|
||||
_MESSAGESRS.MSRS:SetBackend(Backend)
|
||||
end
|
||||
|
||||
_MESSAGESRS.Culture = Culture or MSRS.culture or "en-GB"
|
||||
_MESSAGESRS.MSRS:SetCulture(Culture)
|
||||
|
||||
@@ -528,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()
|
||||
--
|
||||
@@ -560,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()
|
||||
--
|
||||
@@ -582,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()
|
||||
--
|
||||
@@ -604,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 ) ) )
|
||||
|
||||
|
||||
+2057
-1385
File diff suppressed because it is too large
Load Diff
@@ -494,7 +494,7 @@ do -- SETTINGS
|
||||
return (self.A2ASystem and self.A2ASystem == "MGRS") or (not self.A2ASystem and _SETTINGS:IsA2A_MGRS())
|
||||
end
|
||||
|
||||
-- @param #SETTINGS self
|
||||
--- @param #SETTINGS self
|
||||
-- @param Wrapper.Group#GROUP MenuGroup Group for which to add menus.
|
||||
-- @param #table RootMenu Root menu table
|
||||
-- @return #SETTINGS
|
||||
@@ -948,49 +948,49 @@ do -- SETTINGS
|
||||
return self
|
||||
end
|
||||
|
||||
-- @param #SETTINGS self
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:A2GMenuSystem( MenuGroup, RootMenu, A2GSystem )
|
||||
self.A2GSystem = A2GSystem
|
||||
MESSAGE:New( string.format( "Settings: Default A2G coordinate system set to %s for all players!", A2GSystem ), 5 ):ToAll()
|
||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||
end
|
||||
|
||||
-- @param #SETTINGS self
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:A2AMenuSystem( MenuGroup, RootMenu, A2ASystem )
|
||||
self.A2ASystem = A2ASystem
|
||||
MESSAGE:New( string.format( "Settings: Default A2A coordinate system set to %s for all players!", A2ASystem ), 5 ):ToAll()
|
||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||
end
|
||||
|
||||
-- @param #SETTINGS self
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuLL_DDM_Accuracy( MenuGroup, RootMenu, LL_Accuracy )
|
||||
self.LL_Accuracy = LL_Accuracy
|
||||
MESSAGE:New( string.format( "Settings: Default LL accuracy set to %s for all players!", LL_Accuracy ), 5 ):ToAll()
|
||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||
end
|
||||
|
||||
-- @param #SETTINGS self
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuMGRS_Accuracy( MenuGroup, RootMenu, MGRS_Accuracy )
|
||||
self.MGRS_Accuracy = MGRS_Accuracy
|
||||
MESSAGE:New( string.format( "Settings: Default MGRS accuracy set to %s for all players!", MGRS_Accuracy ), 5 ):ToAll()
|
||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||
end
|
||||
|
||||
-- @param #SETTINGS self
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuMWSystem( MenuGroup, RootMenu, MW )
|
||||
self.Metric = MW
|
||||
MESSAGE:New( string.format( "Settings: Default measurement format set to %s for all players!", MW and "Metric" or "Imperial" ), 5 ):ToAll()
|
||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||
end
|
||||
|
||||
-- @param #SETTINGS self
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuMessageTimingsSystem( MenuGroup, RootMenu, MessageType, MessageTime )
|
||||
self:SetMessageTime( MessageType, MessageTime )
|
||||
MESSAGE:New( string.format( "Settings: Default message time set for %s to %d.", MessageType, MessageTime ), 5 ):ToAll()
|
||||
end
|
||||
|
||||
do
|
||||
-- @param #SETTINGS self
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuGroupA2GSystem( PlayerUnit, PlayerGroup, PlayerName, A2GSystem )
|
||||
--BASE:E( {PlayerUnit:GetName(), A2GSystem } )
|
||||
self.A2GSystem = A2GSystem
|
||||
@@ -1001,7 +1001,7 @@ do -- SETTINGS
|
||||
end
|
||||
end
|
||||
|
||||
-- @param #SETTINGS self
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuGroupA2ASystem( PlayerUnit, PlayerGroup, PlayerName, A2ASystem )
|
||||
self.A2ASystem = A2ASystem
|
||||
MESSAGE:New( string.format( "Settings: A2A format set to %s for player %s.", A2ASystem, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||
@@ -1011,7 +1011,7 @@ do -- SETTINGS
|
||||
end
|
||||
end
|
||||
|
||||
-- @param #SETTINGS self
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuGroupLL_DDM_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, LL_Accuracy )
|
||||
self.LL_Accuracy = LL_Accuracy
|
||||
MESSAGE:New( string.format( "Settings: LL format accuracy set to %d decimal places for player %s.", LL_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||
@@ -1021,7 +1021,7 @@ do -- SETTINGS
|
||||
end
|
||||
end
|
||||
|
||||
-- @param #SETTINGS self
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuGroupMGRS_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, MGRS_Accuracy )
|
||||
self.MGRS_Accuracy = MGRS_Accuracy
|
||||
MESSAGE:New( string.format( "Settings: MGRS format accuracy set to %d for player %s.", MGRS_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||
@@ -1031,7 +1031,7 @@ do -- SETTINGS
|
||||
end
|
||||
end
|
||||
|
||||
-- @param #SETTINGS self
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuGroupMWSystem( PlayerUnit, PlayerGroup, PlayerName, MW )
|
||||
self.Metric = MW
|
||||
MESSAGE:New( string.format( "Settings: Measurement format set to %s for player %s.", MW and "Metric" or "Imperial", PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||
@@ -1041,7 +1041,7 @@ do -- SETTINGS
|
||||
end
|
||||
end
|
||||
|
||||
-- @param #SETTINGS self
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuGroupMessageTimingsSystem( PlayerUnit, PlayerGroup, PlayerName, MessageType, MessageTime )
|
||||
self:SetMessageTime( MessageType, MessageTime )
|
||||
MESSAGE:New( string.format( "Settings: Default message time set for %s to %d.", MessageType, MessageTime ), 5 ):ToGroup( PlayerGroup )
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -535,15 +593,17 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
|
||||
-- Name of the spawned static.
|
||||
Template.name = self.InitStaticName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex)
|
||||
|
||||
-- Add and register the new static.
|
||||
local mystatic=_DATABASE:AddStatic(Template.name)
|
||||
|
||||
-- Debug output.
|
||||
self:T(Template)
|
||||
|
||||
-- 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={}
|
||||
@@ -555,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})
|
||||
@@ -562,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 = {
|
||||
@@ -577,13 +645,41 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
|
||||
self:T("Spawning Static")
|
||||
self:T2({Template=Template})
|
||||
Static=coalition.addStaticObject(CountryID, Template)
|
||||
|
||||
if Static then
|
||||
self:T(string.format("Succesfully spawned static object \"%s\" ID=%d", Static:getName(), Static:getID()))
|
||||
--[[
|
||||
local static=StaticObject.getByName(Static:getName())
|
||||
if static then
|
||||
env.info(string.format("FF got static from StaticObject.getByName"))
|
||||
else
|
||||
env.error(string.format("FF error did NOT get static from StaticObject.getByName"))
|
||||
end ]]
|
||||
else
|
||||
self:E(string.format("ERROR: DCS static object \"%s\" is nil!", tostring(Template.name)))
|
||||
end
|
||||
end
|
||||
|
||||
-- Add and register the new static.
|
||||
local mystatic=_DATABASE:AddStatic(Template.name)
|
||||
|
||||
-- If there is a SpawnFunction hook defined, call it.
|
||||
if self.SpawnFunctionHook then
|
||||
-- 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))
|
||||
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
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ do -- world
|
||||
-- @field #world.event event [https://wiki.hoggitworld.com/view/DCS_enum_world](https://wiki.hoggitworld.com/view/DCS_enum_world)
|
||||
-- @field #world.BirthPlace BirthPlace The birthplace enumerator is used to define where an aircraft or helicopter has spawned in association with birth events.
|
||||
-- @field #world.VolumeType VolumeType The volumeType enumerator defines the types of 3d geometery used within the [world.searchObjects](https://wiki.hoggitworld.com/view/DCS_func_searchObjects) function.
|
||||
-- @field #world.weather weather Weather functions for fog etc.
|
||||
|
||||
--- The world singleton contains functions centered around two different but extremely useful functions.
|
||||
-- * Events and event handlers are all governed within world.
|
||||
@@ -132,6 +133,36 @@ do -- world
|
||||
-- @function [parent=#world] getAirbases
|
||||
-- @param #number coalitionId The coalition side number ID. Default is all airbases are returned.
|
||||
-- @return #table Table of DCS airbase objects.
|
||||
|
||||
|
||||
--- Weather functions.
|
||||
-- @type world.weather
|
||||
|
||||
--- Fog animation data structure.
|
||||
-- @type world.FogAnimation
|
||||
-- @field #number time
|
||||
-- @field #number visibility
|
||||
-- @field #number thickness
|
||||
|
||||
--- Returns the current fog thickness.
|
||||
-- @function [parent=#world.weather] getFogThickness Returns the fog thickness.
|
||||
-- @return #number Fog thickness in meters. If there is no fog, zero is returned.
|
||||
|
||||
--- Sets the fog thickness instantly. Any current fog animation is discarded.
|
||||
-- @function [parent=#world.weather] setFogThickness
|
||||
-- @param #number thickness Fog thickness in meters. Set to zero to disable fog.
|
||||
|
||||
--- Returns the current fog visibility distance.
|
||||
-- @function [parent=#world.weather] getFogVisibilityDistance Returns the current maximum visibility distance in meters. Returns zero if fog is not present.
|
||||
|
||||
--- Instantly sets the maximum visibility distance of fog at sea level when looking at the horizon. Any current fog animation is discarded. Set zero to disable the fog.
|
||||
-- @function [parent=#world.weather] setFogVisibilityDistance
|
||||
-- @param #number visibility Max fog visibility in meters. Set to zero to disable fog.
|
||||
|
||||
--- Sets fog animation keys. Time is set in seconds and relative to the current simulation time, where time=0 is the current moment.
|
||||
-- Time must be increasing. Previous animation is always discarded despite the data being correct.
|
||||
-- @function [parent=#world.weather] setFogAnimation
|
||||
-- @param #world.FogAnimation animation List of fog animations
|
||||
|
||||
end -- world
|
||||
|
||||
@@ -167,7 +198,7 @@ end -- env
|
||||
|
||||
do -- radio
|
||||
|
||||
---@type radio
|
||||
--@type radio
|
||||
-- @field #radio.modulation modulation
|
||||
|
||||
---
|
||||
@@ -407,7 +438,7 @@ do -- coalition
|
||||
-- @param #table groupData Group data table.
|
||||
-- @return DCS#Group The spawned Group object.
|
||||
|
||||
--- Dynamically spawns a static object. See [hoggit](https://wiki.hoggitworld.com/view/DCS_func_addGroup)
|
||||
--- Dynamically spawns a static object. See [hoggit](https://wiki.hoggitworld.com/view/DCS_func_addStaticObject)
|
||||
-- @function [parent=#coalition] addStaticObject
|
||||
-- @param #number countryId Id of the country.
|
||||
-- @param #table groupData Group data table.
|
||||
@@ -420,6 +451,7 @@ end -- coalition
|
||||
|
||||
do -- Types
|
||||
|
||||
--- Descriptors.
|
||||
-- @type Desc
|
||||
-- @field #number speedMax0 Max speed in meters/second at zero altitude.
|
||||
-- @field #number massEmpty Empty mass in kg.
|
||||
@@ -598,9 +630,13 @@ do -- Object
|
||||
--- @function [parent=#Object] destroy
|
||||
-- @param #Object self
|
||||
|
||||
--- @function [parent=#Object] getCategory
|
||||
--- Returns an enumerator of the category for the specific object.
|
||||
-- The enumerator returned is dependent on the category of the object and how the function is called.
|
||||
-- As of DCS 2.9.2 when this function is called on an Object, Unit, Weapon, or Airbase a 2nd value will be returned which details the object sub-category value.
|
||||
-- @function [parent=#Object] getCategory
|
||||
-- @param #Object self
|
||||
-- @return #Object.Category
|
||||
-- @return #Object.Category The object category (1=UNIT, 2=WEAPON, 3=STATIC, 4=BASE, 5=SCENERY, 6=Cargo)
|
||||
-- @return #number The subcategory of the passed object, e.g. Unit.Category if a unit object was passed.
|
||||
|
||||
--- Returns type name of the Object.
|
||||
-- @function [parent=#Object] getTypeName
|
||||
@@ -1013,14 +1049,16 @@ do -- Spot
|
||||
end -- Spot
|
||||
|
||||
do -- Controller
|
||||
|
||||
--- Controller is an object that performs A.I.-tasks. Other words controller is an instance of A.I.. Controller stores current main task, active enroute tasks and behavior options. Controller performs commands. Please, read DCS A-10C GUI Manual EN.pdf chapter "Task Planning for Unit Groups", page 91 to understand A.I. system of DCS:A-10C.
|
||||
--
|
||||
-- This class has 2 types of functions:
|
||||
--
|
||||
-- * Tasks
|
||||
-- * Commands: Commands are instant actions those required zero time to perform. Commands may be used both for control unit/group behavior and control game mechanics.
|
||||
-- * Commands: Commands are instant actions those required zero time to perform. Commands may be used both for control unit/group behavior and control game mechanics.
|
||||
--
|
||||
-- @type Controller
|
||||
-- @field #Controller.Detection Detection Enum contains identifiers of surface types.
|
||||
-- @field #Controller.Detection Detection Enum contains identifiers of surface types.
|
||||
|
||||
--- Enables and disables the controller.
|
||||
-- Note: Now it works only for ground / naval groups!
|
||||
@@ -1079,18 +1117,18 @@ do -- Controller
|
||||
|
||||
-- Detection
|
||||
|
||||
--- Enum contains identifiers of surface types.
|
||||
--- Enum containing detection types.
|
||||
-- @type Controller.Detection
|
||||
-- @field VISUAL
|
||||
-- @field OPTIC
|
||||
-- @field RADAR
|
||||
-- @field IRST
|
||||
-- @field RWR
|
||||
-- @field DLINK
|
||||
-- @field #number VISUAL Visual detection. Numeric value 1.
|
||||
-- @field #number OPTIC Optical detection. Numeric value 2.
|
||||
-- @field #number RADAR Radar detection. Numeric value 4.
|
||||
-- @field #number IRST Infra-red search and track detection. Numeric value 8.
|
||||
-- @field #number RWR Radar Warning Receiver detection. Numeric value 16.
|
||||
-- @field #number DLINK Data link detection. Numeric value 32.
|
||||
|
||||
--- Detected target.
|
||||
-- @type DetectedTarget
|
||||
-- @field Wrapper.Object#Object object The target
|
||||
-- @type Controller.DetectedTarget
|
||||
-- @field DCS#Object object The target
|
||||
-- @field #boolean visible The target is visible
|
||||
-- @field #boolean type The target type is known
|
||||
-- @field #boolean distance Distance to the target is known
|
||||
@@ -1103,9 +1141,9 @@ do -- Controller
|
||||
-- @param #Controller.Detection detection Controller.Detection detection1, Controller.Detection detection2, ... Controller.Detection detectionN
|
||||
-- @return #boolean detected True if the target is detected.
|
||||
-- @return #boolean visible Has effect only if detected is true. True if the target is visible now.
|
||||
-- @return #boolean type Has effect only if detected is true. True if the target type is known.
|
||||
-- @return #boolean distance Has effect only if detected is true. True if the distance to the target is known.
|
||||
-- @return #ModelTime lastTime Has effect only if visible is false. Last time when target was seen.
|
||||
-- @return #boolean type Has effect only if detected is true. True if the target type is known.
|
||||
-- @return #boolean distance Has effect only if detected is true. True if the distance to the target is known.
|
||||
-- @return #Vec3 lastPos Has effect only if visible is false. Last position of the target when it was seen.
|
||||
-- @return #Vec3 lastVel Has effect only if visible is false. Last velocity of the target when it was seen.
|
||||
|
||||
@@ -1131,6 +1169,7 @@ end -- Controller
|
||||
|
||||
do -- Unit
|
||||
|
||||
--- Unit.
|
||||
-- @type Unit
|
||||
-- @extends #CoalitionObject
|
||||
-- @field ID Identifier of an unit. It assigned to an unit by the Mission Editor automatically.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
-- ### Author: FlightControl - Framework Design & Programming
|
||||
-- ### Refactoring to use the Runway auto-detection: Applevangelist
|
||||
-- @date August 2022
|
||||
-- Last Update Nov 2023
|
||||
-- Last Update Feb 2025
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -416,7 +416,7 @@ end
|
||||
-- @field #ATC_GROUND_UNIVERSAL
|
||||
ATC_GROUND_UNIVERSAL = {
|
||||
ClassName = "ATC_GROUND_UNIVERSAL",
|
||||
Version = "0.0.1",
|
||||
Version = "0.0.2",
|
||||
SetClient = nil,
|
||||
Airbases = nil,
|
||||
AirbaseList = nil,
|
||||
@@ -441,17 +441,25 @@ function ATC_GROUND_UNIVERSAL:New(AirbaseList)
|
||||
self:T( { self.ClassName } )
|
||||
|
||||
self.Airbases = {}
|
||||
|
||||
for _name,_ in pairs(_DATABASE.AIRBASES) do
|
||||
self.Airbases[_name]={}
|
||||
end
|
||||
|
||||
self.AirbaseList = AirbaseList
|
||||
|
||||
if not self.AirbaseList then
|
||||
self.AirbaseList = {}
|
||||
for _name,_ in pairs(_DATABASE.AIRBASES) do
|
||||
self.AirbaseList[_name]=_name
|
||||
for _name,_base in pairs(_DATABASE.AIRBASES) do
|
||||
-- DONE exclude FARPS and Ships
|
||||
if _base and _base.isAirdrome == true then
|
||||
self.AirbaseList[_name]=_name
|
||||
self.Airbases[_name]={}
|
||||
end
|
||||
end
|
||||
else
|
||||
for _,_name in pairs(AirbaseList) do
|
||||
-- DONE exclude FARPS and Ships
|
||||
local airbase = _DATABASE:FindAirbase(_name)
|
||||
if airbase and (airbase.isAirdrome == true) then
|
||||
self.Airbases[_name]={}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -721,14 +729,18 @@ function ATC_GROUND_UNIVERSAL:_AirbaseMonitor()
|
||||
|
||||
if NotInRunwayZone then
|
||||
|
||||
local Taxi = Client:GetState( self, "Taxi" )
|
||||
|
||||
if IsOnGround then
|
||||
local Taxi = Client:GetState( self, "Taxi" )
|
||||
|
||||
self:T( Taxi )
|
||||
if Taxi == false then
|
||||
local Velocity = VELOCITY:New( AirbaseMeta.KickSpeed or self.KickSpeed )
|
||||
Client:Message( "Welcome to " .. AirbaseID .. ". The maximum taxiing speed is " ..
|
||||
Velocity:ToString() , 20, "ATC" )
|
||||
Client:SetState( self, "Taxi", true )
|
||||
Client:SetState( self, "Speeding", false )
|
||||
Client:SetState( self, "Warnings", 0 )
|
||||
end
|
||||
|
||||
-- TODO: GetVelocityKMH function usage
|
||||
@@ -737,7 +749,7 @@ function ATC_GROUND_UNIVERSAL:_AirbaseMonitor()
|
||||
local IsAboveRunway = Client:IsAboveRunway()
|
||||
self:T( {IsAboveRunway, IsOnGround, Velocity:Get() })
|
||||
|
||||
if IsOnGround then
|
||||
if IsOnGround and not Taxi then
|
||||
local Speeding = false
|
||||
if AirbaseMeta.MaximumKickSpeed then
|
||||
if Velocity:Get() > AirbaseMeta.MaximumKickSpeed then
|
||||
@@ -749,15 +761,17 @@ function ATC_GROUND_UNIVERSAL:_AirbaseMonitor()
|
||||
end
|
||||
end
|
||||
if Speeding == true then
|
||||
MESSAGE:New( "Penalty! Player " .. Client:GetPlayerName() ..
|
||||
" has been kicked, due to a severe airbase traffic rule violation ...", 10, "ATC" ):ToAll()
|
||||
Client:Destroy()
|
||||
Client:SetState( self, "Speeding", false )
|
||||
Client:SetState( self, "Warnings", 0 )
|
||||
--MESSAGE:New( "Penalty! Player " .. Client:GetPlayerName() ..
|
||||
-- " has been kicked, due to a severe airbase traffic rule violation ...", 10, "ATC" ):ToAll()
|
||||
--Client:Destroy()
|
||||
Client:SetState( self, "Speeding", true )
|
||||
local SpeedingWarnings = Client:GetState( self, "Warnings" )
|
||||
Client:SetState( self, "Warnings", SpeedingWarnings + 1 )
|
||||
Client:Message( "Warning " .. SpeedingWarnings .. "/3! Airbase traffic rule violation! Slow down now! Your speed is " ..
|
||||
Velocity:ToString(), 5, "ATC" )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
if IsOnGround then
|
||||
|
||||
local Speeding = false
|
||||
@@ -1035,23 +1049,23 @@ end
|
||||
-- The following airbases are monitored at the Nevada region.
|
||||
-- Use the @{Wrapper.Airbase#AIRBASE.Nevada} enumeration to select the airbases to be monitored.
|
||||
--
|
||||
-- * `AIRBASE.Nevada.Beatty_Airport`
|
||||
-- * `AIRBASE.Nevada.Boulder_City_Airport`
|
||||
-- * `AIRBASE.Nevada.Creech_AFB`
|
||||
-- * `AIRBASE.Nevada.Beatty`
|
||||
-- * `AIRBASE.Nevada.Boulder_City`
|
||||
-- * `AIRBASE.Nevada.Creech`
|
||||
-- * `AIRBASE.Nevada.Echo_Bay`
|
||||
-- * `AIRBASE.Nevada.Groom_Lake_AFB`
|
||||
-- * `AIRBASE.Nevada.Henderson_Executive_Airport`
|
||||
-- * `AIRBASE.Nevada.Jean_Airport`
|
||||
-- * `AIRBASE.Nevada.Laughlin_Airport`
|
||||
-- * `AIRBASE.Nevada.Groom_Lake`
|
||||
-- * `AIRBASE.Nevada.Henderson_Executive`
|
||||
-- * `AIRBASE.Nevada.Jean`
|
||||
-- * `AIRBASE.Nevada.Laughlin`
|
||||
-- * `AIRBASE.Nevada.Lincoln_County`
|
||||
-- * `AIRBASE.Nevada.McCarran_International_Airport`
|
||||
-- * `AIRBASE.Nevada.McCarran_International`
|
||||
-- * `AIRBASE.Nevada.Mesquite`
|
||||
-- * `AIRBASE.Nevada.Mina_Airport`
|
||||
-- * `AIRBASE.Nevada.Nellis_AFB`
|
||||
-- * `AIRBASE.Nevada.Mina`
|
||||
-- * `AIRBASE.Nevada.Nellis`
|
||||
-- * `AIRBASE.Nevada.North_Las_Vegas`
|
||||
-- * `AIRBASE.Nevada.Pahute_Mesa_Airstrip`
|
||||
-- * `AIRBASE.Nevada.Tonopah_Airport`
|
||||
-- * `AIRBASE.Nevada.Tonopah_Test_Range_Airfield`
|
||||
-- * `AIRBASE.Nevada.Pahute_Mesa`
|
||||
-- * `AIRBASE.Nevada.Tonopah`
|
||||
-- * `AIRBASE.Nevada.Tonopah_Test_Range`
|
||||
--
|
||||
-- # Installation
|
||||
--
|
||||
@@ -1088,10 +1102,10 @@ end
|
||||
--
|
||||
-- -- Monitor specific airbases.
|
||||
-- ATC_Ground = ATC_GROUND_NEVADA:New(
|
||||
-- { AIRBASE.Nevada.Laughlin_Airport,
|
||||
-- { AIRBASE.Nevada.Laughlin,
|
||||
-- AIRBASE.Nevada.Lincoln_County,
|
||||
-- AIRBASE.Nevada.North_Las_Vegas,
|
||||
-- AIRBASE.Nevada.McCarran_International_Airport
|
||||
-- AIRBASE.Nevada.McCarran_International
|
||||
-- }
|
||||
-- )
|
||||
--
|
||||
@@ -1330,33 +1344,33 @@ end
|
||||
-- The following airbases are monitored at the PersianGulf region.
|
||||
-- Use the @{Wrapper.Airbase#AIRBASE.PersianGulf} enumeration to select the airbases to be monitored.
|
||||
--
|
||||
-- * `AIRBASE.PersianGulf.Abu_Musa_Island_Airport`
|
||||
-- * `AIRBASE.PersianGulf.Al_Dhafra_AB`
|
||||
-- * `AIRBASE.PersianGulf.Abu_Musa_Island`
|
||||
-- * `AIRBASE.PersianGulf.Al_Dhafra_AFB`
|
||||
-- * `AIRBASE.PersianGulf.Al_Maktoum_Intl`
|
||||
-- * `AIRBASE.PersianGulf.Al_Minhad_AB`
|
||||
-- * `AIRBASE.PersianGulf.Al_Minhad_AFB`
|
||||
-- * `AIRBASE.PersianGulf.Bandar_Abbas_Intl`
|
||||
-- * `AIRBASE.PersianGulf.Bandar_Lengeh`
|
||||
-- * `AIRBASE.PersianGulf.Dubai_Intl`
|
||||
-- * `AIRBASE.PersianGulf.Fujairah_Intl`
|
||||
-- * `AIRBASE.PersianGulf.Havadarya`
|
||||
-- * `AIRBASE.PersianGulf.Kerman_Airport`
|
||||
-- * `AIRBASE.PersianGulf.Kerman`
|
||||
-- * `AIRBASE.PersianGulf.Khasab`
|
||||
-- * `AIRBASE.PersianGulf.Lar_Airbase`
|
||||
-- * `AIRBASE.PersianGulf.Lar`
|
||||
-- * `AIRBASE.PersianGulf.Qeshm_Island`
|
||||
-- * `AIRBASE.PersianGulf.Sharjah_Intl`
|
||||
-- * `AIRBASE.PersianGulf.Shiraz_International_Airport`
|
||||
-- * `AIRBASE.PersianGulf.Shiraz_Intl`
|
||||
-- * `AIRBASE.PersianGulf.Sir_Abu_Nuayr`
|
||||
-- * `AIRBASE.PersianGulf.Sirri_Island`
|
||||
-- * `AIRBASE.PersianGulf.Tunb_Island_AFB`
|
||||
-- * `AIRBASE.PersianGulf.Tunb_Kochak`
|
||||
-- * `AIRBASE.PersianGulf.Sas_Al_Nakheel_Airport`
|
||||
-- * `AIRBASE.PersianGulf.Bandar_e_Jask_airfield`
|
||||
-- * `AIRBASE.PersianGulf.Abu_Dhabi_International_Airport`
|
||||
-- * `AIRBASE.PersianGulf.Al_Bateen_Airport`
|
||||
-- * `AIRBASE.PersianGulf.Kish_International_Airport`
|
||||
-- * `AIRBASE.PersianGulf.Al_Ain_International_Airport`
|
||||
-- * `AIRBASE.PersianGulf.Lavan_Island_Airport`
|
||||
-- * `AIRBASE.PersianGulf.Jiroft_Airport`
|
||||
-- * `AIRBASE.PersianGulf.Sas_Al_Nakheel`
|
||||
-- * `AIRBASE.PersianGulf.Bandar_e_Jask`
|
||||
-- * `AIRBASE.PersianGulf.Abu_Dhabi_Intl`
|
||||
-- * `AIRBASE.PersianGulf.Al_Bateen`
|
||||
-- * `AIRBASE.PersianGulf.Kish_Intl`
|
||||
-- * `AIRBASE.PersianGulf.Al_Ain_Intl`
|
||||
-- * `AIRBASE.PersianGulf.Lavan_Island`
|
||||
-- * `AIRBASE.PersianGulf.Jiroft`
|
||||
--
|
||||
-- # Installation
|
||||
--
|
||||
@@ -1391,8 +1405,8 @@ end
|
||||
-- AirbasePoliceCaucasus = ATC_GROUND_PERSIANGULF:New()
|
||||
--
|
||||
-- ATC_Ground = ATC_GROUND_PERSIANGULF:New(
|
||||
-- { AIRBASE.PersianGulf.Kerman_Airport,
|
||||
-- AIRBASE.PersianGulf.Al_Minhad_AB
|
||||
-- { AIRBASE.PersianGulf.Kerman,
|
||||
-- AIRBASE.PersianGulf.Al_Minhad_AFB
|
||||
-- }
|
||||
-- )
|
||||
--
|
||||
@@ -1441,11 +1455,10 @@ function ATC_GROUND_PERSIANGULF:Start( RepeatScanSeconds )
|
||||
self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, { self }, 0, RepeatScanSeconds )
|
||||
end
|
||||
|
||||
|
||||
-- @type ATC_GROUND_MARIANAISLANDS
|
||||
---
|
||||
-- @type ATC_GROUND_MARIANAISLANDS
|
||||
-- @extends #ATC_GROUND
|
||||
|
||||
|
||||
|
||||
--- # ATC\_GROUND\_MARIANA, extends @{#ATC_GROUND}
|
||||
--
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
--
|
||||
-- ### Author: **funkyfranky**
|
||||
--
|
||||
-- ### Contributions: FlightControl
|
||||
-- ### Contributions: FlightControl, Applevangelist
|
||||
--
|
||||
-- ====
|
||||
-- @module Functional.Artillery
|
||||
@@ -619,63 +619,148 @@ ARTY.WeaponType={
|
||||
}
|
||||
|
||||
--- Database of common artillery unit properties.
|
||||
-- @type ARTY.dbitem
|
||||
-- @field #string displayname Name displayed in ME.
|
||||
-- @field #number minrange Minimum firing range in meters.
|
||||
-- @field #number maxrange Maximum firing range in meters.
|
||||
-- @field #number reloadtime Reload time in seconds.
|
||||
|
||||
--- Database of common artillery unit properties.
|
||||
-- Table key is the "type name" and table value is and `ARTY.dbitem`.
|
||||
-- @type ARTY.db
|
||||
ARTY.db={
|
||||
["2B11 mortar"] = { -- type "2B11 mortar"
|
||||
minrange = 500, -- correct?
|
||||
maxrange = 7000, -- 7 km
|
||||
reloadtime = 30, -- 30 sec
|
||||
["LeFH_18-40-105"] = {
|
||||
displayname = "FH LeFH-18 105mm", -- name displayed in the ME
|
||||
minrange = 500, -- min range (green circle) in meters
|
||||
maxrange = 10500, -- max range (red circle) in meters
|
||||
reloadtime = nil, -- reload time in seconds
|
||||
},
|
||||
["SPH 2S1 Gvozdika"] = { -- type "SAU Gvozdika"
|
||||
minrange = 300, -- correct?
|
||||
maxrange = 15000, -- 15 km
|
||||
reloadtime = nil, -- unknown
|
||||
["M2A1-105"] = {
|
||||
displayname = "FH M2A1 105mm",
|
||||
minrange = 500,
|
||||
maxrange = 11500,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["SPH 2S19 Msta"] = { --type "SAU Msta", alias "2S19 Msta"
|
||||
minrange = 300, -- correct?
|
||||
maxrange = 23500, -- 23.5 km
|
||||
reloadtime = nil, -- unknown
|
||||
["Pak40"] = {
|
||||
displayname = "FH Pak 40 75mm",
|
||||
minrange = 500,
|
||||
maxrange = 3000,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["L118_Unit"] = {
|
||||
displayname = "L118 Light Artillery Gun",
|
||||
minrange = 500,
|
||||
maxrange = 17500,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["SPH 2S3 Akatsia"] = { -- type "SAU Akatsia", alias "2S3 Akatsia"
|
||||
minrange = 300, -- correct?
|
||||
maxrange = 17000, -- 17 km
|
||||
reloadtime = nil, -- unknown
|
||||
["Smerch"] = {
|
||||
displayname = "MLRS 9A52 Smerch CM 300mm",
|
||||
minrange = 20000,
|
||||
maxrange = 70000,
|
||||
reloadtime = 2160,
|
||||
},
|
||||
["SPH 2S9 Nona"] = { --type "SAU 2-C9"
|
||||
minrange = 500, -- correct?
|
||||
maxrange = 7000, -- 7 km
|
||||
reloadtime = nil, -- unknown
|
||||
["Smerch_HE"] = {
|
||||
displayname = "MLRS 9A52 Smerch HE 300mm",
|
||||
minrange = 20000,
|
||||
maxrange = 70000,
|
||||
reloadtime = 2160,
|
||||
},
|
||||
["SPH M109 Paladin"] = { -- type "M-109", alias "M109"
|
||||
minrange = 300, -- correct?
|
||||
maxrange = 22000, -- 22 km
|
||||
reloadtime = nil, -- unknown
|
||||
["Uragan_BM-27"] = {
|
||||
displayname = "MLRS 9K57 Uragan BM-27 220mm",
|
||||
minrange = 11500,
|
||||
maxrange = 35800,
|
||||
reloadtime = 840,
|
||||
},
|
||||
["SpGH Dana"] = { -- type "SpGH_Dana"
|
||||
minrange = 300, -- correct?
|
||||
maxrange = 18700, -- 18.7 km
|
||||
reloadtime = nil, -- unknown
|
||||
["Grad-URAL"] = {
|
||||
displayname = "MLRS BM-21 Grad 122mm",
|
||||
minrange = 5000,
|
||||
maxrange = 19000,
|
||||
reloadtime = 420,
|
||||
},
|
||||
["MLRS BM-21 Grad"] = { --type "Grad-URAL", alias "MLRS BM-21 Grad"
|
||||
minrange = 5000, -- 5 km
|
||||
maxrange = 19000, -- 19 km
|
||||
reloadtime = 420, -- 7 min
|
||||
["HL_B8M1"] = {
|
||||
displayname = "MLRS HL with B8M1 80mm",
|
||||
minrange = 500,
|
||||
maxrange = 5000,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["MLRS 9K57 Uragan BM-27"] = { -- type "Uragan_BM-27"
|
||||
minrange = 11500, -- 11.5 km
|
||||
maxrange = 35800, -- 35.8 km
|
||||
reloadtime = 840, -- 14 min
|
||||
["tt_B8M1"] = {
|
||||
displayname = "MLRS LC with B8M1 80mm",
|
||||
minrange = 500,
|
||||
maxrange = 5000,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["MLRS 9A52 Smerch"] = { -- type "Smerch"
|
||||
minrange = 20000, -- 20 km
|
||||
maxrange = 70000, -- 70 km
|
||||
reloadtime = 2160, -- 36 min
|
||||
["MLRS"] = {
|
||||
displayname = "MLRS M270 227mm",
|
||||
minrange = 10000,
|
||||
maxrange = 32000,
|
||||
reloadtime = 540,
|
||||
},
|
||||
["MLRS M270"] = { --type "MRLS", alias "M270 MRLS"
|
||||
minrange = 10000, -- 10 km
|
||||
maxrange = 32000, -- 32 km
|
||||
reloadtime = 540, -- 9 min
|
||||
["2B11 mortar"] = {
|
||||
displayname = "Mortar 2B11 120mm",
|
||||
minrange = 500,
|
||||
maxrange = 7000,
|
||||
reloadtime = 30,
|
||||
},
|
||||
["PLZ05"] = {
|
||||
displayname = "PLZ-05",
|
||||
minrange = 500,
|
||||
maxrange = 23500,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["SAU Gvozdika"] = {
|
||||
displayname = "SPH 2S1 Gvozdika 122mm",
|
||||
minrange = 300,
|
||||
maxrange = 15000,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["SAU Msta"] = {
|
||||
displayname = "SPH 2S19 Msta 152mm",
|
||||
minrange = 300,
|
||||
maxrange = 23500,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["SAU Akatsia"] = {
|
||||
displayname = "SPH 2S3 Akatsia 152mm",
|
||||
minrange = 300,
|
||||
maxrange = 17000,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["SpGH_Dana"] = {
|
||||
displayname = "SPH Dana vz77 152mm",
|
||||
minrange = 300,
|
||||
maxrange = 18700,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["M-109"] = {
|
||||
displayname = "SPH M109 Paladin 155mm",
|
||||
minrange = 300,
|
||||
maxrange = 22000,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["M12_GMC"] = {
|
||||
displayname = "SPH M12 GMC 155mm",
|
||||
minrange = 300,
|
||||
maxrange = 18200,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["Wespe124"] = {
|
||||
displayname = "SPH Sd.Kfz.124 Wespe 105mm",
|
||||
minrange = 300,
|
||||
maxrange = 7000,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["T155_Firtina"] = {
|
||||
displayname = "SPH T155 Firtina 155mm",
|
||||
minrange = 300,
|
||||
maxrange = 41000,
|
||||
reloadtime = nil,
|
||||
},
|
||||
["SAU 2-C9"] = {
|
||||
displayname = "SPM 2S9 Nona 120mm M",
|
||||
minrange = 500,
|
||||
maxrange = 7000,
|
||||
reloadtime = nil,
|
||||
},
|
||||
}
|
||||
|
||||
--- Target.
|
||||
@@ -695,7 +780,7 @@ ARTY.db={
|
||||
|
||||
--- Arty script version.
|
||||
-- @field #string version
|
||||
ARTY.version="1.3.1"
|
||||
ARTY.version="1.3.4"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -797,8 +882,8 @@ function ARTY:New(group, alias)
|
||||
-- Maximum speed in km/h.
|
||||
self.SpeedMax=group:GetSpeedMax()
|
||||
|
||||
-- Group is mobile or not (e.g. mortars).
|
||||
if self.SpeedMax>1 then
|
||||
-- Group is mobile or not (e.g. mortars). Some immobile units have a speed of 1 m/s = 3.6 km/h. So we check this number.
|
||||
if self.SpeedMax>3.6 then
|
||||
self.ismobile=true
|
||||
else
|
||||
self.ismobile=false
|
||||
@@ -1923,7 +2008,7 @@ function ARTY:onafterStart(Controllable, From, Event, To)
|
||||
end
|
||||
|
||||
-- Check if we have and arty type that is in the DB.
|
||||
local _dbproperties=self:_CheckDB(self.DisplayName)
|
||||
local _dbproperties=self:_CheckDB(self.Type)
|
||||
self:T({dbproperties=_dbproperties})
|
||||
if _dbproperties~=nil then
|
||||
for property,value in pairs(_dbproperties) do
|
||||
@@ -1969,8 +2054,8 @@ function ARTY:onafterStart(Controllable, From, Event, To)
|
||||
text=text..string.format("Type = %s\n", self.Type)
|
||||
text=text..string.format("Display Name = %s\n", self.DisplayName)
|
||||
text=text..string.format("Number of units = %d\n", self.IniGroupStrength)
|
||||
text=text..string.format("Speed max = %d km/h\n", self.SpeedMax)
|
||||
text=text..string.format("Speed default = %d km/h\n", self.Speed)
|
||||
text=text..string.format("Speed max = %.1f km/h\n", self.SpeedMax)
|
||||
text=text..string.format("Speed default = %.1f km/h\n", self.Speed)
|
||||
text=text..string.format("Is mobile = %s\n", tostring(self.ismobile))
|
||||
text=text..string.format("Is cargo = %s\n", tostring(self.iscargo))
|
||||
text=text..string.format("Min range = %.1f km\n", self.minrange/1000)
|
||||
@@ -2183,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)
|
||||
@@ -3049,7 +3134,7 @@ function ARTY:onafterOpenFire(Controllable, From, Event, To, target)
|
||||
local nfire=Narty
|
||||
local _type="shots"
|
||||
if target.weapontype==ARTY.WeaponType.Auto then
|
||||
nfire=Narty
|
||||
nfire=Nammo -- We take everything that is available
|
||||
_type="shots"
|
||||
elseif target.weapontype==ARTY.WeaponType.Cannon then
|
||||
nfire=Narty
|
||||
@@ -3070,6 +3155,8 @@ function ARTY:onafterOpenFire(Controllable, From, Event, To, target)
|
||||
nfire=Nmissiles
|
||||
_type="cruise missiles"
|
||||
end
|
||||
|
||||
--env.info(string.format("FF type=%s, Nrockets=%d, Nfire=%d target.nshells=%d", _type, Nrockets, nfire, target.nshells))
|
||||
|
||||
-- Adjust if less than requested ammo is left.
|
||||
target.nshells=math.min(target.nshells, nfire)
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
-- @image Designation.JPG
|
||||
--
|
||||
-- Date: 24 Oct 2021
|
||||
-- Last Update: May 2024
|
||||
-- Last Update: Mar 2025
|
||||
--
|
||||
--- Class AUTOLASE
|
||||
-- @type AUTOLASE
|
||||
@@ -89,6 +89,10 @@
|
||||
-- @field #table playermenus
|
||||
-- @field #boolean smokemenu
|
||||
-- @field #boolean threatmenu
|
||||
-- @field #number RoundingPrecision
|
||||
-- @field #table smokeoffset
|
||||
-- @field #boolean increasegroundawareness
|
||||
-- @field #number MonitorFrequency
|
||||
-- @extends Ops.Intel#INTEL
|
||||
|
||||
---
|
||||
@@ -100,6 +104,9 @@ AUTOLASE = {
|
||||
alias = "",
|
||||
debug = false,
|
||||
smokemenu = true,
|
||||
RoundingPrecision = 0,
|
||||
increasegroundawareness = false,
|
||||
MonitorFrequency = 30,
|
||||
}
|
||||
|
||||
--- Laser spot info
|
||||
@@ -118,7 +125,7 @@ AUTOLASE = {
|
||||
|
||||
--- AUTOLASE class version.
|
||||
-- @field #string version
|
||||
AUTOLASE.version = "0.1.25"
|
||||
AUTOLASE.version = "0.1.31"
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Begin Functional.Autolase.lua
|
||||
@@ -191,6 +198,7 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet)
|
||||
self.reporttimelong = 30
|
||||
self.smoketargets = false
|
||||
self.smokecolor = SMOKECOLOR.Red
|
||||
self.smokeoffset = nil
|
||||
self.notifypilots = true
|
||||
self.targetsperrecce = {}
|
||||
self.RecceUnits = {}
|
||||
@@ -207,6 +215,11 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet)
|
||||
self.playermenus = {}
|
||||
self.smokemenu = true
|
||||
self.threatmenu = true
|
||||
self.RoundingPrecision = 0
|
||||
self.increasegroundawareness = false
|
||||
self.MonitorFrequency = 30
|
||||
|
||||
self:EnableSmokeMenu({Angle=math.random(0,359),Distance=math.random(10,20)})
|
||||
|
||||
-- Set some string id for output to DCS.log file.
|
||||
self.lid=string.format("AUTOLASE %s (%s) | ", self.alias, self.coalition and UTILS.GetCoalitionName(self.coalition) or "unknown")
|
||||
@@ -309,16 +322,41 @@ end
|
||||
-- Helper Functions
|
||||
-------------------------------------------------------------------
|
||||
|
||||
--- [User] When using Monitor, set the frequency here in which the report will appear
|
||||
-- @param #AUTOLASE self
|
||||
-- @param #number Seconds Run the report loop every number of seconds defined here.
|
||||
-- @return #AUTOLASE self
|
||||
function AUTOLASE:SetMonitorFrequency(Seconds)
|
||||
self.MonitorFrequency = Seconds or 30
|
||||
return self
|
||||
end
|
||||
|
||||
--- [User] Set a table of possible laser codes.
|
||||
-- Each new RECCE can select a code from this table, default is { 1688, 1130, 4785, 6547, 1465, 4578 } .
|
||||
-- Each new RECCE can select a code from this table, default is { 1688, 1130, 4785, 6547, 1465, 4578 }.
|
||||
-- @param #AUTOLASE self
|
||||
-- @param #list<#number> LaserCodes
|
||||
-- @return #AUTOLASE
|
||||
-- @return #AUTOLASE self
|
||||
function AUTOLASE:SetLaserCodes( LaserCodes )
|
||||
self.LaserCodes = ( type( LaserCodes ) == "table" ) and LaserCodes or { LaserCodes }
|
||||
return self
|
||||
end
|
||||
|
||||
--- [User] Improve ground unit detection by using a zone scan and LOS check.
|
||||
-- @param #AUTOLASE self
|
||||
-- @return #AUTOLASE self
|
||||
function AUTOLASE:EnableImproveGroundUnitsDetection()
|
||||
self.increasegroundawareness = true
|
||||
return self
|
||||
end
|
||||
|
||||
--- [User] Do not improve ground unit detection by using a zone scan and LOS check.
|
||||
-- @param #AUTOLASE self
|
||||
-- @return #AUTOLASE self
|
||||
function AUTOLASE:DisableImproveGroundUnitsDetection()
|
||||
self.increasegroundawareness = false
|
||||
return self
|
||||
end
|
||||
|
||||
--- (Internal) Function to set pilot menu.
|
||||
-- @param #AUTOLASE self
|
||||
-- @return #AUTOLASE self
|
||||
@@ -455,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
|
||||
@@ -470,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"
|
||||
@@ -600,11 +638,26 @@ function AUTOLASE:SetSmokeTargets(OnOff,Color)
|
||||
return self
|
||||
end
|
||||
|
||||
--- (User) Function to set rounding precision for BR distance output.
|
||||
-- @param #AUTOLASE self
|
||||
-- @param #number IDP Rounding precision before/after the decimal sign. Defaults to zero. Positive values round right of the decimal sign, negative ones left of the decimal sign.
|
||||
-- @return #AUTOLASE self
|
||||
function AUTOLASE:SetRoundingPrecsion(IDP)
|
||||
self.RoundingPrecision = IDP or 0
|
||||
return self
|
||||
end
|
||||
|
||||
--- (User) Show the "Switch smoke target..." menu entry for pilots. On by default.
|
||||
-- @param #AUTOLASE self
|
||||
-- @param #table Offset (Optional) Define an offset for the smoke, i.e. not directly on the unit itself, angle is degrees and distance is meters. E.g. `autolase:EnableSmokeMenu({Angle=30,Distance=20})`
|
||||
-- @return #AUTOLASE self
|
||||
function AUTOLASE:EnableSmokeMenu()
|
||||
function AUTOLASE:EnableSmokeMenu(Offset)
|
||||
self.smokemenu = true
|
||||
if Offset then
|
||||
self.smokeoffset = {}
|
||||
self.smokeoffset.Distance = Offset.Distance or math.random(10,20)
|
||||
self.smokeoffset.Angle = Offset.Angle or math.random(0,359)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -613,6 +666,7 @@ end
|
||||
-- @return #AUTOLASE self
|
||||
function AUTOLASE:DisableSmokeMenu()
|
||||
self.smokemenu = false
|
||||
self.smokeoffset = nil
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -671,7 +725,8 @@ function AUTOLASE:CleanCurrentLasing()
|
||||
local unit = recce:GetUnit(1)
|
||||
local name = unit:GetName()
|
||||
if not self.RecceUnits[name] then
|
||||
self.RecceUnits[name] = { name=name, unit=unit, cooldown = false, timestamp = timer.getAbsTime() }
|
||||
local isground = (unit and unit.IsGround) and unit:IsGround() or false
|
||||
self.RecceUnits[name] = { name=name, unit=unit, cooldown = false, timestamp = timer.getAbsTime(), isground=isground }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -757,9 +812,11 @@ function AUTOLASE:ShowStatus(Group,Unit)
|
||||
end
|
||||
local code = self:GetLaserCode(unit:GetName())
|
||||
report:Add(string.format("Recce %s has code %d",name,code))
|
||||
report:Add("---------------")
|
||||
end
|
||||
end
|
||||
report:Add(string.format("Lasing min threat level %d",self.minthreatlevel))
|
||||
report:Add("---------------")
|
||||
local lines = 0
|
||||
for _ind,_entry in pairs(self.CurrentLasing) do
|
||||
local entry = _entry -- #AUTOLASE.LaserSpot
|
||||
@@ -779,22 +836,28 @@ function AUTOLASE:ShowStatus(Group,Unit)
|
||||
if playername then
|
||||
local settings = _DATABASE:GetPlayerSettings(playername)
|
||||
if settings then
|
||||
self:I("Get Settings ok!")
|
||||
self:T("Get Settings ok!")
|
||||
if settings:IsA2G_MGRS() then
|
||||
locationstring = entry.coordinate:ToStringMGRS(settings)
|
||||
elseif settings:IsA2G_LL_DMS() then
|
||||
locationstring = entry.coordinate:ToStringLLDMS(settings)
|
||||
elseif settings:IsA2G_LL_DDM() then
|
||||
locationstring = entry.coordinate:ToStringLLDDM(settings)
|
||||
elseif settings:IsA2G_BR() then
|
||||
locationstring = entry.coordinate:ToStringBR(Group:GetCoordinate() or Unit:GetCoordinate(),settings)
|
||||
-- attention this is the distance from the ASKING unit to target, not from RECCE to target!
|
||||
local startcoordinate = Unit:GetCoordinate() or Group:GetCoordinate()
|
||||
locationstring = entry.coordinate:ToStringBR(startcoordinate,settings,false,self.RoundingPrecision)
|
||||
end
|
||||
end
|
||||
end
|
||||
local text = string.format("%s lasing %s code %d\nat %s",reccename,typename,code,locationstring)
|
||||
local text = string.format("+ %s lasing %s code %d\nat %s",reccename,typename,code,locationstring)
|
||||
report:Add(text)
|
||||
report:Add("---------------")
|
||||
lines = lines + 1
|
||||
end
|
||||
if lines == 0 then
|
||||
report:Add("No targets!")
|
||||
report:Add("---------------")
|
||||
end
|
||||
local reporttime = self.reporttimelong
|
||||
if lines == 0 then reporttime = self.reporttimeshort end
|
||||
@@ -913,6 +976,65 @@ function AUTOLASE:CanLase(Recce,Unit)
|
||||
return canlase
|
||||
end
|
||||
|
||||
--- (Internal) Function to do a zone check per ground Recce and make found units and statics "known".
|
||||
-- @param #AUTOLASE self
|
||||
-- @return #AUTOLASE self
|
||||
function AUTOLASE:_Prescient()
|
||||
-- self.RecceUnits[name] = { name=name, unit=unit, cooldown = false, timestamp = timer.getAbsTime(), isground=isground }
|
||||
for _,_data in pairs(self.RecceUnits) do
|
||||
-- ground units only
|
||||
if _data.isground and _data.unit and _data.unit:IsAlive() then
|
||||
local unit = _data.unit -- Wrapper.Unit#UNIT
|
||||
local position = unit:GetCoordinate() -- Core.Point#COORDINATE
|
||||
local needsinit = false
|
||||
if position then
|
||||
local lastposition = unit:GetProperty("lastposition")
|
||||
-- property initiated?
|
||||
if not lastposition then
|
||||
unit:SetProperty("lastposition",position)
|
||||
lastposition = position
|
||||
needsinit = true
|
||||
end
|
||||
-- has moved?
|
||||
local dist = position:Get2DDistance(lastposition)
|
||||
-- refresh?
|
||||
local TNow = timer.getAbsTime()
|
||||
-- check
|
||||
if dist > 10 or needsinit==true or TNow - _data.timestamp > 29 then
|
||||
-- init scan objects
|
||||
local hasunits,hasstatics,_,Units,Statics = position:ScanObjects(self.LaseDistance,true,true,false)
|
||||
-- loop found units
|
||||
if hasunits then
|
||||
self:T(self.lid.."Checking possibly visible UNITs for Recce "..unit:GetName())
|
||||
for _,_target in pairs(Units) do -- Wrapper.Unit#UNIT object here
|
||||
local target = _target -- Wrapper.Unit#UNIT
|
||||
if target and target:GetCoalition() ~= self.coalition then
|
||||
if unit:IsLOS(target) and (not target:IsUnitDetected(unit))then
|
||||
unit:KnowUnit(target,true,true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- loop found statics
|
||||
if hasstatics then
|
||||
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 and static:GetCoordinate() then
|
||||
local IsLOS = position:IsLOS(static:GetCoordinate())
|
||||
if IsLOS then
|
||||
unit:KnowUnit(static,true,true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- FSM Functions
|
||||
-------------------------------------------------------------------
|
||||
@@ -925,6 +1047,9 @@ end
|
||||
-- @return #AUTOLASE self
|
||||
function AUTOLASE:onbeforeMonitor(From, Event, To)
|
||||
self:T({From, Event, To})
|
||||
if self.increasegroundawareness then
|
||||
self:_Prescient()
|
||||
end
|
||||
-- Check if group has detected any units.
|
||||
self:UpdateIntel()
|
||||
return self
|
||||
@@ -953,7 +1078,7 @@ function AUTOLASE:onafterMonitor(From, Event, To)
|
||||
local grp = contact.group
|
||||
local coord = contact.position
|
||||
local reccename = contact.recce or "none"
|
||||
local threat = contact.threatlevel or 0
|
||||
local threat = contact.threatlevel or 0
|
||||
local reccegrp = UNIT:FindByName(reccename)
|
||||
if reccegrp then
|
||||
local reccecoord = reccegrp:GetCoordinate()
|
||||
@@ -1077,6 +1202,9 @@ function AUTOLASE:onafterMonitor(From, Event, To)
|
||||
}
|
||||
if self.smoketargets then
|
||||
local coord = unit:GetCoordinate()
|
||||
if self.smokeoffset then
|
||||
coord:Translate(self.smokeoffset.Distance,self.smokeoffset.Angle,true,true)
|
||||
end
|
||||
local color = self:GetSmokeColor(reccename)
|
||||
coord:Smoke(color)
|
||||
end
|
||||
@@ -1087,7 +1215,8 @@ function AUTOLASE:onafterMonitor(From, Event, To)
|
||||
end
|
||||
end
|
||||
|
||||
self:__Monitor(-30)
|
||||
local nextloop = -self.MonitorFrequency or -30
|
||||
self:__Monitor(nextloop)
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@@ -357,7 +357,7 @@ function CLEANUP_AIRBASE.__:EventAddForCleanUp( Event )
|
||||
self:F({Event})
|
||||
|
||||
|
||||
if Event.IniDCSUnit and Event.IniCategory == Object.Category.UNIT then
|
||||
if Event.IniDCSUnit and Event.IniUnit and Event.IniCategory == Object.Category.UNIT then
|
||||
if self.CleanUpList[Event.IniDCSUnitName] == nil then
|
||||
if self:IsInAirbase( Event.IniUnit:GetVec2() ) then
|
||||
self:AddForCleanUp( Event.IniUnit, Event.IniDCSUnitName )
|
||||
@@ -365,7 +365,7 @@ function CLEANUP_AIRBASE.__:EventAddForCleanUp( Event )
|
||||
end
|
||||
end
|
||||
|
||||
if Event.TgtDCSUnit and Event.TgtCategory == Object.Category.UNIT then
|
||||
if Event.TgtDCSUnit and Event.TgtUnit and Event.TgtCategory == Object.Category.UNIT then
|
||||
if self.CleanUpList[Event.TgtDCSUnitName] == nil then
|
||||
if self:IsInAirbase( Event.TgtUnit:GetVec2() ) then
|
||||
self:AddForCleanUp( Event.TgtUnit, Event.TgtDCSUnitName )
|
||||
@@ -387,7 +387,7 @@ function CLEANUP_AIRBASE.__:CleanUpSchedule()
|
||||
local CleanUpUnit = CleanUpListData.CleanUpUnit -- Wrapper.Unit#UNIT
|
||||
local CleanUpGroupName = CleanUpListData.CleanUpGroupName
|
||||
|
||||
if CleanUpUnit:IsAlive() ~= nil then
|
||||
if CleanUpUnit and CleanUpUnit:IsAlive() ~= nil then
|
||||
|
||||
if self:IsInAirbase( CleanUpUnit:GetVec2() ) then
|
||||
|
||||
@@ -414,7 +414,7 @@ function CLEANUP_AIRBASE.__:CleanUpSchedule()
|
||||
end
|
||||
end
|
||||
-- Clean Units which are waiting for a very long time in the CleanUpZone.
|
||||
if CleanUpUnit and not CleanUpUnit:GetPlayerName() then
|
||||
if CleanUpUnit and (CleanUpUnit.GetPlayerName == nil or not CleanUpUnit:GetPlayerName()) then
|
||||
local CleanUpUnitVelocity = CleanUpUnit:GetVelocityKMH()
|
||||
if CleanUpUnitVelocity < 1 then
|
||||
if CleanUpListData.CleanUpMoved then
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
--
|
||||
-- ====
|
||||
-- @module Functional.ClientWatch
|
||||
-- @image ClientWatch.JPG
|
||||
-- @image clientwatch.jpg
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
--- CLIENTWATCH class
|
||||
@@ -28,6 +28,8 @@
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #boolean Debug Write Debug messages to DCS log file and send Debug messages to all players.
|
||||
-- @field #string lid String for DCS log file.
|
||||
-- @field #number FilterCoalition If not nil, will only activate for aircraft of the given coalition value.
|
||||
-- @field #number FilterCategory If not nil, will only activate for aircraft of the given category value.
|
||||
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
||||
|
||||
--- Manage and track client slots easily to add your own client-based menus and modules to.
|
||||
@@ -66,7 +68,7 @@
|
||||
--
|
||||
-- -- Create an instance with a client unit prefix and send them a message when they spawn
|
||||
-- local clientInstance = CLIENTWATCH:New("Rotary")
|
||||
-- function clientInstance:OnAfterSpawn(From,Event,To,ClientObject)
|
||||
-- function clientInstance:OnAfterSpawn(From,Event,To,ClientObject,EventData)
|
||||
-- MESSAGE:New("Welcome to your aircraft!",10):ToUnit(ClientObject.Unit)
|
||||
-- end
|
||||
--
|
||||
@@ -108,7 +110,7 @@
|
||||
--
|
||||
-- -- Show a message to player when they take damage from a weapon
|
||||
-- local clientInstance = CLIENTWATCH:New("Rotary")
|
||||
-- function clientInstance:OnAfterSpawn(From,Event,To,ClientObject)
|
||||
-- function clientInstance:OnAfterSpawn(From,Event,To,ClientObject,EventData)
|
||||
-- function ClientObject:OnAfterHit(From,Event,To,EventData)
|
||||
-- local typeShooter = EventData.IniTypeName
|
||||
-- local nameWeapon = EventData.weapon_name
|
||||
@@ -120,6 +122,7 @@
|
||||
CLIENTWATCH = {}
|
||||
CLIENTWATCH.ClassName = "CLIENTWATCH"
|
||||
CLIENTWATCH.Debug = false
|
||||
CLIENTWATCH.DebugEventData = false
|
||||
CLIENTWATCH.lid = nil
|
||||
|
||||
-- @type CLIENTWATCHTools
|
||||
@@ -139,7 +142,10 @@ CLIENTWATCH.version="1.0.1"
|
||||
|
||||
--- Creates a new instance of CLIENTWATCH to add scripts to. Can be used multiple times with the same client/prefixes if you need it for multiple scripts.
|
||||
-- @param #CLIENTWATCH self
|
||||
-- @param #string, #table, or Wrapper.Client#CLIENT client Takes multiple inputs. If provided a #string, it will watch for clients whos UNIT NAME or GROUP NAME matches part of the #string as a prefix. You can also provide it with a #table containing multiple #string prefixes. Lastly, you can provide it with a Wrapper.Client#CLIENT of the specific client you want to apply this to.
|
||||
-- @param #string Will watch for clients whos UNIT NAME or GROUP NAME matches part of the #string as a prefix.
|
||||
-- @param #table Put strings in a table to use multiple prefixes for the above method.
|
||||
-- @param Wrapper.Client#CLIENT Provide a Moose CLIENT object to apply to that specific aircraft slot (static slots only!)
|
||||
-- @param #nil Leave blank to activate for ALL CLIENTS
|
||||
-- @return #CLIENTWATCH self
|
||||
function CLIENTWATCH:New(client)
|
||||
--Init FSM
|
||||
@@ -147,6 +153,9 @@ function CLIENTWATCH:New(client)
|
||||
self:SetStartState( "Idle" )
|
||||
self:AddTransition( "*", "Spawn", "*" )
|
||||
|
||||
self.FilterCoalition = nil
|
||||
self.FilterCategory = nil
|
||||
|
||||
--- User function for OnAfter "Spawn" event.
|
||||
-- @function [parent=#CLIENTWATCH] OnAfterSpawn
|
||||
-- @param #CLIENTWATCH self
|
||||
@@ -155,27 +164,50 @@ function CLIENTWATCH:New(client)
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param #table clientObject Custom object that handles events and stores Moose object data. See top documentation for more details.
|
||||
-- @param #table eventdata Data from EVENTS.Birth.
|
||||
|
||||
--Set up spawn tracking
|
||||
if type(client) == "table" or type(client) == "string" then
|
||||
if not client then
|
||||
if self.Debug then self:I({"New client instance created. ClientType = All clients"}) end
|
||||
self:HandleEvent(EVENTS.Birth)
|
||||
function self:OnEventBirth(eventdata)
|
||||
if (eventdata.IniCategory == 0 or eventdata.IniCategory == 1) and eventdata.IniPlayerName
|
||||
and (not self.FilterCoalition or self.FilterCoalition == eventdata.IniCoalition)
|
||||
and (not self.FilterCategory or self.FilterCategory == eventdata.IniCategory) then
|
||||
if self.Debug then
|
||||
self:I({"Client spawned in.",IniCategory = eventdata.IniCategory})
|
||||
end
|
||||
local clientWatchDebug = self.Debug
|
||||
local clientObject = CLIENTWATCHTools:_newClient(clientWatchDebug,eventdata)
|
||||
self:Spawn(clientObject,eventdata)
|
||||
end
|
||||
end
|
||||
elseif type(client) == "table" or type(client) == "string" then
|
||||
if type(client) == "table" then
|
||||
|
||||
--CLIENT TABLE
|
||||
if client.ClassName == "CLIENT" then
|
||||
if self.Debug then self:I({"New client instance created. ClientType = Wrapper.CLIENT",client}) end
|
||||
self.ClientName = client:GetName()
|
||||
self:HandleEvent(EVENTS.Birth)
|
||||
function self:OnEventBirth(eventdata)
|
||||
if self.Debug then UTILS.PrintTableToLog(eventdata) end
|
||||
if eventdata.IniCategory and eventdata.IniCategory <= 1 then
|
||||
if (eventdata.IniCategory == 0 or eventdata.IniCategory == 1) and eventdata.IniPlayerName
|
||||
and (not self.FilterCoalition or self.FilterCoalition == eventdata.IniCoalition)
|
||||
and (not self.FilterCategory or self.FilterCategory == eventdata.IniCategory) then
|
||||
if self.ClientName == eventdata.IniUnitName then
|
||||
local clientObject = CLIENTWATCHTools:_newClient(eventdata)
|
||||
self:Spawn(clientObject)
|
||||
if self.Debug then
|
||||
self:I({"Client spawned in.",IniCategory = eventdata.IniCategory})
|
||||
end
|
||||
local clientWatchDebug = self.Debug
|
||||
local clientObject = CLIENTWATCHTools:_newClient(clientWatchDebug,eventdata)
|
||||
self:Spawn(clientObject,eventdata)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--STRING TABLE
|
||||
else
|
||||
if self.Debug then self:I({"New client instance created. ClientType = Multiple Prefixes",client}) end
|
||||
local tableValid = true
|
||||
for _,entry in pairs(client) do
|
||||
if type(entry) ~= "string" then
|
||||
@@ -187,12 +219,17 @@ function CLIENTWATCH:New(client)
|
||||
if tableValid then
|
||||
self:HandleEvent(EVENTS.Birth)
|
||||
function self:OnEventBirth(eventdata)
|
||||
if self.Debug then UTILS.PrintTableToLog(eventdata) end
|
||||
for _,entry in pairs(client) do
|
||||
if eventdata.IniCategory and eventdata.IniCategory <= 1 then
|
||||
if (eventdata.IniCategory == 0 or eventdata.IniCategory == 1) and eventdata.IniPlayerName
|
||||
and (not self.FilterCoalition or self.FilterCoalition == eventdata.IniCoalition)
|
||||
and (not self.FilterCategory or self.FilterCategory == eventdata.IniCategory) then
|
||||
if string.match(eventdata.IniUnitName,entry) or string.match(eventdata.IniGroupName,entry) then
|
||||
local clientObject = CLIENTWATCHTools:_newClient(eventdata)
|
||||
self:Spawn(clientObject)
|
||||
if self.Debug then
|
||||
self:I({"Client spawned in.",IniCategory = eventdata.IniCategory})
|
||||
end
|
||||
local clientWatchDebug = self.Debug
|
||||
local clientObject = CLIENTWATCHTools:_newClient(clientWatchDebug,eventdata)
|
||||
self:Spawn(clientObject,eventdata)
|
||||
break
|
||||
end
|
||||
end
|
||||
@@ -201,15 +238,21 @@ function CLIENTWATCH:New(client)
|
||||
end
|
||||
end
|
||||
else
|
||||
if self.Debug then self:I({"New client instance created. ClientType = Single Prefix",client}) end
|
||||
|
||||
--SOLO STRING
|
||||
self:HandleEvent(EVENTS.Birth)
|
||||
function self:OnEventBirth(eventdata)
|
||||
if self.Debug then UTILS.PrintTableToLog(eventdata) end
|
||||
if eventdata.IniCategory and eventdata.IniCategory <= 1 then
|
||||
if (eventdata.IniCategory == 0 or eventdata.IniCategory == 1) and eventdata.IniPlayerName
|
||||
and (not self.FilterCoalition or self.FilterCoalition == eventdata.IniCoalition)
|
||||
and (not self.FilterCategory or self.FilterCategory == eventdata.IniCategory) then
|
||||
if string.match(eventdata.IniUnitName,client) or string.match(eventdata.IniGroupName,client) then
|
||||
local clientObject = CLIENTWATCHTools:_newClient(eventdata)
|
||||
self:Spawn(clientObject)
|
||||
if self.Debug then
|
||||
self:I({"Client spawned in.",IniCategory = eventdata.IniCategory})
|
||||
end
|
||||
local clientWatchDebug = self.Debug
|
||||
local clientObject = CLIENTWATCHTools:_newClient(clientWatchDebug,eventdata)
|
||||
self:Spawn(clientObject,eventdata)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -222,13 +265,41 @@ function CLIENTWATCH:New(client)
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Filter out all clients not belonging to the provided coalition
|
||||
-- @param #CLIENTWATCH self
|
||||
-- @param #number Coalition number (1 = red, 2 = blue)
|
||||
-- @param #string Coalition string ('red' or 'blue')
|
||||
function CLIENTWATCH:FilterByCoalition(value)
|
||||
if value == 1 or value == "red" then
|
||||
self.FilterCoalition = 1
|
||||
else
|
||||
self.FilterCoalition = 2
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Filter out all clients that are not of the given category
|
||||
-- @param #CLIENTWATCH self
|
||||
-- @param #number Category number (0 = airplane, 1 = helicopter)
|
||||
-- @param #string Category string ('airplane' or 'helicopter')
|
||||
function CLIENTWATCH:FilterByCategory(value)
|
||||
if value == 1 or value == "helicopter" then
|
||||
self.FilterCategory = 1
|
||||
else
|
||||
self.FilterCategory = 0
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Internal function for creating a new client on birth. Do not use!!!.
|
||||
-- @param #CLIENTWATCHTools self
|
||||
-- @param #EVENTS.Birth EventData
|
||||
-- @return #CLIENTWATCHTools self
|
||||
function CLIENTWATCHTools:_newClient(eventdata)
|
||||
function CLIENTWATCHTools:_newClient(clientWatchDebug,eventdata)
|
||||
--Init FSM
|
||||
local self=BASE:Inherit(self, FSM:New())
|
||||
self:SetStartState( "Alive" )
|
||||
@@ -299,78 +370,130 @@ function CLIENTWATCHTools:_newClient(eventdata)
|
||||
|
||||
function self:OnEventHit(EventData)
|
||||
if EventData.TgtUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered hit event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:Hit(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventKill(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered kill event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:Kill(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventScore(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered score event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:Score(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventShot(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered shot event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:Shot(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventShootingStart(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered shooting start event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:ShootingStart(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventShootingEnd(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered shooting end event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:ShootingEnd(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventLand(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered land event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:Land(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventTakeoff(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered takeoff event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:Takeoff(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventRunwayTakeoff(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered runway takeoff event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:RunwayTakeoff(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventRunwayTouch(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered runway touch event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:RunwayTouch(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventRefueling(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered refueling event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:Refueling(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventRefuelingStop(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered refueling event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:RefuelingStop(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventPlayerLeaveUnit(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered leave unit event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:PlayerLeaveUnit(EventData)
|
||||
self._deadRoutine()
|
||||
end
|
||||
@@ -378,6 +501,10 @@ function CLIENTWATCHTools:_newClient(eventdata)
|
||||
|
||||
function self:OnEventCrash(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered crash event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:Crash(EventData)
|
||||
self._deadRoutine()
|
||||
end
|
||||
@@ -385,6 +512,10 @@ function CLIENTWATCHTools:_newClient(eventdata)
|
||||
|
||||
function self:OnEventDead(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered dead event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:Dead(EventData)
|
||||
self._deadRoutine()
|
||||
end
|
||||
@@ -392,6 +523,10 @@ function CLIENTWATCHTools:_newClient(eventdata)
|
||||
|
||||
function self:OnEventPilotDead(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered pilot dead event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:PilotDead(EventData)
|
||||
self._deadRoutine()
|
||||
end
|
||||
@@ -399,6 +534,10 @@ function CLIENTWATCHTools:_newClient(eventdata)
|
||||
|
||||
function self:OnEventUnitLost(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered unit lost event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:UnitLost(EventData)
|
||||
self._deadRoutine()
|
||||
end
|
||||
@@ -406,6 +545,10 @@ function CLIENTWATCHTools:_newClient(eventdata)
|
||||
|
||||
function self:OnEventEjection(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered ejection event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:Ejection(EventData)
|
||||
self._deadRoutine()
|
||||
end
|
||||
@@ -413,6 +556,10 @@ function CLIENTWATCHTools:_newClient(eventdata)
|
||||
|
||||
function self:OnEventHumanFailure(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered human failure event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:HumanFailure(EventData)
|
||||
if not self.Unit:IsAlive() then
|
||||
self._deadRoutine()
|
||||
@@ -422,42 +569,70 @@ function CLIENTWATCHTools:_newClient(eventdata)
|
||||
|
||||
function self:OnEventHumanAircraftRepairFinish(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered repair finished event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:HumanAircraftRepairFinish(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventHumanAircraftRepairStart(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered repair start event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:HumanAircraftRepairStart(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventEngineShutdown(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered engine shutdown event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:EngineShutdown(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventEngineStartup(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered engine startup event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:EngineStartup(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventWeaponAdd(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered weapon add event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:WeaponAdd(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventWeaponDrop(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered weapon drop event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:WeaponDrop(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventWeaponRearm(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered weapon rearm event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:WeaponRearm(EventData)
|
||||
end
|
||||
end
|
||||
@@ -466,6 +641,9 @@ function CLIENTWATCHTools:_newClient(eventdata)
|
||||
--Fallback timer
|
||||
self.FallbackTimer = TIMER:New(function()
|
||||
if not self.Unit:IsAlive() then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client is registered as dead without an event trigger. Running fallback dead routine.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
end
|
||||
self._deadRoutine()
|
||||
end
|
||||
end)
|
||||
@@ -473,6 +651,7 @@ function CLIENTWATCHTools:_newClient(eventdata)
|
||||
|
||||
--Stop event handlers and trigger Despawn
|
||||
function self._deadRoutine()
|
||||
if clientWatchDebug then self:I({"Client dead routine triggered. Shutting down tracking...",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName}) end
|
||||
self:UnHandleEvent( EVENTS.Hit )
|
||||
self:UnHandleEvent( EVENTS.Kill )
|
||||
self:UnHandleEvent( EVENTS.Score )
|
||||
@@ -503,6 +682,6 @@ function CLIENTWATCHTools:_newClient(eventdata)
|
||||
self:Despawn()
|
||||
end
|
||||
|
||||
self:I({"CLIENT SPAWN EVENT", PlayerName = self.PlayerName, UnitName = self.UnitName, GroupName = self.GroupName})
|
||||
self:I({"Detected client spawn and applied internal functions and events.", PlayerName = self.PlayerName, UnitName = self.UnitName, GroupName = self.GroupName})
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -595,7 +595,8 @@ do -- DETECTION_BASE
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
@@ -615,7 +616,7 @@ do -- DETECTION_BASE
|
||||
self:T( { "DetectionGroup is Alive", Detection:GetName() } )
|
||||
|
||||
local DetectionGroupName = Detection:GetName()
|
||||
local DetectionUnit = Detection:GetUnit( 1 )
|
||||
local DetectionUnit = Detection:GetFirstUnitAlive()
|
||||
|
||||
local DetectedUnits = {}
|
||||
|
||||
@@ -632,26 +633,26 @@ do -- DETECTION_BASE
|
||||
--self:T(UTILS.PrintTableToLog(DetectedTargets))
|
||||
|
||||
|
||||
for DetectionObjectID, Detection in pairs( DetectedTargets ) do
|
||||
for DetectionObjectID, Detection in pairs( DetectedTargets or {}) do
|
||||
local DetectedObject = Detection.object -- DCS#Object
|
||||
|
||||
if DetectedObject and DetectedObject:isExist() and DetectedObject.id_ < 50000000 then -- and ( DetectedObject:getCategory() == Object.Category.UNIT or DetectedObject:getCategory() == Object.Category.STATIC ) then
|
||||
local DetectedObjectName = DetectedObject:getName()
|
||||
if not self.DetectedObjects[DetectedObjectName] then
|
||||
self.DetectedObjects[DetectedObjectName] = self.DetectedObjects[DetectedObjectName] or {}
|
||||
self.DetectedObjects[DetectedObjectName].Name = DetectedObjectName
|
||||
self.DetectedObjects[DetectedObjectName].Name = DetectedObjectName
|
||||
self.DetectedObjects[DetectedObjectName].Object = DetectedObject
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for DetectionObjectName, DetectedObjectData in pairs( self.DetectedObjects ) do
|
||||
for DetectionObjectName, DetectedObjectData in pairs( self.DetectedObjects or {}) do
|
||||
|
||||
local DetectedObject = DetectedObjectData.Object
|
||||
|
||||
if DetectedObject:isExist() then
|
||||
|
||||
local TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity = DetectionUnit:IsTargetDetected(
|
||||
local TargetIsDetected, TargetIsVisible, TargetKnowType, TargetKnowDistance, TargetLastTime, TargetLastPos, TargetLastVelocity = DetectionUnit:IsTargetDetected(
|
||||
DetectedObject,
|
||||
self.DetectVisual,
|
||||
self.DetectOptical,
|
||||
|
||||
@@ -1154,8 +1154,6 @@ function ESCORT:_ReportTargetsScheduler()
|
||||
|
||||
if self.EscortGroup:IsAlive() and self.EscortClient:IsAlive() then
|
||||
|
||||
if true then
|
||||
|
||||
local EscortGroupName = self.EscortGroup:GetName()
|
||||
|
||||
self.EscortMenuAttackNearbyTargets:RemoveSubMenus()
|
||||
@@ -1226,177 +1224,6 @@ function ESCORT:_ReportTargetsScheduler()
|
||||
end
|
||||
|
||||
return true
|
||||
else
|
||||
-- local EscortGroupName = self.EscortGroup:GetName()
|
||||
-- local EscortTargets = self.EscortGroup:GetDetectedTargets()
|
||||
--
|
||||
-- local ClientEscortTargets = self.EscortClient._EscortGroups[EscortGroupName].Targets
|
||||
--
|
||||
-- local EscortTargetMessages = ""
|
||||
-- for EscortTargetID, EscortTarget in pairs( EscortTargets ) do
|
||||
-- local EscortObject = EscortTarget.object
|
||||
-- self:T( EscortObject )
|
||||
-- if EscortObject and EscortObject:isExist() and EscortObject.id_ < 50000000 then
|
||||
--
|
||||
-- local EscortTargetUnit = UNIT:Find( EscortObject )
|
||||
-- local EscortTargetUnitName = EscortTargetUnit:GetName()
|
||||
--
|
||||
--
|
||||
--
|
||||
-- -- local EscortTargetIsDetected,
|
||||
-- -- EscortTargetIsVisible,
|
||||
-- -- EscortTargetLastTime,
|
||||
-- -- EscortTargetKnowType,
|
||||
-- -- EscortTargetKnowDistance,
|
||||
-- -- EscortTargetLastPos,
|
||||
-- -- EscortTargetLastVelocity
|
||||
-- -- = self.EscortGroup:IsTargetDetected( EscortObject )
|
||||
-- --
|
||||
-- -- self:T( { EscortTargetIsDetected,
|
||||
-- -- EscortTargetIsVisible,
|
||||
-- -- EscortTargetLastTime,
|
||||
-- -- EscortTargetKnowType,
|
||||
-- -- EscortTargetKnowDistance,
|
||||
-- -- EscortTargetLastPos,
|
||||
-- -- EscortTargetLastVelocity } )
|
||||
--
|
||||
--
|
||||
-- local EscortTargetUnitVec3 = EscortTargetUnit:GetVec3()
|
||||
-- local EscortVec3 = self.EscortGroup:GetVec3()
|
||||
-- local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 +
|
||||
-- ( EscortTargetUnitVec3.y - EscortVec3.y )^2 +
|
||||
-- ( EscortTargetUnitVec3.z - EscortVec3.z )^2
|
||||
-- ) ^ 0.5 / 1000
|
||||
--
|
||||
-- self:T( { self.EscortGroup:GetName(), EscortTargetUnit:GetName(), Distance, EscortTarget } )
|
||||
--
|
||||
-- if Distance <= 15 then
|
||||
--
|
||||
-- if not ClientEscortTargets[EscortTargetUnitName] then
|
||||
-- ClientEscortTargets[EscortTargetUnitName] = {}
|
||||
-- end
|
||||
-- ClientEscortTargets[EscortTargetUnitName].AttackUnit = EscortTargetUnit
|
||||
-- ClientEscortTargets[EscortTargetUnitName].visible = EscortTarget.visible
|
||||
-- ClientEscortTargets[EscortTargetUnitName].type = EscortTarget.type
|
||||
-- ClientEscortTargets[EscortTargetUnitName].distance = EscortTarget.distance
|
||||
-- else
|
||||
-- if ClientEscortTargets[EscortTargetUnitName] then
|
||||
-- ClientEscortTargets[EscortTargetUnitName] = nil
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
--
|
||||
-- self:T( { "Sorting Targets Table:", ClientEscortTargets } )
|
||||
-- table.sort( ClientEscortTargets, function( a, b ) return a.Distance < b.Distance end )
|
||||
-- self:T( { "Sorted Targets Table:", ClientEscortTargets } )
|
||||
--
|
||||
-- -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup.
|
||||
-- self.EscortMenuAttackNearbyTargets:RemoveSubMenus()
|
||||
--
|
||||
-- if self.EscortMenuTargetAssistance then
|
||||
-- self.EscortMenuTargetAssistance:RemoveSubMenus()
|
||||
-- end
|
||||
--
|
||||
-- --for MenuIndex = 1, #self.EscortMenuAttackTargets do
|
||||
-- -- self:T( { "Remove Menu:", self.EscortMenuAttackTargets[MenuIndex] } )
|
||||
-- -- self.EscortMenuAttackTargets[MenuIndex] = self.EscortMenuAttackTargets[MenuIndex]:Remove()
|
||||
-- --end
|
||||
--
|
||||
--
|
||||
-- if ClientEscortTargets then
|
||||
-- for ClientEscortTargetUnitName, ClientEscortTargetData in pairs( ClientEscortTargets ) do
|
||||
--
|
||||
-- for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do
|
||||
--
|
||||
-- if ClientEscortTargetData and ClientEscortTargetData.AttackUnit:IsAlive() then
|
||||
--
|
||||
-- local EscortTargetMessage = ""
|
||||
-- local EscortTargetCategoryName = ClientEscortTargetData.AttackUnit:GetCategoryName()
|
||||
-- local EscortTargetCategoryType = ClientEscortTargetData.AttackUnit:GetTypeName()
|
||||
-- if ClientEscortTargetData.type then
|
||||
-- EscortTargetMessage = EscortTargetMessage .. EscortTargetCategoryName .. " (" .. EscortTargetCategoryType .. ") at "
|
||||
-- else
|
||||
-- EscortTargetMessage = EscortTargetMessage .. "Unknown target at "
|
||||
-- end
|
||||
--
|
||||
-- local EscortTargetUnitVec3 = ClientEscortTargetData.AttackUnit:GetVec3()
|
||||
-- local EscortVec3 = self.EscortGroup:GetVec3()
|
||||
-- local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 +
|
||||
-- ( EscortTargetUnitVec3.y - EscortVec3.y )^2 +
|
||||
-- ( EscortTargetUnitVec3.z - EscortVec3.z )^2
|
||||
-- ) ^ 0.5 / 1000
|
||||
--
|
||||
-- self:T( { self.EscortGroup:GetName(), ClientEscortTargetData.AttackUnit:GetName(), Distance, ClientEscortTargetData.AttackUnit } )
|
||||
-- if ClientEscortTargetData.visible == false then
|
||||
-- EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " estimated km"
|
||||
-- else
|
||||
-- EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " km"
|
||||
-- end
|
||||
--
|
||||
-- if ClientEscortTargetData.visible then
|
||||
-- EscortTargetMessage = EscortTargetMessage .. ", visual"
|
||||
-- end
|
||||
--
|
||||
-- if ClientEscortGroupName == EscortGroupName then
|
||||
--
|
||||
-- MENU_GROUP_COMMAND:New( self.EscortClient,
|
||||
-- EscortTargetMessage,
|
||||
-- self.EscortMenuAttackNearbyTargets,
|
||||
-- ESCORT._AttackTarget,
|
||||
-- { ParamSelf = self,
|
||||
-- ParamUnit = ClientEscortTargetData.AttackUnit
|
||||
-- }
|
||||
-- )
|
||||
-- EscortTargetMessages = EscortTargetMessages .. "\n - " .. EscortTargetMessage
|
||||
-- else
|
||||
-- if self.EscortMenuTargetAssistance then
|
||||
-- local MenuTargetAssistance = MENU_GROUP:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance )
|
||||
-- MENU_GROUP_COMMAND:New( self.EscortClient,
|
||||
-- EscortTargetMessage,
|
||||
-- MenuTargetAssistance,
|
||||
-- ESCORT._AssistTarget,
|
||||
-- self,
|
||||
-- EscortGroupData.EscortGroup,
|
||||
-- ClientEscortTargetData.AttackUnit
|
||||
-- )
|
||||
-- end
|
||||
-- end
|
||||
-- else
|
||||
-- ClientEscortTargetData = nil
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
--
|
||||
-- if EscortTargetMessages ~= "" and self.ReportTargets == true then
|
||||
-- self.EscortGroup:MessageToClient( "Detected targets within 15 km range:" .. EscortTargetMessages:gsub("\n$",""), 20, self.EscortClient )
|
||||
-- else
|
||||
-- self.EscortGroup:MessageToClient( "No targets detected!", 20, self.EscortClient )
|
||||
-- end
|
||||
-- end
|
||||
--
|
||||
-- if self.EscortMenuResumeMission then
|
||||
-- self.EscortMenuResumeMission:RemoveSubMenus()
|
||||
--
|
||||
-- -- if self.EscortMenuResumeWayPoints then
|
||||
-- -- for MenuIndex = 1, #self.EscortMenuResumeWayPoints do
|
||||
-- -- self:T( { "Remove Menu:", self.EscortMenuResumeWayPoints[MenuIndex] } )
|
||||
-- -- self.EscortMenuResumeWayPoints[MenuIndex] = self.EscortMenuResumeWayPoints[MenuIndex]:Remove()
|
||||
-- -- end
|
||||
-- -- end
|
||||
--
|
||||
-- local TaskPoints = self:RegisterRoute()
|
||||
-- for WayPointID, WayPoint in pairs( TaskPoints ) do
|
||||
-- local EscortVec3 = self.EscortGroup:GetVec3()
|
||||
-- local Distance = ( ( WayPoint.x - EscortVec3.x )^2 +
|
||||
-- ( WayPoint.y - EscortVec3.z )^2
|
||||
-- ) ^ 0.5 / 1000
|
||||
-- MENU_GROUP_COMMAND:New( self.EscortClient, "Waypoint " .. WayPointID .. " at " .. string.format( "%.2f", Distance ).. "km", self.EscortMenuResumeMission, ESCORT._ResumeMission, { ParamSelf = self, ParamWayPoint = WayPointID } )
|
||||
-- end
|
||||
-- end
|
||||
--
|
||||
-- return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
|
||||
@@ -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
|
||||
@@ -1060,7 +1060,7 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
|
||||
|
||||
-- Tracking info and init of last bomb position.
|
||||
local text=string.format("FOX: Tracking missile %s(%s) - target %s - shooter %s", missile.missileType, missile.missileName, tostring(missile.targetName), missile.shooterName)
|
||||
self:I(FOX.lid..text)
|
||||
self:T(FOX.lid..text)
|
||||
MESSAGE:New(text, 10):ToAllIf(self.Debug)
|
||||
|
||||
-- Loop over players.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
|
||||
@@ -3814,15 +3814,20 @@ function RAT:Status(message, forID)
|
||||
local N0units=group:GetInitialSize()
|
||||
|
||||
-- Monitor travelled distance since last check.
|
||||
local Pnow=coords
|
||||
local Dtravel=Pnow:Get2DDistance(ratcraft.Pnow)
|
||||
ratcraft.Pnow=Pnow
|
||||
local Dtravel=0
|
||||
if coords and ratcraft.Pnow then
|
||||
local Dtravel=coords:Get2DDistance(ratcraft.Pnow)
|
||||
ratcraft.Pnow=coords
|
||||
end
|
||||
|
||||
-- Add up the travelled distance.
|
||||
ratcraft.Distance=ratcraft.Distance+Dtravel
|
||||
|
||||
-- Distance remaining to destination.
|
||||
local Ddestination=Pnow:Get2DDistance(ratcraft.destination:GetCoordinate())
|
||||
local Ddestination=-1
|
||||
if ratcraft.Pnow then
|
||||
Ddestination=ratcraft.Pnow:Get2DDistance(ratcraft.destination:GetCoordinate())
|
||||
end
|
||||
|
||||
-- Status report.
|
||||
if (forID and spawnindex==forID) or (not forID) then
|
||||
@@ -5579,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
|
||||
@@ -5598,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
|
||||
@@ -5609,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
|
||||
@@ -5644,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
|
||||
|
||||
@@ -5706,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)
|
||||
@@ -5767,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,"^(.+)#")
|
||||
@@ -5794,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
|
||||
|
||||
@@ -5818,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,"^(.+)#")
|
||||
|
||||
@@ -107,6 +107,8 @@
|
||||
-- @field Sound.SRS#MSRSQUEUE instructsrsQ SRS queue for range instructor.
|
||||
-- @field #number Coalition Coalition side for the menu, if any.
|
||||
-- @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
|
||||
@@ -273,6 +275,10 @@
|
||||
-- -- Create a range object.
|
||||
-- GoldwaterRange=RANGE:New("Goldwater Range")
|
||||
--
|
||||
-- -- Set and enable the range ceiling altitude in feet MSL. If aircraft are above this altitude they are not considered to be in the range.
|
||||
-- GoldwaterRange:SetRangeCeiling(20000)
|
||||
-- GoldwaterRange:EnableRangeCeiling(true)
|
||||
--
|
||||
-- -- Distance between strafe target and foul line. You have to specify the names of the unit or static objects.
|
||||
-- -- Note that this could also be done manually by simply measuring the distance between the target and the foul line in the ME.
|
||||
-- GoldwaterRange:GetFoullineDistance("GWR Strafe Pit Left 1", "GWR Foul Line Left")
|
||||
@@ -358,6 +364,8 @@ RANGE = {
|
||||
targetpath = nil,
|
||||
targetprefix = nil,
|
||||
Coalition = nil,
|
||||
ceilingaltitude = 20000,
|
||||
ceilingenabled = false,
|
||||
}
|
||||
|
||||
--- Default range parameters.
|
||||
@@ -594,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.
|
||||
@@ -1085,6 +1093,37 @@ function RANGE:SetRangeZone( zone )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set range ceiling altitude in feet MSL.
|
||||
-- @param #RANGE self
|
||||
-- @param #number altitude (optional) Ceiling altitude of the range in ft MSL. Default 20000ft MSL
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetRangeCeiling( altitude )
|
||||
self:T(self.lid.."SetRangeCeiling")
|
||||
if altitude and type(altitude) == "number" then
|
||||
self.ceilingaltitude=altitude
|
||||
else
|
||||
self:E(self.lid.."Altitude either not provided or is not a number, using default setting (20000).")
|
||||
self.ceilingaltitude=20000
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Enable range ceiling. Aircraft must be below the ceiling altitude to be considered in the range zone.
|
||||
-- @param #RANGE self
|
||||
-- @param #boolean enabled True if you would like to enable the ceiling check. If no value give, will Default to false.
|
||||
-- @return #RANGE self
|
||||
function RANGE:EnableRangeCeiling( enabled )
|
||||
self:T(self.lid.."EnableRangeCeiling")
|
||||
if enabled and type(enabled) == "boolean" then
|
||||
self.ceilingenabled=enabled
|
||||
else
|
||||
self:E(self.lid.."Enabled either not provide or is not a boolean, using default setting (false).")
|
||||
self.ceilingenabled=false
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set smoke color for marking bomb targets. By default bomb targets are marked by red smoke.
|
||||
-- @param #RANGE self
|
||||
-- @param Utilities.Utils#SMOKECOLOR colorid Color id. Default `SMOKECOLOR.Red`.
|
||||
@@ -1231,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)
|
||||
@@ -1238,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
|
||||
@@ -1276,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
|
||||
@@ -1305,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
|
||||
@@ -1340,7 +1394,7 @@ end
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetSoundfilesPath( path )
|
||||
self.soundpath = tostring( path or "Range Soundfiles/" )
|
||||
self:I( self.lid .. string.format( "Setting sound files path to %s", self.soundpath ) )
|
||||
self:T2( self.lid .. string.format( "Setting sound files path to %s", self.soundpath ) )
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1636,9 +1690,9 @@ function RANGE:AddBombingTargetUnit( unit, goodhitrange, randommove )
|
||||
|
||||
-- Debug or error output.
|
||||
if _isstatic == true then
|
||||
self:I( self.lid .. string.format( "Adding STATIC bombing target %s with good hit range %d. Random move = %s.", name, goodhitrange, tostring( randommove ) ) )
|
||||
self:T( self.lid .. string.format( "Adding STATIC bombing target %s with good hit range %d. Random move = %s.", name, goodhitrange, tostring( randommove ) ) )
|
||||
elseif _isstatic == false then
|
||||
self:I( self.lid .. string.format( "Adding UNIT bombing target %s with good hit range %d. Random move = %s.", name, goodhitrange, tostring( randommove ) ) )
|
||||
self:T( self.lid .. string.format( "Adding UNIT bombing target %s with good hit range %d. Random move = %s.", name, goodhitrange, tostring( randommove ) ) )
|
||||
else
|
||||
self:E( self.lid .. string.format( "ERROR! No bombing target with name %s could be found. Carefully check all UNIT and STATIC names defined in the mission editor!", name ) )
|
||||
end
|
||||
@@ -1706,7 +1760,7 @@ function RANGE:AddBombingTargetScenery( scenery, goodhitrange)
|
||||
|
||||
-- Debug or error output.
|
||||
if name then
|
||||
self:I( self.lid .. string.format( "Adding SCENERY bombing target %s with good hit range %d", name, goodhitrange) )
|
||||
self:T( self.lid .. string.format( "Adding SCENERY bombing target %s with good hit range %d", name, goodhitrange) )
|
||||
else
|
||||
self:E( self.lid .. string.format( "ERROR! No bombing target with name %s could be found!", name ) )
|
||||
end
|
||||
@@ -1811,7 +1865,7 @@ function RANGE:OnEventBirth( EventData )
|
||||
if not EventData.IniPlayerName then return end
|
||||
|
||||
local _unitName = EventData.IniUnitName
|
||||
local _unit, _playername = self:_GetPlayerUnitAndName( _unitName )
|
||||
local _unit, _playername = self:_GetPlayerUnitAndName( _unitName, EventData.IniPlayerName )
|
||||
|
||||
self:T3( self.lid .. "BIRTH: unit = " .. tostring( EventData.IniUnitName ) )
|
||||
self:T3( self.lid .. "BIRTH: group = " .. tostring( EventData.IniGroupName ) )
|
||||
@@ -1893,7 +1947,7 @@ function RANGE:OnEventHit( EventData )
|
||||
local _currentTarget = self.strafeStatus[_unitID] --#RANGE.StrafeStatus
|
||||
|
||||
-- Player has rolled in on a strafing target.
|
||||
if _currentTarget and target:IsAlive() then
|
||||
if _currentTarget and target and target:IsAlive() then
|
||||
|
||||
local playerPos = _unit:GetCoordinate()
|
||||
local targetPos = target:GetCoordinate()
|
||||
@@ -1967,7 +2021,9 @@ end
|
||||
-- @param #number attackAlt Attack altitude.
|
||||
-- @param #number attackVel Attack velocity.
|
||||
function RANGE._OnImpact(weapon, self, playerData, attackHdg, attackAlt, attackVel)
|
||||
|
||||
|
||||
if not playerData then return end
|
||||
|
||||
-- Get closet target to last position.
|
||||
local _closetTarget = nil -- #RANGE.BombTarget
|
||||
local _distance = nil
|
||||
@@ -1984,16 +2040,16 @@ function RANGE._OnImpact(weapon, self, playerData, attackHdg, attackAlt, attackV
|
||||
-- Coordinate of impact point.
|
||||
local impactcoord = weapon:GetImpactCoordinate()
|
||||
|
||||
-- Check if impact happened in range zone.
|
||||
-- Check if impact happened in range zone.+
|
||||
local insidezone = self.rangezone:IsCoordinateInZone( impactcoord )
|
||||
|
||||
|
||||
-- Smoke impact point of bomb.
|
||||
if playerData.smokebombimpact and insidezone then
|
||||
if playerData and playerData.smokebombimpact and insidezone then
|
||||
if playerData.delaysmoke then
|
||||
timer.scheduleFunction( self._DelayedSmoke, { coord = impactcoord, color = playerData.smokecolor }, timer.getTime() + self.TdelaySmoke )
|
||||
impactcoord:Smoke(playerData.smokecolor, 30, self.TdelaySmoke)
|
||||
else
|
||||
impactcoord:Smoke( playerData.smokecolor )
|
||||
impactcoord:Smoke(playerData.smokecolor, 30)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2060,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 )
|
||||
@@ -2115,7 +2176,7 @@ function RANGE:OnEventShot( EventData )
|
||||
local _unitName = EventData.IniUnitName
|
||||
|
||||
-- Get player unit and name.
|
||||
local _unit, _playername = self:_GetPlayerUnitAndName( _unitName )
|
||||
local _unit, _playername = self:_GetPlayerUnitAndName( _unitName, EventData.IniPlayerName )
|
||||
|
||||
-- Distance Player-to-Range. Set this to larger value than the threshold.
|
||||
local dPR = self.BombtrackThreshold * 2
|
||||
@@ -2127,11 +2188,13 @@ function RANGE:OnEventShot( EventData )
|
||||
end
|
||||
|
||||
-- Only track if distance player to range is < 25 km. Also check that a player shot. No need to track AI weapons.
|
||||
if _track and dPR <= self.BombtrackThreshold and _unit and _playername then
|
||||
if _track and dPR <= self.BombtrackThreshold and _unit and _playername and self.PlayerSettings[_playername] then
|
||||
|
||||
-- Player data.
|
||||
local playerData = self.PlayerSettings[_playername] -- #RANGE.PlayerData
|
||||
|
||||
|
||||
if not playerData then return end
|
||||
|
||||
-- Attack parameters.
|
||||
local attackHdg=_unit:GetHeading()
|
||||
local attackAlt=_unit:GetHeight()
|
||||
@@ -2192,7 +2255,7 @@ function RANGE:onafterStatus( From, Event, To )
|
||||
end
|
||||
|
||||
-- Check range status.
|
||||
self:I( self.lid .. text )
|
||||
self:T( self.lid .. text )
|
||||
|
||||
end
|
||||
|
||||
@@ -2393,7 +2456,7 @@ function RANGE:onafterSave( From, Event, To )
|
||||
if f then
|
||||
f:write( data )
|
||||
f:close()
|
||||
self:I( self.lid .. string.format( "Saving player results to file %s", tostring( filename ) ) )
|
||||
self:T( self.lid .. string.format( "Saving player results to file %s", tostring( filename ) ) )
|
||||
else
|
||||
self:E( self.lid .. string.format( "ERROR: Could not save results to file %s", tostring( filename ) ) )
|
||||
end
|
||||
@@ -2472,7 +2535,7 @@ function RANGE:onafterLoad( From, Event, To )
|
||||
|
||||
-- Info message.
|
||||
local text = string.format( "Loading player bomb results from file %s", filename )
|
||||
self:I( self.lid .. text )
|
||||
self:T( self.lid .. text )
|
||||
|
||||
-- Load asset data from file.
|
||||
local data = _loadfile( filename )
|
||||
@@ -2591,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.
|
||||
@@ -2845,7 +2901,7 @@ function RANGE:_DisplayRangeInfo( _unitname )
|
||||
|
||||
-- Check if we have a player.
|
||||
if unit and playername then
|
||||
self:I(playername)
|
||||
--self:I(playername)
|
||||
-- Message text.
|
||||
local text = ""
|
||||
|
||||
@@ -2983,7 +3039,7 @@ function RANGE:_DisplayBombTargets( _unitname )
|
||||
end
|
||||
end
|
||||
|
||||
self:_DisplayMessageToGroup( _unit, _text, 120, true, true, _multiplayer )
|
||||
self:_DisplayMessageToGroup( _unit, _text, 150, true, true, _multiplayer )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3108,7 +3164,10 @@ function RANGE:_CheckPlayers()
|
||||
|
||||
if unit and unit:IsAlive() then
|
||||
|
||||
if unit:IsInZone( self.rangezone ) then
|
||||
local unitalt = unit:GetAltitude(false)
|
||||
local unitaltinfeet = UTILS.MetersToFeet(unitalt)
|
||||
|
||||
if unit:IsInZone(self.rangezone) and (not self.ceilingenabled or unitaltinfeet < self.ceilingaltitude) then
|
||||
|
||||
------------------------------
|
||||
-- Player INSIDE Range Zone --
|
||||
@@ -3449,10 +3508,10 @@ function RANGE:_AddF10Commands( _unitName )
|
||||
-- Range menu
|
||||
local _rangePath = MENU_GROUP:New( group, self.rangename, _rootMenu )
|
||||
|
||||
local _statsPath = MENU_GROUP:New( group, "Statistics", _rangePath )
|
||||
local _markPath = MENU_GROUP:New( group, "Mark Targets", _rangePath )
|
||||
local _settingsPath = MENU_GROUP:New( group, "My Settings", _rangePath )
|
||||
local _infoPath = MENU_GROUP:New( group, "Range Info", _rangePath )
|
||||
local _markPath = MENU_GROUP:New( group, "Mark Targets", _rangePath )
|
||||
local _statsPath = MENU_GROUP:New( group, "Statistics", _rangePath )
|
||||
local _settingsPath = MENU_GROUP:New( group, "My Settings", _rangePath )
|
||||
|
||||
-- F10/On the Range/<Range Name>/My Settings/
|
||||
local _mysmokePath = MENU_GROUP:New( group, "Smoke Color", _settingsPath )
|
||||
@@ -4099,8 +4158,8 @@ end
|
||||
-- @return Wrapper.Unit#UNIT Unit of player.
|
||||
-- @return #string Name of the player.
|
||||
-- @return #boolean If true, group has > 1 player in it
|
||||
function RANGE:_GetPlayerUnitAndName( _unitName )
|
||||
self:F2( _unitName )
|
||||
function RANGE:_GetPlayerUnitAndName( _unitName, PlayerName )
|
||||
--self:I( _unitName )
|
||||
|
||||
if _unitName ~= nil then
|
||||
|
||||
@@ -4109,9 +4168,9 @@ function RANGE:_GetPlayerUnitAndName( _unitName )
|
||||
-- Get DCS unit from its name.
|
||||
local DCSunit = Unit.getByName( _unitName )
|
||||
|
||||
if DCSunit then
|
||||
if DCSunit and DCSunit.getPlayerName then
|
||||
|
||||
local playername = DCSunit:getPlayerName()
|
||||
local playername = DCSunit:getPlayerName() or PlayerName or "None"
|
||||
local unit = UNIT:Find( DCSunit )
|
||||
|
||||
self:T2( { DCSunit = DCSunit, unit = unit, playername = playername } )
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
--
|
||||
-- ### Authors: **applevangelist**, **FlightControl**
|
||||
--
|
||||
-- Last Update: Dec 2023
|
||||
-- Last Update: Dec 2024
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -28,6 +28,16 @@
|
||||
|
||||
---
|
||||
-- @type SEAD
|
||||
-- @field #string ClassName The Class Name.
|
||||
-- @field #table TargetSkill Table of target skills.
|
||||
-- @field #table SEADGroupPrefixes Table of SEAD prefixes.
|
||||
-- @field #table SuppressedGroups Table of currently suppressed groups.
|
||||
-- @field #number EngagementRange Engagement Range.
|
||||
-- @field #number Padding Padding in seconds.
|
||||
-- @field #function CallBack Callback function for suppression plans.
|
||||
-- @field #boolean UseCallBack Switch for callback function to be used.
|
||||
-- @field #boolean debug Debug switch.
|
||||
-- @field #boolen WeaponTrack Track switch, if true track weapon speed for 30 secs.
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- Make SAM sites execute evasive and defensive behaviour when being fired upon.
|
||||
@@ -56,10 +66,11 @@ SEAD = {
|
||||
SEADGroupPrefixes = {},
|
||||
SuppressedGroups = {},
|
||||
EngagementRange = 75, -- default 75% engagement range Feature Request #1355
|
||||
Padding = 10,
|
||||
Padding = 15,
|
||||
CallBack = nil,
|
||||
UseCallBack = false,
|
||||
debug = false,
|
||||
WeaponTrack = false,
|
||||
}
|
||||
|
||||
--- Missile enumerators
|
||||
@@ -69,6 +80,7 @@ SEAD = {
|
||||
["AGM_122"] = "AGM_122",
|
||||
["AGM_84"] = "AGM_84",
|
||||
["AGM_45"] = "AGM_45",
|
||||
["AGM_65"] = "AGM_65",
|
||||
["ALARM"] = "ALARM",
|
||||
["LD-10"] = "LD-10",
|
||||
["X_58"] = "X_58",
|
||||
@@ -88,6 +100,7 @@ SEAD = {
|
||||
-- km and mach
|
||||
["AGM_88"] = { 150, 3},
|
||||
["AGM_45"] = { 12, 2},
|
||||
["AGM_65"] = { 16, 0.9},
|
||||
["AGM_122"] = { 16.5, 2.3},
|
||||
["AGM_84"] = { 280, 0.8},
|
||||
["ALARM"] = { 45, 2},
|
||||
@@ -144,7 +157,7 @@ function SEAD:New( SEADGroupPrefixes, Padding )
|
||||
self:AddTransition("*", "ManageEvasion", "*")
|
||||
self:AddTransition("*", "CalculateHitZone", "*")
|
||||
|
||||
self:I("*** SEAD - Started Version 0.4.6")
|
||||
self:I("*** SEAD - Started Version 0.4.9")
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -371,7 +384,7 @@ function SEAD:onafterManageEvasion(From,Event,To,_targetskill,_targetgroup,SEADP
|
||||
reach = wpndata[1] * 1.1
|
||||
local mach = wpndata[2]
|
||||
wpnspeed = math.floor(mach * 340.29)
|
||||
if Weapon then
|
||||
if Weapon and Weapon:GetSpeed() > 0 then
|
||||
wpnspeed = Weapon:GetSpeed()
|
||||
self:T(string.format("*** SEAD - Weapon Speed from WEAPON: %f m/s",wpnspeed))
|
||||
end
|
||||
@@ -452,29 +465,38 @@ end
|
||||
-- @return #SEAD self
|
||||
function SEAD:HandleEventShot( EventData )
|
||||
self:T( { EventData.id } )
|
||||
local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT
|
||||
local SEADGroup = EventData.IniGroup -- Wrapper.Group#GROUP
|
||||
local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE
|
||||
local SEADUnit = EventData.IniDCSUnit
|
||||
local SEADUnitName = EventData.IniDCSUnitName
|
||||
local SEADWeapon = EventData.Weapon -- Identify the weapon fired
|
||||
local SEADWeaponName = EventData.WeaponName -- return weapon type
|
||||
|
||||
local WeaponWrapper = WEAPON:New(EventData.Weapon)
|
||||
--local SEADWeaponSpeed = WeaponWrapper:GetSpeed() -- mps
|
||||
|
||||
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName)
|
||||
--self:T({ SEADWeapon })
|
||||
|
||||
local SEADWeapon = EventData.Weapon -- Identify the weapon fired
|
||||
local SEADWeaponName = EventData.WeaponName or "None" -- return weapon type
|
||||
|
||||
if self:_CheckHarms(SEADWeaponName) then
|
||||
--UTILS.PrintTableToLog(EventData)
|
||||
local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT
|
||||
|
||||
if not SEADPlane then return self end -- case IniUnit is empty
|
||||
|
||||
local SEADGroup = EventData.IniGroup -- Wrapper.Group#GROUP
|
||||
local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE
|
||||
local SEADUnit = EventData.IniDCSUnit
|
||||
local SEADUnitName = EventData.IniDCSUnitName
|
||||
|
||||
local WeaponWrapper = WEAPON:New(EventData.Weapon) -- Wrapper.Weapon#WEAPON
|
||||
|
||||
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName)
|
||||
|
||||
self:T( '*** SEAD - Weapon Match' )
|
||||
if self.WeaponTrack == true then
|
||||
WeaponWrapper:SetFuncTrack(function(weapon) env.info(string.format("*** Weapon Speed: %d m/s",weapon:GetSpeed() or -1)) end)
|
||||
WeaponWrapper:StartTrack(0.1)
|
||||
WeaponWrapper:StopTrack(30)
|
||||
end
|
||||
local _targetskill = "Random"
|
||||
local _targetgroupname = "none"
|
||||
local _target = EventData.Weapon:getTarget() -- Identify target
|
||||
if not _target or self.debug then -- AGM-88 or 154 w/o target data
|
||||
self:E("***** SEAD - No target data for " .. (SEADWeaponName or "None"))
|
||||
if string.find(SEADWeaponName,"AGM_88",1,true) or string.find(SEADWeaponName,"AGM_154",1,true) then
|
||||
self:I("**** Tracking AGM-88/154 with no target data.")
|
||||
self:T("**** Tracking AGM-88/154 with no target data.")
|
||||
local pos0 = SEADPlane:GetCoordinate()
|
||||
local fheight = SEADPlane:GetHeight()
|
||||
self:__CalculateHitZone(20,SEADWeapon,pos0,fheight,SEADGroup,SEADWeaponName)
|
||||
@@ -520,7 +542,7 @@ function SEAD:HandleEventShot( EventData )
|
||||
end
|
||||
if SEADGroupFound == true then -- yes we are being attacked
|
||||
if string.find(SEADWeaponName,"ADM_141",1,true) then
|
||||
self:__ManageEvasion(2,_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup,0,WeaponWrapper)
|
||||
self:__ManageEvasion(2,_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup,2,WeaponWrapper)
|
||||
else
|
||||
self:ManageEvasion(_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup,0,WeaponWrapper)
|
||||
end
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
-- @image Functional.Shorad.jpg
|
||||
--
|
||||
-- Date: Nov 2021
|
||||
-- Last Update: Nov 2023
|
||||
-- Last Update: Jan 2025
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
--- **SHORAD** class, extends Core.Base#BASE
|
||||
@@ -48,7 +48,9 @@
|
||||
-- @field #number minscootdist Min distance of the next zone
|
||||
-- @field #number maxscootdist Max distance of the next zone
|
||||
-- @field #boolean scootrandomcoord If true, use a random coordinate in the zone and not the center
|
||||
-- @field #string scootformation Formation to take for scooting, e.g. "Vee" or "Cone"
|
||||
-- @field #string scootformation Formation to take for scooting, e.g. "Vee" or "Cone"
|
||||
-- @field #boolean SmokeDecoy = false,
|
||||
-- @field #number SmokeDecoyColor = SMOKECOLOR.White
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
@@ -113,8 +115,10 @@ SHORAD = {
|
||||
SkateNumber = 3,
|
||||
SkateZones = nil,
|
||||
minscootdist = 100,
|
||||
minscootdist = 3000,
|
||||
scootrandomcoord = false,
|
||||
maxscootdist = 3000,
|
||||
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()
|
||||
@@ -443,12 +457,40 @@ do
|
||||
for _,_groups in pairs (shoradset) do
|
||||
local groupname = _groups:GetName()
|
||||
if string.find(groupname, tgtgrp, 1, true) then
|
||||
returnname = true
|
||||
if _groups:IsSAM() then
|
||||
returnname = true
|
||||
end
|
||||
end
|
||||
end
|
||||
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
|
||||
@@ -470,6 +512,7 @@ do
|
||||
-- @param #number Radius Radius of the #ZONE
|
||||
-- @param #number ActiveTimer Number of seconds to stay active
|
||||
-- @param #number TargetCat (optional) Category, i.e. Object.Category.UNIT or Object.Category.STATIC
|
||||
-- @param #boolean ShotAt If true, function is called after a shot
|
||||
-- @return #SHORAD self
|
||||
-- @usage Use this function to integrate with other systems, example
|
||||
--
|
||||
@@ -479,7 +522,7 @@ do
|
||||
-- mymantis = MANTIS:New("BlueMantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")
|
||||
-- mymantis:AddShorad(myshorad,720)
|
||||
-- mymantis:Start()
|
||||
function SHORAD:onafterWakeUpShorad(From, Event, To, TargetGroup, Radius, ActiveTimer, TargetCat)
|
||||
function SHORAD:onafterWakeUpShorad(From, Event, To, TargetGroup, Radius, ActiveTimer, TargetCat, ShotAt)
|
||||
self:T(self.lid .. " WakeUpShorad")
|
||||
self:T({TargetGroup, Radius, ActiveTimer, TargetCat})
|
||||
local targetcat = TargetCat or Object.Category.UNIT
|
||||
@@ -521,7 +564,27 @@ do
|
||||
-- go through set and find the one(s) to activate
|
||||
local TDiff = 4
|
||||
for _,_group in pairs (shoradset) do
|
||||
if _group:IsAnyInZone(targetzone) then
|
||||
|
||||
local groupname = _group:GetName()
|
||||
|
||||
if groupname == TargetGroup and ShotAt==true then
|
||||
-- Shot at a SHORAD group
|
||||
if self.UseEmOnOff then
|
||||
_group:EnableEmission(false)
|
||||
end
|
||||
_group:OptionAlarmStateGreen()
|
||||
self.ActiveGroups[groupname] = nil
|
||||
local text = string.format("Shot at SHORAD %s! Evading!", _group:GetName())
|
||||
self:T(text)
|
||||
local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
|
||||
self:_SmokeUnits(_group)
|
||||
--Shoot and Scoot
|
||||
if self.shootandscoot then
|
||||
self:__ShootAndScoot(1,_group)
|
||||
end
|
||||
|
||||
elseif _group:IsAnyInZone(targetzone) or groupname == TargetGroup then
|
||||
-- shot at a group we protect
|
||||
local text = string.format("Waking up SHORAD %s", _group:GetName())
|
||||
self:T(text)
|
||||
local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
|
||||
@@ -529,7 +592,7 @@ do
|
||||
_group:EnableEmission(true)
|
||||
end
|
||||
_group:OptionAlarmStateRed()
|
||||
local groupname = _group:GetName()
|
||||
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
|
||||
@@ -607,7 +670,7 @@ do
|
||||
_targetgroupname = tgtgrp:GetName() -- group name
|
||||
_targetskill = tgtgrp:GetUnit(1):GetSkill()
|
||||
self:T("*** Found Target = ".. _targetgroupname)
|
||||
self:WakeUpShorad(_targetgroupname, self.Radius, self.ActiveTimer, Object.Category.UNIT)
|
||||
self:WakeUpShorad(_targetgroupname, self.Radius, self.ActiveTimer, Object.Category.UNIT,true)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -662,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
|
||||
@@ -736,7 +800,7 @@ do
|
||||
-- if being shot at, find closest SHORADs to activate
|
||||
if shotatsams or shotatus then
|
||||
self:T({shotatsams=shotatsams,shotatus=shotatus})
|
||||
self:WakeUpShorad(targetgroupname, self.Radius, self.ActiveTimer, targetcat)
|
||||
self:WakeUpShorad(targetgroupname, self.Radius, self.ActiveTimer, targetcat, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
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
|
||||
|
||||
@@ -6047,7 +6059,7 @@ function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrol
|
||||
|
||||
else
|
||||
|
||||
if #parking<#template.units and not airstart then
|
||||
if parking and #parking<#template.units and not airstart then
|
||||
local text=string.format("ERROR: Not enough parking! Free parking = %d < %d aircraft to be spawned.", #parking, #template.units)
|
||||
self:_DebugMessage(text)
|
||||
return nil
|
||||
@@ -6089,7 +6101,7 @@ function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrol
|
||||
terminal=parking[i].TerminalID
|
||||
end
|
||||
|
||||
if self.Debug then
|
||||
if self.Debug and terminal then
|
||||
local text=string.format("Spawnplace unit %s terminal %d.", unit.name, terminal)
|
||||
coord:MarkToAll(text)
|
||||
env.info(text)
|
||||
@@ -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
|
||||
|
||||
@@ -6732,7 +6742,7 @@ end
|
||||
-- @param Wrapper.Group#GROUP deadgroup Group of unit that died.
|
||||
-- @param #WAREHOUSE.Pendingitem request Request that needs to be updated.
|
||||
function WAREHOUSE:_UnitDead(deadunit, deadgroup, request)
|
||||
self:F(self.lid.."FF unit dead "..deadunit:GetName())
|
||||
--self:F(self.lid.."FF unit dead "..deadunit:GetName())
|
||||
|
||||
-- Find opsgroup.
|
||||
local opsgroup=_DATABASE:FindOpsGroup(deadgroup)
|
||||
@@ -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
|
||||
@@ -8122,9 +8132,11 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets)
|
||||
-- Debug output for occupied spots.
|
||||
if self.Debug then
|
||||
local coord=problem.coord --Core.Point#COORDINATE
|
||||
local text=string.format("Obstacle %s [type=%s] blocking spot=%d! Size=%.1f m and distance=%.1f m.", problem.name, problem.type, _termid, problem.size, problem.dist)
|
||||
self:I(self.lid..text)
|
||||
coord:MarkToAll(string.format(text))
|
||||
if coord then
|
||||
local text=string.format("Obstacle %s [type=%s] blocking spot=%d! Size=%.1f m and distance=%.1f m.", problem.name, problem.type, _termid, problem.size, problem.dist)
|
||||
self:I(self.lid..text)
|
||||
coord:MarkToAll(text)
|
||||
end
|
||||
else
|
||||
self:T(self.lid..string.format("Parking spot %d is occupied or not big enough!", _termid))
|
||||
end
|
||||
@@ -8433,12 +8445,14 @@ function WAREHOUSE:_GetAttribute(group)
|
||||
local attribute=WAREHOUSE.Attribute.OTHER_UNKNOWN --#WAREHOUSE.Attribute
|
||||
|
||||
if group then
|
||||
|
||||
local groupCat=group:GetCategory()
|
||||
|
||||
-----------
|
||||
--- Air ---
|
||||
-----------
|
||||
-- Planes
|
||||
local transportplane=group:HasAttribute("Transports") and group:HasAttribute("Planes")
|
||||
local transportplane=group:HasAttribute("Transports") and group:HasAttribute("Planes") and groupCat==Group.Category.AIRPLANE
|
||||
local awacs=group:HasAttribute("AWACS")
|
||||
local fighter=group:HasAttribute("Fighters") or group:HasAttribute("Interceptors") or group:HasAttribute("Multirole fighters") or (group:HasAttribute("Bombers") and not group:HasAttribute("Strategic bombers"))
|
||||
local bomber=group:HasAttribute("Strategic bombers")
|
||||
@@ -8583,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
|
||||
@@ -8593,7 +8609,6 @@ end
|
||||
-- @param #WAREHOUSE.Queueitem qitem Item of queue to be removed.
|
||||
-- @param #table queue The queue from which the item should be deleted.
|
||||
function WAREHOUSE:_DeleteQueueItem(qitem, queue)
|
||||
self:F({qitem=qitem, queue=queue})
|
||||
|
||||
for i=1,#queue do
|
||||
local _item=queue[i] --#WAREHOUSE.Queueitem
|
||||
|
||||
@@ -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
|
||||
--
|
||||
|
||||
@@ -10,7 +10,7 @@ _SCHEDULEDISPATCHER = SCHEDULEDISPATCHER:New() -- Core.ScheduleDispatcher#SCHEDU
|
||||
_DATABASE = DATABASE:New() -- Core.Database#DATABASE
|
||||
|
||||
--- Settings
|
||||
_SETTINGS = SETTINGS:Set()
|
||||
_SETTINGS = SETTINGS:Set() -- Core.Settings#SETTINGS
|
||||
_SETTINGS:SetPlayerMenuOn()
|
||||
|
||||
--- Register cargos.
|
||||
@@ -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
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/Enums.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/Utils.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/Profiler.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/Templates.lua' )
|
||||
--__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/STTS.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/FiFo.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Utilities/Socket.lua' )
|
||||
|
||||
@@ -34,6 +32,7 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/MarkerOps_Base.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/TextAndSound.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/Pathline.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/ClientMenu.lua')
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/Vector.lua')
|
||||
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Object.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Identifiable.lua' )
|
||||
@@ -49,6 +48,7 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Marker.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Weapon.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Net.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Storage.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/DynamicCargo.lua' )
|
||||
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Cargo/Cargo.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Cargo/CargoUnit.lua' )
|
||||
@@ -85,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' )
|
||||
@@ -117,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' )
|
||||
@@ -187,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' )
|
||||
@@ -47,6 +48,7 @@ __Moose.Include( 'Wrapper\\Marker.lua' )
|
||||
__Moose.Include( 'Wrapper\\Net.lua' )
|
||||
__Moose.Include( 'Wrapper\\Weapon.lua' )
|
||||
__Moose.Include( 'Wrapper\\Storage.lua' )
|
||||
__Moose.Include( 'Wrapper\\DynamicCargo.lua' )
|
||||
|
||||
__Moose.Include( 'Cargo\\Cargo.lua' )
|
||||
__Moose.Include( 'Cargo\\CargoUnit.lua' )
|
||||
@@ -81,6 +83,7 @@ __Moose.Include( 'Functional\\AICSAR.lua' )
|
||||
__Moose.Include( 'Functional\\AmmoTruck.lua' )
|
||||
__Moose.Include( 'Functional\\Tiresias.lua' )
|
||||
__Moose.Include( 'Functional\\Stratego.lua' )
|
||||
__Moose.Include( 'Functional\\ClientWatch.lua' )
|
||||
|
||||
__Moose.Include( 'Ops\\Airboss.lua' )
|
||||
__Moose.Include( 'Ops\\RecoveryTanker.lua' )
|
||||
@@ -176,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
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -19,7 +19,7 @@
|
||||
-- * Option to present information in imperial or metric units
|
||||
-- * Runway length and airfield elevation (optional)
|
||||
-- * Frequencies/channels of nav aids (ILS, VOR, NDB, TACAN, PRMG, RSBN) (optional)
|
||||
-- * SRS Simple-Text-To-Speech (STTS) integration (no sound files necessary)
|
||||
-- * SRS Simple-Text-To-Speech (MSRS) integration (no sound files necessary)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -291,7 +291,7 @@
|
||||
-- ## Nevada: Nellis AFB
|
||||
--
|
||||
-- -- ATIS Nellis AFB on 270.10 MHz AM.
|
||||
-- atisNellis=ATIS:New(AIRBASE.Nevada.Nellis_AFB, 270.1)
|
||||
-- atisNellis=ATIS:New(AIRBASE.Nevada.Nellis, 270.1)
|
||||
-- atisNellis:SetRadioRelayUnitName("Radio Relay Nellis")
|
||||
-- atisNellis:SetActiveRunway("21L")
|
||||
-- atisNellis:SetTowerFrequencies({327.000, 132.550})
|
||||
@@ -302,7 +302,7 @@
|
||||
-- ## Persian Gulf: Abu Dhabi International Airport
|
||||
--
|
||||
-- -- ATIS Abu Dhabi International on 125.1 MHz AM.
|
||||
-- atisAbuDhabi=ATIS:New(AIRBASE.PersianGulf.Abu_Dhabi_International_Airport, 125.1)
|
||||
-- atisAbuDhabi=ATIS:New(AIRBASE.PersianGulf.Abu_Dhabi_Intl, 125.1)
|
||||
-- atisAbuDhabi:SetRadioRelayUnitName("Radio Relay Abu Dhabi International Airport")
|
||||
-- atisAbuDhabi:SetMetricUnits()
|
||||
-- atisAbuDhabi:SetActiveRunway("L")
|
||||
@@ -498,6 +498,10 @@ ATIS.Alphabet = {
|
||||
-- @field #number Syria +5° (East).
|
||||
-- @field #number MarianaIslands +2° (East).
|
||||
-- @field #number SinaiMap +5° (East).
|
||||
-- @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,
|
||||
@@ -508,6 +512,10 @@ ATIS.RunwayM2T = {
|
||||
MarianaIslands = 2,
|
||||
Falklands = 12,
|
||||
SinaiMap = 5,
|
||||
Kola = 15,
|
||||
Afghanistan = 3,
|
||||
Iraq=4.4,
|
||||
GermanyCW=0.1,
|
||||
}
|
||||
|
||||
--- Whether ICAO phraseology is used for ATIS broadcasts.
|
||||
@@ -521,6 +529,10 @@ ATIS.RunwayM2T = {
|
||||
-- @field #boolean MarianaIslands true.
|
||||
-- @field #boolean Falklands true.
|
||||
-- @field #boolean SinaiMap true.
|
||||
-- @field #boolean Kola true.
|
||||
-- @field #boolean Afghanistan true.
|
||||
-- @field #boolean Iraq true.
|
||||
-- @field #boolean GermanyCW true.
|
||||
ATIS.ICAOPhraseology = {
|
||||
Caucasus = true,
|
||||
Nevada = false,
|
||||
@@ -531,6 +543,10 @@ ATIS.ICAOPhraseology = {
|
||||
MarianaIslands = true,
|
||||
Falklands = true,
|
||||
SinaiMap = true,
|
||||
Kola = true,
|
||||
Afghanistan = true,
|
||||
Iraq = true,
|
||||
GermanyCW = true,
|
||||
}
|
||||
|
||||
--- Nav point data.
|
||||
@@ -619,83 +635,83 @@ ATIS.ICAOPhraseology = {
|
||||
-- @field #ATIS.Soundfile TACANChannel
|
||||
-- @field #ATIS.Soundfile VORFrequency
|
||||
ATIS.Sound = {
|
||||
ActiveRunway = { filename = "ActiveRunway.ogg", duration = 0.99 },
|
||||
ActiveRunwayDeparture = { filename = "ActiveRunwayDeparture.ogg", duration = 0.99 },
|
||||
ActiveRunwayArrival = { filename = "ActiveRunwayArrival.ogg", duration = 0.99 },
|
||||
AdviceOnInitial = { filename = "AdviceOnInitial.ogg", duration = 3.00 },
|
||||
Airport = { filename = "Airport.ogg", duration = 0.66 },
|
||||
Altimeter = { filename = "Altimeter.ogg", duration = 0.68 },
|
||||
At = { filename = "At.ogg", duration = 0.41 },
|
||||
CloudBase = { filename = "CloudBase.ogg", duration = 0.82 },
|
||||
CloudCeiling = { filename = "CloudCeiling.ogg", duration = 0.61 },
|
||||
CloudsBroken = { filename = "CloudsBroken.ogg", duration = 1.07 },
|
||||
CloudsFew = { filename = "CloudsFew.ogg", duration = 0.99 },
|
||||
CloudsNo = { filename = "CloudsNo.ogg", duration = 1.01 },
|
||||
CloudsNotAvailable = { filename = "CloudsNotAvailable.ogg", duration = 2.35 },
|
||||
CloudsOvercast = { filename = "CloudsOvercast.ogg", duration = 0.83 },
|
||||
CloudsScattered = { filename = "CloudsScattered.ogg", duration = 1.18 },
|
||||
Decimal = { filename = "Decimal.ogg", duration = 0.54 },
|
||||
DegreesCelsius = { filename = "DegreesCelsius.ogg", duration = 1.27 },
|
||||
DegreesFahrenheit = { filename = "DegreesFahrenheit.ogg", duration = 1.23 },
|
||||
DewPoint = { filename = "DewPoint.ogg", duration = 0.65 },
|
||||
Dust = { filename = "Dust.ogg", duration = 0.54 },
|
||||
Elevation = { filename = "Elevation.ogg", duration = 0.78 },
|
||||
EndOfInformation = { filename = "EndOfInformation.ogg", duration = 1.15 },
|
||||
Feet = { filename = "Feet.ogg", duration = 0.45 },
|
||||
Fog = { filename = "Fog.ogg", duration = 0.47 },
|
||||
Gusting = { filename = "Gusting.ogg", duration = 0.55 },
|
||||
HectoPascal = { filename = "HectoPascal.ogg", duration = 1.15 },
|
||||
Hundred = { filename = "Hundred.ogg", duration = 0.47 },
|
||||
InchesOfMercury = { filename = "InchesOfMercury.ogg", duration = 1.16 },
|
||||
Information = { filename = "Information.ogg", duration = 0.85 },
|
||||
Kilometers = { filename = "Kilometers.ogg", duration = 0.78 },
|
||||
Knots = { filename = "Knots.ogg", duration = 0.59 },
|
||||
Left = { filename = "Left.ogg", duration = 0.54 },
|
||||
MegaHertz = { filename = "MegaHertz.ogg", duration = 0.87 },
|
||||
Meters = { filename = "Meters.ogg", duration = 0.59 },
|
||||
MetersPerSecond = { filename = "MetersPerSecond.ogg", duration = 1.14 },
|
||||
Miles = { filename = "Miles.ogg", duration = 0.60 },
|
||||
MillimetersOfMercury = { filename = "MillimetersOfMercury.ogg", duration = 1.53 },
|
||||
Minus = { filename = "Minus.ogg", duration = 0.64 },
|
||||
N0 = { filename = "N-0.ogg", duration = 0.55 },
|
||||
N1 = { filename = "N-1.ogg", duration = 0.41 },
|
||||
N2 = { filename = "N-2.ogg", duration = 0.37 },
|
||||
N3 = { filename = "N-3.ogg", duration = 0.41 },
|
||||
N4 = { filename = "N-4.ogg", duration = 0.37 },
|
||||
N5 = { filename = "N-5.ogg", duration = 0.43 },
|
||||
N6 = { filename = "N-6.ogg", duration = 0.55 },
|
||||
N7 = { filename = "N-7.ogg", duration = 0.43 },
|
||||
N8 = { filename = "N-8.ogg", duration = 0.38 },
|
||||
N9 = { filename = "N-9.ogg", duration = 0.55 },
|
||||
NauticalMiles = { filename = "NauticalMiles.ogg", duration = 1.04 },
|
||||
None = { filename = "None.ogg", duration = 0.43 },
|
||||
QFE = { filename = "QFE.ogg", duration = 0.63 },
|
||||
QNH = { filename = "QNH.ogg", duration = 0.71 },
|
||||
Rain = { filename = "Rain.ogg", duration = 0.41 },
|
||||
Right = { filename = "Right.ogg", duration = 0.44 },
|
||||
Snow = { filename = "Snow.ogg", duration = 0.48 },
|
||||
SnowStorm = { filename = "SnowStorm.ogg", duration = 0.82 },
|
||||
StatuteMiles = { filename = "StatuteMiles.ogg", duration = 1.15 },
|
||||
SunriseAt = { filename = "SunriseAt.ogg", duration = 0.92 },
|
||||
SunsetAt = { filename = "SunsetAt.ogg", duration = 0.95 },
|
||||
Temperature = { filename = "Temperature.ogg", duration = 0.64 },
|
||||
Thousand = { filename = "Thousand.ogg", duration = 0.55 },
|
||||
ThunderStorm = { filename = "ThunderStorm.ogg", duration = 0.81 },
|
||||
TimeLocal = { filename = "TimeLocal.ogg", duration = 0.90 },
|
||||
TimeZulu = { filename = "TimeZulu.ogg", duration = 0.86 },
|
||||
TowerFrequency = { filename = "TowerFrequency.ogg", duration = 1.19 },
|
||||
Visibilty = { filename = "Visibility.ogg", duration = 0.79 },
|
||||
WeatherPhenomena = { filename = "WeatherPhenomena.ogg", duration = 1.07 },
|
||||
WindFrom = { filename = "WindFrom.ogg", duration = 0.60 },
|
||||
ActiveRunway = { filename = "ActiveRunway.ogg", duration = 0.85 },
|
||||
ActiveRunwayDeparture = { filename = "ActiveRunwayDeparture.ogg", duration = 1.50 },
|
||||
ActiveRunwayArrival = { filename = "ActiveRunwayArrival.ogg", duration = 1.38 },
|
||||
AdviceOnInitial = { filename = "AdviceOnInitial.ogg", duration = 2.98 },
|
||||
Airport = { filename = "Airport.ogg", duration = 0.55 },
|
||||
Altimeter = { filename = "Altimeter.ogg", duration = 0.91 },
|
||||
At = { filename = "At.ogg", duration = 0.32 },
|
||||
CloudBase = { filename = "CloudBase.ogg", duration = 0.69 },
|
||||
CloudCeiling = { filename = "CloudCeiling.ogg", duration = 0.53 },
|
||||
CloudsBroken = { filename = "CloudsBroken.ogg", duration = 0.81 },
|
||||
CloudsFew = { filename = "CloudsFew.ogg", duration = 0.74 },
|
||||
CloudsNo = { filename = "CloudsNo.ogg", duration = 0.69},
|
||||
CloudsNotAvailable = { filename = "CloudsNotAvailable.ogg", duration = 2.64 },
|
||||
CloudsOvercast = { filename = "CloudsOvercast.ogg", duration = 0.82 },
|
||||
CloudsScattered = { filename = "CloudsScattered.ogg", duration = 0.89 },
|
||||
Decimal = { filename = "Decimal.ogg", duration = 0.71 },
|
||||
DegreesCelsius = { filename = "DegreesCelsius.ogg", duration = 1.08 },
|
||||
DegreesFahrenheit = { filename = "DegreesFahrenheit.ogg", duration = 1.07 },
|
||||
DewPoint = { filename = "DewPoint.ogg", duration = 0.59 },
|
||||
Dust = { filename = "Dust.ogg", duration = 0.37 },
|
||||
Elevation = { filename = "Elevation.ogg", duration = 0.92 },
|
||||
EndOfInformation = { filename = "EndOfInformation.ogg", duration = 1.24 },
|
||||
Feet = { filename = "Feet.ogg", duration = 0.34 },
|
||||
Fog = { filename = "Fog.ogg", duration = 0.41 },
|
||||
Gusting = { filename = "Gusting.ogg", duration = 0.58 },
|
||||
HectoPascal = { filename = "HectoPascal.ogg", duration = 0.92 },
|
||||
Hundred = { filename = "Hundred.ogg", duration = 0.53 },
|
||||
ILSFrequency = { filename = "ILSFrequency.ogg", duration = 1.30 },
|
||||
InnerNDBFrequency = { filename = "InnerNDBFrequency.ogg", duration = 1.56 },
|
||||
OuterNDBFrequency = { filename = "OuterNDBFrequency.ogg", duration = 1.59 },
|
||||
RunwayLength = { filename = "RunwayLength.ogg", duration = 0.91 },
|
||||
VORFrequency = { filename = "VORFrequency.ogg", duration = 1.38 },
|
||||
TACANChannel = { filename = "TACANChannel.ogg", duration = 0.88 },
|
||||
PRMGChannel = { filename = "PRMGChannel.ogg", duration = 1.18 },
|
||||
RSBNChannel = { filename = "RSBNChannel.ogg", duration = 1.14 },
|
||||
Zulu = { filename = "Zulu.ogg", duration = 0.62 },
|
||||
InchesOfMercury = { filename = "InchesOfMercury.ogg", duration = 1.26 },
|
||||
Information = { filename = "Information.ogg", duration = 0.99 },
|
||||
InnerNDBFrequency = { filename = "InnerNDBFrequency.ogg", duration = 1.69 },
|
||||
Kilometers = { filename = "Kilometers.ogg", duration = 0.93 },
|
||||
Knots = { filename = "Knots.ogg", duration = 0.46 },
|
||||
Left = { filename = "Left.ogg", duration = 0.41 },
|
||||
MegaHertz = { filename = "MegaHertz.ogg", duration = 0.83 },
|
||||
Meters = { filename = "Meters.ogg", duration = 0.55 },
|
||||
MetersPerSecond = { filename = "MetersPerSecond.ogg", duration = 1.03 },
|
||||
Miles = { filename = "Miles.ogg", duration = 0.44 },
|
||||
MillimetersOfMercury = { filename = "MillimetersOfMercury.ogg", duration = 1.59 },
|
||||
Minus = { filename = "Minus.ogg", duration = 0.55 },
|
||||
N0 = { filename = "N-0.ogg", duration = 0.52 },
|
||||
N1 = { filename = "N-1.ogg", duration = 0.35 },
|
||||
N2 = { filename = "N-2.ogg", duration = 0.41 },
|
||||
N3 = { filename = "N-3.ogg", duration = 0.34 },
|
||||
N4 = { filename = "N-4.ogg", duration = 0.37 },
|
||||
N5 = { filename = "N-5.ogg", duration = 0.40 },
|
||||
N6 = { filename = "N-6.ogg", duration = 0.46 },
|
||||
N7 = { filename = "N-7.ogg", duration = 0.52 },
|
||||
N8 = { filename = "N-8.ogg", duration = 0.36 },
|
||||
N9 = { filename = "N-9.ogg", duration = 0.51 },
|
||||
NauticalMiles = { filename = "NauticalMiles.ogg", duration = 0.93 },
|
||||
None = { filename = "None.ogg", duration = 0.33 },
|
||||
OuterNDBFrequency = { filename = "OuterNDBFrequency.ogg", duration = 1.70 },
|
||||
PRMGChannel = { filename = "PRMGChannel.ogg", duration = 1.27 },
|
||||
QFE = { filename = "QFE.ogg", duration = 0.90 },
|
||||
QNH = { filename = "QNH.ogg", duration = 0.94 },
|
||||
Rain = { filename = "Rain.ogg", duration = 0.35 },
|
||||
Right = { filename = "Right.ogg", duration = 0.31 },
|
||||
RSBNChannel = { filename = "RSBNChannel.ogg", duration = 1.26 },
|
||||
RunwayLength = { filename = "RunwayLength.ogg", duration = 0.81 },
|
||||
Snow = { filename = "Snow.ogg", duration = 0.40 },
|
||||
SnowStorm = { filename = "SnowStorm.ogg", duration = 0.73 },
|
||||
StatuteMiles = { filename = "StatuteMiles.ogg", duration = 0.90 },
|
||||
SunriseAt = { filename = "SunriseAt.ogg", duration = 0.82 },
|
||||
SunsetAt = { filename = "SunsetAt.ogg", duration = 0.87 },
|
||||
TACANChannel = { filename = "TACANChannel.ogg", duration = 0.81 },
|
||||
Temperature = { filename = "Temperature.ogg", duration = 0.70 },
|
||||
Thousand = { filename = "Thousand.ogg", duration = 0.58 },
|
||||
ThunderStorm = { filename = "ThunderStorm.ogg", duration = 0.79 },
|
||||
TimeLocal = { filename = "TimeLocal.ogg", duration = 0.83 },
|
||||
TimeZulu = { filename = "TimeZulu.ogg", duration = 0.83 },
|
||||
TowerFrequency = { filename = "TowerFrequency.ogg", duration = 1.05 },
|
||||
Visibilty = { filename = "Visibility.ogg", duration = 1.16 },
|
||||
VORFrequency = { filename = "VORFrequency.ogg", duration = 1.28 },
|
||||
WeatherPhenomena = { filename = "WeatherPhenomena.ogg", duration = 1.09 },
|
||||
WindFrom = { filename = "WindFrom.ogg", duration = 0.63 },
|
||||
Zulu = { filename = "Zulu.ogg", duration = 0.51 },
|
||||
}
|
||||
|
||||
---
|
||||
@@ -882,6 +898,66 @@ ATIS.Messages = {
|
||||
FARP = "Farp",
|
||||
DELIMITER = "Punto", -- decimal delimiter
|
||||
},
|
||||
-- French messages thanks to @Wojtech and Bing
|
||||
FR = {
|
||||
HOURS = "Heures",
|
||||
TIME = "Temps",
|
||||
NOCLOUDINFO = "Informations sur la couverture nuageuse non disponibles",
|
||||
OVERCAST = "Ciel couvert",
|
||||
BROKEN = "Nuages fragmentés",
|
||||
SCATTERED = "Nuages épars",
|
||||
FEWCLOUDS = "Nuages rares",
|
||||
NOCLOUDS = "Clair",
|
||||
AIRPORT = "Aéroport",
|
||||
INFORMATION ="Information",
|
||||
SUNRISEAT = "Levé du soleil à %s heure locale",
|
||||
SUNSETAT = "Couché du soleil à %s heure locale",
|
||||
WINDFROMMS = "Vent du %s pour %s mètres par seconde",
|
||||
WINDFROMKNOTS = "Vent du %s pour %s noeuds",
|
||||
GUSTING = "Rafale de vent",
|
||||
VISIKM = "Visibilité %s kilomètres",
|
||||
VISISM = "Visibilité %s Miles",
|
||||
RAIN = "Pluie",
|
||||
TSTORM = "Orage",
|
||||
SNOW = "Neige",
|
||||
SSTROM = "Tempête de neige",
|
||||
FOG = "Brouillard",
|
||||
DUST = "Poussière",
|
||||
PHENOMENA = "Phénomène météorologique",
|
||||
CLOUDBASEM = "Couverture nuageuse de %s à %s mètres",
|
||||
CLOUDBASEFT = "Couverture nuageuse de %s à %s pieds",
|
||||
TEMPERATURE = "Température",
|
||||
DEWPOINT = "Point de rosée",
|
||||
ALTIMETER = "Altimètre",
|
||||
ACTIVERUN = "Décollages piste",
|
||||
ACTIVELANDING = "Atterrissages piste",
|
||||
LEFT = "Gauche",
|
||||
RIGHT = "Droite",
|
||||
RWYLENGTH = "Longueur de piste",
|
||||
METERS = "Mètre",
|
||||
FEET = "Pieds",
|
||||
ELEVATION = "Hauteur",
|
||||
TOWERFREQ = "Fréquences de la tour",
|
||||
ILSFREQ = "Fréquences ILS",
|
||||
OUTERNDB = "Fréquences Outer NDB",
|
||||
INNERNDB = "Fréquences Inner NDB",
|
||||
VORFREQ = "Fréquences VOR",
|
||||
VORFREQTTS = "Fréquences V O R",
|
||||
TACANCH = "Canal TACAN %d",
|
||||
RSBNCH = "Canal RSBN",
|
||||
PRMGCH = "Canal PRMG",
|
||||
ADVISE = "Informez le contrôle que vous avez copié l'information",
|
||||
STATUTE = "Statute Miles",
|
||||
DEGREES = "Degré celcius",
|
||||
FAHRENHEIT = "Degré Fahrenheit",
|
||||
INCHHG = "Pouces de mercure",
|
||||
MMHG = "Millimètres de mercure",
|
||||
HECTO = "Hectopascals",
|
||||
METERSPER = "Mètres par seconde",
|
||||
TACAN = "TAKAN",
|
||||
FARP = "FARPE",
|
||||
DELIMITER = "Décimal", -- decimal delimiter
|
||||
}
|
||||
}
|
||||
|
||||
---
|
||||
@@ -894,7 +970,7 @@ _ATIS = {}
|
||||
|
||||
--- ATIS class version.
|
||||
-- @field #string version
|
||||
ATIS.version = "1.0.0"
|
||||
ATIS.version = "1.0.1"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -1061,7 +1137,7 @@ end
|
||||
-- @return #ATIS self
|
||||
function ATIS:_InitLocalization()
|
||||
self:T(self.lid.."_InitLocalization")
|
||||
self.gettext = TEXTANDSOUND:New("AWACS","en") -- Core.TextAndSound#TEXTANDSOUND
|
||||
self.gettext = TEXTANDSOUND:New("ATIS","en") -- Core.TextAndSound#TEXTANDSOUND
|
||||
self.locale = "en"
|
||||
for locale,table in pairs(self.Messages) do
|
||||
local Locale = string.lower(tostring(locale))
|
||||
@@ -1975,17 +2051,28 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
|
||||
local hours = self.gettext:GetEntry("HOURS",self.locale)
|
||||
local sunrise = coord:GetSunrise()
|
||||
sunrise = UTILS.Split( sunrise, ":" )
|
||||
local SUNRISE = string.format( "%s%s", sunrise[1], sunrise[2] )
|
||||
if self.useSRS then
|
||||
SUNRISE = string.format( "%s %s %s", sunrise[1], sunrise[2], hours )
|
||||
--self:I(sunrise)
|
||||
local SUNRISE = "no time"
|
||||
local NorthPolar = true
|
||||
if tostring(sunrise) ~= "N/S" and tostring(sunrise) ~= "N/R" then
|
||||
sunrise = UTILS.Split( sunrise, ":" )
|
||||
SUNRISE = string.format( "%s%s", sunrise[1], sunrise[2] )
|
||||
if self.useSRS then
|
||||
SUNRISE = string.format( "%s %s %s", sunrise[1], sunrise[2], hours )
|
||||
end
|
||||
NorthPolar = false
|
||||
end
|
||||
|
||||
|
||||
local sunset = coord:GetSunset()
|
||||
sunset = UTILS.Split( sunset, ":" )
|
||||
local SUNSET = string.format( "%s%s", sunset[1], sunset[2] )
|
||||
if self.useSRS then
|
||||
SUNSET = string.format( "%s %s %s", sunset[1], sunset[2], hours )
|
||||
--self:I(sunset)
|
||||
local SUNSET = "no time"
|
||||
if tostring(sunset) ~= "N/S" and tostring(sunset) ~= "N/R" then
|
||||
sunset = UTILS.Split( sunset, ":" )
|
||||
SUNSET = string.format( "%s%s", sunset[1], sunset[2] )
|
||||
if self.useSRS then
|
||||
SUNSET = string.format( "%s %s %s", sunset[1], sunset[2], hours )
|
||||
end
|
||||
NorthPolar = false
|
||||
end
|
||||
|
||||
---------------------------------
|
||||
@@ -2012,34 +2099,32 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
---------------
|
||||
|
||||
-- Get mission weather info. Most of this is static.
|
||||
local clouds, visibility, turbulence, fog, dust, static = self:GetMissionWeather()
|
||||
|
||||
-- Check that fog is actually "thick" enough to reach the airport. If an airport is in the mountains, fog might not affect it as it is measured from sea level.
|
||||
if fog and fog.thickness < height + 25 then
|
||||
fog = nil
|
||||
end
|
||||
|
||||
-- Dust only up to 1500 ft = 457 m ASL.
|
||||
if dust and height + 25 > UTILS.FeetToMeters( 1500 ) then
|
||||
dust = nil
|
||||
end
|
||||
local clouds, visibility, turbulence, dustdens, static = self:GetMissionWeather()
|
||||
|
||||
local dust=false
|
||||
local fog=false
|
||||
|
||||
------------------
|
||||
--- Visibility ---
|
||||
------------------
|
||||
|
||||
-- Get min visibility.
|
||||
local visibilitymin = visibility
|
||||
|
||||
if fog then
|
||||
if fog.visibility < visibilitymin then
|
||||
visibilitymin = fog.visibility
|
||||
if dustdens then
|
||||
|
||||
-- Dust only up to 1500 ft = 457 m ASL.
|
||||
if UTILS.FeetToMeters( 1500 )> height+25 then
|
||||
dust=true
|
||||
visibility=math.min(visibility, dustdens)
|
||||
end
|
||||
end
|
||||
|
||||
if dust then
|
||||
if dust < visibilitymin then
|
||||
visibilitymin = dust
|
||||
|
||||
else -- As of DCS 2.9.10.3948 (December 2024), fog and dust are mutually exclusive!
|
||||
|
||||
-- Get current fog visibility and thickness
|
||||
local fvis=world.weather.getFogVisibilityDistance()
|
||||
local fheight=world.weather.getFogThickness()
|
||||
|
||||
if fvis>0 and fheight>height+25 then
|
||||
fog=true
|
||||
visibility=math.min(visibility, fvis)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2047,7 +2132,7 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
|
||||
if self.metric then
|
||||
-- Visibility in km.
|
||||
local reportedviz = UTILS.Round( visibilitymin / 1000 )
|
||||
local reportedviz = UTILS.Round( visibility / 1000 )
|
||||
-- max reported visibility 9999 m
|
||||
if reportedviz > 10 then
|
||||
reportedviz = 10
|
||||
@@ -2055,7 +2140,7 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
VISIBILITY = string.format( "%d", reportedviz )
|
||||
else
|
||||
-- max reported visibility 10 NM
|
||||
local reportedviz = UTILS.Round( UTILS.MetersToSM( visibilitymin ) )
|
||||
local reportedviz = UTILS.Round( UTILS.MetersToSM( visibility ) )
|
||||
if reportedviz > 10 then
|
||||
reportedviz = 10
|
||||
end
|
||||
@@ -2069,7 +2154,7 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
local cloudbase = clouds.base
|
||||
local cloudceil = clouds.base + clouds.thickness
|
||||
local clouddens = clouds.density
|
||||
|
||||
|
||||
-- Cloud preset (DCS 2.7)
|
||||
local cloudspreset = clouds.preset or "Nothing"
|
||||
|
||||
@@ -2100,6 +2185,39 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
else
|
||||
precepitation = 3 -- snow
|
||||
end
|
||||
elseif cloudspreset:find( "RainyPreset4" ) then
|
||||
-- Overcast + Rain
|
||||
clouddens = 5
|
||||
if temperature > 5 then
|
||||
precepitation = 1 -- rain
|
||||
else
|
||||
precepitation = 3 -- snow
|
||||
end
|
||||
elseif cloudspreset:find( "RainyPreset5" ) then
|
||||
-- Overcast + Rain
|
||||
clouddens = 5
|
||||
if temperature > 5 then
|
||||
precepitation = 1 -- rain
|
||||
else
|
||||
precepitation = 3 -- snow
|
||||
end
|
||||
elseif cloudspreset:find( "RainyPreset6" ) then
|
||||
-- Overcast + Rain
|
||||
clouddens = 5
|
||||
if temperature > 5 then
|
||||
precepitation = 1 -- rain
|
||||
else
|
||||
precepitation = 3 -- snow
|
||||
end
|
||||
-- NEWRAINPRESET4
|
||||
elseif cloudspreset:find( "NEWRAINPRESET4" ) then
|
||||
-- Overcast + Rain
|
||||
clouddens = 5
|
||||
if temperature > 5 then
|
||||
precepitation = 1 -- rain
|
||||
else
|
||||
precepitation = 3 -- snow
|
||||
end
|
||||
elseif cloudspreset:find( "RainyPreset" ) then
|
||||
-- Overcast + Rain
|
||||
clouddens = 9
|
||||
@@ -2294,7 +2412,7 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
local sunrise = self.gettext:GetEntry("SUNRISEAT",self.locale)
|
||||
--subtitle = string.format( "Sunrise at %s local time", SUNRISE )
|
||||
subtitle = string.format( sunrise, SUNRISE )
|
||||
if not self.useSRS then
|
||||
if not self.useSRS and NorthPolar == false then
|
||||
self:Transmission( self.Sound.SunriseAt, 0.5, subtitle )
|
||||
self.radioqueue:Number2Transmission( SUNRISE, nil, 0.2 )
|
||||
self:Transmission( self.Sound.TimeLocal, 0.2 )
|
||||
@@ -2305,7 +2423,7 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
local sunset = self.gettext:GetEntry("SUNSETAT",self.locale)
|
||||
--subtitle = string.format( "Sunset at %s local time", SUNSET )
|
||||
subtitle = string.format( sunset, SUNSET )
|
||||
if not self.useSRS then
|
||||
if not self.useSRS and NorthPolar == false then
|
||||
self:Transmission( self.Sound.SunsetAt, 0.5, subtitle )
|
||||
self.radioqueue:Number2Transmission( SUNSET, nil, 0.5 )
|
||||
self:Transmission( self.Sound.TimeLocal, 0.2 )
|
||||
@@ -2680,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
|
||||
@@ -3249,28 +3367,13 @@ function ATIS:GetMissionWeather()
|
||||
dust = weather.dust_density
|
||||
end
|
||||
|
||||
-- Fog
|
||||
--[[
|
||||
["enable_fog"] = false,
|
||||
["fog"] =
|
||||
{
|
||||
["thickness"] = 0,
|
||||
["visibility"] = 25,
|
||||
}, -- end of ["fog"]
|
||||
]]
|
||||
local fog = nil
|
||||
if weather.enable_fog == true then
|
||||
fog = weather.fog
|
||||
end
|
||||
|
||||
self:T( "FF weather:" )
|
||||
self:T( { clouds = clouds } )
|
||||
self:T( { visibility = visibility } )
|
||||
self:T( { turbulence = turbulence } )
|
||||
self:T( { fog = fog } )
|
||||
self:T( { dust = dust } )
|
||||
self:T( { static = static } )
|
||||
return clouds, visibility, turbulence, fog, dust, static
|
||||
return clouds, visibility, turbulence, dust, static
|
||||
end
|
||||
|
||||
--- Get thousands of a number.
|
||||
|
||||
@@ -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.5"
|
||||
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.warehouse 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)
|
||||
|
||||
@@ -1365,16 +1444,20 @@ function AIRWING:CheckRescuhelo()
|
||||
|
||||
local N=self:CountMissionsInQueue({AUFTRAG.Type.RESCUEHELO})
|
||||
|
||||
local name=self.airbase:GetName()
|
||||
|
||||
local carrier=UNIT:FindByName(name)
|
||||
|
||||
for i=1,self.nflightsRescueHelo-N do
|
||||
|
||||
local mission=AUFTRAG:NewRESCUEHELO(carrier)
|
||||
|
||||
self:AddMission(mission)
|
||||
|
||||
if self.airbase then
|
||||
|
||||
local name=self.airbase:GetName()
|
||||
|
||||
local carrier=UNIT:FindByName(name)
|
||||
|
||||
for i=1,self.nflightsRescueHelo-N do
|
||||
|
||||
local mission=AUFTRAG:NewRESCUEHELO(carrier)
|
||||
|
||||
self:AddMission(mission)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -1460,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
@@ -68,7 +68,7 @@ ARMYGROUP = {
|
||||
|
||||
--- Army Group version.
|
||||
-- @field #string version
|
||||
ARMYGROUP.version="1.0.1"
|
||||
ARMYGROUP.version="1.0.3"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -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
|
||||
|
||||
@@ -2049,114 +2048,70 @@ end
|
||||
--- Initialize group parameters. Also initializes waypoints if self.waypoints is nil.
|
||||
-- @param #ARMYGROUP self
|
||||
-- @param #table Template Template used to init the group. Default is `self.template`.
|
||||
-- @param #number Delay Delay in seconds before group is initialized. Default `nil`, *i.e.* instantaneous.
|
||||
-- @return #ARMYGROUP self
|
||||
function ARMYGROUP:_InitGroup(Template, Delay)
|
||||
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, ARMYGROUP._InitGroup, self, Template, 0)
|
||||
else
|
||||
-- First check if group was already initialized.
|
||||
if self.groupinitialized then
|
||||
self:T(self.lid.."WARNING: Group was already initialized! Will NOT do it again!")
|
||||
return
|
||||
end
|
||||
|
||||
-- Get template of group.
|
||||
local template=Template or self:_GetTemplate()
|
||||
|
||||
-- Ground are always AI.
|
||||
self.isAI=true
|
||||
|
||||
-- Is (template) group late activated.
|
||||
self.isLateActivated=template.lateActivation
|
||||
|
||||
-- Ground groups cannot be uncontrolled.
|
||||
self.isUncontrolled=false
|
||||
|
||||
-- Max speed in km/h.
|
||||
self.speedMax=self.group:GetSpeedMax()
|
||||
|
||||
-- Is group mobile?
|
||||
if self.speedMax and self.speedMax>3.6 then
|
||||
self.isMobile=true
|
||||
else
|
||||
self.isMobile=false
|
||||
self.speedMax = 0
|
||||
end
|
||||
|
||||
-- Cruise speed in km/h
|
||||
self.speedCruise=self.speedMax*0.7
|
||||
|
||||
-- Group ammo.
|
||||
self.ammo=self:GetAmmoTot()
|
||||
|
||||
-- Radio parameters from template.
|
||||
self.radio.On=false -- Radio is always OFF for ground.
|
||||
self.radio.Freq=133
|
||||
self.radio.Modu=radio.modulation.AM
|
||||
|
||||
-- Set default radio.
|
||||
self:SetDefaultRadio(self.radio.Freq, self.radio.Modu, self.radio.On)
|
||||
|
||||
-- Get current formation from first waypoint.
|
||||
self.option.Formation=template.route.points[1].action
|
||||
|
||||
-- Set default formation to "on road".
|
||||
self.optionDefault.Formation=ENUMS.Formation.Vehicle.OnRoad
|
||||
|
||||
|
||||
-- First check if group was already initialized.
|
||||
if self.groupinitialized then
|
||||
self:T(self.lid.."WARNING: Group was already initialized! Will NOT do it again!")
|
||||
return
|
||||
end
|
||||
|
||||
self:T(self.lid.."FF Initializing Group")
|
||||
|
||||
|
||||
-- Get template of group.
|
||||
local template=Template or self:_GetTemplate()
|
||||
|
||||
|
||||
-- Ground are always AI.
|
||||
self.isAI=true
|
||||
|
||||
|
||||
-- Is (template) group late activated.
|
||||
self.isLateActivated=template.lateActivation
|
||||
|
||||
|
||||
-- Ground groups cannot be uncontrolled.
|
||||
self.isUncontrolled=false
|
||||
|
||||
|
||||
-- Max speed in km/h.
|
||||
self.speedMax=self.group:GetSpeedMax()
|
||||
|
||||
|
||||
-- Is group mobile?
|
||||
if self.speedMax>3.6 then
|
||||
if self.speedMax and self.speedMax>3.6 then
|
||||
self.isMobile=true
|
||||
else
|
||||
self.isMobile=false
|
||||
self.speedMax = 0
|
||||
end
|
||||
|
||||
|
||||
-- Cruise speed in km/h
|
||||
self.speedCruise=self.speedMax*0.7
|
||||
|
||||
|
||||
-- Group ammo.
|
||||
self.ammo=self:GetAmmoTot()
|
||||
|
||||
|
||||
-- Radio parameters from template.
|
||||
self.radio.On=false -- Radio is always OFF for ground.
|
||||
self.radio.Freq=133
|
||||
self.radio.Modu=radio.modulation.AM
|
||||
|
||||
|
||||
-- Set default radio.
|
||||
self:SetDefaultRadio(self.radio.Freq, self.radio.Modu, self.radio.On)
|
||||
|
||||
|
||||
-- Get current formation from first waypoint.
|
||||
self.option.Formation=template.route.points[1].action
|
||||
|
||||
|
||||
-- Set default formation to "on road".
|
||||
self.optionDefault.Formation=ENUMS.Formation.Vehicle.OnRoad
|
||||
|
||||
-- Default TACAN off.
|
||||
self:SetDefaultTACAN(nil, nil, nil, nil, true)
|
||||
self.tacan=UTILS.DeepCopy(self.tacanDefault)
|
||||
if not self.tacanDefault then
|
||||
self:SetDefaultTACAN(nil, nil, nil, nil, true)
|
||||
end
|
||||
if not self.tacan then
|
||||
self.tacan=UTILS.DeepCopy(self.tacanDefault)
|
||||
end
|
||||
|
||||
-- Units of the group.
|
||||
local units=self.group:GetUnits()
|
||||
@@ -2177,7 +2132,6 @@ function ARMYGROUP:_InitGroup(Template, Delay)
|
||||
self:_AddElementByName(unitname)
|
||||
end
|
||||
|
||||
|
||||
-- Init done.
|
||||
self.groupinitialized=true
|
||||
end
|
||||
|
||||
@@ -162,6 +162,7 @@
|
||||
-- @field #number missionRange Mission range in meters. Used by LEGION classes (AIRWING, BRIGADE, ...).
|
||||
-- @field Core.Point#COORDINATE missionWaypointCoord Mission waypoint coordinate.
|
||||
-- @field Core.Point#COORDINATE missionEgressCoord Mission egress waypoint coordinate.
|
||||
-- @field Core.Point#COORDINATE missionIngressCoord Mission Ingress waypoint coordinate.
|
||||
-- @field #number missionWaypointRadius Random radius in meters.
|
||||
-- @field #boolean legionReturn If `true`, assets return to their legion (default). If `false`, they will stay alive.
|
||||
--
|
||||
@@ -323,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.
|
||||
@@ -396,6 +401,7 @@ AUFTRAG = {
|
||||
conditionPush = {},
|
||||
conditionSuccessSet = false,
|
||||
conditionFailureSet = false,
|
||||
repeatDelay = 1,
|
||||
}
|
||||
|
||||
--- Global mission counter.
|
||||
@@ -441,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.
|
||||
@@ -489,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",
|
||||
@@ -513,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.
|
||||
@@ -535,6 +544,7 @@ AUFTRAG.SpecialTask={
|
||||
ARMORATTACK="AmorAttack",
|
||||
HOVER="Hover",
|
||||
GROUNDATTACK="Ground Attack",
|
||||
NAVALENGAGEMENT="Naval Engagement",
|
||||
FERRY="Ferry",
|
||||
RELOCATECOHORT="Relocate Cohort",
|
||||
AIRDEFENSE="Air Defense",
|
||||
@@ -664,7 +674,7 @@ AUFTRAG.Category={
|
||||
|
||||
--- AUFTRAG class version.
|
||||
-- @field #string version
|
||||
AUFTRAG.version="1.2.1"
|
||||
AUFTRAG.version="1.3.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -1319,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
|
||||
@@ -1427,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:
|
||||
@@ -1714,19 +1730,56 @@ 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
|
||||
function AUFTRAG:NewSTRIKE(Target, Altitude)
|
||||
function AUFTRAG:NewSTRIKE(Target, Altitude, EngageWeaponType)
|
||||
|
||||
local mission=AUFTRAG:New(AUFTRAG.Type.STRIKE)
|
||||
|
||||
mission:_TargetFromObject(Target)
|
||||
|
||||
-- DCS Task options:
|
||||
mission.engageWeaponType=ENUMS.WeaponFlag.Auto
|
||||
mission.engageWeaponType=EngageWeaponType or ENUMS.WeaponFlag.Auto
|
||||
mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL
|
||||
mission.engageAltitude=UTILS.FeetToMeters(Altitude or 2000)
|
||||
|
||||
@@ -1747,17 +1800,19 @@ 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)
|
||||
function AUFTRAG:NewBOMBING(Target, Altitude, EngageWeaponType, Divebomb)
|
||||
|
||||
local mission=AUFTRAG:New(AUFTRAG.Type.BOMBING)
|
||||
|
||||
mission:_TargetFromObject(Target)
|
||||
|
||||
-- DCS task options:
|
||||
mission.engageWeaponType=ENUMS.WeaponFlag.Auto
|
||||
mission.engageWeaponType=EngageWeaponType or ENUMS.WeaponFlag.Auto
|
||||
mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL
|
||||
mission.engageAltitude=UTILS.FeetToMeters(Altitude or 25000)
|
||||
|
||||
@@ -1767,6 +1822,7 @@ function AUFTRAG:NewBOMBING(Target, Altitude)
|
||||
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
|
||||
@@ -1819,7 +1875,7 @@ end
|
||||
|
||||
--- **[AIR]** Create a BOMBRUNWAY mission.
|
||||
-- @param #AUFTRAG self
|
||||
-- @param Wrapper.Airbase#AIRBASE Airdrome The airbase to bomb. This must be an airdrome (not a FARP or ship) as these to not have a runway.
|
||||
-- @param Wrapper.Airbase#AIRBASE Airdrome The airbase to bomb. This must be an airdrome (not a FARP or ship) as these do not have a runway.
|
||||
-- @param #number Altitude Engage altitude in feet. Default 25000 ft.
|
||||
-- @return #AUFTRAG self
|
||||
function AUFTRAG:NewBOMBRUNWAY(Airdrome, Altitude)
|
||||
@@ -1981,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
|
||||
|
||||
@@ -2155,10 +2211,14 @@ end
|
||||
|
||||
]]
|
||||
|
||||
--- **[GROUND, NAVAL]** Create an ARTY mission.
|
||||
--- **[GROUND, NAVAL]** Create an ARTY mission ("Fire at point" task).
|
||||
--
|
||||
-- If the group has more than one weapon type supporting the "Fire at point" task, the employed weapon type can be set via the `AUFTRAG:SetWeaponType()` function.
|
||||
--
|
||||
-- **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
|
||||
@@ -2178,6 +2238,7 @@ function AUFTRAG:NewARTY(Target, Nshots, Radius, Altitude)
|
||||
mission.optionAlarm=0
|
||||
|
||||
mission.missionFraction=0.0
|
||||
mission.missionWaypointRadius=0.0
|
||||
|
||||
-- Evaluate after 8 min.
|
||||
mission.dTevaluate=8*60
|
||||
@@ -2271,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)
|
||||
|
||||
@@ -2286,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
|
||||
@@ -2302,7 +2365,7 @@ function AUFTRAG:NewCAPTUREZONE(OpsZone, Coalition, Speed, Altitude, Formation)
|
||||
params.formation=Formation or "Off Road"
|
||||
params.zone=mission:GetObjective()
|
||||
params.altitude=mission.missionAltitude
|
||||
params.speed=mission.missionSpeed
|
||||
params.speed=mission.missionSpeed and UTILS.KmphToMps(mission.missionSpeed) or nil
|
||||
|
||||
mission.DCStask.params=params
|
||||
|
||||
@@ -2327,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)
|
||||
|
||||
@@ -2352,12 +2417,44 @@ function AUFTRAG:NewGROUNDATTACK(Target, Speed, Formation)
|
||||
|
||||
mission.DCStask=mission:GetDCSMissionTask()
|
||||
|
||||
mission.DCStask.params.speed=Speed
|
||||
mission.DCStask.params.speed=mission.missionSpeed and UTILS.KmphToMps(mission.missionSpeed) or nil
|
||||
mission.DCStask.params.formation=Formation or ENUMS.Formation.Vehicle.Vee
|
||||
|
||||
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.
|
||||
@@ -2650,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
|
||||
@@ -2793,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
|
||||
@@ -2958,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.
|
||||
@@ -3481,7 +3590,7 @@ end
|
||||
|
||||
--- Set Rules of Engagement (ROE) for this mission.
|
||||
-- @param #AUFTRAG self
|
||||
-- @param #string roe Mission ROE.
|
||||
-- @param #number roe Mission ROE, e.g. `ENUMS.ROE.ReturnFire` (whiche equals 3)
|
||||
-- @return #AUFTRAG self
|
||||
function AUFTRAG:SetROE(roe)
|
||||
|
||||
@@ -3493,7 +3602,7 @@ end
|
||||
|
||||
--- Set Reaction on Threat (ROT) for this mission.
|
||||
-- @param #AUFTRAG self
|
||||
-- @param #string rot Mission ROT.
|
||||
-- @param #number rot Mission ROT, e.g. `ENUMS.ROT.NoReaction` (whiche equals 0)
|
||||
-- @return #AUFTRAG self
|
||||
function AUFTRAG:SetROT(rot)
|
||||
|
||||
@@ -3953,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.
|
||||
@@ -4317,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))
|
||||
@@ -4358,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()
|
||||
@@ -4657,6 +4783,16 @@ function AUFTRAG:SetGroupWaypointCoordinate(opsgroup, coordinate)
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Air] Set mission (ingress) waypoint coordinate for FLIGHT group.
|
||||
-- @param #AUFTRAG self
|
||||
-- @param Core.Point#COORDINATE coordinate Waypoint Coordinate.
|
||||
-- @return #AUFTRAG self
|
||||
function AUFTRAG:SetIngressCoordinate(coordinate)
|
||||
self.missionIngressCoord = coordinate
|
||||
self.missionIngressCoordAlt = UTILS.MetersToFeet(coordinate.y) or 10000
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get mission (ingress) waypoint coordinate of OPS group
|
||||
-- @param #AUFTRAG self
|
||||
-- @param Ops.OpsGroup#OPSGROUP opsgroup The OPS group.
|
||||
@@ -4747,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
|
||||
@@ -4797,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
|
||||
@@ -5142,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
|
||||
|
||||
@@ -5184,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
|
||||
|
||||
@@ -5395,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
|
||||
|
||||
@@ -5404,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
|
||||
@@ -5712,7 +5865,7 @@ function AUFTRAG:GetMissionTypesText(MissionTypes)
|
||||
return text
|
||||
end
|
||||
|
||||
--- Set the mission waypoint coordinate where the mission is executed. Note that altitude is set via `:SetMissionAltitude`.
|
||||
--- [NON-AIR] Set the mission waypoint coordinate from where the mission is executed. Note that altitude is set via `:SetMissionAltitude`.
|
||||
-- @param #AUFTRAG self
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate where the mission is executed.
|
||||
-- @return #AUFTRAG self
|
||||
@@ -5740,8 +5893,9 @@ end
|
||||
-- @param #AUFTRAG self
|
||||
-- @param Core.Point#COORDINATE Coordinate Egrees coordinate.
|
||||
-- @param #number Altitude (Optional) Altitude in feet. Default is y component of coordinate.
|
||||
-- @param #number Speed (Optional) Speed in knots to reach this waypoint. Defaults to mission speed.
|
||||
-- @return #AUFTRAG self
|
||||
function AUFTRAG:SetMissionEgressCoord(Coordinate, Altitude)
|
||||
function AUFTRAG:SetMissionEgressCoord(Coordinate, Altitude, Speed)
|
||||
|
||||
-- Obviously a zone was passed. We get the coordinate.
|
||||
if Coordinate:IsInstanceOf("ZONE_BASE") then
|
||||
@@ -5752,7 +5906,64 @@ function AUFTRAG:SetMissionEgressCoord(Coordinate, Altitude)
|
||||
|
||||
if Altitude then
|
||||
self.missionEgressCoord.y=UTILS.FeetToMeters(Altitude)
|
||||
self.missionEgressCoordAlt = UTILS.FeetToMeters(Altitude)
|
||||
end
|
||||
|
||||
self.missionEgressCoordSpeed=Speed and Speed or nil
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Air] Set the mission ingress coordinate. This is the coordinate where the assigned group will fly before the actual mission coordinate.
|
||||
-- @param #AUFTRAG self
|
||||
-- @param Core.Point#COORDINATE Coordinate Ingrees coordinate.
|
||||
-- @param #number Altitude (Optional) Altitude in feet. Default is y component of coordinate.
|
||||
-- @param #number Speed (Optional) Speed in knots to reach this waypoint. Defaults to mission speed.
|
||||
-- @return #AUFTRAG self
|
||||
function AUFTRAG:SetMissionIngressCoord(Coordinate, Altitude, Speed)
|
||||
|
||||
-- Obviously a zone was passed. We get the coordinate.
|
||||
if Coordinate:IsInstanceOf("ZONE_BASE") then
|
||||
Coordinate=Coordinate:GetCoordinate()
|
||||
end
|
||||
|
||||
self.missionIngressCoord=Coordinate
|
||||
|
||||
if Altitude then
|
||||
self.missionIngressCoord.y=UTILS.FeetToMeters(Altitude)
|
||||
self.missionIngressCoordAlt = UTILS.FeetToMeters(Altitude or 10000)
|
||||
end
|
||||
|
||||
self.missionIngressCoordSpeed=Speed and Speed or nil
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Air] Set the mission holding coordinate. This is the coordinate where the assigned group will fly before the actual mission execution starts. Do not forget to add a push condition, too!
|
||||
-- @param #AUFTRAG self
|
||||
-- @param Core.Point#COORDINATE Coordinate Holding coordinate.
|
||||
-- @param #number Altitude (Optional) Altitude in feet. Default is y component of coordinate.
|
||||
-- @param #number Speed (Optional) Speed in knots to reach this waypoint and hold there. Defaults to mission speed.
|
||||
-- @param #number Duration (Optional) Duration in seconds on how long to hold, defaults to 15 minutes. Mission continues if either a push condition is met or the time is up.
|
||||
-- @return #AUFTRAG self
|
||||
function AUFTRAG:SetMissionHoldingCoord(Coordinate, Altitude, Speed, Duration)
|
||||
|
||||
-- Obviously a zone was passed. We get the coordinate.
|
||||
if Coordinate:IsInstanceOf("ZONE_BASE") then
|
||||
Coordinate=Coordinate:GetCoordinate()
|
||||
end
|
||||
|
||||
self.missionHoldingCoord=Coordinate
|
||||
self.missionHoldingDuration=Duration or 900
|
||||
|
||||
if Altitude then
|
||||
self.missionHoldingCoord.y=UTILS.FeetToMeters(Altitude)
|
||||
self.missionHoldingCoordAlt = UTILS.FeetToMeters(Altitude or 10000)
|
||||
end
|
||||
|
||||
self.missionHoldingCoordSpeed=Speed and Speed or nil
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the mission egress coordinate if this was defined.
|
||||
@@ -5762,6 +5973,20 @@ function AUFTRAG:GetMissionEgressCoord()
|
||||
return self.missionEgressCoord
|
||||
end
|
||||
|
||||
--- Get the mission ingress coordinate if this was defined.
|
||||
-- @param #AUFTRAG self
|
||||
-- @return Core.Point#COORDINATE Coordinate Coordinate or nil.
|
||||
function AUFTRAG:GetMissionIngressCoord()
|
||||
return self.missionIngressCoord
|
||||
end
|
||||
|
||||
--- Get the mission holding coordinate if this was defined.
|
||||
-- @param #AUFTRAG self
|
||||
-- @return Core.Point#COORDINATE Coordinate Coordinate or nil.
|
||||
function AUFTRAG:GetMissionHoldingCoord()
|
||||
return self.missionHoldingCoord
|
||||
end
|
||||
|
||||
--- Get coordinate which was set as mission waypoint coordinate.
|
||||
-- @param #AUFTRAG self
|
||||
-- @return Core.Point#COORDINATE Coordinate where the mission is executed or `#nil`.
|
||||
@@ -5796,10 +6021,27 @@ function AUFTRAG:GetMissionWaypointCoord(group, randomradius, surfacetypes)
|
||||
end
|
||||
return coord
|
||||
end
|
||||
|
||||
local coord=group:GetCoordinate()
|
||||
|
||||
-- Check if an ingress or holding coord has been explicitly set.
|
||||
if self.missionHoldingCoord then
|
||||
coord=self.missionHoldingCoord
|
||||
if self.missionHoldingCoorddAlt then
|
||||
coord:SetAltitude(self.missionHoldingCoordAlt, true)
|
||||
end
|
||||
end
|
||||
|
||||
if self.missionIngressCoord then
|
||||
coord=self.missionIngressCoord
|
||||
if self.missionIngressCoordAlt then
|
||||
coord:SetAltitude(self.missionIngressCoordAlt, true)
|
||||
end
|
||||
end
|
||||
|
||||
-- Create waypoint coordinate half way between us and the target.
|
||||
local waypointcoord=COORDINATE:New(0,0,0)
|
||||
local coord=group:GetCoordinate()
|
||||
|
||||
if coord then
|
||||
waypointcoord=coord:GetIntermediateCoordinate(self:GetTargetCoordinate(), self.missionFraction)
|
||||
else
|
||||
@@ -6001,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
|
||||
|
||||
----------------------
|
||||
@@ -6040,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)
|
||||
|
||||
@@ -6099,7 +6352,7 @@ function AUFTRAG:GetDCSMissionTask()
|
||||
local param={}
|
||||
param.zone=self:GetObjective()
|
||||
param.altitude=self.missionAltitude
|
||||
param.speed=self.missionSpeed
|
||||
param.speed=self.missionSpeed and UTILS.KmphToMps(self.missionSpeed) or nil
|
||||
|
||||
DCStask.params=param
|
||||
|
||||
@@ -6179,7 +6432,7 @@ function AUFTRAG:GetDCSMissionTask()
|
||||
local param={}
|
||||
param.target=self.engageTarget
|
||||
param.altitude=self.missionAltitude
|
||||
param.speed=self.missionSpeed
|
||||
param.speed=self.missionSpeed and UTILS.KmphToMps(self.missionSpeed) or nil
|
||||
param.lastindex=nil
|
||||
|
||||
DCStask.params=param
|
||||
@@ -6195,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
|
||||
|
||||
@@ -6352,7 +6634,7 @@ function AUFTRAG:GetDCSMissionTask()
|
||||
local param={}
|
||||
param.zone=self:GetObjective()
|
||||
param.altitude=self.missionAltitude
|
||||
param.speed=self.missionSpeed
|
||||
param.speed=self.missionSpeed and UTILS.KmphToMps(self.missionSpeed) or nil
|
||||
|
||||
DCStask.params=param
|
||||
|
||||
@@ -6388,7 +6670,7 @@ function AUFTRAG:GetDCSMissionTask()
|
||||
local param={}
|
||||
param.zone=self:GetObjective()
|
||||
param.altitude=self.missionAltitude
|
||||
param.speed=self.missionSpeed
|
||||
param.speed=self.missionSpeed and UTILS.KmphToMps(self.missionSpeed) or nil
|
||||
|
||||
DCStask.params=param
|
||||
|
||||
@@ -6408,7 +6690,27 @@ function AUFTRAG:GetDCSMissionTask()
|
||||
local param={}
|
||||
param.target=self:GetTargetData()
|
||||
param.action="Wedge"
|
||||
param.speed=self.missionSpeed
|
||||
param.speed=self.missionSpeed and UTILS.KmphToMps(self.missionSpeed) or nil
|
||||
|
||||
DCStask.params=param
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **applevangelist**
|
||||
-- @date Last Update July 2024
|
||||
-- @date Last Update July 2025
|
||||
-- @module Ops.AWACS
|
||||
-- @image OPS_AWACS.jpg
|
||||
|
||||
@@ -122,6 +122,7 @@ do
|
||||
-- @field #number TacticalModulation
|
||||
-- @field #number TacticalInterval
|
||||
-- @field Core.Set#SET_GROUP DetectionSet
|
||||
-- @field #number MaxMissionRange
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
|
||||
@@ -183,7 +184,7 @@ do
|
||||
--
|
||||
-- Add Escorts Squad (recommended, optional)
|
||||
--
|
||||
-- local Squad_Two = SQUADRON:New("Escorts",4,"Escorts North")
|
||||
-- local Squad_Two = SQUADRON:New("Escorts",4,"Escorts North") -- taking a template with 2 planes here, will result in a group of 2 escorts which can fly in formation escorting the AWACS.
|
||||
-- Squad_Two:AddMissionCapability({AUFTRAG.Type.ESCORT})
|
||||
-- Squad_Two:SetFuelLowRefuel(true)
|
||||
-- Squad_Two:SetFuelLowThreshold(0.3)
|
||||
@@ -231,12 +232,12 @@ do
|
||||
-- -- set up in the mission editor with a late activated helo named "Rock#ZONE_POLYGON". Note this also sets the BullsEye to be referenced as "Rock".
|
||||
-- -- The CAP station zone is called "Fremont". We will be on 255 AM.
|
||||
-- local testawacs = AWACS:New("AWACS North",AwacsAW,"blue",AIRBASE.Caucasus.Kutaisi,"Awacs Orbit",ZONE:FindByName("Rock"),"Fremont",255,radio.modulation.AM )
|
||||
-- -- set two escorts
|
||||
-- testawacs:SetEscort(2)
|
||||
-- -- set one escort group; this example has two units in the template group, so they can fly a nice formation.
|
||||
-- testawacs:SetEscort(1,ENUMS.Formation.FixedWing.FingerFour.Group,{x=-500,y=50,z=500},45)
|
||||
-- -- 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.
|
||||
@@ -254,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.
|
||||
@@ -508,7 +509,7 @@ do
|
||||
-- @field #AWACS
|
||||
AWACS = {
|
||||
ClassName = "AWACS", -- #string
|
||||
version = "0.2.65", -- #string
|
||||
version = "0.2.74", -- #string
|
||||
lid = "", -- #string
|
||||
coalition = coalition.side.BLUE, -- #number
|
||||
coalitiontxt = "blue", -- #string
|
||||
@@ -605,6 +606,7 @@ AWACS = {
|
||||
TacticalModulation = radio.modulation.AM,
|
||||
TacticalInterval = 120,
|
||||
DetectionSet = nil,
|
||||
MaxMissionRange = 125,
|
||||
}
|
||||
|
||||
---
|
||||
@@ -1121,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
|
||||
@@ -1240,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.
|
||||
|
||||
|
||||
@@ -1363,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
|
||||
@@ -1572,6 +1596,25 @@ 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
|
||||
-- @return #AWACS self
|
||||
function AWACS:SetMaxMissionRange(NM)
|
||||
self.MaxMissionRange = NM or 125
|
||||
return self
|
||||
end
|
||||
|
||||
--- [User] Add additional frequency and modulation for AWACS SRS output.
|
||||
-- @param #AWACS self
|
||||
-- @param #number Frequency The frequency to add, e.g. 132.5
|
||||
@@ -1762,7 +1805,7 @@ function AWACS:_EventHandler(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
if Event.id == EVENTS.PlayerLeaveUnit then --player left unit
|
||||
if Event.id == EVENTS.PlayerLeaveUnit and Event.IniGroupName then --player left unit
|
||||
-- check known player?
|
||||
self:T("Player group left unit: " .. Event.IniGroupName)
|
||||
self:T("Player name left: " .. Event.IniPlayerName)
|
||||
@@ -1988,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
|
||||
@@ -2008,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
|
||||
@@ -2080,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
|
||||
@@ -2093,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
|
||||
@@ -2158,9 +2254,12 @@ end
|
||||
|
||||
--- [User] Set AWACS Escorts Template
|
||||
-- @param #AWACS self
|
||||
-- @param #number EscortNumber Number of fighther planes to accompany this AWACS. 0 or nil means no escorts.
|
||||
-- @param #number EscortNumber Number of fighther plane GROUPs to accompany this AWACS. 0 or nil means no escorts. If you want >1 plane in an escort group, you can either set the respective squadron grouping to the desired number, or use a template for escorts with >1 unit.
|
||||
-- @param #number Formation Formation the escort should take (if more than one plane), e.g. `ENUMS.Formation.FixedWing.FingerFour.Group`. Formation is used on GROUP level, multiple groups of one unit will NOT conform to this formation.
|
||||
-- @param #table OffsetVector Offset the escorts should fly behind the AWACS, given as table, distance in meters, e.g. `{x=-500,y=0,z=500}` - 500m behind (negative value) and to the right (negative for left), no vertical separation (positive over, negative under the AWACS flight). For multiple groups, the vectors will be slightly changed to avoid collisions.
|
||||
-- @param #number EscortEngageMaxDistance Escorts engage air targets max this NM away, defaults to 45NM.
|
||||
-- @return #AWACS self
|
||||
function AWACS:SetEscort(EscortNumber)
|
||||
function AWACS:SetEscort(EscortNumber,Formation,OffsetVector,EscortEngageMaxDistance)
|
||||
self:T(self.lid.."SetEscort")
|
||||
if EscortNumber and EscortNumber > 0 then
|
||||
self.HasEscorts = true
|
||||
@@ -2169,6 +2268,9 @@ function AWACS:SetEscort(EscortNumber)
|
||||
self.HasEscorts = false
|
||||
self.EscortNumber = 0
|
||||
end
|
||||
self.EscortFormation = Formation
|
||||
self.OffsetVec = OffsetVector or {x=500,y=100,z=500}
|
||||
self.EscortEngageMaxDistance = EscortEngageMaxDistance or 45
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -2223,11 +2325,26 @@ function AWACS:_StartEscorts(Shiftchange)
|
||||
local group = AwacsFG:GetGroup()
|
||||
|
||||
local timeonstation = (self.EscortsTimeOnStation + self.ShiftChangeTime) * 3600 -- hours to seconds
|
||||
local OffsetX = 500
|
||||
local OffsetY = 500
|
||||
local OffsetZ = 500
|
||||
if self.OffsetVec then
|
||||
OffsetX = self.OffsetVec.x or 500
|
||||
OffsetY = self.OffsetVec.y or 500
|
||||
OffsetZ = self.OffsetVec.z or 500
|
||||
end
|
||||
|
||||
for i=1,self.EscortNumber do
|
||||
-- every
|
||||
local escort = AUFTRAG:NewESCORT(group, {x= -100*((i + (i%2))/2), y=0, z=(100 + 100*((i + (i%2))/2))*(-1)^i},45,{"Air"})
|
||||
escort:SetRequiredAssets(1)
|
||||
-- every
|
||||
local escort = AUFTRAG:NewESCORT(group, {x= OffsetX*((i + (i%2))/2), y=OffsetY*((i + (i%2))/2), z=(OffsetZ + OffsetZ*((i + (i%2))/2))*(-1)^i},self.EscortEngageMaxDistance,{"Air"})
|
||||
--local escort = AUFTRAG:NewESCORT(group,self.OffsetVec,self.EscortEngageMaxDistance,{"Air"})
|
||||
--escort:SetRequiredAssets(self.EscortNumber)
|
||||
escort:SetTime(nil,timeonstation)
|
||||
if self.Escortformation then
|
||||
escort:SetFormation(self.Escortformation)
|
||||
end
|
||||
escort:SetMissionRange(self.MaxMissionRange)
|
||||
|
||||
self.AirWing:AddMission(escort)
|
||||
self.CatchAllMissions[#self.CatchAllMissions+1] = escort
|
||||
|
||||
@@ -2434,7 +2551,7 @@ function AWACS:_GetCallSign(Group,GID, IsPlayer)
|
||||
|
||||
local callsign = "Ghost 1"
|
||||
if Group and Group:IsAlive() then
|
||||
callsign = Group:GetCustomCallSign(self.callsignshort,self.keepnumber,self.callsignTranslations)
|
||||
callsign = Group:GetCustomCallSign(self.callsignshort,self.keepnumber,self.callsignTranslations,self.callsignCustomFunc,self.callsignCustomArgs)
|
||||
end
|
||||
return callsign
|
||||
end
|
||||
@@ -2443,10 +2560,12 @@ end
|
||||
-- @param #AWACS self
|
||||
-- @param #boolean ShortCallsign If true, only call out the major flight number
|
||||
-- @param #boolean Keepnumber If true, keep the **customized callsign** in the #GROUP name as-is, no amendments or numbers.
|
||||
-- @param #table CallsignTranslations (optional) Table to translate between DCS standard callsigns and bespoke ones. Does not apply if using customized
|
||||
-- @param #table CallsignTranslations (Optional) Table to translate between DCS standard callsigns and bespoke ones. Does not apply if using customized.
|
||||
-- callsigns from playername or group name.
|
||||
-- @param #func CallsignCustomFunc (Optional) For player names only(!). If given, this function will return the callsign. Needs to take the groupname and the playername as first two arguments.
|
||||
-- @param #arg ... (Optional) Comma separated arguments to add to the custom function call after groupname and playername.
|
||||
-- @return #AWACS self
|
||||
function AWACS:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTranslations)
|
||||
function AWACS:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTranslations,CallsignCustomFunc,...)
|
||||
if not ShortCallsign or ShortCallsign == false then
|
||||
self.callsignshort = false
|
||||
else
|
||||
@@ -2454,6 +2573,8 @@ function AWACS:SetCallSignOptions(ShortCallsign,Keepnumber,CallsignTranslations)
|
||||
end
|
||||
self.keepnumber = Keepnumber or false
|
||||
self.callsignTranslations = CallsignTranslations
|
||||
self.callsignCustomFunc = CallsignCustomFunc
|
||||
self.callsignCustomArgs = arg or {}
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -3227,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
|
||||
@@ -3626,7 +3749,7 @@ function AWACS:_CheckIn(Group)
|
||||
managedgroup.LastTasking = timer.getTime()
|
||||
|
||||
GID = managedgroup.GID
|
||||
self.ManagedGrps[self.ManagedGrpID]=managedgroup
|
||||
self.ManagedGrps[self.ManagedGrpID]=managedgroup
|
||||
|
||||
local alphacheckbulls = self:_ToStringBULLS(Group:GetCoordinate())
|
||||
local alphacheckbullstts = self:_ToStringBULLS(Group:GetCoordinate(),false,true)
|
||||
@@ -3893,6 +4016,12 @@ function AWACS:_SetClientMenus()
|
||||
checkin = checkin,
|
||||
}
|
||||
self.clientmenus:Push(menus,cgrpname)
|
||||
-- catch errors - when this entry is built we should NOT have a managed entry
|
||||
local GID,hasentry = self:_GetManagedGrpID(cgrp)
|
||||
if hasentry then
|
||||
-- this user is checked in but has the check in entry ... not good.
|
||||
self:_CheckOut(cgrp,GID,true)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
@@ -4028,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)
|
||||
@@ -4074,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
|
||||
@@ -4101,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
|
||||
@@ -4244,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)
|
||||
@@ -5036,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
|
||||
@@ -5739,7 +5892,7 @@ function AWACS:_AssignPilotToTarget(Pilots,Targets)
|
||||
local intercept = AUFTRAG:NewINTERCEPT(Target.Target)
|
||||
intercept:SetWeaponExpend(AI.Task.WeaponExpend.ALL)
|
||||
intercept:SetWeaponType(ENUMS.WeaponFlag.Auto)
|
||||
|
||||
intercept:SetMissionRange(self.MaxMissionRange)
|
||||
-- TODO
|
||||
-- now this is going to be interesting...
|
||||
-- Check if the target left the "hot" area or is dead already
|
||||
@@ -5787,6 +5940,7 @@ function AWACS:_AssignPilotToTarget(Pilots,Targets)
|
||||
AnchorSpeed = UTILS.KnotsToAltKIAS(AnchorSpeed,Angels)
|
||||
local Anchor = self.AnchorStacks:ReadByPointer(Pilot.AnchorStackNo) -- #AWACS.AnchorData
|
||||
local capauftrag = AUFTRAG:NewCAP(Anchor.StationZone,Angels,AnchorSpeed,Anchor.StationZoneCoordinate,0,15,{})
|
||||
capauftrag:SetMissionRange(self.MaxMissionRange)
|
||||
capauftrag:SetTime(nil,((self.CAPTimeOnStation*3600)+(15*60)))
|
||||
Pilot.FlightGroup:AddMission(capauftrag)
|
||||
|
||||
@@ -5881,29 +6035,43 @@ 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
|
||||
-- set up the AWACS and let it orbit
|
||||
local AwacsAW = self.AirWing -- Ops.Airwing#AIRWING
|
||||
local mission = AUFTRAG:NewORBIT_RACETRACK(self.OrbitZone:GetCoordinate(),self.AwacsAngels*1000,self.Speed,self.Heading,self.Leg)
|
||||
mission:SetMissionRange(self.MaxMissionRange)
|
||||
mission:SetRequiredAttribute({ GROUP.Attribute.AIR_AWACS }) -- prefered plane type, thanks to Heart8reaker
|
||||
local timeonstation = (self.AwacsTimeOnStation + self.ShiftChangeTime) * 3600
|
||||
mission:SetTime(nil,timeonstation)
|
||||
self.CatchAllMissions[#self.CatchAllMissions+1] = mission
|
||||
@@ -6046,6 +6214,7 @@ function AWACS:_CheckAwacsStatus()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------
|
||||
-- AWACS
|
||||
--------------------------------
|
||||
@@ -6194,12 +6363,13 @@ function AWACS:_CheckAwacsStatus()
|
||||
|
||||
report:Add("====================")
|
||||
|
||||
local RESMission
|
||||
-- Check for replacement mission - if any
|
||||
if self.ShiftChangeEscortsFlag and self.ShiftChangeEscortsRequested then -- Ops.Auftrag#AUFTRAG
|
||||
ESmission = self.EscortMissionReplacement[i]
|
||||
local esstatus = ESmission:GetState()
|
||||
local ESmissiontime = (timer.getTime() - self.EscortsTimeStamp)
|
||||
local ESTOSLeft = UTILS.Round((((self.EscortsTimeOnStation+self.ShiftChangeTime)*3600) - ESmissiontime),0) -- seconds
|
||||
RESMission = self.EscortMissionReplacement[i]
|
||||
local esstatus = RESMission:GetState()
|
||||
local RESMissiontime = (timer.getTime() - self.EscortsTimeStamp)
|
||||
local ESTOSLeft = UTILS.Round((((self.EscortsTimeOnStation+self.ShiftChangeTime)*3600) - RESMissiontime),0) -- seconds
|
||||
ESTOSLeft = UTILS.Round(ESTOSLeft/60,0) -- minutes
|
||||
local ChangeTime = UTILS.Round(((self.ShiftChangeTime * 3600)/60),0)
|
||||
|
||||
@@ -6207,7 +6377,7 @@ function AWACS:_CheckAwacsStatus()
|
||||
report:Add(string.format("Auftrag Status: %s",esstatus))
|
||||
report:Add(string.format("TOS Left: %d min",ESTOSLeft))
|
||||
|
||||
local OpsGroups = ESmission:GetOpsGroups()
|
||||
local OpsGroups = RESMission:GetOpsGroups()
|
||||
local OpsGroup = self:_GetAliveOpsGroupFromTable(OpsGroups) -- Ops.OpsGroup#OPSGROUP
|
||||
if OpsGroup then
|
||||
local OpsName = OpsGroup:GetName() or "Unknown"
|
||||
@@ -6219,13 +6389,13 @@ function AWACS:_CheckAwacsStatus()
|
||||
report:Add("***** Cannot obtain (yet) this missions OpsGroup!")
|
||||
end
|
||||
|
||||
if ESmission:IsExecuting() then
|
||||
if RESMission and RESMission:IsExecuting() then
|
||||
-- make the actual change in the queue
|
||||
self.ShiftChangeEscortsFlag = false
|
||||
self.ShiftChangeEscortsRequested = false
|
||||
-- cancel old mission
|
||||
if ESmission and ESmission:IsNotOver() then
|
||||
ESmission:Cancel()
|
||||
ESmission:__Cancel(1)
|
||||
end
|
||||
self.EscortMission[i] = self.EscortMissionReplacement[i]
|
||||
self.EscortMissionReplacement[i] = nil
|
||||
@@ -6477,6 +6647,7 @@ function AWACS:onafterAssignedAnchor(From, Event, To, GID, Anchor, AnchorStackNo
|
||||
if auftragtype == AUFTRAG.Type.ALERT5 then
|
||||
-- all correct
|
||||
local capauftrag = AUFTRAG:NewCAP(Anchor.StationZone,Angels*1000,AnchorSpeed,Anchor.StationZone:GetCoordinate(),0,15,{})
|
||||
capauftrag:SetMissionRange(self.MaxMissionRange)
|
||||
capauftrag:SetTime(nil,((self.CAPTimeOnStation*3600)+(15*60)))
|
||||
capauftrag:AddAsset(managedgroup.FlightGroup)
|
||||
self.CatchAllMissions[#self.CatchAllMissions+1] = capauftrag
|
||||
@@ -6840,7 +7011,8 @@ function AWACS:onafterAwacsShiftChange(From,Event,To)
|
||||
self.CatchAllMissions[#self.CatchAllMissions+1] = mission
|
||||
local timeonstation = (self.AwacsTimeOnStation + self.ShiftChangeTime) * 3600
|
||||
mission:SetTime(nil,timeonstation)
|
||||
|
||||
mission:SetMissionRange(self.MaxMissionRange)
|
||||
|
||||
AwacsAW:AddMission(mission)
|
||||
|
||||
self.AwacsMissionReplacement = mission
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user