mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-10-29 16:58:06 +00:00
Compare commits
488 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
56d7638617 | ||
|
|
386be19b8b | ||
|
|
42221f406a | ||
|
|
0e4eb9a263 | ||
|
|
654f752ca0 | ||
|
|
3da6187782 | ||
|
|
0bd79d518f | ||
|
|
86798ae9ea | ||
|
|
dfc63de745 | ||
|
|
2830e6d199 | ||
|
|
8c07573f8f | ||
|
|
f3af0262df | ||
|
|
d7841b47a5 | ||
|
|
0f270a6a35 | ||
|
|
92db68c36b | ||
|
|
8432f46e48 | ||
|
|
e8246b3b90 | ||
|
|
eb4e7b9281 | ||
|
|
e91bcdfe5e | ||
|
|
1f1a068d45 | ||
|
|
a57c0a8c7d | ||
|
|
1e2190a6cc | ||
|
|
1644c1dc5b | ||
|
|
5fc3798c42 | ||
|
|
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 | ||
|
|
baa3f3234e | ||
|
|
ab5a4c43c3 | ||
|
|
1f20b830b7 | ||
|
|
ce61f454bf | ||
|
|
4772dafe7f | ||
|
|
629925e2d8 | ||
|
|
17f672dad4 | ||
|
|
0a9717a8c2 | ||
|
|
e0049bea2b | ||
|
|
73525feb68 | ||
|
|
0531b8f57e | ||
|
|
98e2997e26 | ||
|
|
7d3bffcfef | ||
|
|
5b76ec6b99 | ||
|
|
eeca95c77f | ||
|
|
e406fb0c88 | ||
|
|
bdec6eb82d | ||
|
|
9bdacfbcf7 | ||
|
|
be2eda25df | ||
|
|
fa8e1d2a56 | ||
|
|
5242754c60 | ||
|
|
3dbafd7a19 | ||
|
|
56e751193a | ||
|
|
b2b258cff0 | ||
|
|
75068a2a26 | ||
|
|
d82df93eff | ||
|
|
5168380eac | ||
|
|
a7ffbab850 | ||
|
|
a238294079 | ||
|
|
accb4b45fb | ||
|
|
ba4ff5c9e9 | ||
|
|
171c450576 | ||
|
|
f35078b426 | ||
|
|
8921007805 | ||
|
|
8fd3034503 | ||
|
|
a20b6e8a62 | ||
|
|
4ce6cc7776 | ||
|
|
434296ab11 | ||
|
|
a5232f49c0 | ||
|
|
befc8207f5 | ||
|
|
23947b7c30 | ||
|
|
da91b710a9 | ||
|
|
d656bbc014 | ||
|
|
ed23f11e4a | ||
|
|
068465a8f2 | ||
|
|
4e24a7bf80 | ||
|
|
1ece7238dc | ||
|
|
db53f427e3 | ||
|
|
d2e2c51275 | ||
|
|
691748082b | ||
|
|
b294ef10c8 | ||
|
|
5d40091947 | ||
|
|
7453a6c55d | ||
|
|
153ef7cd08 | ||
|
|
b6a550a247 | ||
|
|
3568f27150 | ||
|
|
c46061466c | ||
|
|
9c7b5e8506 | ||
|
|
b11df6b523 | ||
|
|
66dcd44fb8 | ||
|
|
f35237f86f | ||
|
|
f19c877a11 | ||
|
|
792a487eeb | ||
|
|
f5e8bd0ffc | ||
|
|
cbdbf36f32 | ||
|
|
1116b19b00 | ||
|
|
edb490fa97 | ||
|
|
9fa16c6385 | ||
|
|
a0ee957493 | ||
|
|
f32c303c0a | ||
|
|
6febbbc8e6 | ||
|
|
d5aa9eaf0f | ||
|
|
321b2d761d | ||
|
|
18581e4a78 | ||
|
|
b03905154d | ||
|
|
2fda1709bc | ||
|
|
39f626390a | ||
|
|
aa1e12163d | ||
|
|
abb7f860ae | ||
|
|
cffada1a1e | ||
|
|
f8d91798e3 | ||
|
|
548aa8d5a9 | ||
|
|
1e929d1d19 | ||
|
|
d5f215505e | ||
|
|
851660c793 | ||
|
|
ee57b46c14 | ||
|
|
36eac9fccc | ||
|
|
1f630ab490 | ||
|
|
8f0c6948ac | ||
|
|
eb2380c3f6 | ||
|
|
df3e182de4 | ||
|
|
bda7988118 | ||
|
|
72deb9ee17 | ||
|
|
ced1f30f3f | ||
|
|
47c0006537 | ||
|
|
21e5c124c0 | ||
|
|
3ff374e1b9 | ||
|
|
02000be9af | ||
|
|
663c6cead0 | ||
|
|
6867df58c4 | ||
|
|
639625d3d5 | ||
|
|
0ace200e5a | ||
|
|
e76c26ff59 | ||
|
|
cb11de6f9c | ||
|
|
cdd78e5163 | ||
|
|
a8da02774a | ||
|
|
04258a69c4 | ||
|
|
49882f03d9 |
@@ -157,6 +157,8 @@ ASTAR = {
|
|||||||
-- @field #number surfacetype Surface type.
|
-- @field #number surfacetype Surface type.
|
||||||
-- @field #table valid Cached valid/invalid nodes.
|
-- @field #table valid Cached valid/invalid nodes.
|
||||||
-- @field #table cost Cached cost.
|
-- @field #table cost Cached cost.
|
||||||
|
-- @field Core.Pathline#PATHLINE pathline Pathline that node is part of.
|
||||||
|
-- @field Core.Pathline#PATHLINE.Point pathpoint Pathline point.
|
||||||
|
|
||||||
--- ASTAR infinity.
|
--- ASTAR infinity.
|
||||||
-- @field #number INF
|
-- @field #number INF
|
||||||
@@ -164,7 +166,7 @@ ASTAR.INF=1/0
|
|||||||
|
|
||||||
--- ASTAR class version.
|
--- ASTAR class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
ASTAR.version="0.4.0"
|
ASTAR.version="0.5.0"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- TODO list
|
-- TODO list
|
||||||
@@ -172,6 +174,7 @@ ASTAR.version="0.4.0"
|
|||||||
|
|
||||||
-- TODO: Add more valid neighbour functions.
|
-- TODO: Add more valid neighbour functions.
|
||||||
-- TODO: Write docs.
|
-- TODO: Write docs.
|
||||||
|
-- DONE: Add pathlines for seach/valid neighbours.
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- Constructor
|
-- Constructor
|
||||||
@@ -244,7 +247,10 @@ end
|
|||||||
function ASTAR:AddNode(Node)
|
function ASTAR:AddNode(Node)
|
||||||
|
|
||||||
self.nodes[Node.id]=Node
|
self.nodes[Node.id]=Node
|
||||||
self.Nnodes=self.Nnodes+1
|
self.Nnodes=self.Nnodes+1
|
||||||
|
|
||||||
|
self:T3(self.lid..string.format("Adding node UID=%d", Node.id))
|
||||||
|
--Node.coordinate:MarkToAll(string.format("Node ID=%d", Node.id))
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -262,6 +268,47 @@ function ASTAR:AddNodeFromCoordinate(Coordinate)
|
|||||||
return node
|
return node
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Adds nodes to the table of grid nodes from a PATHLINE.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param Core.Pathline#PATHLINE Pathline Pathline or name of pathline. Has to exist.
|
||||||
|
-- @return #ASTAR self
|
||||||
|
function ASTAR:AddNodeFromPathlineName(Pathline)
|
||||||
|
|
||||||
|
if type(Pathline)=="string" then
|
||||||
|
Pathline=PATHLINE:FindByName(Pathline)
|
||||||
|
end
|
||||||
|
|
||||||
|
if Pathline then
|
||||||
|
|
||||||
|
for i,_point in pairs(Pathline.points) do
|
||||||
|
local point=_point --Core.Pathline#PATHLINE.Point
|
||||||
|
|
||||||
|
-- Create node from point coordinate.
|
||||||
|
local node=self:AddNodeFromCoordinate(COORDINATE:NewFromVec3(point.vec3))
|
||||||
|
|
||||||
|
-- Add pathline parameters.
|
||||||
|
node.pathline=Pathline
|
||||||
|
node.pathpoint=point
|
||||||
|
|
||||||
|
-- Debug.
|
||||||
|
local name=node.pathline and node.pathline.name or "N/A"
|
||||||
|
local idx=node.pathline and node.pathline:_GetPointIndex(node.pathpoint) or "N/A"
|
||||||
|
|
||||||
|
-- Debug message.
|
||||||
|
self:T(self.lid..string.format("Adding node UID=%d pathline=%s [%s]", node.id, name, tostring(idx)))
|
||||||
|
|
||||||
|
-- Debug mark
|
||||||
|
--node.coordinate:MarkToAll(string.format("Node ID=%d\npathline=%s [%s]", node.id, name, tostring(idx)))
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
env.error("FF error pathline")
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Check if the coordinate of a node has is at a valid surface type.
|
--- Check if the coordinate of a node has is at a valid surface type.
|
||||||
-- @param #ASTAR self
|
-- @param #ASTAR self
|
||||||
-- @param #ASTAR.Node Node The node to be added.
|
-- @param #ASTAR.Node Node The node to be added.
|
||||||
@@ -340,6 +387,18 @@ function ASTAR:SetValidNeighbourRoad(MaxDistance)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set valid neighbours to be on the same pathline or not further apart than 10 meters to jump from one pathline to another.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #number MaxDistance Max allowed distance between nodes of different pathlines in meters. Default is 10 m.
|
||||||
|
-- @return #ASTAR self
|
||||||
|
function ASTAR:SetValidNeighbourPathline(MaxDistance)
|
||||||
|
|
||||||
|
self:SetValidNeighbourFunction(ASTAR.Pathline, MaxDistance)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Set the function which calculates the "cost" to go from one to another node.
|
--- Set the function which calculates the "cost" to go from one to another node.
|
||||||
-- The first to arguments of this function are always the two nodes under consideration. But you can add optional arguments.
|
-- The first to arguments of this function are always the two nodes under consideration. But you can add optional arguments.
|
||||||
-- Very often the distance between nodes is a good measure for the cost.
|
-- Very often the distance between nodes is a good measure for the cost.
|
||||||
@@ -384,7 +443,7 @@ end
|
|||||||
-- @return #ASTAR self
|
-- @return #ASTAR self
|
||||||
function ASTAR:SetCostRoad()
|
function ASTAR:SetCostRoad()
|
||||||
|
|
||||||
self:SetCostFunction(ASTAR)
|
self:SetCostFunction(ASTAR.Road)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -544,6 +603,55 @@ function ASTAR.Road(nodeA, nodeB)
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Function to check if two nodes are on the same pathline or if nodes are less than 10 meters apart.
|
||||||
|
-- @param #ASTAR.Node nodeA First node.
|
||||||
|
-- @param #ASTAR.Node nodeB Other node.
|
||||||
|
-- @param #number distmax Max distance in meters. Default is 10 m.
|
||||||
|
-- @return #boolean If true, two nodes are connected.
|
||||||
|
function ASTAR.Pathline(nodeA, nodeB, distmax)
|
||||||
|
|
||||||
|
distmax=distmax or 10
|
||||||
|
|
||||||
|
if nodeA.pathline.name==nodeB.pathline.name then
|
||||||
|
|
||||||
|
-- Nodes are on the same pathline. We use the index to check if they are neighbours.
|
||||||
|
|
||||||
|
local pathline=nodeA.pathline
|
||||||
|
|
||||||
|
local idxA=pathline:_GetPointIndex(nodeA.pathpoint)
|
||||||
|
local idxB=pathline:_GetPointIndex(nodeB.pathpoint)
|
||||||
|
|
||||||
|
if math.abs(idxA-idxB)<=1 then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
-- Check if nodeB is close to pathline of nodeA.
|
||||||
|
local c, dist, segA=nodeA.pathline:GetClosestPoint3D(nodeB.coordinate)
|
||||||
|
local seg=segA --Core.Pathline#PATHLINE.Segment
|
||||||
|
|
||||||
|
if dist<distmax and (nodeA.pathpoint.uid==seg.p1.uid or nodeA.pathpoint.uid==seg.p2.uid) then
|
||||||
|
--env.info(string.format("FF NodeB=%d [pathline=%s] is close to NodeA=%d [pathline=%s] ==> valid neighbour", nodeB.id, nodeB.pathline.name, nodeA.id, nodeA.pathline.name))
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Check if nodeA is close to pathline of nodeB.
|
||||||
|
local c, dist, segB=nodeB.pathline:GetClosestPoint3D(nodeA.coordinate)
|
||||||
|
local seg=segB --Core.Pathline#PATHLINE.Segment
|
||||||
|
|
||||||
|
if dist<distmax and (nodeB.pathpoint.uid==seg.p1.uid or nodeB.pathpoint.uid==seg.p2.uid) then
|
||||||
|
--env.info(string.format("FF NodeA=%d [pathline=%s] is close to NodeB=%d [pathline=%s] ==> valid neighbour", nodeA.id, nodeA.pathline.name, nodeB.id, nodeB.pathline.name))
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Function to check if distance between two nodes is less than a threshold distance.
|
--- Function to check if distance between two nodes is less than a threshold distance.
|
||||||
-- @param #ASTAR.Node nodeA First node.
|
-- @param #ASTAR.Node nodeA First node.
|
||||||
-- @param #ASTAR.Node nodeB Other node.
|
-- @param #ASTAR.Node nodeB Other node.
|
||||||
@@ -567,7 +675,9 @@ end
|
|||||||
-- @param #ASTAR.Node nodeB Other node.
|
-- @param #ASTAR.Node nodeB Other node.
|
||||||
-- @return #number Distance between the two nodes.
|
-- @return #number Distance between the two nodes.
|
||||||
function ASTAR.Dist2D(nodeA, nodeB)
|
function ASTAR.Dist2D(nodeA, nodeB)
|
||||||
local dist=nodeA.coordinate:Get2DDistance(nodeB)
|
local dist=nodeA.coordinate:Get2DDistance(nodeB.coordinate)
|
||||||
|
--local text=string.format("FF Cost Dist2D NodeA=%d-->NodeB=%d = %.1f", nodeA.id, nodeB.id, dist)
|
||||||
|
--env.info(text)
|
||||||
return dist
|
return dist
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -594,7 +704,7 @@ function ASTAR.DistRoad(nodeA, nodeB)
|
|||||||
local dist=0
|
local dist=0
|
||||||
|
|
||||||
for i=2,#path do
|
for i=2,#path do
|
||||||
local b=path[i] --DCS#Vec2
|
local b=path[i] --DCS#Vec2
|
||||||
local a=path[i-1] --DCS#Vec2
|
local a=path[i-1] --DCS#Vec2
|
||||||
|
|
||||||
dist=dist+UTILS.VecDist2D(a,b)
|
dist=dist+UTILS.VecDist2D(a,b)
|
||||||
@@ -604,7 +714,6 @@ function ASTAR.DistRoad(nodeA, nodeB)
|
|||||||
return dist
|
return dist
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
return math.huge
|
return math.huge
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -614,10 +723,11 @@ end
|
|||||||
|
|
||||||
--- Find the closest node from a given coordinate.
|
--- Find the closest node from a given coordinate.
|
||||||
-- @param #ASTAR self
|
-- @param #ASTAR self
|
||||||
-- @param Core.Point#COORDINATE Coordinate.
|
-- @param Core.Point#COORDINATE Coordinate Reference coordinate.
|
||||||
-- @return #ASTAR.Node Cloest node to the coordinate.
|
-- @param #table ExcludeNodes Table of nodes that are excluded.
|
||||||
|
-- @return #ASTAR.Node Closest node to the coordinate.
|
||||||
-- @return #number Distance to closest node in meters.
|
-- @return #number Distance to closest node in meters.
|
||||||
function ASTAR:FindClosestNode(Coordinate)
|
function ASTAR:FindClosestNode(Coordinate, ExcludeNodes)
|
||||||
|
|
||||||
local distMin=math.huge
|
local distMin=math.huge
|
||||||
local closeNode=nil
|
local closeNode=nil
|
||||||
@@ -625,11 +735,15 @@ function ASTAR:FindClosestNode(Coordinate)
|
|||||||
for _,_node in pairs(self.nodes) do
|
for _,_node in pairs(self.nodes) do
|
||||||
local node=_node --#ASTAR.Node
|
local node=_node --#ASTAR.Node
|
||||||
|
|
||||||
local dist=node.coordinate:Get2DDistance(Coordinate)
|
if ExcludeNodes==nil or self:_IsNodeNotInTable(ExcludeNodes, node) then
|
||||||
|
|
||||||
if dist<distMin then
|
local dist=node.coordinate:Get2DDistance(Coordinate)
|
||||||
distMin=dist
|
|
||||||
closeNode=node
|
if dist<distMin then
|
||||||
|
distMin=dist
|
||||||
|
closeNode=node
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -637,38 +751,162 @@ function ASTAR:FindClosestNode(Coordinate)
|
|||||||
return closeNode, distMin
|
return closeNode, distMin
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Find the closest pathline to a given reference coordinate.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate Reference coordinate.
|
||||||
|
-- @return Core.Pathline#PATHLINE Closest pathline
|
||||||
|
-- @return #number Distance in meters.
|
||||||
|
-- @return DCS#Vec3 Closest point on pathline to the ref coordinate.
|
||||||
|
-- @return Core.Pathline#PATHLINE.Segment Segment.
|
||||||
|
function ASTAR:FindClosestPathline(Coordinate)
|
||||||
|
|
||||||
|
local pathline=nil --Core.Pathline#PATHLINE
|
||||||
|
local dist=math.huge
|
||||||
|
local vec3=nil
|
||||||
|
local S=nil
|
||||||
|
|
||||||
|
for _,_node in pairs(self.nodes) do
|
||||||
|
local node=_node --#ASTAR.Node
|
||||||
|
|
||||||
|
if node.pathline then
|
||||||
|
|
||||||
|
local vec, d, s=node.pathline:GetClosestPoint3D(Coordinate)
|
||||||
|
|
||||||
|
if d<dist then
|
||||||
|
pathline=node.pathline
|
||||||
|
dist=d
|
||||||
|
vec3=vec
|
||||||
|
S=s
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if pathline then
|
||||||
|
-- Debug info.
|
||||||
|
self:T(self.lid..string.format("Closest pathline %s: dist=%.1f", pathline.name, dist))
|
||||||
|
end
|
||||||
|
|
||||||
|
return pathline, dist, vec3, S
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Find the closest node to the given coordinate.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param Core.Point#COORDINATE Coord Reference coordinate.
|
||||||
|
-- @param #table ExcludeNodes Nodes that are excluded.
|
||||||
|
-- @return #ASTAR.Node The node that was fround
|
||||||
|
function ASTAR:_FindClosestTerminalNode(Coord, ExcludeNodes)
|
||||||
|
|
||||||
|
-- Find the closest pathline to the ref coordinate.
|
||||||
|
local pathline, dist, vec3, s=self:FindClosestPathline(Coord)
|
||||||
|
|
||||||
|
-- Find the closest node to the given start coordinate.
|
||||||
|
local node, dist2=self:FindClosestNode(Coord)
|
||||||
|
|
||||||
|
if pathline and vec3 and dist and dist2>dist then
|
||||||
|
|
||||||
|
-- Create a node on the closest pathline so we first go straight there and then along the pathline.
|
||||||
|
local node=self:AddNodeFromCoordinate(COORDINATE:NewFromVec3(vec3))
|
||||||
|
|
||||||
|
-- We also need the pathline point.
|
||||||
|
local point=pathline:AddPointFromVec3(vec3, nil, s.p1)
|
||||||
|
|
||||||
|
node.pathline=pathline
|
||||||
|
node.pathpoint=point
|
||||||
|
|
||||||
|
self:T2(self.lid..string.format("Added new node=%d, which is closest to start coord. dist=%.1f m", node.id, dist))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Find the closest node to the given start coordinate.
|
||||||
|
local Node, dist3=self:FindClosestNode(Coord, ExcludeNodes)
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T(self.lid..string.format("CLOSEST node ID=%d, distance=%.1f", Node.id, dist3))
|
||||||
|
|
||||||
|
return Node, dist3
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Find the start node.
|
--- Find the start node.
|
||||||
-- @param #ASTAR self
|
-- @param #ASTAR self
|
||||||
-- @param #ASTAR.Node Node The node to be added to the nodes table.
|
|
||||||
-- @return #ASTAR self
|
-- @return #ASTAR self
|
||||||
function ASTAR:FindStartNode()
|
function ASTAR:FindStartNode()
|
||||||
|
|
||||||
|
-- Find the closest pathline to the
|
||||||
|
local pathline, dist, vec3, s=self:FindClosestPathline(self.startCoord)
|
||||||
|
|
||||||
local node, dist=self:FindClosestNode(self.startCoord)
|
-- Find the closest node to the given start coordinate.
|
||||||
|
local node, dist2=self:FindClosestNode(self.startCoord)
|
||||||
|
|
||||||
self.startNode=node
|
if pathline and vec3 and dist and dist2>dist then
|
||||||
|
|
||||||
if dist>1000 then
|
-- Create a node on the closest pathline so we first go straight there and then along the pathline.
|
||||||
self:T(self.lid.."Adding start node to node grid!")
|
local node=self:AddNodeFromCoordinate(COORDINATE:NewFromVec3(vec3))
|
||||||
self:AddNode(node)
|
|
||||||
|
-- We also need the pathline point.
|
||||||
|
local point=pathline:AddPointFromVec3(vec3, nil, s.p1)
|
||||||
|
|
||||||
|
node.pathline=pathline
|
||||||
|
node.pathpoint=point
|
||||||
|
|
||||||
|
self:T2(self.lid..string.format("Added new node=%d, which is closest to start coord. dist=%.1f m", node.id, dist))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Find the closest node to the given start coordinate.
|
||||||
|
self.startNode, dist2=self:FindClosestNode(self.startCoord)
|
||||||
|
|
||||||
|
--self.startNode.coordinate:MarkToAll("Start Node")
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T(self.lid..string.format("START node ID=%d", self.startNode.id))
|
||||||
|
|
||||||
|
-- Not sure why I did this. The node does not need to be added again as it is already contained in self.nodes!
|
||||||
|
-- if dist>1000 then
|
||||||
|
-- self:T(self.lid.."Adding start node to node grid!")
|
||||||
|
-- self:AddNode(node)
|
||||||
|
-- end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Add a node.
|
--- Find the end node.
|
||||||
-- @param #ASTAR self
|
-- @param #ASTAR self
|
||||||
-- @param #ASTAR.Node Node The node to be added to the nodes table.
|
|
||||||
-- @return #ASTAR self
|
-- @return #ASTAR self
|
||||||
function ASTAR:FindEndNode()
|
function ASTAR:FindEndNode()
|
||||||
|
|
||||||
local node, dist=self:FindClosestNode(self.endCoord)
|
|
||||||
|
|
||||||
self.endNode=node
|
|
||||||
|
|
||||||
if dist>1000 then
|
local pathline, dist, vec3, s=self:FindClosestPathline(self.endCoord)
|
||||||
self:T(self.lid.."Adding end node to node grid!")
|
|
||||||
self:AddNode(node)
|
-- Find the closest node to the given start coordinate.
|
||||||
|
local node, dist2=self:FindClosestNode(self.endCoord)
|
||||||
|
|
||||||
|
if pathline and vec3 and dist and dist2>dist then
|
||||||
|
|
||||||
|
-- Create a node on the closest pathline so we first go straight there and then along the pathline.
|
||||||
|
local node=self:AddNodeFromCoordinate(COORDINATE:NewFromVec3(vec3))
|
||||||
|
|
||||||
|
-- We also need the point.
|
||||||
|
local point=pathline:AddPointFromVec3(vec3, nil, s.p1)
|
||||||
|
|
||||||
|
-- Add pathline parameters to node.
|
||||||
|
node.pathline=pathline
|
||||||
|
node.pathpoint=point
|
||||||
|
|
||||||
|
self:T2(self.lid..string.format("Added new node=%d, which is closest to END coord: dist=%.1f m", node.id, dist))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Find closest node to the end coordinate (exclude the start coordinate.
|
||||||
|
self.endNode, dist=self:FindClosestNode(self.endCoord, {self.startNode})
|
||||||
|
|
||||||
|
--self.endNode.coordinate:MarkToAll("End Node")
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T(self.lid..string.format("END node ID=%d", self.endNode.id))
|
||||||
|
|
||||||
|
-- Not sure why I did this. The node does not need to be added again as it is already contained in self.nodes!
|
||||||
|
-- if dist>1000 then
|
||||||
|
-- self:T(self.lid.."Adding end node to node grid!")
|
||||||
|
-- self:AddNode(node)
|
||||||
|
-- end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -684,12 +922,21 @@ end
|
|||||||
-- @return #table Table of nodes from start to finish.
|
-- @return #table Table of nodes from start to finish.
|
||||||
function ASTAR:GetPath(ExcludeStartNode, ExcludeEndNode)
|
function ASTAR:GetPath(ExcludeStartNode, ExcludeEndNode)
|
||||||
|
|
||||||
self:FindStartNode()
|
-- self:FindStartNode()
|
||||||
self:FindEndNode()
|
-- self:FindEndNode()
|
||||||
|
|
||||||
|
-- Find start Node (closest node to start coordinate).
|
||||||
|
self.startNode=self:_FindClosestTerminalNode(self.startCoord)
|
||||||
|
|
||||||
|
-- Find end node, which is not the start node (excluded).
|
||||||
|
self.endNode=self:_FindClosestTerminalNode(self.endCoord, {self.startNode})
|
||||||
|
|
||||||
local nodes=self.nodes
|
local nodes=self.nodes
|
||||||
local start=self.startNode
|
local start=self.startNode
|
||||||
local goal=self.endNode
|
local goal=self.endNode
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T(self.lid..string.format("GetPath Start Node=%d, End Node=%d", start.id, goal.id))
|
||||||
|
|
||||||
-- Sets.
|
-- Sets.
|
||||||
local openset = {}
|
local openset = {}
|
||||||
@@ -746,7 +993,12 @@ function ASTAR:GetPath(ExcludeStartNode, ExcludeEndNode)
|
|||||||
text=text..string.format(", OS Time %.6f sec", dT)
|
text=text..string.format(", OS Time %.6f sec", dT)
|
||||||
end
|
end
|
||||||
text=text..string.format(", Nvalid=%d [%d cached]", self.nvalid, self.nvalidcache)
|
text=text..string.format(", Nvalid=%d [%d cached]", self.nvalid, self.nvalidcache)
|
||||||
text=text..string.format(", Ncost=%d [%d cached]", self.ncost, self.ncostcache)
|
text=text..string.format(", Ncost=%d [%d cached]", self.ncost, self.ncostcache)
|
||||||
|
text=text..string.format("\nNodes:")
|
||||||
|
for i,_node in ipairs(path) do
|
||||||
|
local node=_node --#ASTAR.Node
|
||||||
|
text=text..string.format("\n[%d] Node ID=%d", i, node.id)
|
||||||
|
end
|
||||||
self:T(self.lid..text)
|
self:T(self.lid..text)
|
||||||
|
|
||||||
return path
|
return path
|
||||||
@@ -759,13 +1011,16 @@ function ASTAR:GetPath(ExcludeStartNode, ExcludeEndNode)
|
|||||||
|
|
||||||
-- Get neighbour nodes.
|
-- Get neighbour nodes.
|
||||||
local neighbors=self:_NeighbourNodes(current, nodes)
|
local neighbors=self:_NeighbourNodes(current, nodes)
|
||||||
|
|
||||||
-- Loop over neighbours.
|
-- Loop over neighbours.
|
||||||
for _,neighbor in pairs(neighbors) do
|
for _,neighbor in pairs(neighbors) do
|
||||||
|
|
||||||
|
-- Node is not in closed set.
|
||||||
if self:_NotIn(closedset, neighbor.id) then
|
if self:_NotIn(closedset, neighbor.id) then
|
||||||
|
|
||||||
local tentative_g_score=g_score[current.id]+self:_DistNodes(current, neighbor)
|
-- Calculate tentative_g_score.
|
||||||
|
--local tentative_g_score=g_score[current.id] + self:_DistNodes(current, neighbor)
|
||||||
|
local tentative_g_score=g_score[current.id] + self:_HeuristicCost(current, neighbor)
|
||||||
|
|
||||||
if self:_NotIn(openset, neighbor.id) or tentative_g_score < g_score[neighbor.id] then
|
if self:_NotIn(openset, neighbor.id) or tentative_g_score < g_score[neighbor.id] then
|
||||||
|
|
||||||
@@ -793,6 +1048,73 @@ function ASTAR:GetPath(ExcludeStartNode, ExcludeEndNode)
|
|||||||
return nil -- no valid path
|
return nil -- no valid path
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- A* pathfinding function. This seaches the path along nodes between start and end nodes/coordinates.
|
||||||
|
-- It automatically creates a PATHLINE object that is returned in combination with the nodes of the optimal path.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #boolean ExcludeStartNode If *true*, do not include start node in found path. Default is to include it.
|
||||||
|
-- @param #boolean ExcludeEndNode If *true*, do not include end node in found path. Default is to include it.
|
||||||
|
-- @return Core.Pathline#PATHLINE Pathline.
|
||||||
|
-- @return #table Nodes of path.
|
||||||
|
function ASTAR:GetPathline(ExcludeStartNode, ExcludeEndNode)
|
||||||
|
|
||||||
|
local nodes=self:GetPath(ExcludeStartNode, ExcludeEndNode)
|
||||||
|
|
||||||
|
local pathline=nil --Core.Pathline#PATHLINE
|
||||||
|
if nodes then
|
||||||
|
|
||||||
|
pathline=PATHLINE:New("Astar")
|
||||||
|
|
||||||
|
for _,_node in pairs(nodes) do
|
||||||
|
local node=_node --#ASTAR.Node
|
||||||
|
|
||||||
|
local point=pathline:AddPointFromVec3(node.coordinate)
|
||||||
|
point.name=node.pathline.name
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return pathline, nodes
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get pathlines from nodes.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #table Nodes Given nodes.
|
||||||
|
-- @return #table Table of PATHLINES used in the path.
|
||||||
|
function ASTAR:GetPathlinesFromNodes(Nodes)
|
||||||
|
|
||||||
|
local pathlines={}
|
||||||
|
|
||||||
|
--for _,_node in pairs(Nodes or {}) do
|
||||||
|
for i=1,#Nodes do
|
||||||
|
local node=Nodes[i] --#ASTAR.Node
|
||||||
|
|
||||||
|
-- Pathline.
|
||||||
|
local pathline=node.pathline
|
||||||
|
|
||||||
|
if pathline and i>1 and i<#Nodes then
|
||||||
|
|
||||||
|
-- Previous and next nodes.
|
||||||
|
local n=Nodes[i-1] --#ASTAR.Node
|
||||||
|
local N=Nodes[i+1] --#ASTAR.Node
|
||||||
|
|
||||||
|
-- Check if previous and next nodes are on the same pathline.
|
||||||
|
-- If only one point in beteen is of another pathline, this is a junction and we dont actually switch to the other pathline.
|
||||||
|
if n.pathline and N.pathline and n.pathline.name==N.pathline.name and n.pathline.name~=pathline.name then
|
||||||
|
pathline=n.pathline
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- We do not want to add the same pathline two times in a row.
|
||||||
|
if #pathlines==0 or (#pathlines>0 and pathlines[#pathlines].name~=pathline.name) then
|
||||||
|
table.insert(pathlines, pathline)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return pathlines
|
||||||
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- A* pathfinding helper functions
|
-- A* pathfinding helper functions
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -811,16 +1133,18 @@ function ASTAR:_HeuristicCost(nodeA, nodeB)
|
|||||||
local cost=nodeA.cost[nodeB.id]
|
local cost=nodeA.cost[nodeB.id]
|
||||||
if cost~=nil then
|
if cost~=nil then
|
||||||
self.ncostcache=self.ncostcache+1
|
self.ncostcache=self.ncostcache+1
|
||||||
|
self:T(self.lid..string.format("Cost nodeA=%d --> nodeB=%d = %.1f (Cashed!)", nodeA.id, nodeB.id, cost))
|
||||||
return cost
|
return cost
|
||||||
end
|
end
|
||||||
|
|
||||||
local cost=nil
|
|
||||||
if self.CostFunc then
|
if self.CostFunc then
|
||||||
cost=self.CostFunc(nodeA, nodeB, unpack(self.CostArg))
|
cost=self.CostFunc(nodeA, nodeB, unpack(self.CostArg))
|
||||||
else
|
else
|
||||||
cost=self:_DistNodes(nodeA, nodeB)
|
cost=self:_DistNodes(nodeA, nodeB)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
self:T(self.lid..string.format("Cost nodeA=%d --> nodeB=%d = %.1f", nodeA.id, nodeB.id, cost))
|
||||||
|
|
||||||
nodeA.cost[nodeB.id]=cost
|
nodeA.cost[nodeB.id]=cost
|
||||||
nodeB.cost[nodeA.id]=cost -- Symmetric problem.
|
nodeB.cost[nodeA.id]=cost -- Symmetric problem.
|
||||||
|
|
||||||
@@ -834,9 +1158,10 @@ end
|
|||||||
-- @return #boolean If true, transition between nodes is possible.
|
-- @return #boolean If true, transition between nodes is possible.
|
||||||
function ASTAR:_IsValidNeighbour(node, neighbor)
|
function ASTAR:_IsValidNeighbour(node, neighbor)
|
||||||
|
|
||||||
-- Counter.
|
-- Counter of function calls.
|
||||||
self.nvalid=self.nvalid+1
|
self.nvalid=self.nvalid+1
|
||||||
|
|
||||||
|
-- Check if neighbour is in cached set.
|
||||||
local valid=node.valid[neighbor.id]
|
local valid=node.valid[neighbor.id]
|
||||||
if valid~=nil then
|
if valid~=nil then
|
||||||
--env.info(string.format("Node %d has valid=%s neighbour %d", node.id, tostring(valid), neighbor.id))
|
--env.info(string.format("Node %d has valid=%s neighbour %d", node.id, tostring(valid), neighbor.id))
|
||||||
@@ -844,13 +1169,16 @@ function ASTAR:_IsValidNeighbour(node, neighbor)
|
|||||||
return valid
|
return valid
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Check if this is a valid neighbour.
|
||||||
local valid=nil
|
local valid=nil
|
||||||
if self.ValidNeighbourFunc then
|
if self.ValidNeighbourFunc then
|
||||||
valid=self.ValidNeighbourFunc(node, neighbor, unpack(self.ValidNeighbourArg))
|
valid=self.ValidNeighbourFunc(node, neighbor, unpack(self.ValidNeighbourArg))
|
||||||
else
|
else
|
||||||
|
-- If no valid neighbour function is defined, we assume all nodes are valid neighbours.
|
||||||
valid=true
|
valid=true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Cache valid neighbour.
|
||||||
node.valid[neighbor.id]=valid
|
node.valid[neighbor.id]=valid
|
||||||
neighbor.valid[node.id]=valid -- Symmetric problem.
|
neighbor.valid[node.id]=valid -- Symmetric problem.
|
||||||
|
|
||||||
@@ -884,6 +1212,9 @@ function ASTAR:_LowestFscore(set, f_score)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T(self.lid..string.format("Lowest Fscore=%.1f, Node=%s", lowest, tostring(bestNode)))
|
||||||
|
|
||||||
return self.nodes[bestNode]
|
return self.nodes[bestNode]
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -928,16 +1259,46 @@ end
|
|||||||
-- @param #table map Map.
|
-- @param #table map Map.
|
||||||
-- @param #ASTAR.Node current_node The current node.
|
-- @param #ASTAR.Node current_node The current node.
|
||||||
-- @return #table Unwinded path.
|
-- @return #table Unwinded path.
|
||||||
function ASTAR:_UnwindPath( flat_path, map, current_node )
|
function ASTAR:_UnwindPath(flat_path, map, current_node)
|
||||||
|
|
||||||
if map [current_node] then
|
local previous_node=map[current_node]
|
||||||
table.insert (flat_path, 1, map[current_node])
|
|
||||||
return self:_UnwindPath(flat_path, map, map[current_node])
|
if previous_node then
|
||||||
|
table.insert(flat_path, 1, previous_node)
|
||||||
|
return self:_UnwindPath(flat_path, map, previous_node)
|
||||||
else
|
else
|
||||||
|
-- No previous node ==> return path.
|
||||||
return flat_path
|
return flat_path
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Function to check if a certain node is in a given table.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #table Nodes Nodes table.
|
||||||
|
-- @param #ASTAR.Node Node The node to check.
|
||||||
|
-- @return #boolean If true, the node is not in the set.
|
||||||
|
function ASTAR:_IsNodeInTable(Nodes, Node)
|
||||||
|
|
||||||
|
for _,_node in pairs(Nodes) do
|
||||||
|
local node=_node --#ASTAR.Node
|
||||||
|
if node.id==Node.id then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to check if a certain node is **not** in a given table.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #table Nodes Nodes table.
|
||||||
|
-- @param #ASTAR.Node Node The node to check.
|
||||||
|
-- @return #boolean If true, the node is not in the set.
|
||||||
|
function ASTAR:_IsNodeNotInTable(Nodes, Node)
|
||||||
|
local is=self:_IsNodeInTable(Nodes, Node)
|
||||||
|
return not is
|
||||||
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
--
|
--
|
||||||
-- @module Core.ClientMenu
|
-- @module Core.ClientMenu
|
||||||
-- @image Core_Menu.JPG
|
-- @image Core_Menu.JPG
|
||||||
-- last change: Jan 2025
|
-- last change: Sept 2025
|
||||||
|
|
||||||
-- TODO
|
-- TODO
|
||||||
----------------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------------
|
||||||
@@ -417,7 +417,7 @@ end
|
|||||||
CLIENTMENUMANAGER = {
|
CLIENTMENUMANAGER = {
|
||||||
ClassName = "CLIENTMENUMANAGER",
|
ClassName = "CLIENTMENUMANAGER",
|
||||||
lid = "",
|
lid = "",
|
||||||
version = "0.1.6",
|
version = "0.1.7",
|
||||||
name = nil,
|
name = nil,
|
||||||
clientset = nil,
|
clientset = nil,
|
||||||
menutree = {},
|
menutree = {},
|
||||||
@@ -806,6 +806,16 @@ function CLIENTMENUMANAGER:ResetMenuComplete()
|
|||||||
return self
|
return self
|
||||||
end
|
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.
|
--- Remove the entry and all entries below the given entry from the client's F10 menus.
|
||||||
-- @param #CLIENTMENUMANAGER self
|
-- @param #CLIENTMENUMANAGER self
|
||||||
-- @param #CLIENTMENU Entry The entry to remove
|
-- @param #CLIENTMENU Entry The entry to remove
|
||||||
|
|||||||
@@ -577,13 +577,19 @@ do -- Zones and Pathlines
|
|||||||
-- For a rectangular polygon drawing, we have the width (y) and height (x).
|
-- For a rectangular polygon drawing, we have the width (y) and height (x).
|
||||||
local w=objectData.width
|
local w=objectData.width
|
||||||
local h=objectData.height
|
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 sinRot = math.sin(rotation)
|
||||||
local points={}
|
local cosRot = math.cos(rotation)
|
||||||
points[1]={x=vec2.x-h/2, y=vec2.y+w/2} --Upper left
|
local dx = h / 2
|
||||||
points[2]={x=vec2.x+h/2, y=vec2.y+w/2} --Upper right
|
local dy = w / 2
|
||||||
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 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")
|
--local coord=COORDINATE:NewFromVec2(vec2):MarkToAll("MapX, MapY")
|
||||||
|
|
||||||
@@ -1112,7 +1118,7 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category
|
|||||||
self:E("WARNING: Invalid STN "..tostring(UnitTemplate.AddPropAircraft.STN_L16).." for ".. UnitTemplate.name)
|
self:E("WARNING: Invalid STN "..tostring(UnitTemplate.AddPropAircraft.STN_L16).." for ".. UnitTemplate.name)
|
||||||
else
|
else
|
||||||
self.STNS[stn] = UnitTemplate.name
|
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
|
||||||
end
|
end
|
||||||
if UnitTemplate.AddPropAircraft.SADL_TN then
|
if UnitTemplate.AddPropAircraft.SADL_TN then
|
||||||
@@ -1121,7 +1127,7 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category
|
|||||||
self:E("WARNING: Invalid SADL "..tostring(UnitTemplate.AddPropAircraft.SADL_TN).." for ".. UnitTemplate.name)
|
self:E("WARNING: Invalid SADL "..tostring(UnitTemplate.AddPropAircraft.SADL_TN).." for ".. UnitTemplate.name)
|
||||||
else
|
else
|
||||||
self.SADL[sadl] = UnitTemplate.name
|
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
|
end
|
||||||
end
|
end
|
||||||
@@ -1382,7 +1388,7 @@ function DATABASE:GetCoalitionFromClientTemplate( ClientName )
|
|||||||
if self.Templates.ClientsByName[ClientName] then
|
if self.Templates.ClientsByName[ClientName] then
|
||||||
return self.Templates.ClientsByName[ClientName].CoalitionID
|
return self.Templates.ClientsByName[ClientName].CoalitionID
|
||||||
end
|
end
|
||||||
self:E("WARNING: Template does not exist for client "..tostring(ClientName))
|
self:T("WARNING: Template does not exist for client "..tostring(ClientName))
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1394,7 +1400,7 @@ function DATABASE:GetCategoryFromClientTemplate( ClientName )
|
|||||||
if self.Templates.ClientsByName[ClientName] then
|
if self.Templates.ClientsByName[ClientName] then
|
||||||
return self.Templates.ClientsByName[ClientName].CategoryID
|
return self.Templates.ClientsByName[ClientName].CategoryID
|
||||||
end
|
end
|
||||||
self:E("WARNING: Template does not exist for client "..tostring(ClientName))
|
self:T("WARNING: Template does not exist for client "..tostring(ClientName))
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1406,7 +1412,7 @@ function DATABASE:GetCountryFromClientTemplate( ClientName )
|
|||||||
if self.Templates.ClientsByName[ClientName] then
|
if self.Templates.ClientsByName[ClientName] then
|
||||||
return self.Templates.ClientsByName[ClientName].CountryID
|
return self.Templates.ClientsByName[ClientName].CountryID
|
||||||
end
|
end
|
||||||
self:E("WARNING: Template does not exist for client "..tostring(ClientName))
|
self:T("WARNING: Template does not exist for client "..tostring(ClientName))
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1699,7 +1705,7 @@ function DATABASE:_EventOnBirth( Event )
|
|||||||
if PlayerName then
|
if PlayerName then
|
||||||
|
|
||||||
-- Debug info.
|
-- 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.
|
-- Add client in case it does not exist already.
|
||||||
if client == nil or (client and client:CountPlayers() == 0) then
|
if client == nil or (client and client:CountPlayers() == 0) then
|
||||||
|
|||||||
@@ -1508,7 +1508,9 @@ function EVENT:onEvent( Event )
|
|||||||
else
|
else
|
||||||
if Event.place:isExist() and Object.getCategory(Event.place) ~= Object.Category.SCENERY then
|
if Event.place:isExist() and Object.getCategory(Event.place) ~= Object.Category.SCENERY then
|
||||||
Event.Place=AIRBASE:Find(Event.place)
|
Event.Place=AIRBASE:Find(Event.place)
|
||||||
Event.PlaceName=Event.Place:GetName()
|
if Event.Place then
|
||||||
|
Event.PlaceName=Event.Place:GetName()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ MARKEROPS_BASE = {
|
|||||||
ClassName = "MARKEROPS",
|
ClassName = "MARKEROPS",
|
||||||
Tag = "mytag",
|
Tag = "mytag",
|
||||||
Keywords = {},
|
Keywords = {},
|
||||||
version = "0.1.3",
|
version = "0.1.4",
|
||||||
debug = false,
|
debug = false,
|
||||||
Casesensitive = true,
|
Casesensitive = true,
|
||||||
}
|
}
|
||||||
@@ -154,14 +154,7 @@ function MARKEROPS_BASE:OnEventMark(Event)
|
|||||||
self:E("Skipping onEvent. Event or Event.idx unknown.")
|
self:E("Skipping onEvent. Event or Event.idx unknown.")
|
||||||
return true
|
return true
|
||||||
end
|
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
|
local coalition = Event.MarkCoalition
|
||||||
-- decision
|
-- decision
|
||||||
if Event.id==world.event.S_EVENT_MARK_ADDED then
|
if Event.id==world.event.S_EVENT_MARK_ADDED then
|
||||||
@@ -170,8 +163,14 @@ function MARKEROPS_BASE:OnEventMark(Event)
|
|||||||
local Eventtext = tostring(Event.text)
|
local Eventtext = tostring(Event.text)
|
||||||
if Eventtext~=nil then
|
if Eventtext~=nil then
|
||||||
if self:_MatchTag(Eventtext) then
|
if self:_MatchTag(Eventtext) then
|
||||||
local matchtable = self:_MatchKeywords(Eventtext)
|
local coord=COORDINATE:NewFromVec3({y=Event.pos.y, x=Event.pos.x, z=Event.pos.z})
|
||||||
self:MarkAdded(Eventtext,matchtable,coord,Event.idx,coalition,Event.PlayerName,Event)
|
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
|
||||||
end
|
end
|
||||||
elseif Event.id==world.event.S_EVENT_MARK_CHANGE then
|
elseif Event.id==world.event.S_EVENT_MARK_CHANGE then
|
||||||
@@ -180,8 +179,14 @@ function MARKEROPS_BASE:OnEventMark(Event)
|
|||||||
local Eventtext = tostring(Event.text)
|
local Eventtext = tostring(Event.text)
|
||||||
if Eventtext~=nil then
|
if Eventtext~=nil then
|
||||||
if self:_MatchTag(Eventtext) then
|
if self:_MatchTag(Eventtext) then
|
||||||
local matchtable = self:_MatchKeywords(Eventtext)
|
local coord=COORDINATE:NewFromVec3({y=Event.pos.y, x=Event.pos.x, z=Event.pos.z})
|
||||||
self:MarkChanged(Eventtext,matchtable,coord,Event.idx,coalition,Event.PlayerName,Event)
|
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
|
||||||
end
|
end
|
||||||
elseif Event.id==world.event.S_EVENT_MARK_REMOVED then
|
elseif Event.id==world.event.S_EVENT_MARK_REMOVED then
|
||||||
|
|||||||
@@ -452,7 +452,7 @@ end
|
|||||||
_MESSAGESRS = {}
|
_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.
|
--- 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 #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 #string PathToCredentials (optional) Path to credentials file for Google.
|
||||||
-- @param #number Frequency Frequency in MHz. Can also be given as a #table of frequencies.
|
-- @param #number Frequency Frequency in MHz. Can also be given as a #table of frequencies.
|
||||||
@@ -468,13 +468,13 @@ _MESSAGESRS = {}
|
|||||||
-- @usage
|
-- @usage
|
||||||
-- -- Mind the dot here, not using the colon this time around!
|
-- -- Mind the dot here, not using the colon this time around!
|
||||||
-- -- Needed once only
|
-- -- 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
|
-- -- later on in your code
|
||||||
-- MESSAGE:New("Test message!",15,"SPAWN"):ToSRS()
|
-- MESSAGE:New("Test message!",15,"SPAWN"):ToSRS()
|
||||||
--
|
--
|
||||||
function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,Gender,Culture,Voice,Coalition,Volume,Label,Coordinate,Backend)
|
function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,Gender,Culture,Voice,Coalition,Volume,Label,Coordinate,Backend)
|
||||||
|
|
||||||
_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.frequency = Frequency or MSRS.frequencies or 243
|
||||||
_MESSAGESRS.modulation = Modulation or MSRS.modulations or radio.modulation.AM
|
_MESSAGESRS.modulation = Modulation or MSRS.modulations or radio.modulation.AM
|
||||||
@@ -535,7 +535,7 @@ end
|
|||||||
-- @usage
|
-- @usage
|
||||||
-- -- Mind the dot here, not using the colon this time around!
|
-- -- Mind the dot here, not using the colon this time around!
|
||||||
-- -- Needed once only
|
-- -- 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
|
-- -- later on in your code
|
||||||
-- MESSAGE:New("Test message!",15,"SPAWN"):ToSRS()
|
-- MESSAGE:New("Test message!",15,"SPAWN"):ToSRS()
|
||||||
--
|
--
|
||||||
@@ -567,7 +567,7 @@ end
|
|||||||
-- @usage
|
-- @usage
|
||||||
-- -- Mind the dot here, not using the colon this time around!
|
-- -- Mind the dot here, not using the colon this time around!
|
||||||
-- -- Needed once only
|
-- -- 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
|
-- -- later on in your code
|
||||||
-- MESSAGE:New("Test message!",15,"SPAWN"):ToSRSBlue()
|
-- MESSAGE:New("Test message!",15,"SPAWN"):ToSRSBlue()
|
||||||
--
|
--
|
||||||
@@ -589,7 +589,7 @@ end
|
|||||||
-- @usage
|
-- @usage
|
||||||
-- -- Mind the dot here, not using the colon this time around!
|
-- -- Mind the dot here, not using the colon this time around!
|
||||||
-- -- Needed once only
|
-- -- 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
|
-- -- later on in your code
|
||||||
-- MESSAGE:New("Test message!",15,"SPAWN"):ToSRSRed()
|
-- MESSAGE:New("Test message!",15,"SPAWN"):ToSRSRed()
|
||||||
--
|
--
|
||||||
@@ -611,7 +611,7 @@ end
|
|||||||
-- @usage
|
-- @usage
|
||||||
-- -- Mind the dot here, not using the colon this time around!
|
-- -- Mind the dot here, not using the colon this time around!
|
||||||
-- -- Needed once only
|
-- -- 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
|
-- -- later on in your code
|
||||||
-- MESSAGE:New("Test message!",15,"SPAWN"):ToSRSAll()
|
-- MESSAGE:New("Test message!",15,"SPAWN"):ToSRSAll()
|
||||||
--
|
--
|
||||||
|
|||||||
@@ -5,11 +5,13 @@
|
|||||||
-- * Path from A to B
|
-- * Path from A to B
|
||||||
-- * Arbitrary number of points
|
-- * Arbitrary number of points
|
||||||
-- * Automatically from lines drawtool
|
-- * Automatically from lines drawtool
|
||||||
|
-- * Draw line or mark points on F10 map
|
||||||
|
-- * Find closest points to path
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Author: **funkyfranky**
|
-- ### Author: **funkyfranky**
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
-- @module Core.Pathline
|
-- @module Core.Pathline
|
||||||
-- @image CORE_Pathline.png
|
-- @image CORE_Pathline.png
|
||||||
@@ -21,6 +23,7 @@
|
|||||||
-- @field #string lid Class id string for output to DCS log file.
|
-- @field #string lid Class id string for output to DCS log file.
|
||||||
-- @field #string name Name of the path line.
|
-- @field #string name Name of the path line.
|
||||||
-- @field #table points List of 3D points defining the path.
|
-- @field #table points List of 3D points defining the path.
|
||||||
|
-- @field #number counter Running number counting the point IDs.
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
--- *The shortest distance between two points is a straight line.* -- Archimedes
|
--- *The shortest distance between two points is a straight line.* -- Archimedes
|
||||||
@@ -28,30 +31,30 @@
|
|||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- # The PATHLINE Concept
|
-- # The PATHLINE Concept
|
||||||
--
|
--
|
||||||
-- List of points defining a path from A to B. The pathline can consist of multiple points. Each point holds the information of its position, the surface type, the land height
|
-- List of points defining a path from A to B. The pathline can consist of multiple points. Each point holds the information of its position, the surface type, the land height
|
||||||
-- and the water depth (if over sea).
|
-- and the water depth (if over sea).
|
||||||
--
|
--
|
||||||
-- Line drawings created in the mission editor are automatically registered as pathlines and stored in the MOOSE database.
|
-- Line drawings created in the mission editor are automatically registered as pathlines and stored in the MOOSE database.
|
||||||
-- They can be accessed with the @{#PATHLINE.FindByName) function.
|
-- They can be accessed with the @{#PATHLINE.FindByName) function.
|
||||||
--
|
--
|
||||||
-- # Constructor
|
-- # Constructor
|
||||||
--
|
--
|
||||||
-- The @{PATHLINE.New) function creates a new PATHLINE object. This does not hold any points. Points can be added with the @{#PATHLINE.AddPointFromVec2} and @{#PATHLINE.AddPointFromVec3}
|
-- The @{PATHLINE.New) function creates a new PATHLINE object. This does not hold any points. Points can be added with the @{#PATHLINE.AddPointFromVec2} and @{#PATHLINE.AddPointFromVec3}
|
||||||
--
|
--
|
||||||
-- For a given table of 2D or 3D positions, a new PATHLINE object can be created with the @{#PATHLINE.NewFromVec2Array} or @{#PATHLINE.NewFromVec3Array}, respectively.
|
-- For a given table of 2D or 3D positions, a new PATHLINE object can be created with the @{#PATHLINE.NewFromVec2Array} or @{#PATHLINE.NewFromVec3Array}, respectively.
|
||||||
--
|
--
|
||||||
-- # Line Drawings
|
-- # Line Drawings
|
||||||
--
|
--
|
||||||
-- The most convenient way to create a pathline is the draw panel feature in the DCS mission editor. You can select "Line" and then "Segments", "Segment" or "Free" to draw your lines.
|
-- The most convenient way to create a pathline is the draw panel feature in the DCS mission editor. You can select "Line" and then "Segments", "Segment" or "Free" to draw your lines.
|
||||||
-- These line drawings are then automatically added to the MOOSE database as PATHLINE objects and can be retrieved with the @{#PATHLINE.FindByName) function, where the name is the one
|
-- These line drawings are then automatically added to the MOOSE database as PATHLINE objects and can be retrieved with the @{#PATHLINE.FindByName) function, where the name is the one
|
||||||
-- you specify in the draw panel.
|
-- you specify in the draw panel.
|
||||||
--
|
--
|
||||||
-- # Mark on F10 map
|
-- # 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 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
|
||||||
-- water depth.
|
-- water depth.
|
||||||
--
|
--
|
||||||
-- To remove the marks, use @{#PATHLINE.MarkPoints}(`false`).
|
-- To remove the marks, use @{#PATHLINE.MarkPoints}(`false`).
|
||||||
--
|
--
|
||||||
-- @field #PATHLINE
|
-- @field #PATHLINE
|
||||||
@@ -59,27 +62,39 @@ PATHLINE = {
|
|||||||
ClassName = "PATHLINE",
|
ClassName = "PATHLINE",
|
||||||
lid = nil,
|
lid = nil,
|
||||||
points = {},
|
points = {},
|
||||||
|
counter = 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Point of line.
|
--- Point of line.
|
||||||
-- @type PATHLINE.Point
|
-- @type PATHLINE.Point
|
||||||
|
-- @field #number uid Unique ID of this point.
|
||||||
|
-- @field #string mother Name of the pathline this point belongs to.
|
||||||
|
-- @field #string name Name of this point.
|
||||||
-- @field DCS#Vec3 vec3 3D position.
|
-- @field DCS#Vec3 vec3 3D position.
|
||||||
-- @field DCS#Vec2 vec2 2D position.
|
-- @field DCS#Vec2 vec2 2D position.
|
||||||
-- @field #number surfaceType Surface type.
|
-- @field #number surfaceType Surface type.
|
||||||
-- @field #number landHeight Land height in meters.
|
-- @field #number landHeight Land height in meters.
|
||||||
-- @field #number depth Water depth in meters.
|
-- @field #number depth Water depth in meters.
|
||||||
-- @field #number markerID Marker ID.
|
-- @field #number markerID Marker ID.
|
||||||
|
-- @field #number lineID Marker of pathline ID.
|
||||||
|
|
||||||
|
--- Segment of line.
|
||||||
|
-- @type PATHLINE.Segment
|
||||||
|
-- @field #PATHLINE.Point p1 First point.
|
||||||
|
-- @field #PATHLINE.Point p2 Second point.
|
||||||
|
|
||||||
|
|
||||||
--- PATHLINE class version.
|
--- PATHLINE class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
PATHLINE.version="0.1.1"
|
PATHLINE.version="0.3.0"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- TODO list
|
-- TODO list
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- TODO: A lot...
|
-- TODO: Read/write to JSON file
|
||||||
|
-- TODO: Translate/rotate pathline
|
||||||
|
-- TODO: Add color.
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- Constructor
|
-- Constructor
|
||||||
@@ -93,10 +108,10 @@ function PATHLINE:New(Name)
|
|||||||
|
|
||||||
-- Inherit everything from INTEL class.
|
-- Inherit everything from INTEL class.
|
||||||
local self=BASE:Inherit(self, BASE:New()) --#PATHLINE
|
local self=BASE:Inherit(self, BASE:New()) --#PATHLINE
|
||||||
|
|
||||||
self.name=Name or "Unknown Path"
|
self.name=Name or "Unknown Path"
|
||||||
|
|
||||||
self.lid=string.format("PATHLINE %s | ", Name)
|
self.lid=string.format("PATHLINE %s | ", self.name)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -151,41 +166,70 @@ end
|
|||||||
--- Add a point to the path from a given 2D position. The third dimension is determined from the land height.
|
--- Add a point to the path from a given 2D position. The third dimension is determined from the land height.
|
||||||
-- @param #PATHLINE self
|
-- @param #PATHLINE self
|
||||||
-- @param DCS#Vec2 Vec2 The 2D vector (x,y) to add.
|
-- @param DCS#Vec2 Vec2 The 2D vector (x,y) to add.
|
||||||
|
-- @param #number Index Index to add this point, *e.g.* 1 for first point or 2 for second point. Default is at the end.
|
||||||
|
-- @param #PATHLINE.Point Point Add point after given point. Default is at the end or at given index.
|
||||||
-- @return #PATHLINE self
|
-- @return #PATHLINE self
|
||||||
function PATHLINE:AddPointFromVec2(Vec2)
|
function PATHLINE:AddPointFromVec2(Vec2, Index, Point)
|
||||||
|
|
||||||
if Vec2 then
|
if Vec2 then
|
||||||
|
|
||||||
|
-- Create a new point.
|
||||||
local point=self:_CreatePoint(Vec2)
|
local point=self:_CreatePoint(Vec2)
|
||||||
|
|
||||||
table.insert(self.points, point)
|
if Index then
|
||||||
|
-- Add at given index.
|
||||||
|
table.insert(self.points, Index, point)
|
||||||
|
else
|
||||||
|
if Point then
|
||||||
|
-- Get index of given point.
|
||||||
|
local i=self:_GetPointIndex(Point)
|
||||||
|
-- Add new point after given point.
|
||||||
|
table.insert(self.points, i+1, point)
|
||||||
|
else
|
||||||
|
-- Add add the end.
|
||||||
|
table.insert(self.points, point)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Add a point to the path from a given 3D position.
|
--- Add a point to the path from a given 3D position.
|
||||||
-- @param #PATHLINE self
|
-- @param #PATHLINE self
|
||||||
-- @param DCS#Vec3 Vec3 The 3D vector (x,y) to add.
|
-- @param DCS#Vec3 Vec3 The 3D vector (x,y) to add.
|
||||||
-- @return #PATHLINE self
|
-- @param #number Index Index to add this point, *e.g.* 1 for first point or 2 for second point. Default is at the end.
|
||||||
function PATHLINE:AddPointFromVec3(Vec3)
|
-- @param #PATHLINE.Point Point Add point after given point. Default is at the end or at given index.
|
||||||
|
-- @return #PATHLINE.Point Point that was added.
|
||||||
|
function PATHLINE:AddPointFromVec3(Vec3, Index, Point)
|
||||||
|
|
||||||
if Vec3 then
|
if Vec3 then
|
||||||
|
|
||||||
local point=self:_CreatePoint(Vec3)
|
local point=self:_CreatePoint(Vec3)
|
||||||
|
|
||||||
table.insert(self.points, point)
|
if Index then
|
||||||
|
-- Add add given index.
|
||||||
|
table.insert(self.points, Index, point)
|
||||||
|
else
|
||||||
|
if Point then
|
||||||
|
local i=self:_GetPointIndex(Point)
|
||||||
|
table.insert(self.points, i+1, point)
|
||||||
|
else
|
||||||
|
-- Add add the end.
|
||||||
|
table.insert(self.points, point)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return point
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get name of pathline.
|
--- Get name of pathline.
|
||||||
-- @param #PATHLINE self
|
-- @param #PATHLINE self
|
||||||
-- @return #string Name of the pathline.
|
-- @return #string Name of the pathline.
|
||||||
function PATHLINE:GetName()
|
function PATHLINE:GetName()
|
||||||
return self.name
|
return self.name
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -199,18 +243,35 @@ end
|
|||||||
|
|
||||||
--- Get points of pathline. Not that points are tables, that contain more information as just the 2D or 3D position but also the surface type etc.
|
--- Get points of pathline. Not that points are tables, that contain more information as just the 2D or 3D position but also the surface type etc.
|
||||||
-- @param #PATHLINE self
|
-- @param #PATHLINE self
|
||||||
-- @return #list <#PATHLINE.Point> List of points.
|
-- @return #list <Core.Pathline#PATHLINE.Point> List of points.
|
||||||
function PATHLINE:GetPoints()
|
function PATHLINE:GetPoints()
|
||||||
return self.points
|
return self.points
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get segments of pathline.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @return #list <Core.Pathline#PATHLINE.Segment> List of points.
|
||||||
|
function PATHLINE:GetSetments()
|
||||||
|
|
||||||
|
local segments={}
|
||||||
|
|
||||||
|
for i=1,#self.points-1 do
|
||||||
|
local segment={} --#PATHLINE.Segment
|
||||||
|
segment.p1=self.points[i]
|
||||||
|
segment.p2=self.points[i+1]
|
||||||
|
table.insert(segments, segment)
|
||||||
|
end
|
||||||
|
|
||||||
|
return segments
|
||||||
|
end
|
||||||
|
|
||||||
--- Get 3D points of pathline.
|
--- Get 3D points of pathline.
|
||||||
-- @param #PATHLINE self
|
-- @param #PATHLINE self
|
||||||
-- @return <DCS#Vec3> List of DCS#Vec3 points.
|
-- @return #list <DCS#Vec3> List of DCS#Vec3 points.
|
||||||
function PATHLINE:GetPoints3D()
|
function PATHLINE:GetPoints3D()
|
||||||
|
|
||||||
local vecs={}
|
local vecs={}
|
||||||
|
|
||||||
for _,_point in pairs(self.points) do
|
for _,_point in pairs(self.points) do
|
||||||
local point=_point --#PATHLINE.Point
|
local point=_point --#PATHLINE.Point
|
||||||
table.insert(vecs, point.vec3)
|
table.insert(vecs, point.vec3)
|
||||||
@@ -221,11 +282,11 @@ end
|
|||||||
|
|
||||||
--- Get 2D points of pathline.
|
--- Get 2D points of pathline.
|
||||||
-- @param #PATHLINE self
|
-- @param #PATHLINE self
|
||||||
-- @return <DCS#Vec2> List of DCS#Vec2 points.
|
-- @return #list <DCS#Vec2> List of DCS#Vec2 points.
|
||||||
function PATHLINE:GetPoints2D()
|
function PATHLINE:GetPoints2D()
|
||||||
|
|
||||||
local vecs={}
|
local vecs={}
|
||||||
|
|
||||||
for _,_point in pairs(self.points) do
|
for _,_point in pairs(self.points) do
|
||||||
local point=_point --#PATHLINE.Point
|
local point=_point --#PATHLINE.Point
|
||||||
table.insert(vecs, point.vec2)
|
table.insert(vecs, point.vec2)
|
||||||
@@ -240,11 +301,11 @@ end
|
|||||||
function PATHLINE:GetCoordinates()
|
function PATHLINE:GetCoordinates()
|
||||||
|
|
||||||
local vecs={}
|
local vecs={}
|
||||||
|
|
||||||
for _,_point in pairs(self.points) do
|
for _,_point in pairs(self.points) do
|
||||||
local point=_point --#PATHLINE.Point
|
local point=_point --#PATHLINE.Point
|
||||||
local coord=COORDINATE:NewFromVec3(point.vec3)
|
local coord=COORDINATE:NewFromVec3(point.vec3)
|
||||||
table.insert(vecs,coord)
|
table.insert(vecs, coord)
|
||||||
end
|
end
|
||||||
|
|
||||||
return vecs
|
return vecs
|
||||||
@@ -257,11 +318,11 @@ end
|
|||||||
function PATHLINE:GetPointFromIndex(n)
|
function PATHLINE:GetPointFromIndex(n)
|
||||||
|
|
||||||
local N=self:GetNumberOfPoints()
|
local N=self:GetNumberOfPoints()
|
||||||
|
|
||||||
n=n or 1
|
n=n or 1
|
||||||
|
|
||||||
local point=nil --#PATHLINE.Point
|
local point=nil --#PATHLINE.Point
|
||||||
|
|
||||||
if n>=1 and n<=N then
|
if n>=1 and n<=N then
|
||||||
point=self.points[n]
|
point=self.points[n]
|
||||||
else
|
else
|
||||||
@@ -278,11 +339,11 @@ end
|
|||||||
function PATHLINE:GetPoint3DFromIndex(n)
|
function PATHLINE:GetPoint3DFromIndex(n)
|
||||||
|
|
||||||
local point=self:GetPointFromIndex(n)
|
local point=self:GetPointFromIndex(n)
|
||||||
|
|
||||||
if point then
|
if point then
|
||||||
return point.vec3
|
return point.vec3
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -293,11 +354,11 @@ end
|
|||||||
function PATHLINE:GetPoint2DFromIndex(n)
|
function PATHLINE:GetPoint2DFromIndex(n)
|
||||||
|
|
||||||
local point=self:GetPointFromIndex(n)
|
local point=self:GetPointFromIndex(n)
|
||||||
|
|
||||||
if point then
|
if point then
|
||||||
return point.vec2
|
return point.vec2
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -305,33 +366,314 @@ end
|
|||||||
--- Mark points on F10 map.
|
--- Mark points on F10 map.
|
||||||
-- @param #PATHLINE self
|
-- @param #PATHLINE self
|
||||||
-- @param #boolean Switch If `true` or nil, set marks. If `false`, remove marks.
|
-- @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)
|
function PATHLINE:MarkPoints(Switch)
|
||||||
|
|
||||||
for i,_point in pairs(self.points) do
|
for i,_point in pairs(self.points) do
|
||||||
local point=_point --#PATHLINE.Point
|
local point=_point --#PATHLINE.Point
|
||||||
|
|
||||||
if Switch==false then
|
if Switch==false then
|
||||||
|
|
||||||
if point.markerID then
|
|
||||||
UTILS.RemoveMark(point.markerID, Delay)
|
|
||||||
end
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
if point.markerID then
|
if point.markerID then
|
||||||
UTILS.RemoveMark(point.markerID)
|
UTILS.RemoveMark(point.markerID)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
if point.markerID then
|
||||||
|
UTILS.RemoveMark(point.markerID)
|
||||||
|
end
|
||||||
|
|
||||||
point.markerID=UTILS.GetMarkID()
|
point.markerID=UTILS.GetMarkID()
|
||||||
|
|
||||||
local text=string.format("Pathline %s: Point #%d\nSurface Type=%d\nHeight=%.1f m\nDepth=%.1f m", self.name, i, point.surfaceType, point.landHeight, point.depth)
|
local text=string.format("Pathline %s: Point #%d [UID=%d]\nSurface Type=%d\nHeight=%.1f m\nDepth=%.1f m", self.name, i, point.uid, point.surfaceType, point.landHeight, point.depth)
|
||||||
|
|
||||||
trigger.action.markToAll(point.markerID, text, point.vec3, "")
|
trigger.action.markToAll(point.markerID, text, point.vec3, "")
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Draw line on F10 map.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param #boolean Switch If `true` or nil, draw pathline. If `false`, remove drawing.
|
||||||
|
-- @param #number Coalition Coalition side. Default -1 for all.
|
||||||
|
-- @param #table Color RGB color and alpha `{r, g, b, a}`. Default {0, 1, 0, 0.5}.
|
||||||
|
-- @param #number LineType Line type. Default 1=solid.
|
||||||
|
-- @return #PATHLINE self
|
||||||
|
function PATHLINE:Draw(Switch, Coalition, Color, LineType)
|
||||||
|
|
||||||
|
Coalition=Coalition or -1
|
||||||
|
Color=Color or {0, 1, 0, 0.5}
|
||||||
|
LineType=LineType or 1
|
||||||
|
|
||||||
|
if Switch==false then
|
||||||
|
|
||||||
|
for i,_point in pairs(self.points) do
|
||||||
|
local point=_point --#PATHLINE.Point
|
||||||
|
|
||||||
|
if point.lineID then
|
||||||
|
UTILS.RemoveMark(point.lineID)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
for i=2,#self.points do
|
||||||
|
|
||||||
|
local p1=self.points[i-1] --#PATHLINE.Point
|
||||||
|
local p2=self.points[i] --#PATHLINE.Point
|
||||||
|
|
||||||
|
if p2.lineID then
|
||||||
|
UTILS.RemoveMark(p2.lineID)
|
||||||
|
end
|
||||||
|
|
||||||
|
p2.lineID=UTILS.GetMarkID()
|
||||||
|
|
||||||
|
trigger.action.lineToAll(Coalition, p2.lineID, p1.vec3, p2.vec3, Color, LineType)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the closest point on the pathline for a given reference point.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param DCS#Vec2 Vec2 Reference Point in 2D.
|
||||||
|
-- @return DCS#Vec2 Cloest point on pathline.
|
||||||
|
-- @return #number Distance from closest point to ref point in meters.
|
||||||
|
-- @return #PATHLINE.Segment Closest segment of ref point.
|
||||||
|
function PATHLINE:GetClosestPoint2D(Vec2)
|
||||||
|
|
||||||
|
local P=nil --DCS#Vec2
|
||||||
|
local D=math.huge
|
||||||
|
local S={} --#PATHLINE.Segment
|
||||||
|
|
||||||
|
for i=2,#self.points do
|
||||||
|
|
||||||
|
local A=self.points[i-1] --#PATHLINE.Point
|
||||||
|
local B=self.points[i] --#PATHLINE.Point
|
||||||
|
|
||||||
|
local a=A.vec2
|
||||||
|
local b=B.vec2
|
||||||
|
|
||||||
|
local ab=UTILS.Vec2Substract(b, a)
|
||||||
|
local ap=UTILS.Vec2Substract(Vec2, a)
|
||||||
|
|
||||||
|
local proj=UTILS.Vec2Dot(ap, ab)
|
||||||
|
|
||||||
|
local lab=UTILS.Vec2Norm(ab)
|
||||||
|
|
||||||
|
local f=proj/lab/lab
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
local text=string.format("FF Proj=%.1f, |ab|=%.1f, f=%.1f", proj, lab, f)
|
||||||
|
self:T(self.lid..text)
|
||||||
|
|
||||||
|
-- Cases for finite segment.
|
||||||
|
local p=nil --DCS#Vec2
|
||||||
|
if f<0 then
|
||||||
|
p=a
|
||||||
|
elseif f>1 then
|
||||||
|
p=b
|
||||||
|
else
|
||||||
|
local r=UTILS.Vec2Mult(ab, f)
|
||||||
|
p=UTILS.Vec2Add(a, r)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Distance.
|
||||||
|
local d=UTILS.VecDist2D(p, Vec2)
|
||||||
|
|
||||||
|
if d<=D then
|
||||||
|
D=d
|
||||||
|
P=p
|
||||||
|
S.p1=A
|
||||||
|
S.p2=B
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return P, D, S
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the closest point on the pathline for a given reference point.
|
||||||
|
-- This point does not necessarily is a node of the pathline. In general it will be somewhere in between the nodes defining the pathline.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param DCS#Vec3 Vec3 Reference Point in 3D. Can also be a `COORDINATE`.
|
||||||
|
-- @return DCS#Vec3 Closest point on pathline.
|
||||||
|
-- @return #number Distance from closest point to ref point in meters.
|
||||||
|
-- @return #PATHLINE.Segment Closest segment of ref point.
|
||||||
|
function PATHLINE:GetClosestPoint3D(Vec3)
|
||||||
|
|
||||||
|
local P=nil --DCS#Vec3
|
||||||
|
local D=math.huge
|
||||||
|
local S={} --#PATHLINE.Segment
|
||||||
|
|
||||||
|
if not Vec3 then
|
||||||
|
self:E(self.lid.."ERROR: input Vec3 is nil!")
|
||||||
|
return nil, nil, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
for i=2,#self.points do
|
||||||
|
|
||||||
|
local A=self.points[i-1] --#PATHLINE.Point
|
||||||
|
local B=self.points[i] --#PATHLINE.Point
|
||||||
|
|
||||||
|
local a=A.vec3
|
||||||
|
local b=B.vec3
|
||||||
|
|
||||||
|
local ab=UTILS.VecSubstract(b, a)
|
||||||
|
local ap=UTILS.VecSubstract(Vec3, a)
|
||||||
|
|
||||||
|
local proj=UTILS.VecDot(ap, ab)
|
||||||
|
|
||||||
|
local lab=UTILS.VecNorm(ab)
|
||||||
|
|
||||||
|
local f=proj/lab/lab
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T(self.lid..string.format("Proj=%.1f, |ab|=%.1f, f=%.1f", proj, lab, f))
|
||||||
|
|
||||||
|
-- Cases for finite segment.
|
||||||
|
local p=nil --DCS#Vec2
|
||||||
|
if f<0 then
|
||||||
|
p=a
|
||||||
|
elseif f>1 then
|
||||||
|
p=b
|
||||||
|
else
|
||||||
|
local r=UTILS.VecMult(ab, f)
|
||||||
|
p=UTILS.VecAdd(a, r)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Distance.
|
||||||
|
local d=UTILS.VecDist3D(p, Vec3)
|
||||||
|
|
||||||
|
if d<=D then
|
||||||
|
D=d
|
||||||
|
P=p
|
||||||
|
S.p1=A
|
||||||
|
S.p2=B
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return P, D, S
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Write PATHLINE to JSON file.
|
||||||
|
-- **NOTE**: Requires `io` and `lfs` to be de-sanitized!
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param #string FileName Name of the file. Default is the name of the pathline.
|
||||||
|
-- @return #PATHLINE self
|
||||||
|
function PATHLINE:WriteJSON(FileName)
|
||||||
|
|
||||||
|
if io and lfs then
|
||||||
|
|
||||||
|
-- JSON script.
|
||||||
|
local json=loadfile("Scripts\\JSON.lua")()
|
||||||
|
|
||||||
|
local data={}
|
||||||
|
|
||||||
|
-- We store the name and the points.
|
||||||
|
data.name=self.name
|
||||||
|
data.points=self.points
|
||||||
|
for i,_point in pairs(self.points) do
|
||||||
|
local point=_point --#PATHLINE.Point
|
||||||
|
--point.markerID=nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Encode data to raw JSON. Encode converts a lua table into JSON string that can be written to file.
|
||||||
|
local raw_json=json:encode(data)
|
||||||
|
|
||||||
|
-- Debug data.
|
||||||
|
self:T(data)
|
||||||
|
|
||||||
|
-- Write in "User/Saved Games/" Folder.
|
||||||
|
local filepath=lfs.writedir() .. FileName
|
||||||
|
|
||||||
|
-- Open file for writing.
|
||||||
|
local f = io.open(filepath, "wb")
|
||||||
|
if f then
|
||||||
|
f:write(raw_json)
|
||||||
|
f:close()
|
||||||
|
self:T(self.lid .. string.format("Saving PATHLINE %s file %s", self.name, tostring(filepath)))
|
||||||
|
else
|
||||||
|
self:E(self.lid .. string.format( "ERROR: Could not save PATHLINE to file %s", tostring(filepath)))
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
self:E(self.lid .. string.format( "ERROR: Could not save results because IO and/or LFS are not de-sanitized!"))
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Read PATHLINE from JSON file.
|
||||||
|
-- **NOTE**: Requires `io` and `lfs` to be de-sanitized!
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param #string FileName Name of the file.
|
||||||
|
-- @return #PATHLINE self
|
||||||
|
function PATHLINE:NewFromJSON(FileName)
|
||||||
|
|
||||||
|
if io and lfs then
|
||||||
|
|
||||||
|
-- JSON script.
|
||||||
|
local json=loadfile("Scripts\\JSON.lua")()
|
||||||
|
|
||||||
|
local data={}
|
||||||
|
|
||||||
|
-- Write in "User/Saved Games/" Folder.
|
||||||
|
local filepath=lfs.writedir() .. FileName
|
||||||
|
|
||||||
|
--env.info(filepath)
|
||||||
|
|
||||||
|
-- Open file in binary mode for reading.
|
||||||
|
local f = io.open(filepath, "rb")
|
||||||
|
if f then
|
||||||
|
data = f:read("*all")
|
||||||
|
f:close()
|
||||||
|
else
|
||||||
|
env.info(string.format("WARNING: Could not load PATHLINE from file %s!", tostring(filepath)))
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Decode JSON data to get a lua table.
|
||||||
|
local data=json:decode(data)
|
||||||
|
|
||||||
|
if data and data.name then
|
||||||
|
|
||||||
|
-- Create a new pathline instance.
|
||||||
|
local self=PATHLINE:New(data.name)
|
||||||
|
|
||||||
|
for i=1,#data.points do
|
||||||
|
local point=data.points[i] --#PATHLINE.Point
|
||||||
|
|
||||||
|
-- Create new point from data.
|
||||||
|
local p=self:AddPointFromVec3(point.vec3)
|
||||||
|
|
||||||
|
-- Set name.
|
||||||
|
p.name=point.name
|
||||||
|
|
||||||
|
-- Remove marker ID.
|
||||||
|
p.markerID=nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
else
|
||||||
|
BASE:E("ERROR: Cannot find pathline name in data from JSON file. File may be corrupted!")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
BASE:E("ERROR: IO and/or LFS not de-sanitized! Cannot read file.")
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- Private functions
|
-- Private functions
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -339,32 +681,56 @@ end
|
|||||||
--- Get 3D points of pathline.
|
--- Get 3D points of pathline.
|
||||||
-- @param #PATHLINE self
|
-- @param #PATHLINE self
|
||||||
-- @param DCS#Vec3 Vec Position vector. Can also be a DCS#Vec2 in which case the altitude at landheight is taken.
|
-- @param DCS#Vec3 Vec Position vector. Can also be a DCS#Vec2 in which case the altitude at landheight is taken.
|
||||||
-- @return #PATHLINE.Point
|
-- @return #PATHLINE.Point Pathline Point.
|
||||||
function PATHLINE:_CreatePoint(Vec)
|
function PATHLINE:_CreatePoint(Vec)
|
||||||
|
|
||||||
local point={} --#PATHLINE.Point
|
local point={} --#PATHLINE.Point
|
||||||
|
|
||||||
|
self.counter=self.counter+1
|
||||||
|
|
||||||
|
point.uid=self.counter
|
||||||
|
point.mother=self.name
|
||||||
|
|
||||||
|
point.name=string.format("%s #%d", self.name, point.uid)
|
||||||
|
|
||||||
if Vec.z then
|
if Vec.z then
|
||||||
-- Given vec is 3D
|
-- Given vec is 3D
|
||||||
point.vec3=UTILS.DeepCopy(Vec)
|
point.vec3=UTILS.DeepCopy(Vec)
|
||||||
point.vec2={x=Vec.x, y=Vec.z}
|
point.vec2={x=Vec.x, y=Vec.z}
|
||||||
else
|
else
|
||||||
-- Given vec is 2D
|
-- Given vec is 2D
|
||||||
point.vec2=UTILS.DeepCopy(Vec)
|
point.vec2=UTILS.DeepCopy(Vec)
|
||||||
point.vec3={x=Vec.x, y=land.getHeight(Vec), z=Vec.y}
|
point.vec3={x=Vec.x, y=land.getHeight(Vec), z=Vec.y}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get surface type.
|
-- Get surface type.
|
||||||
point.surfaceType=land.getSurfaceType(point.vec2)
|
point.surfaceType=land.getSurfaceType(point.vec2)
|
||||||
|
|
||||||
-- Get land height and depth.
|
-- Get land height and depth.
|
||||||
point.landHeight, point.depth=land.getSurfaceHeightWithSeabed(point.vec2)
|
point.landHeight, point.depth=land.getSurfaceHeightWithSeabed(point.vec2)
|
||||||
|
|
||||||
point.markerID=nil
|
point.markerID=nil
|
||||||
|
|
||||||
return point
|
return point
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get index of point in the lua table.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param #PATHLINE.Point Point Given point.
|
||||||
|
-- @return #number index
|
||||||
|
function PATHLINE:_GetPointIndex(Point)
|
||||||
|
|
||||||
|
for i,_point in pairs(self.points) do
|
||||||
|
local point=_point --#PATHLINE.Point
|
||||||
|
|
||||||
|
if point.uid==Point.uid then
|
||||||
|
return i
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -59,6 +59,10 @@ do -- COORDINATE
|
|||||||
-- * @{#COORDINATE.SmokeOrange}(): To smoke the point in orange.
|
-- * @{#COORDINATE.SmokeOrange}(): To smoke the point in orange.
|
||||||
-- * @{#COORDINATE.SmokeWhite}(): To smoke the point in white.
|
-- * @{#COORDINATE.SmokeWhite}(): To smoke the point in white.
|
||||||
-- * @{#COORDINATE.SmokeGreen}(): To smoke the point in green.
|
-- * @{#COORDINATE.SmokeGreen}(): To smoke the point in green.
|
||||||
|
-- * @{#COORDINATE.SetSmokeOffsetDirection}(): To set an offset point direction for smoke.
|
||||||
|
-- * @{#COORDINATE.SetSmokeOffsetDistance}(): To set an offset point distance for smoke.
|
||||||
|
-- * @{#COORDINATE.SwitchSmokeOffsetOn}(): To set an offset point for smoke to on.
|
||||||
|
-- * @{#COORDINATE.SwitchSmokeOffsetOff}(): To set an offset point for smoke to off.
|
||||||
--
|
--
|
||||||
-- ## 2.2) Flare
|
-- ## 2.2) Flare
|
||||||
--
|
--
|
||||||
@@ -453,6 +457,23 @@ do -- COORDINATE
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Returns the coordinate from the latitude and longitude given in degrees, minutes and seconds (DMS).
|
||||||
|
-- @param #COORDINATE self
|
||||||
|
-- @param #string Latitude Latitude in DMS as string, e.g. "`42° 24' 14.3"`". Not that the characters `°`, `'` and `"` are important.
|
||||||
|
-- @param #string Longitude Longitude in DMS as string, e.g. "`42° 24' 14.3"`". Not that the characters `°`, `'` and `"` are important.
|
||||||
|
-- @param #number Altitude (Optional) Altitude in meters. Default is the land height at the coordinate.
|
||||||
|
-- @return #COORDINATE
|
||||||
|
function COORDINATE:NewFromLLDMS(Latitude, Longitude, Altitude)
|
||||||
|
|
||||||
|
local lat=UTILS.LLDMSstringToDD(Latitude)
|
||||||
|
local lon=UTILS.LLDMSstringToDD(Longitude)
|
||||||
|
|
||||||
|
self=COORDINATE:NewFromLLDD(lat, lon, Altitude)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Returns if the 2 coordinates are at the same 2D position.
|
--- Returns if the 2 coordinates are at the same 2D position.
|
||||||
-- @param #COORDINATE self
|
-- @param #COORDINATE self
|
||||||
-- @param #COORDINATE Coordinate
|
-- @param #COORDINATE Coordinate
|
||||||
@@ -661,7 +682,7 @@ do -- COORDINATE
|
|||||||
local _,_,_,_,_,scenerys=self:ScanObjects(radius, false, false, true)
|
local _,_,_,_,_,scenerys=self:ScanObjects(radius, false, false, true)
|
||||||
|
|
||||||
local set={}
|
local set={}
|
||||||
|
|
||||||
for _,_scenery in pairs(scenerys) do
|
for _,_scenery in pairs(scenerys) do
|
||||||
local scenery=_scenery --DCS#Object
|
local scenery=_scenery --DCS#Object
|
||||||
|
|
||||||
@@ -773,7 +794,9 @@ do -- COORDINATE
|
|||||||
-- @return DCS#Vec2 Vec2
|
-- @return DCS#Vec2 Vec2
|
||||||
function COORDINATE:GetRandomVec2InRadius( OuterRadius, InnerRadius )
|
function COORDINATE:GetRandomVec2InRadius( OuterRadius, InnerRadius )
|
||||||
self:F2( { OuterRadius, InnerRadius } )
|
self:F2( { OuterRadius, InnerRadius } )
|
||||||
|
math.random()
|
||||||
|
math.random()
|
||||||
|
math.random()
|
||||||
local Theta = 2 * math.pi * math.random()
|
local Theta = 2 * math.pi * math.random()
|
||||||
local Radials = math.random() + math.random()
|
local Radials = math.random() + math.random()
|
||||||
if Radials > 1 then
|
if Radials > 1 then
|
||||||
@@ -833,6 +856,26 @@ do -- COORDINATE
|
|||||||
return land.getHeight( Vec2 )
|
return land.getHeight( Vec2 )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Returns a table of DCS#Vec3 points representing the terrain profile between two points.
|
||||||
|
-- @param #COORDINATE self
|
||||||
|
-- @param Destination DCS#Vec3 Ending point of the profile.
|
||||||
|
-- @return #table DCS#Vec3 table of the profile
|
||||||
|
function COORDINATE:GetLandProfileVec3(Destination)
|
||||||
|
return land.profile(self:GetVec3(), Destination)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns a table of #COORDINATE representing the terrain profile between two points.
|
||||||
|
-- @param #COORDINATE self
|
||||||
|
-- @param Destination #COORDINATE Ending coordinate of the profile.
|
||||||
|
-- @return #table #COORDINATE table of the profile
|
||||||
|
function COORDINATE:GetLandProfileCoordinates(Destination)
|
||||||
|
local points = self:GetLandProfileVec3(Destination:GetVec3())
|
||||||
|
local coords = {}
|
||||||
|
for _, point in ipairs(points) do
|
||||||
|
table.insert(coords, COORDINATE:NewFromVec3(point))
|
||||||
|
end
|
||||||
|
return coords
|
||||||
|
end
|
||||||
|
|
||||||
--- Set the heading of the coordinate, if applicable.
|
--- Set the heading of the coordinate, if applicable.
|
||||||
-- @param #COORDINATE self
|
-- @param #COORDINATE self
|
||||||
@@ -2124,21 +2167,32 @@ do -- COORDINATE
|
|||||||
-- @param #number Duration (Optional) Duration of the smoke in seconds. DCS stopps the smoke automatically after 5 min.
|
-- @param #number Duration (Optional) Duration of the smoke in seconds. DCS stopps the smoke automatically after 5 min.
|
||||||
-- @param #number Delay (Optional) Delay before the smoke is started in seconds.
|
-- @param #number Delay (Optional) Delay before the smoke is started in seconds.
|
||||||
-- @param #string Name (Optional) Name if you want to stop the smoke early (normal duration: 5mins)
|
-- @param #string Name (Optional) Name if you want to stop the smoke early (normal duration: 5mins)
|
||||||
|
-- @param #boolean Offset (Optional) If true, offset the smokle a bit.
|
||||||
|
-- @param #number Direction (Optional) If Offset is true this is the direction of the offset, 1-359 (degrees). Default random.
|
||||||
|
-- @param #number Distance (Optional) If Offset is true this is the distance of the offset in meters. Default random 10-20.
|
||||||
-- @return #COORDINATE self
|
-- @return #COORDINATE self
|
||||||
function COORDINATE:Smoke( SmokeColor, Duration, Delay, Name)
|
function COORDINATE:Smoke( SmokeColor, Duration, Delay, Name, Offset,Direction,Distance)
|
||||||
self:F2( { SmokeColor, Name, Duration, Delay } )
|
self:F2( { SmokeColor, Name, Duration, Delay, Offset } )
|
||||||
|
|
||||||
SmokeColor=SmokeColor or SMOKECOLOR.Green
|
SmokeColor=SmokeColor or SMOKECOLOR.Green
|
||||||
|
|
||||||
if Delay and Delay>0 then
|
if Delay and Delay>0 then
|
||||||
self:ScheduleOnce(Delay, COORDINATE.Smoke, self, SmokeColor, Duration, 0, Name)
|
self:ScheduleOnce(Delay, COORDINATE.Smoke, self, SmokeColor, Duration, 0, Name, Direction,Distance)
|
||||||
else
|
else
|
||||||
|
|
||||||
-- Create a name which is used to stop the smoke manually
|
-- Create a name which is used to stop the smoke manually
|
||||||
self.firename = Name or "Smoke-"..math.random(1,100000)
|
self.firename = Name or "Smoke-"..math.random(1,100000)
|
||||||
|
|
||||||
-- Create smoke
|
-- Create smoke
|
||||||
trigger.action.smoke( self:GetVec3(), SmokeColor, self.firename )
|
if Offset or self.SmokeOffset then
|
||||||
|
local Angle = Direction or self:GetSmokeOffsetDirection()
|
||||||
|
local Distance = Distance or self:GetSmokeOffsetDistance()
|
||||||
|
local newpos = self:Translate(Distance,Angle,true,false)
|
||||||
|
local newvec3 = newpos:GetVec3()
|
||||||
|
trigger.action.smoke( newvec3, SmokeColor, self.firename )
|
||||||
|
else
|
||||||
|
trigger.action.smoke( self:GetVec3(), SmokeColor, self.firename )
|
||||||
|
end
|
||||||
|
|
||||||
-- Stop smoke
|
-- Stop smoke
|
||||||
if Duration and Duration>0 then
|
if Duration and Duration>0 then
|
||||||
@@ -2148,6 +2202,72 @@ do -- COORDINATE
|
|||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get the offset direction when using `COORDINATE:Smoke()`.
|
||||||
|
-- @param #COORDINATE self
|
||||||
|
-- @return #number Direction in degrees.
|
||||||
|
function COORDINATE:GetSmokeOffsetDirection()
|
||||||
|
local direction = self.SmokeOffsetDirection or math.random(1,359)
|
||||||
|
return direction
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the offset direction when using `COORDINATE:Smoke()`.
|
||||||
|
-- @param #COORDINATE self
|
||||||
|
-- @param #number Direction (Optional) This is the direction of the offset, 1-359 (degrees). Default random.
|
||||||
|
-- @return #COORDINATE self
|
||||||
|
function COORDINATE:SetSmokeOffsetDirection(Direction)
|
||||||
|
if self then
|
||||||
|
self.SmokeOffsetDirection = Direction or math.random(1,359)
|
||||||
|
return self
|
||||||
|
else
|
||||||
|
COORDINATE.SmokeOffsetDirection = Direction or math.random(1,359)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the offset distance when using `COORDINATE:Smoke()`.
|
||||||
|
-- @param #COORDINATE self
|
||||||
|
-- @return #number Distance Distance in meters.
|
||||||
|
function COORDINATE:GetSmokeOffsetDistance()
|
||||||
|
local distance = self.SmokeOffsetDistance or math.random(10,20)
|
||||||
|
return distance
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the offset distance when using `COORDINATE:Smoke()`.
|
||||||
|
-- @param #COORDINATE self
|
||||||
|
-- @param #number Distance (Optional) This is the distance of the offset in meters. Default random 10-20.
|
||||||
|
-- @return #COORDINATE self
|
||||||
|
function COORDINATE:SetSmokeOffsetDistance(Distance)
|
||||||
|
if self then
|
||||||
|
self.SmokeOffsetDistance = Distance or math.random(10,20)
|
||||||
|
return self
|
||||||
|
else
|
||||||
|
COORDINATE.SmokeOffsetDistance = Distance or math.random(10,20)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the offset on when using `COORDINATE:Smoke()`.
|
||||||
|
-- @param #COORDINATE self
|
||||||
|
-- @return #COORDINATE self
|
||||||
|
function COORDINATE:SwitchSmokeOffsetOn()
|
||||||
|
if self then
|
||||||
|
self.SmokeOffset = true
|
||||||
|
return self
|
||||||
|
else
|
||||||
|
COORDINATE.SmokeOffset = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the offset off when using `COORDINATE:Smoke()`.
|
||||||
|
-- @param #COORDINATE self
|
||||||
|
-- @return #COORDINATE self
|
||||||
|
function COORDINATE:SwitchSmokeOffsetOff()
|
||||||
|
if self then
|
||||||
|
self.SmokeOffset = false
|
||||||
|
return self
|
||||||
|
else
|
||||||
|
COORDINATE.SmokeOffset = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--- Stops smoking the point in a color.
|
--- Stops smoking the point in a color.
|
||||||
-- @param #COORDINATE self
|
-- @param #COORDINATE self
|
||||||
@@ -3716,7 +3836,26 @@ do -- COORDINATE
|
|||||||
function COORDINATE:GetRandomPointVec3InRadius( OuterRadius, InnerRadius )
|
function COORDINATE:GetRandomPointVec3InRadius( OuterRadius, InnerRadius )
|
||||||
return COORDINATE:NewFromVec3( self:GetRandomVec3InRadius( OuterRadius, InnerRadius ) )
|
return COORDINATE:NewFromVec3( self:GetRandomVec3InRadius( OuterRadius, InnerRadius ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Search for clear zones in a given area. A powerful and efficient function using Disposition to find clear areas for spawning ground units avoiding trees, water and map scenery.
|
||||||
|
-- @param #number SearchRadius Radius of the search area.
|
||||||
|
-- @param #number PosRadius Required clear radius around each position.
|
||||||
|
-- @param #number NumPositions Number of positions to find.
|
||||||
|
-- @return #table A table of Core.Point#COORDINATE that are clear of map objects within the given PosRadius. nil if no positions are found.
|
||||||
|
function COORDINATE:GetSimpleZones(SearchRadius, PosRadius, NumPositions)
|
||||||
|
local clearPositions = UTILS.GetSimpleZones(self:GetVec3(), SearchRadius, PosRadius, NumPositions)
|
||||||
|
if clearPositions and #clearPositions > 0 then
|
||||||
|
local coords = {}
|
||||||
|
for _, pos in pairs(clearPositions) do
|
||||||
|
local coord = COORDINATE:NewFromVec2(pos)
|
||||||
|
table.insert(coords, coord)
|
||||||
|
end
|
||||||
|
return coords
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
do
|
do
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
|||||||
local Name = Info.name or "?"
|
local Name = Info.name or "?"
|
||||||
|
|
||||||
local ErrorHandler = function( errmsg )
|
local ErrorHandler = function( errmsg )
|
||||||
env.info( "Error in timer function: " .. errmsg )
|
env.info( "Error in timer function: " .. errmsg or "" )
|
||||||
if BASE.Debug ~= nil then
|
if BASE.Debug ~= nil then
|
||||||
env.info( BASE.Debug.traceback() )
|
env.info( BASE.Debug.traceback() )
|
||||||
end
|
end
|
||||||
@@ -326,7 +326,7 @@ function SCHEDULEDISPATCHER:Stop( Scheduler, CallID )
|
|||||||
local Schedule = self.Schedule[Scheduler][CallID] -- #SCHEDULEDISPATCHER.ScheduleData
|
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.
|
-- 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 ) ) )
|
self:T( string.format( "SCHEDULEDISPATCHER stopping scheduler CallID=%s, ScheduleID=%s", tostring( CallID ), tostring( Schedule.ScheduleID ) ) )
|
||||||
|
|
||||||
|
|||||||
@@ -958,7 +958,26 @@ do -- SET_BASE
|
|||||||
|
|
||||||
return ObjectNames
|
return ObjectNames
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get a *new* set table that only contains alive objects.
|
||||||
|
-- @param #SET_BASE self
|
||||||
|
-- @return #table Set table of alive objects.
|
||||||
|
function SET_BASE:GetAliveSet()
|
||||||
|
--self:F2()
|
||||||
|
|
||||||
|
local AliveSet = {}
|
||||||
|
-- Clean the Set before returning with only the alive Objects.
|
||||||
|
for ObjectName, Object in pairs( self.Set ) do
|
||||||
|
if Object then
|
||||||
|
if Object:IsAlive() then
|
||||||
|
AliveSet[#AliveSet+1] = Object
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return AliveSet or {}
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
do
|
do
|
||||||
@@ -1125,25 +1144,25 @@ do
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get a *new* set that only contains alive groups.
|
--- Get a *new* set table that only contains alive groups.
|
||||||
-- @param #SET_GROUP self
|
-- @param #SET_GROUP self
|
||||||
-- @return #SET_GROUP Set of alive groups.
|
-- @return #table Set of alive groups.
|
||||||
function SET_GROUP:GetAliveSet()
|
function SET_GROUP:GetAliveSet()
|
||||||
--self:F2()
|
--self:F2()
|
||||||
|
|
||||||
local AliveSet = SET_GROUP:New()
|
--local AliveSet = SET_GROUP:New()
|
||||||
|
local AliveSet = {}
|
||||||
-- Clean the Set before returning with only the alive Groups.
|
-- Clean the Set before returning with only the alive Groups.
|
||||||
for GroupName, GroupObject in pairs( self.Set ) do
|
for GroupName, GroupObject in pairs( self.Set ) do
|
||||||
local GroupObject = GroupObject -- Wrapper.Group#GROUP
|
local GroupObject = GroupObject -- Wrapper.Group#GROUP
|
||||||
if GroupObject then
|
if GroupObject then
|
||||||
if GroupObject:IsAlive() then
|
if GroupObject:IsAlive() then
|
||||||
AliveSet:Add( GroupName, GroupObject )
|
AliveSet[GroupName] = GroupObject
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return AliveSet.Set or {}
|
return AliveSet or {}
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns a report of of unit types.
|
--- Returns a report of of unit types.
|
||||||
@@ -2595,18 +2614,16 @@ do -- SET_UNIT
|
|||||||
|
|
||||||
--- Gets the alive set.
|
--- Gets the alive set.
|
||||||
-- @param #SET_UNIT self
|
-- @param #SET_UNIT self
|
||||||
-- @return #table Table of SET objects
|
-- @return #table Table of alive UNIT objects
|
||||||
-- @return #SET_UNIT AliveSet
|
-- @return #SET_UNIT AliveSet
|
||||||
function SET_UNIT:GetAliveSet()
|
function SET_UNIT:GetAliveSet()
|
||||||
|
|
||||||
local AliveSet = SET_UNIT:New()
|
local AliveSet = SET_UNIT:New()
|
||||||
|
|
||||||
-- Clean the Set before returning with only the alive Groups.
|
-- Clean the Set before returning with only the alive Groups.
|
||||||
for GroupName, GroupObject in pairs(self.Set) do
|
for GroupName, GroupObject in pairs(self.Set) do
|
||||||
local GroupObject=GroupObject --Wrapper.Client#CLIENT
|
|
||||||
|
|
||||||
if GroupObject and GroupObject:IsAlive() then
|
if GroupObject and GroupObject:IsAlive() then
|
||||||
AliveSet:Add(GroupName, GroupObject)
|
AliveSet[GroupName] = GroupObject
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -4784,18 +4801,16 @@ do -- SET_CLIENT
|
|||||||
-- @return #table Table of SET objects
|
-- @return #table Table of SET objects
|
||||||
function SET_CLIENT:GetAliveSet()
|
function SET_CLIENT:GetAliveSet()
|
||||||
|
|
||||||
local AliveSet = SET_CLIENT:New()
|
local AliveSet = {}
|
||||||
|
|
||||||
-- Clean the Set before returning with only the alive Groups.
|
-- Clean the Set before returning with only the alive Groups.
|
||||||
for GroupName, GroupObject in pairs(self.Set) do
|
for GroupName, GroupObject in pairs(self.Set) do
|
||||||
local GroupObject=GroupObject --Wrapper.Client#CLIENT
|
|
||||||
|
|
||||||
if GroupObject and GroupObject:IsAlive() then
|
if GroupObject and GroupObject:IsAlive() then
|
||||||
AliveSet:Add(GroupName, GroupObject)
|
AliveSet[GroupName] = GroupObject
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return AliveSet.Set or {}
|
return AliveSet or {}
|
||||||
end
|
end
|
||||||
|
|
||||||
--- [User] Add a custom condition function.
|
--- [User] Add a custom condition function.
|
||||||
@@ -6676,6 +6691,8 @@ do -- SET_ZONE
|
|||||||
--
|
--
|
||||||
-- -- Stop watching after 1 hour
|
-- -- Stop watching after 1 hour
|
||||||
-- zoneset:__TriggerStop(3600)
|
-- zoneset:__TriggerStop(3600)
|
||||||
|
-- -- Call :SetPartlyInside() on any zone (or SET_ZONE) if you want GROUPs to count as inside when any of their units enters even if they are far apart.
|
||||||
|
-- -- Make sure to call :SetPartlyInside() before :Trigger()!.
|
||||||
function SET_ZONE:Trigger(Objects)
|
function SET_ZONE:Trigger(Objects)
|
||||||
--self:I("Added Set_Zone Trigger")
|
--self:I("Added Set_Zone Trigger")
|
||||||
self:AddTransition("*","TriggerStart","TriggerRunning")
|
self:AddTransition("*","TriggerStart","TriggerRunning")
|
||||||
@@ -6726,6 +6743,20 @@ do -- SET_ZONE
|
|||||||
-- @param Core.Zone#ZONE_BASE Zone The zone left.
|
-- @param Core.Zone#ZONE_BASE Zone The zone left.
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Toggle “partly-inside” handling for every zone in the set when those zones are used with :Trigger().
|
||||||
|
-- * Call with no argument or **true** → enable for all.
|
||||||
|
-- * Call with **false** → disable again (handy if it was enabled before).
|
||||||
|
-- @param #SET_ZONE self
|
||||||
|
-- @return #SET_ZONE self
|
||||||
|
function SET_ZONE:SetPartlyInside(state)
|
||||||
|
for _,Zone in pairs(self.Set) do
|
||||||
|
if Zone.SetPartlyInside then
|
||||||
|
Zone:SetPartlyInside(state)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- (Internal) Check the assigned objects for being in/out of the zone
|
--- (Internal) Check the assigned objects for being in/out of the zone
|
||||||
-- @param #SET_ZONE self
|
-- @param #SET_ZONE self
|
||||||
-- @param #boolean fromstart If true, do the init of the objects
|
-- @param #boolean fromstart If true, do the init of the objects
|
||||||
@@ -6761,8 +6792,13 @@ do -- SET_ZONE
|
|||||||
-- has not been tagged previously - wasn't in set!
|
-- has not been tagged previously - wasn't in set!
|
||||||
obj.TriggerInZone[_zone.ZoneName] = false
|
obj.TriggerInZone[_zone.ZoneName] = false
|
||||||
end
|
end
|
||||||
-- is obj in zone?
|
-- is obj in this zone?
|
||||||
local inzone = _zone:IsCoordinateInZone(obj:GetCoordinate())
|
local inzone
|
||||||
|
if _zone.PartlyInside and obj.ClassName == "GROUP" then
|
||||||
|
inzone = obj:IsAnyInZone(_zone) -- TRUE as soon as any unit is inside
|
||||||
|
else
|
||||||
|
inzone = _zone:IsCoordinateInZone(obj:GetCoordinate()) -- original centroid test
|
||||||
|
end
|
||||||
--self:I("Object "..obj:GetName().." is in zone: "..tostring(inzone))
|
--self:I("Object "..obj:GetName().." is in zone: "..tostring(inzone))
|
||||||
if inzone and not obj.TriggerInZone[_zone.ZoneName] then
|
if inzone and not obj.TriggerInZone[_zone.ZoneName] then
|
||||||
-- wasn't in zone before
|
-- wasn't in zone before
|
||||||
@@ -7829,6 +7865,28 @@ do -- SET_OPSGROUP
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Iterate the SET_OPSGROUP and count how many GROUPs and UNITs are alive.
|
||||||
|
-- @param #SET_GROUP self
|
||||||
|
-- @return #number The number of GROUPs alive.
|
||||||
|
-- @return #number The number of UNITs alive.
|
||||||
|
function SET_OPSGROUP:CountAlive()
|
||||||
|
local CountG = 0
|
||||||
|
local CountU = 0
|
||||||
|
|
||||||
|
local Set = self:GetSet()
|
||||||
|
|
||||||
|
for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP
|
||||||
|
if GroupData and GroupData:IsAlive() then
|
||||||
|
CountG = CountG + 1
|
||||||
|
-- Count Units.
|
||||||
|
CountU = CountU + GroupData:GetGroup():CountAliveUnits()
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return CountG, CountU
|
||||||
|
end
|
||||||
|
|
||||||
--- Finds an OPSGROUP based on the group name.
|
--- Finds an OPSGROUP based on the group name.
|
||||||
-- @param #SET_OPSGROUP self
|
-- @param #SET_OPSGROUP self
|
||||||
-- @param #string GroupName Name of the group.
|
-- @param #string GroupName Name of the group.
|
||||||
|
|||||||
@@ -1049,6 +1049,23 @@ function SPAWN:InitSetUnitAbsolutePositions(Positions)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Uses Disposition and other fallback logic to find better ground positions for ground units.
|
||||||
|
--- NOTE: This is not a spawn randomizer.
|
||||||
|
--- It will try to find clear ground locations avoiding trees, water, roads, runways, map scenery, statics and other units in the area.
|
||||||
|
--- Maintains the original layout and unit positions as close as possible by searching for the next closest valid position to each unit.
|
||||||
|
-- @param #SPAWN self
|
||||||
|
-- @param #boolean OnOff Enable/disable the feature.
|
||||||
|
-- @param #number MaxRadius (Optional) Max radius to search for valid ground locations in meters. Default is double the max radius of the units.
|
||||||
|
-- @param #number Spacing (Optional) Minimum spacing between units in meters. Default is 5% of the search radius or 5 meters, whichever is larger.
|
||||||
|
-- @return #SPAWN
|
||||||
|
function SPAWN:InitValidateAndRepositionGroundUnits(OnOff, MaxRadius, Spacing)
|
||||||
|
self.SpawnValidateAndRepositionGroundUnits = OnOff
|
||||||
|
self.SpawnValidateAndRepositionGroundUnitsRadius = MaxRadius
|
||||||
|
self.SpawnValidateAndRepositionGroundUnitsSpacing = Spacing
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- This method is rather complicated to understand. But I'll try to explain.
|
--- This method is rather complicated to understand. But I'll try to explain.
|
||||||
-- This method becomes useful when you need to spawn groups with random templates of groups defined within the mission editor,
|
-- This method becomes useful when you need to spawn groups with random templates of groups defined within the mission editor,
|
||||||
-- but they will all follow the same Template route and have the same prefix name.
|
-- but they will all follow the same Template route and have the same prefix name.
|
||||||
@@ -1829,7 +1846,13 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
|
|||||||
if self.SpawnHiddenOnMap then
|
if self.SpawnHiddenOnMap then
|
||||||
SpawnTemplate.hidden=self.SpawnHiddenOnMap
|
SpawnTemplate.hidden=self.SpawnHiddenOnMap
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if self.SpawnValidateAndRepositionGroundUnits then
|
||||||
|
local units = SpawnTemplate.units
|
||||||
|
local gPos = { x = SpawnTemplate.x, y = SpawnTemplate.y }
|
||||||
|
UTILS.ValidateAndRepositionGroundUnits(units, gPos, self.SpawnValidateAndRepositionGroundUnitsRadius, self.SpawnValidateAndRepositionGroundUnitsSpacing)
|
||||||
|
end
|
||||||
|
|
||||||
-- Set country, coalition and category.
|
-- Set country, coalition and category.
|
||||||
SpawnTemplate.CategoryID = self.SpawnInitCategory or SpawnTemplate.CategoryID
|
SpawnTemplate.CategoryID = self.SpawnInitCategory or SpawnTemplate.CategoryID
|
||||||
SpawnTemplate.CountryID = self.SpawnInitCountry or SpawnTemplate.CountryID
|
SpawnTemplate.CountryID = self.SpawnInitCountry or SpawnTemplate.CountryID
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ function SPAWNSTATIC:NewFromStatic(SpawnTemplateName, SpawnCountryID)
|
|||||||
self.CategoryID = CategoryID
|
self.CategoryID = CategoryID
|
||||||
self.CoalitionID = CoalitionID
|
self.CoalitionID = CoalitionID
|
||||||
self.SpawnIndex = 0
|
self.SpawnIndex = 0
|
||||||
|
self.StaticCopyFrom = SpawnTemplateName
|
||||||
else
|
else
|
||||||
error( "SPAWNSTATIC:New: There is no static declared in the mission editor with SpawnTemplatePrefix = '" .. tostring(SpawnTemplateName) .. "'" )
|
error( "SPAWNSTATIC:New: There is no static declared in the mission editor with SpawnTemplatePrefix = '" .. tostring(SpawnTemplateName) .. "'" )
|
||||||
end
|
end
|
||||||
@@ -302,12 +303,16 @@ end
|
|||||||
-- @param #number CallsignID Callsign ID. Default 1 (="London").
|
-- @param #number CallsignID Callsign ID. Default 1 (="London").
|
||||||
-- @param #number Frequency Frequency in MHz. Default 127.5 MHz.
|
-- @param #number Frequency Frequency in MHz. Default 127.5 MHz.
|
||||||
-- @param #number Modulation Modulation 0=AM, 1=FM.
|
-- @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
|
-- @return #SPAWNSTATIC self
|
||||||
function SPAWNSTATIC:InitFARP(CallsignID, Frequency, Modulation)
|
function SPAWNSTATIC:InitFARP(CallsignID, Frequency, Modulation, DynamicSpawns,DynamicHotStarts)
|
||||||
self.InitFarp=true
|
self.InitFarp=true
|
||||||
self.InitFarpCallsignID=CallsignID or 1
|
self.InitFarpCallsignID=CallsignID or 1
|
||||||
self.InitFarpFreq=Frequency or 127.5
|
self.InitFarpFreq=Frequency or 127.5
|
||||||
self.InitFarpModu=Modulation or 0
|
self.InitFarpModu=Modulation or 0
|
||||||
|
self.InitFarpDynamicSpawns = DynamicSpawns
|
||||||
|
self.InitFarpDynamicHotStarts = (DynamicSpawns == true and DynamicHotStarts == true) and true or nil
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -373,6 +378,20 @@ function SPAWNSTATIC:InitLinkToUnit(Unit, OffsetX, OffsetY, OffsetAngle)
|
|||||||
return self
|
return self
|
||||||
end
|
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.
|
--- 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 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.
|
-- The first parameter of the SpawnFunction is the @{Wrapper.Static#STATIC} that was spawned.
|
||||||
@@ -459,8 +478,9 @@ end
|
|||||||
function SPAWNSTATIC:SpawnFromZone(Zone, Heading, NewName)
|
function SPAWNSTATIC:SpawnFromZone(Zone, Heading, NewName)
|
||||||
|
|
||||||
-- Spawn the new static at the center of the zone.
|
-- 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
|
return Static
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -538,6 +558,14 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
|
|||||||
-- Add static to the game.
|
-- Add static to the game.
|
||||||
local Static=nil --DCS#StaticObject
|
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
|
if self.InitFarp then
|
||||||
|
|
||||||
local TemplateGroup={}
|
local TemplateGroup={}
|
||||||
@@ -549,6 +577,13 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
|
|||||||
TemplateGroup.x=Template.x
|
TemplateGroup.x=Template.x
|
||||||
TemplateGroup.y=Template.y
|
TemplateGroup.y=Template.y
|
||||||
TemplateGroup.name=Template.name
|
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("Spawning FARP")
|
||||||
self:T({Template=Template})
|
self:T({Template=Template})
|
||||||
@@ -556,7 +591,8 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
|
|||||||
|
|
||||||
-- ED's dirty way to spawn FARPS.
|
-- ED's dirty way to spawn FARPS.
|
||||||
Static=coalition.addGroup(CountryID, -1, TemplateGroup)
|
Static=coalition.addGroup(CountryID, -1, TemplateGroup)
|
||||||
|
--Static=coalition.addStaticObject(CountryID, Template)
|
||||||
|
|
||||||
-- Currently DCS 2.8 does not trigger birth events if FARPS are spawned!
|
-- 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
|
-- We create such an event. The airbase is registered in Core.Event
|
||||||
local Event = {
|
local Event = {
|
||||||
@@ -594,6 +630,18 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
|
|||||||
-- delay calling this for .3 seconds so that it hopefully comes after the BIRTH event of the group.
|
-- 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
|
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
|
return mystatic
|
||||||
end
|
end
|
||||||
|
|||||||
1200
Moose Development/Moose/Core/Vector.lua
Normal file
1200
Moose Development/Moose/Core/Vector.lua
Normal file
File diff suppressed because it is too large
Load Diff
@@ -70,6 +70,7 @@
|
|||||||
-- @field #table Table of any trigger zone properties from the ME. The key is the Name of the property, and the value is the property's Value.
|
-- @field #table Table of any trigger zone properties from the ME. The key is the Name of the property, and the value is the property's Value.
|
||||||
-- @field #number Surface Type of surface. Only determined at the center of the zone!
|
-- @field #number Surface Type of surface. Only determined at the center of the zone!
|
||||||
-- @field #number Checktime Check every Checktime seconds, used for ZONE:Trigger()
|
-- @field #number Checktime Check every Checktime seconds, used for ZONE:Trigger()
|
||||||
|
-- @field #boolean PartlyInside When called, a GROUP is considered inside as soon as any of its units enters the zone even if they are far apart.
|
||||||
-- @extends Core.Fsm#FSM
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
|
|
||||||
@@ -305,6 +306,20 @@ function ZONE_BASE:GetCoordinate( Height ) --R2.1
|
|||||||
return self.Coordinate
|
return self.Coordinate
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Returns the @{Core.Vector#VECTOR} of the zone.
|
||||||
|
-- @param #ZONE_BASE self
|
||||||
|
-- @param DCS#Distance Height The height in meters to add to the land height where the center of the zone is located.
|
||||||
|
-- @return Core.Vector#VECTOR The vector of the zone.
|
||||||
|
function ZONE_BASE:GetVector( Height )
|
||||||
|
self:F2(self.ZoneName)
|
||||||
|
|
||||||
|
local Vec3 = self:GetVec3( Height )
|
||||||
|
|
||||||
|
local vector=VECTOR:NewFromVec(Vec3)
|
||||||
|
|
||||||
|
return vector
|
||||||
|
end
|
||||||
|
|
||||||
--- Get 2D distance to a coordinate.
|
--- Get 2D distance to a coordinate.
|
||||||
-- @param #ZONE_BASE self
|
-- @param #ZONE_BASE self
|
||||||
-- @param Core.Point#COORDINATE Coordinate Reference coordinate. Can also be a DCS#Vec2 or DCS#Vec3 object.
|
-- @param Core.Point#COORDINATE Coordinate Reference coordinate. Can also be a DCS#Vec2 or DCS#Vec3 object.
|
||||||
@@ -534,6 +549,19 @@ function ZONE_BASE:GetZoneProbability()
|
|||||||
return self.ZoneProbability
|
return self.ZoneProbability
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get the coordinate on the radius of the zone nearest to Outsidecoordinate. Useto e.g. find an ingress point.
|
||||||
|
-- @param #ZONE_BASE self
|
||||||
|
-- @param Core.Point#COORDINATE Outsidecoordinate The coordinate outside of the zone from where to look.
|
||||||
|
-- @return Core.Point#COORDINATE CoordinateOnRadius
|
||||||
|
function ZONE_BASE:FindNearestCoordinateOnRadius(Outsidecoordinate)
|
||||||
|
local Vec1 = self:GetVec2()
|
||||||
|
local Radius = self:GetRadius()
|
||||||
|
local Vec2 = Outsidecoordinate:GetVec2()
|
||||||
|
local Point = UTILS.FindNearestPointOnCircle(Vec1,Radius,Vec2)
|
||||||
|
local rc = COORDINATE:NewFromVec2(Point)
|
||||||
|
return rc
|
||||||
|
end
|
||||||
|
|
||||||
--- Get the zone taking into account the randomization probability of a zone to be selected.
|
--- Get the zone taking into account the randomization probability of a zone to be selected.
|
||||||
-- @param #ZONE_BASE self
|
-- @param #ZONE_BASE self
|
||||||
-- @return #ZONE_BASE The zone is selected taking into account the randomization probability factor.
|
-- @return #ZONE_BASE The zone is selected taking into account the randomization probability factor.
|
||||||
@@ -599,6 +627,8 @@ end
|
|||||||
--
|
--
|
||||||
-- -- Stop watching the zone after 1 hour
|
-- -- Stop watching the zone after 1 hour
|
||||||
-- triggerzone:__TriggerStop(3600)
|
-- triggerzone:__TriggerStop(3600)
|
||||||
|
-- -- Call :SetPartlyInside() if you use SET_GROUP to count as inside when any of their units enters even when they are far apart.
|
||||||
|
-- -- Make sure to call :SetPartlyInside() before :Trigger()!
|
||||||
function ZONE_BASE:Trigger(Objects)
|
function ZONE_BASE:Trigger(Objects)
|
||||||
--self:I("Added Zone Trigger")
|
--self:I("Added Zone Trigger")
|
||||||
self:SetStartState("TriggerStopped")
|
self:SetStartState("TriggerStopped")
|
||||||
@@ -667,6 +697,16 @@ function ZONE_BASE:Trigger(Objects)
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Toggle “partly-inside” handling for this zone. To be used before :Trigger().
|
||||||
|
-- * Default:* flag is **false** until you call the method.
|
||||||
|
-- * Call with no argument or with **true** → enable.
|
||||||
|
-- * Call with **false** → disable again (handy if it was enabled before).
|
||||||
|
-- @param #ZONE_BASE self
|
||||||
|
-- @return #ZONE_BASE self
|
||||||
|
function ZONE_BASE:SetPartlyInside(state)
|
||||||
|
self.PartlyInside = state or not ( state == false )
|
||||||
|
return self
|
||||||
|
end
|
||||||
--- (Internal) Check the assigned objects for being in/out of the zone
|
--- (Internal) Check the assigned objects for being in/out of the zone
|
||||||
-- @param #ZONE_BASE self
|
-- @param #ZONE_BASE self
|
||||||
-- @param #boolean fromstart If true, do the init of the objects
|
-- @param #boolean fromstart If true, do the init of the objects
|
||||||
@@ -705,7 +745,12 @@ function ZONE_BASE:_TriggerCheck(fromstart)
|
|||||||
obj.TriggerInZone[self.ZoneName] = false
|
obj.TriggerInZone[self.ZoneName] = false
|
||||||
end
|
end
|
||||||
-- is obj in zone?
|
-- is obj in zone?
|
||||||
local inzone = self:IsCoordinateInZone(obj:GetCoordinate())
|
local inzone
|
||||||
|
if self.PartlyInside and obj.ClassName == "GROUP" then
|
||||||
|
inzone = obj:IsAnyInZone(self) -- TRUE if any unit is inside
|
||||||
|
else
|
||||||
|
inzone = self:IsCoordinateInZone(obj:GetCoordinate()) -- original barycentre test
|
||||||
|
end
|
||||||
--self:I("Object "..obj:GetName().." is in zone: "..tostring(inzone))
|
--self:I("Object "..obj:GetName().." is in zone: "..tostring(inzone))
|
||||||
if inzone and obj.TriggerInZone[self.ZoneName] then
|
if inzone and obj.TriggerInZone[self.ZoneName] then
|
||||||
-- just count
|
-- just count
|
||||||
@@ -1149,15 +1194,13 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories )
|
|||||||
|
|
||||||
local function EvaluateZone( ZoneObject )
|
local function EvaluateZone( ZoneObject )
|
||||||
--if ZoneObject:isExist() then --FF: isExist always returns false for SCENERY objects since DCS 2.2 and still in DCS 2.5
|
--if ZoneObject:isExist() then --FF: isExist always returns false for SCENERY objects since DCS 2.2 and still in DCS 2.5
|
||||||
if ZoneObject then
|
if ZoneObject and self:IsVec3InZone(ZoneObject:getPoint()) then
|
||||||
|
|
||||||
-- Get object category.
|
-- Get object category.
|
||||||
local ObjectCategory = Object.getCategory(ZoneObject)
|
local ObjectCategory = Object.getCategory(ZoneObject)
|
||||||
|
|
||||||
if ( ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive() ) or (ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then
|
if ( ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive() ) or (ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then
|
||||||
|
|
||||||
local CoalitionDCSUnit = ZoneObject:getCoalition()
|
|
||||||
|
|
||||||
local Include = false
|
local Include = false
|
||||||
if not UnitCategories then
|
if not UnitCategories then
|
||||||
-- Anything found is included.
|
-- Anything found is included.
|
||||||
@@ -1509,6 +1552,26 @@ function ZONE_RADIUS:IsVec3InZone( Vec3 )
|
|||||||
return InZone
|
return InZone
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Search for clear ground spawn zones within this zone. A powerful and efficient function using Disposition to find clear areas for spawning ground units avoiding trees, water and map scenery.
|
||||||
|
-- @param #ZONE_RADIUS self
|
||||||
|
-- @param #number PosRadius Required clear radius around each position.
|
||||||
|
-- @param #number NumPositions Number of positions to find.
|
||||||
|
-- @return #table A table of DCS#Vec2 positions that are clear of map objects within the given PosRadius. nil if no clear positions are found.
|
||||||
|
function ZONE_RADIUS:GetClearZonePositions(PosRadius, NumPositions)
|
||||||
|
return UTILS.GetClearZonePositions(self, PosRadius, NumPositions)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Search for a random clear ground spawn coordinate within this zone. A powerful and efficient function using Disposition to find clear areas for spawning ground units avoiding trees, water and map scenery.
|
||||||
|
-- @param #ZONE_RADIUS self
|
||||||
|
-- @param #number PosRadius (Optional) Required clear radius around each position. (Default is math.min(Radius/10, 200))
|
||||||
|
-- @param #number NumPositions (Optional) Number of positions to find. (Default 50)
|
||||||
|
-- @return Core.Point#COORDINATE A random coordinate for a clear zone. nil if no clear positions are found.
|
||||||
|
-- @return #number Assigned radius for the found zones. nil if no clear positions are found.
|
||||||
|
function ZONE_RADIUS:GetRandomClearZoneCoordinate(PosRadius, NumPositions)
|
||||||
|
return UTILS.GetRandomClearZoneCoordinate(self, PosRadius, NumPositions)
|
||||||
|
end
|
||||||
|
|
||||||
--- Returns a random Vec2 location within the zone.
|
--- Returns a random Vec2 location within the zone.
|
||||||
-- @param #ZONE_RADIUS self
|
-- @param #ZONE_RADIUS self
|
||||||
-- @param #number inner (Optional) Minimal distance from the center of the zone. Default is 0.
|
-- @param #number inner (Optional) Minimal distance from the center of the zone. Default is 0.
|
||||||
@@ -1520,6 +1583,10 @@ function ZONE_RADIUS:GetRandomVec2(inner, outer, surfacetypes)
|
|||||||
local Vec2 = self:GetVec2()
|
local Vec2 = self:GetVec2()
|
||||||
local _inner = inner or 0
|
local _inner = inner or 0
|
||||||
local _outer = outer or self:GetRadius()
|
local _outer = outer or self:GetRadius()
|
||||||
|
|
||||||
|
math.random()
|
||||||
|
math.random()
|
||||||
|
math.random()
|
||||||
|
|
||||||
if surfacetypes and type(surfacetypes)~="table" then
|
if surfacetypes and type(surfacetypes)~="table" then
|
||||||
surfacetypes={surfacetypes}
|
surfacetypes={surfacetypes}
|
||||||
@@ -1881,6 +1948,21 @@ function ZONE_UNIT:New( ZoneName, ZoneUNIT, Radius, Offset)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Updates the current location from a @{Wrapper.Group}.
|
||||||
|
-- @param #ZONE_UNIT self
|
||||||
|
-- @param Wrapper.Group#GROUP Group (optional) Update from this Unit, if nil, update from the UNIT this zone is based on.
|
||||||
|
-- @return self
|
||||||
|
function ZONE_UNIT:UpdateFromUnit(Unit)
|
||||||
|
if Unit and Unit:IsAlive() then
|
||||||
|
local vec2 = Unit:GetVec2()
|
||||||
|
self.LastVec2 = vec2
|
||||||
|
elseif self.ZoneUNIT and self.ZoneUNIT:IsAlive() then
|
||||||
|
local ZoneVec2 = self.ZoneUNIT:GetVec2()
|
||||||
|
self.LastVec2 = ZoneVec2
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Returns the current location of the @{Wrapper.Unit#UNIT}.
|
--- Returns the current location of the @{Wrapper.Unit#UNIT}.
|
||||||
-- @param #ZONE_UNIT self
|
-- @param #ZONE_UNIT self
|
||||||
@@ -2018,6 +2100,22 @@ function ZONE_GROUP:GetVec2()
|
|||||||
return ZoneVec2
|
return ZoneVec2
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Updates the current location from a @{Wrapper.Group}.
|
||||||
|
-- @param #ZONE_GROUP self
|
||||||
|
-- @param Wrapper.Group#GROUP Group (optional) Update from this Group, if nil, update from the GROUP this zone is based on.
|
||||||
|
-- @return self
|
||||||
|
function ZONE_GROUP:UpdateFromGroup(Group)
|
||||||
|
if Group and Group:IsAlive() then
|
||||||
|
local vec2 = Group:GetVec2()
|
||||||
|
self.Vec2 = vec2
|
||||||
|
elseif self._.ZoneGROUP and self._.ZoneGROUP:IsAlive() then
|
||||||
|
local ZoneVec2 = self._.ZoneGROUP:GetVec2()
|
||||||
|
self.Vec2 = ZoneVec2
|
||||||
|
self._.ZoneVec2Cache = ZoneVec2
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Returns a random location within the zone of the @{Wrapper.Group}.
|
--- Returns a random location within the zone of the @{Wrapper.Group}.
|
||||||
-- @param #ZONE_GROUP self
|
-- @param #ZONE_GROUP self
|
||||||
-- @return DCS#Vec2 The random location of the zone based on the @{Wrapper.Group} location.
|
-- @return DCS#Vec2 The random location of the zone based on the @{Wrapper.Group} location.
|
||||||
@@ -2487,6 +2585,26 @@ function ZONE_POLYGON_BASE:Flush()
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Search for clear ground spawn zones within this zone. A powerful and efficient function using Disposition to find clear areas for spawning ground units avoiding trees, water and map scenery.
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @param #number PosRadius Required clear radius around each position.
|
||||||
|
-- @param #number NumPositions Number of positions to find.
|
||||||
|
-- @return #table A table of DCS#Vec2 positions that are clear of map objects within the given PosRadius. nil if no clear positions are found.
|
||||||
|
function ZONE_POLYGON_BASE:GetClearZonePositions(PosRadius, NumPositions)
|
||||||
|
return UTILS.GetClearZonePositions(self, PosRadius, NumPositions)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Search for a random clear ground spawn coordinate within this zone. A powerful and efficient function using Disposition to find clear areas for spawning ground units avoiding trees, water and map scenery.
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @param #number PosRadius (Optional) Required clear radius around each position. (Default is math.min(Radius/10, 200))
|
||||||
|
-- @param #number NumPositions (Optional) Number of positions to find. (Default 50)
|
||||||
|
-- @return Core.Point#COORDINATE A random coordinate for a clear zone. nil if no clear positions are found.
|
||||||
|
-- @return #number Assigned radius for the found zones. nil if no clear positions are found.
|
||||||
|
function ZONE_POLYGON_BASE:GetRandomClearZoneCoordinate(PosRadius, NumPositions)
|
||||||
|
return UTILS.GetRandomClearZoneCoordinate(self, PosRadius, NumPositions)
|
||||||
|
end
|
||||||
|
|
||||||
--- Smokes the zone boundaries in a color.
|
--- Smokes the zone boundaries in a color.
|
||||||
-- @param #ZONE_POLYGON_BASE self
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
-- @param #boolean UnBound If true, the tyres will be destroyed.
|
-- @param #boolean UnBound If true, the tyres will be destroyed.
|
||||||
@@ -2865,6 +2983,11 @@ end
|
|||||||
function ZONE_POLYGON_BASE:GetRandomVec2()
|
function ZONE_POLYGON_BASE:GetRandomVec2()
|
||||||
-- make sure we assign weights to the triangles based on their surface area, otherwise
|
-- make sure we assign weights to the triangles based on their surface area, otherwise
|
||||||
-- we'll be more likely to generate random points in smaller triangles
|
-- we'll be more likely to generate random points in smaller triangles
|
||||||
|
|
||||||
|
math.random()
|
||||||
|
math.random()
|
||||||
|
math.random()
|
||||||
|
|
||||||
local weights = {}
|
local weights = {}
|
||||||
for _, triangle in pairs(self._Triangles) do
|
for _, triangle in pairs(self._Triangles) do
|
||||||
weights[triangle] = triangle.SurfaceArea / self.SurfaceArea
|
weights[triangle] = triangle.SurfaceArea / self.SurfaceArea
|
||||||
@@ -3204,12 +3327,7 @@ function ZONE_POLYGON:Scan( ObjectCategories, UnitCategories )
|
|||||||
|
|
||||||
local vectors = self:GetBoundingSquare()
|
local vectors = self:GetBoundingSquare()
|
||||||
|
|
||||||
local minVec3 = {x=vectors.x1, y=0, z=vectors.y1}
|
local ZoneRadius = UTILS.VecDist2D({x=vectors.x1, y=vectors.y1}, {x=vectors.x2, y=vectors.y2})/2
|
||||||
local maxVec3 = {x=vectors.x2, y=0, z=vectors.y2}
|
|
||||||
|
|
||||||
local minmarkcoord = COORDINATE:NewFromVec3(minVec3)
|
|
||||||
local maxmarkcoord = COORDINATE:NewFromVec3(maxVec3)
|
|
||||||
local ZoneRadius = minmarkcoord:Get2DDistance(maxmarkcoord)/2
|
|
||||||
-- self:I("Scan Radius:" ..ZoneRadius)
|
-- self:I("Scan Radius:" ..ZoneRadius)
|
||||||
local CenterVec3 = self:GetCoordinate():GetVec3()
|
local CenterVec3 = self:GetCoordinate():GetVec3()
|
||||||
|
|
||||||
@@ -3233,14 +3351,12 @@ function ZONE_POLYGON:Scan( ObjectCategories, UnitCategories )
|
|||||||
|
|
||||||
local function EvaluateZone( ZoneObject )
|
local function EvaluateZone( ZoneObject )
|
||||||
|
|
||||||
if ZoneObject then
|
if ZoneObject and self:IsVec3InZone(ZoneObject:getPoint()) then
|
||||||
|
|
||||||
local ObjectCategory = Object.getCategory(ZoneObject)
|
local ObjectCategory = Object.getCategory(ZoneObject)
|
||||||
|
|
||||||
if ( ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive() ) or (ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then
|
if ( ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive() ) or (ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then
|
||||||
|
|
||||||
local CoalitionDCSUnit = ZoneObject:getCoalition()
|
|
||||||
|
|
||||||
local Include = false
|
local Include = false
|
||||||
if not UnitCategories then
|
if not UnitCategories then
|
||||||
-- Anything found is included.
|
-- Anything found is included.
|
||||||
@@ -3272,7 +3388,7 @@ function ZONE_POLYGON:Scan( ObjectCategories, UnitCategories )
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- trying with box search
|
-- trying with box search
|
||||||
if ObjectCategory == Object.Category.SCENERY and self:IsVec3InZone(ZoneObject:getPoint()) then
|
if ObjectCategory == Object.Category.SCENERY then
|
||||||
local SceneryType = ZoneObject:getTypeName()
|
local SceneryType = ZoneObject:getTypeName()
|
||||||
local SceneryName = ZoneObject:getName()
|
local SceneryName = ZoneObject:getName()
|
||||||
self.ScanData.Scenery[SceneryType] = self.ScanData.Scenery[SceneryType] or {}
|
self.ScanData.Scenery[SceneryType] = self.ScanData.Scenery[SceneryType] or {}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Author: **Applevangelist**
|
-- ### Author: **Applevangelist**
|
||||||
-- Last Update Sept 2023
|
-- Last Update July 2025
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
-- @module Functional.AICSAR
|
-- @module Functional.AICSAR
|
||||||
@@ -57,6 +57,8 @@
|
|||||||
-- @field #number Speed Default speed setting for the helicopter FLIGHTGROUP is 100kn.
|
-- @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 #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 #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
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
|
|
||||||
@@ -153,10 +155,10 @@
|
|||||||
-- To set up AICSAR for SRS TTS output, add e.g. the following to your script:
|
-- 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)
|
-- -- 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!)
|
-- -- 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)
|
-- -- define a different voice for the downed pilot(s)
|
||||||
-- my_aicsar:SetPilotTTSVoice(MSRS.Voices.Google.Standard.en_AU_Standard_D,"en-AU","male")
|
-- 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:
|
-- 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
|
-- or
|
||||||
--
|
--
|
||||||
@@ -191,7 +193,7 @@
|
|||||||
-- @field #AICSAR
|
-- @field #AICSAR
|
||||||
AICSAR = {
|
AICSAR = {
|
||||||
ClassName = "AICSAR",
|
ClassName = "AICSAR",
|
||||||
version = "0.1.16",
|
version = "0.1.18",
|
||||||
lid = "",
|
lid = "",
|
||||||
coalition = coalition.side.BLUE,
|
coalition = coalition.side.BLUE,
|
||||||
template = "",
|
template = "",
|
||||||
@@ -236,6 +238,8 @@ AICSAR = {
|
|||||||
Altitude = 1500,
|
Altitude = 1500,
|
||||||
UseEventEject = false,
|
UseEventEject = false,
|
||||||
Delay = 100,
|
Delay = 100,
|
||||||
|
UseRescueZone = false,
|
||||||
|
RescueZone = nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
-- TODO Messages
|
-- TODO Messages
|
||||||
@@ -304,8 +308,9 @@ AICSAR.RadioLength = {
|
|||||||
-- @param #string Helotemplate Helicopter template name.
|
-- @param #string Helotemplate Helicopter template name.
|
||||||
-- @param Wrapper.Airbase#AIRBASE FARP FARP object or Airbase from where to start.
|
-- @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 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
|
-- @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.
|
-- Inherit everything from FSM class.
|
||||||
local self=BASE:Inherit(self, FSM:New())
|
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
|
-- limit number of available helos at the same time
|
||||||
self.limithelos = true
|
self.limithelos = true
|
||||||
self.helonumber = 3
|
self.helonumber = Helonumber or 3
|
||||||
|
|
||||||
-- localization
|
-- localization
|
||||||
self:InitLocalization()
|
self:InitLocalization()
|
||||||
@@ -524,10 +529,20 @@ function AICSAR:InitLocalization()
|
|||||||
return self
|
return self
|
||||||
end
|
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.
|
--- [User] Switch sound output on and use SRS output for sound files.
|
||||||
-- @param #AICSAR self
|
-- @param #AICSAR self
|
||||||
-- @param #boolean OnOff Switch on (true) or off (false).
|
-- @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 Frequency Defaults to 243 (guard)
|
||||||
-- @param #number Modulation Radio modulation. Defaults to radio.modulation.AM
|
-- @param #number Modulation Radio modulation. Defaults to radio.modulation.AM
|
||||||
-- @param #string SoundPath Where to find the audio files. Defaults to nil, i.e. add messages via "Sound to..." in the Mission Editor.
|
-- @param #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.SRSRadio = OnOff and true
|
||||||
self.SRSTTSRadio = false
|
self.SRSTTSRadio = false
|
||||||
self.SRSFrequency = Frequency or 243
|
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:SetLabel("ACSR")
|
||||||
self.SRS:SetCoalition(self.coalition)
|
self.SRS:SetCoalition(self.coalition)
|
||||||
self.SRSModulation = Modulation or radio.modulation.AM
|
self.SRSModulation = Modulation or radio.modulation.AM
|
||||||
@@ -556,7 +571,7 @@ end
|
|||||||
-- See `AICSAR:SetPilotTTSVoice()` and `AICSAR:SetOperatorTTSVoice()`
|
-- See `AICSAR:SetPilotTTSVoice()` and `AICSAR:SetOperatorTTSVoice()`
|
||||||
-- @param #AICSAR self
|
-- @param #AICSAR self
|
||||||
-- @param #boolean OnOff Switch on (true) or off (false).
|
-- @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 Frequency (Optional) Defaults to 243 (guard)
|
||||||
-- @param #number Modulation (Optional) Radio modulation. Defaults to radio.modulation.AM
|
-- @param #number Modulation (Optional) Radio modulation. Defaults to radio.modulation.AM
|
||||||
-- @param #number Port (Optional) Port of the SRS, defaults to 5002.
|
-- @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.SRSTTSRadio = OnOff and true
|
||||||
self.SRSRadio = false
|
self.SRSRadio = false
|
||||||
self.SRSFrequency = Frequency or 243
|
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.SRSModulation = Modulation or radio.modulation.AM
|
||||||
self.SRSPort = Port or MSRS.port or 5002
|
self.SRSPort = Port or MSRS.port or 5002
|
||||||
if OnOff then
|
if OnOff then
|
||||||
@@ -693,7 +708,7 @@ function AICSAR:_EjectEventHandler(EventData)
|
|||||||
local _LandingPos = COORDINATE:NewFromVec3(_event.initiator:getPosition().p)
|
local _LandingPos = COORDINATE:NewFromVec3(_event.initiator:getPosition().p)
|
||||||
local _country = _event.initiator:getCountry()
|
local _country = _event.initiator:getCountry()
|
||||||
local _coalition = coalition.getCountryCoalition( _country )
|
local _coalition = coalition.getCountryCoalition( _country )
|
||||||
local data = UTILS.DeepCopy(EventData)
|
--local data = UTILS.DeepCopy(EventData)
|
||||||
Unit.destroy(_event.initiator) -- shagrat remove static Pilot model
|
Unit.destroy(_event.initiator) -- shagrat remove static Pilot model
|
||||||
self:ScheduleOnce(self.Delay,self._DelayedSpawnPilot,self,_LandingPos,_coalition)
|
self:ScheduleOnce(self.Delay,self._DelayedSpawnPilot,self,_LandingPos,_coalition)
|
||||||
end
|
end
|
||||||
@@ -708,7 +723,14 @@ end
|
|||||||
-- @return #AICSAR self
|
-- @return #AICSAR self
|
||||||
function AICSAR:_DelayedSpawnPilot(_LandingPos,_coalition)
|
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
|
-- Mayday Message
|
||||||
local Text,Soundfile,Soundlength,Subtitle = self.gettext:GetEntry("PILOTDOWN",self.locale)
|
local Text,Soundfile,Soundlength,Subtitle = self.gettext:GetEntry("PILOTDOWN",self.locale)
|
||||||
local text = ""
|
local text = ""
|
||||||
@@ -795,7 +817,13 @@ function AICSAR:_EventHandler(EventData, FromEject)
|
|||||||
|
|
||||||
-- DONE: add distance check
|
-- DONE: add distance check
|
||||||
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
|
-- Mayday Message
|
||||||
local Text,Soundfile,Soundlength,Subtitle = self.gettext:GetEntry("PILOTDOWN",self.locale)
|
local Text,Soundfile,Soundlength,Subtitle = self.gettext:GetEntry("PILOTDOWN",self.locale)
|
||||||
local text = ""
|
local text = ""
|
||||||
@@ -817,7 +845,6 @@ function AICSAR:_EventHandler(EventData, FromEject)
|
|||||||
if _coalition == self.coalition then
|
if _coalition == self.coalition then
|
||||||
if self.verbose then
|
if self.verbose then
|
||||||
MESSAGE:New(msgtxt,15,"AICSAR"):ToCoalition(self.coalition)
|
MESSAGE:New(msgtxt,15,"AICSAR"):ToCoalition(self.coalition)
|
||||||
-- MESSAGE:New(msgtxt,15,"AICSAR"):ToLog()
|
|
||||||
end
|
end
|
||||||
if self.SRSRadio then
|
if self.SRSRadio then
|
||||||
local sound = SOUNDFILE:New(Soundfile,self.SRSSoundPath,Soundlength)
|
local sound = SOUNDFILE:New(Soundfile,self.SRSSoundPath,Soundlength)
|
||||||
@@ -869,6 +896,7 @@ function AICSAR:_GetFlight()
|
|||||||
:InitUnControlled(true)
|
:InitUnControlled(true)
|
||||||
:OnSpawnGroup(
|
:OnSpawnGroup(
|
||||||
function(Group)
|
function(Group)
|
||||||
|
Group:OptionPreferVerticalLanding()
|
||||||
self:__HeloOnDuty(1,Group)
|
self:__HeloOnDuty(1,Group)
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
@@ -892,7 +920,7 @@ function AICSAR:_InitMission(Pilot,Index)
|
|||||||
--local pilotset = SET_GROUP:New()
|
--local pilotset = SET_GROUP:New()
|
||||||
--pilotset:AddGroup(Pilot)
|
--pilotset:AddGroup(Pilot)
|
||||||
|
|
||||||
-- Cargo transport assignment.
|
-- Cargo transport assignment.
|
||||||
local opstransport=OPSTRANSPORT:New(Pilot, pickupzone, self.farpzone)
|
local opstransport=OPSTRANSPORT:New(Pilot, pickupzone, self.farpzone)
|
||||||
--opstransport:SetVerbosity(3)
|
--opstransport:SetVerbosity(3)
|
||||||
|
|
||||||
@@ -934,6 +962,10 @@ function AICSAR:_InitMission(Pilot,Index)
|
|||||||
helo:__UnloadingDone(5)
|
helo:__UnloadingDone(5)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function helo:OnAfterLandAtAirbase(From,Event,To,airbase)
|
||||||
|
helo:Despawn(2)
|
||||||
|
end
|
||||||
|
|
||||||
self.helos[Index] = helo
|
self.helos[Index] = helo
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -984,7 +1016,9 @@ function AICSAR:_CheckHelos()
|
|||||||
local name = helo:GetName()
|
local name = helo:GetName()
|
||||||
self:T("Helo group "..name.." in state "..state)
|
self:T("Helo group "..name.." in state "..state)
|
||||||
if state == "Arrived" then
|
if state == "Arrived" then
|
||||||
helo:__Stop(5)
|
--helo:__Stop(5)
|
||||||
|
helo.OnAfterDead = nil
|
||||||
|
helo:Despawn(35)
|
||||||
self.helos[_index] = nil
|
self.helos[_index] = nil
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@@ -1025,7 +1059,7 @@ function AICSAR:_CheckQueue(OpsGroup)
|
|||||||
if self:_CheckInMashZone(_pilot) then
|
if self:_CheckInMashZone(_pilot) then
|
||||||
self:T("Pilot" .. _pilot.GroupName .. " rescued!")
|
self:T("Pilot" .. _pilot.GroupName .. " rescued!")
|
||||||
if OpsGroup then
|
if OpsGroup then
|
||||||
OpsGroup:Despawn(10)
|
--OpsGroup:Despawn(10)
|
||||||
else
|
else
|
||||||
_pilot:Destroy(true,10)
|
_pilot:Destroy(true,10)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ AUTOLASE = {
|
|||||||
debug = false,
|
debug = false,
|
||||||
smokemenu = true,
|
smokemenu = true,
|
||||||
RoundingPrecision = 0,
|
RoundingPrecision = 0,
|
||||||
increasegroundawareness = true,
|
increasegroundawareness = false,
|
||||||
MonitorFrequency = 30,
|
MonitorFrequency = 30,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,7 +216,7 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet)
|
|||||||
self.smokemenu = true
|
self.smokemenu = true
|
||||||
self.threatmenu = true
|
self.threatmenu = true
|
||||||
self.RoundingPrecision = 0
|
self.RoundingPrecision = 0
|
||||||
self.increasegroundawareness = true
|
self.increasegroundawareness = false
|
||||||
self.MonitorFrequency = 30
|
self.MonitorFrequency = 30
|
||||||
|
|
||||||
self:EnableSmokeMenu({Angle=math.random(0,359),Distance=math.random(10,20)})
|
self:EnableSmokeMenu({Angle=math.random(0,359),Distance=math.random(10,20)})
|
||||||
@@ -493,7 +493,7 @@ end
|
|||||||
--- (User) Function enable sending messages via SRS.
|
--- (User) Function enable sending messages via SRS.
|
||||||
-- @param #AUTOLASE self
|
-- @param #AUTOLASE self
|
||||||
-- @param #boolean OnOff Switch usage on and off
|
-- @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 Frequency Frequency to send, e.g. 243
|
||||||
-- @param #number Modulation Modulation i.e. radio.modulation.AM or radio.modulation.FM
|
-- @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
|
-- @param #string Label (Optional) Short label to be used on the SRS Client Overlay
|
||||||
@@ -508,7 +508,7 @@ end
|
|||||||
function AUTOLASE:SetUsingSRS(OnOff,Path,Frequency,Modulation,Label,Gender,Culture,Port,Voice,Volume,PathToGoogleKey)
|
function AUTOLASE:SetUsingSRS(OnOff,Path,Frequency,Modulation,Label,Gender,Culture,Port,Voice,Volume,PathToGoogleKey)
|
||||||
if OnOff then
|
if OnOff then
|
||||||
self.useSRS = true
|
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.SRSFreq = Frequency or 271
|
||||||
self.SRSMod = Modulation or radio.modulation.AM
|
self.SRSMod = Modulation or radio.modulation.AM
|
||||||
self.Gender = Gender or MSRS.gender or "male"
|
self.Gender = Gender or MSRS.gender or "male"
|
||||||
@@ -1020,7 +1020,7 @@ function AUTOLASE:_Prescient()
|
|||||||
self:T(self.lid.."Checking possibly visible STATICs for Recce "..unit:GetName())
|
self:T(self.lid.."Checking possibly visible STATICs for Recce "..unit:GetName())
|
||||||
for _,_static in pairs(Statics) do -- DCS static object here
|
for _,_static in pairs(Statics) do -- DCS static object here
|
||||||
local static = STATIC:Find(_static)
|
local static = STATIC:Find(_static)
|
||||||
if static and static:GetCoalition() ~= self.coalition then
|
if static and static:GetCoalition() ~= self.coalition and static:GetCoordinate() then
|
||||||
local IsLOS = position:IsLOS(static:GetCoordinate())
|
local IsLOS = position:IsLOS(static:GetCoordinate())
|
||||||
if IsLOS then
|
if IsLOS then
|
||||||
unit:KnowUnit(static,true,true)
|
unit:KnowUnit(static,true,true)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
-- @module Functional.Mantis
|
-- @module Functional.Mantis
|
||||||
-- @image Functional.Mantis.jpg
|
-- @image Functional.Mantis.jpg
|
||||||
--
|
--
|
||||||
-- Last Update: May 2025
|
-- Last Update: August 2025
|
||||||
|
|
||||||
-------------------------------------------------------------------------
|
-------------------------------------------------------------------------
|
||||||
--- **MANTIS** class, extends Core.Base#BASE
|
--- **MANTIS** class, extends Core.Base#BASE
|
||||||
@@ -108,10 +108,15 @@
|
|||||||
-- * Patriot
|
-- * Patriot
|
||||||
-- * Rapier
|
-- * Rapier
|
||||||
-- * Roland
|
-- * Roland
|
||||||
|
-- * IRIS-T SLM
|
||||||
|
-- * Pantsir S1
|
||||||
|
-- * TOR M2
|
||||||
|
-- * C-RAM
|
||||||
-- * Silkworm (though strictly speaking this is a surface to ship missile)
|
-- * Silkworm (though strictly speaking this is a surface to ship missile)
|
||||||
-- * SA-2, SA-3, SA-5, SA-6, SA-7, SA-8, SA-9, SA-10, SA-11, SA-13, SA-15, SA-19
|
-- * SA-2, SA-3, SA-5, SA-6, SA-7, SA-8, SA-9, SA-10, SA-11, SA-13, SA-15, SA-19
|
||||||
-- * From IDF mod: STUNNER IDFA, TAMIR IDFA (Note all caps!)
|
-- * From IDF mod: STUNNER IDFA, TAMIR IDFA (Note all caps!)
|
||||||
-- * From HDS (see note on HDS below): SA-2, SA-3, SA-10B, SA-10C, SA-12, SA-17, SA-20A, SA-20B, SA-23, HQ-2
|
-- * From HDS (see note on HDS below): SA-2, SA-3, SA-10B, SA-10C, SA-12, SA-17, SA-20A, SA-20B, SA-23, HQ-2, SAMP/T Block 1, SAMP/T Block 1INT, SAMP/T Block2
|
||||||
|
-- * Other Mods: Nike
|
||||||
--
|
--
|
||||||
-- * From SMA: RBS98M, RBS70, RBS90, RBS90M, RBS103A, RBS103B, RBS103AM, RBS103BM, Lvkv9040M
|
-- * From SMA: RBS98M, RBS70, RBS90, RBS90M, RBS103A, RBS103B, RBS103AM, RBS103BM, Lvkv9040M
|
||||||
-- **NOTE** If you are using the Swedish Military Assets (SMA), please note that the **group name** for RBS-SAM types also needs to contain the keyword "SMA"
|
-- **NOTE** If you are using the Swedish Military Assets (SMA), please note that the **group name** for RBS-SAM types also needs to contain the keyword "SMA"
|
||||||
@@ -125,10 +130,11 @@
|
|||||||
-- * SA-2 (with V759 missile, e.g. "Red SAM SA-2 HDS")
|
-- * SA-2 (with V759 missile, e.g. "Red SAM SA-2 HDS")
|
||||||
-- * SA-2 (with HQ-2 launcher, use HQ-2 in the group name, e.g. "Red SAM HQ-2" )
|
-- * SA-2 (with HQ-2 launcher, use HQ-2 in the group name, e.g. "Red SAM HQ-2" )
|
||||||
-- * SA-3 (with V601P missile, e.g. "Red SAM SA-3 HDS")
|
-- * SA-3 (with V601P missile, e.g. "Red SAM SA-3 HDS")
|
||||||
-- * SA-10B (overlap with other SA-10 types, e.g. "Red SAM SA-10B HDS")
|
-- * SA-10B (overlap with other SA-10 types, e.g. "Red SAM SA-10B HDS" with 5P85CE launcher)
|
||||||
-- * SA-10C (overlap with other SA-10 types, e.g. "Red SAM SA-10C HDS")
|
-- * SA-10C (overlap with other SA-10 types, e.g. "Red SAM SA-10C HDS" with 5P85SE launcher)
|
||||||
-- * SA-12 (launcher dependent range, e.g. "Red SAM SA-12 HDS")
|
-- * SA-12 (launcher dependent range, e.g. "Red SAM SA-12 HDS 2" for the 9A82 variant and "Red SAM SA-12 HDS 1" for the 9A83 variant)
|
||||||
-- * SA-23 (launcher dependent range, e.g. "Red SAM SA-23 HDS")
|
-- * SA-23 (launcher dependent range, e.g. "Red SAM SA-23 HDS 2" for the 9A82ME variant and "Red SAM SA-23 HDS 1" for the 9A83ME variant)
|
||||||
|
-- * SAMP/T (launcher dependent range, e.g. "Blue SAM SAMPT Block 1 HDS" for Block 1, "Blue SAM SAMPT Block 1INT HDS", "Blue SAM SAMPT Block 2 HDS")
|
||||||
--
|
--
|
||||||
-- The other HDS types work like the rest of the known SAM systems.
|
-- The other HDS types work like the rest of the known SAM systems.
|
||||||
--
|
--
|
||||||
@@ -274,6 +280,7 @@
|
|||||||
MANTIS = {
|
MANTIS = {
|
||||||
ClassName = "MANTIS",
|
ClassName = "MANTIS",
|
||||||
name = "mymantis",
|
name = "mymantis",
|
||||||
|
version = "0.9.34",
|
||||||
SAM_Templates_Prefix = "",
|
SAM_Templates_Prefix = "",
|
||||||
SAM_Group = nil,
|
SAM_Group = nil,
|
||||||
EWR_Templates_Prefix = "",
|
EWR_Templates_Prefix = "",
|
||||||
@@ -382,15 +389,21 @@ MANTIS.SamData = {
|
|||||||
["Chaparral"] = { Range=8, Blindspot=0, Height=3, Type="Short", Radar="Chaparral" },
|
["Chaparral"] = { Range=8, Blindspot=0, Height=3, Type="Short", Radar="Chaparral" },
|
||||||
["Linebacker"] = { Range=4, Blindspot=0, Height=3, Type="Point", Radar="Linebacker", Point="true" },
|
["Linebacker"] = { Range=4, Blindspot=0, Height=3, Type="Point", Radar="Linebacker", Point="true" },
|
||||||
["Silkworm"] = { Range=90, Blindspot=1, Height=0.2, Type="Long", Radar="Silkworm" },
|
["Silkworm"] = { Range=90, Blindspot=1, Height=0.2, Type="Long", Radar="Silkworm" },
|
||||||
["HEMTT_C-RAM_Phalanx"] = { Range=2, Blindspot=0, Height=2, Type="Point", Radar="HEMTT_C-RAM_Phalanx", Point="true" },
|
["C-RAM"] = { Range=2, Blindspot=0, Height=2, Type="Point", Radar="HEMTT_C-RAM_Phalanx", Point="true" },
|
||||||
-- units from HDS Mod, multi launcher options is tricky
|
-- units from HDS Mod, multi launcher options is tricky
|
||||||
["SA-10B"] = { Range=75, Blindspot=0, Height=18, Type="Medium" , Radar="SA-10B"},
|
["SA-10B"] = { Range=75, Blindspot=0, Height=18, Type="Medium" , Radar="SA-10B"},
|
||||||
["SA-17"] = { Range=50, Blindspot=3, Height=30, Type="Medium", Radar="SA-17" },
|
["SA-17"] = { Range=50, Blindspot=3, Height=50, Type="Medium", Radar="SA-17" },
|
||||||
["SA-20A"] = { Range=150, Blindspot=5, Height=27, Type="Long" , Radar="S-300PMU1"},
|
["SA-20A"] = { Range=150, Blindspot=5, Height=27, Type="Long" , Radar="S-300PMU1"},
|
||||||
["SA-20B"] = { Range=200, Blindspot=4, Height=27, Type="Long" , Radar="S-300PMU2"},
|
["SA-20B"] = { Range=200, Blindspot=4, Height=27, Type="Long" , Radar="S-300PMU2"},
|
||||||
["HQ-2"] = { Range=50, Blindspot=6, Height=35, Type="Medium", Radar="HQ_2_Guideline_LN" },
|
["HQ-2"] = { Range=50, Blindspot=6, Height=35, Type="Medium", Radar="HQ_2_Guideline_LN" },
|
||||||
["TAMIR IDFA"] = { Range=20, Blindspot=0.6, Height=12.3, Type="Short", Radar="IRON_DOME_LN" },
|
["TAMIR IDFA"] = { Range=20, Blindspot=0.6, Height=12.3, Type="Short", Radar="IRON_DOME_LN" },
|
||||||
["STUNNER IDFA"] = { Range=250, Blindspot=1, Height=45, Type="Long", Radar="DAVID_SLING_LN" },
|
["STUNNER IDFA"] = { Range=250, Blindspot=1, Height=45, Type="Long", Radar="DAVID_SLING_LN" },
|
||||||
|
["NIKE"] = { Range=155, Blindspot=6, Height=30, Type="Long", Radar="HIPAR" },
|
||||||
|
["Dog Ear"] = { Range=11, Blindspot=0, Height=9, Type="Point", Radar="Dog Ear", Point="true" },
|
||||||
|
-- CH Added to DCS core 2.9.19.x
|
||||||
|
["Pantsir S1"] = { Range=20, Blindspot=1.2, Height=15, Type="Point", Radar="PantsirS1" , Point="true" },
|
||||||
|
["Tor M2"] = { Range=12, Blindspot=1, Height=10, Type="Point", Radar="TorM2", Point="true" },
|
||||||
|
["IRIS-T SLM"] = { Range=40, Blindspot=0.5, Height=20, Type="Medium", Radar="CH_IRIST_SLM" },
|
||||||
}
|
}
|
||||||
|
|
||||||
--- SAM data HDS
|
--- SAM data HDS
|
||||||
@@ -406,13 +419,17 @@ MANTIS.SamDataHDS = {
|
|||||||
-- group name MUST contain HDS to ID launcher type correctly!
|
-- group name MUST contain HDS to ID launcher type correctly!
|
||||||
["SA-2 HDS"] = { Range=56, Blindspot=7, Height=30, Type="Medium", Radar="V759" },
|
["SA-2 HDS"] = { Range=56, Blindspot=7, Height=30, Type="Medium", Radar="V759" },
|
||||||
["SA-3 HDS"] = { Range=20, Blindspot=6, Height=30, Type="Short", Radar="V-601P" },
|
["SA-3 HDS"] = { Range=20, Blindspot=6, Height=30, Type="Short", Radar="V-601P" },
|
||||||
["SA-10C HDS 2"] = { Range=90, Blindspot=5, Height=25, Type="Long" , Radar="5P85DE ln"}, -- V55RUD
|
["SA-10B HDS"] = { Range=90, Blindspot=5, Height=25, Type="Long" , Radar="5P85CE ln"}, -- V55RUD
|
||||||
["SA-10C HDS 1"] = { Range=90, Blindspot=5, Height=25, Type="Long" , Radar="5P85CE ln"}, -- V55RUD
|
["SA-10C HDS"] = { Range=75, Blindspot=5, Height=25, Type="Long" , Radar="5P85SE ln"}, -- V55RUD
|
||||||
["SA-12 HDS 2"] = { Range=100, Blindspot=10, Height=25, Type="Long" , Radar="S-300V 9A82 l"},
|
["SA-17 HDS"] = { Range=50, Blindspot=3, Height=50, Type="Medium", Radar="SA-17 " },
|
||||||
["SA-12 HDS 1"] = { Range=75, Blindspot=1, Height=25, Type="Long" , Radar="S-300V 9A83 l"},
|
["SA-12 HDS 2"] = { Range=100, Blindspot=13, Height=30, Type="Long" , Radar="S-300V 9A82 l"},
|
||||||
|
["SA-12 HDS 1"] = { Range=75, Blindspot=6, Height=25, Type="Long" , Radar="S-300V 9A83 l"},
|
||||||
["SA-23 HDS 2"] = { Range=200, Blindspot=5, Height=37, Type="Long", Radar="S-300VM 9A82ME" },
|
["SA-23 HDS 2"] = { Range=200, Blindspot=5, Height=37, Type="Long", Radar="S-300VM 9A82ME" },
|
||||||
["SA-23 HDS 1"] = { Range=100, Blindspot=1, Height=50, Type="Long", Radar="S-300VM 9A83ME" },
|
["SA-23 HDS 1"] = { Range=100, Blindspot=1, Height=50, Type="Long", Radar="S-300VM 9A83ME" },
|
||||||
["HQ-2 HDS"] = { Range=50, Blindspot=6, Height=35, Type="Medium", Radar="HQ_2_Guideline_LN" },
|
["HQ-2 HDS"] = { Range=50, Blindspot=6, Height=35, Type="Medium", Radar="HQ_2_Guideline_LN" },
|
||||||
|
["SAMPT Block 1 HDS"] = { Range=120, Blindspot=1, Height=20, Type="long", Radar="SAMPT_MLT_Blk1" }, -- Block 1 Launcher
|
||||||
|
["SAMPT Block 1INT HDS"] = { Range=150, Blindspot=1, Height=25, Type="long", Radar="SAMPT_MLT_Blk1NT" }, -- Block 1-INT Launcher
|
||||||
|
["SAMPT Block 2 HDS"] = { Range=200, Blindspot=10, Height=70, Type="long", Radar="SAMPT_MLT_Blk2" }, -- Block 2 Launcher
|
||||||
}
|
}
|
||||||
|
|
||||||
--- SAM data SMA
|
--- SAM data SMA
|
||||||
@@ -452,15 +469,15 @@ MANTIS.SamDataCH = {
|
|||||||
-- https://www.currenthill.com/
|
-- https://www.currenthill.com/
|
||||||
-- group name MUST contain CHM to ID launcher type correctly!
|
-- group name MUST contain CHM to ID launcher type correctly!
|
||||||
["2S38 CHM"] = { Range=6, Blindspot=0.1, Height=4.5, Type="Short", Radar="2S38" },
|
["2S38 CHM"] = { Range=6, Blindspot=0.1, Height=4.5, Type="Short", Radar="2S38" },
|
||||||
["PantsirS1 CHM"] = { Range=20, Blindspot=1.2, Height=15, Type="Short", Radar="PantsirS1" },
|
["PantsirS1 CHM"] = { Range=20, Blindspot=1.2, Height=15, Type="Point", Radar="PantsirS1", Point="true" },
|
||||||
["PantsirS2 CHM"] = { Range=30, Blindspot=1.2, Height=18, Type="Medium", Radar="PantsirS2" },
|
["PantsirS2 CHM"] = { Range=30, Blindspot=1.2, Height=18, Type="Medium", Radar="PantsirS2" },
|
||||||
["PGL-625 CHM"] = { Range=10, Blindspot=1, Height=5, Type="Short", Radar="PGL_625" },
|
["PGL-625 CHM"] = { Range=10, Blindspot=1, Height=5, Type="Short", Radar="PGL_625" },
|
||||||
["HQ-17A CHM"] = { Range=15, Blindspot=1.5, Height=10, Type="Short", Radar="HQ17A" },
|
["HQ-17A CHM"] = { Range=15, Blindspot=1.5, Height=10, Type="Short", Radar="HQ17A" },
|
||||||
["M903PAC2 CHM"] = { Range=120, Blindspot=3, Height=24.5, Type="Long", Radar="MIM104_M903_PAC2" },
|
["M903PAC2 CHM"] = { Range=120, Blindspot=3, Height=24.5, Type="Long", Radar="MIM104_M903_PAC2" },
|
||||||
["M903PAC3 CHM"] = { Range=160, Blindspot=1, Height=40, Type="Long", Radar="MIM104_M903_PAC3" },
|
["M903PAC3 CHM"] = { Range=160, Blindspot=1, Height=40, Type="Long", Radar="MIM104_M903_PAC3" },
|
||||||
["TorM2 CHM"] = { Range=12, Blindspot=1, Height=10, Type="Short", Radar="TorM2" },
|
["TorM2 CHM"] = { Range=12, Blindspot=1, Height=10, Type="Point", Radar="TorM2", Point="true" },
|
||||||
["TorM2K CHM"] = { Range=12, Blindspot=1, Height=10, Type="Short", Radar="TorM2K" },
|
["TorM2K CHM"] = { Range=12, Blindspot=1, Height=10, Type="Point", Radar="TorM2K", Point="true" },
|
||||||
["TorM2M CHM"] = { Range=16, Blindspot=1, Height=10, Type="Short", Radar="TorM2M" },
|
["TorM2M CHM"] = { Range=16, Blindspot=1, Height=10, Type="Point", Radar="TorM2M", Point="true" },
|
||||||
["NASAMS3-AMRAAMER CHM"] = { Range=50, Blindspot=2, Height=35.7, Type="Medium", Radar="CH_NASAMS3_LN_AMRAAM_ER" },
|
["NASAMS3-AMRAAMER CHM"] = { Range=50, Blindspot=2, Height=35.7, Type="Medium", Radar="CH_NASAMS3_LN_AMRAAM_ER" },
|
||||||
["NASAMS3-AIM9X2 CHM"] = { Range=20, Blindspot=0.2, Height=18, Type="Short", Radar="CH_NASAMS3_LN_AIM9X2" },
|
["NASAMS3-AIM9X2 CHM"] = { Range=20, Blindspot=0.2, Height=18, Type="Short", Radar="CH_NASAMS3_LN_AIM9X2" },
|
||||||
["C-RAM CHM"] = { Range=2, Blindspot=0, Height=2, Type="Point", Radar="CH_Centurion_C_RAM", Point="true" },
|
["C-RAM CHM"] = { Range=2, Blindspot=0, Height=2, Type="Point", Radar="CH_Centurion_C_RAM", Point="true" },
|
||||||
@@ -680,9 +697,6 @@ do
|
|||||||
-- counter for SAM table updates
|
-- counter for SAM table updates
|
||||||
self.checkcounter = 1
|
self.checkcounter = 1
|
||||||
|
|
||||||
-- TODO Version
|
|
||||||
-- @field #string version
|
|
||||||
self.version="0.9.30"
|
|
||||||
self:I(string.format("***** Starting MANTIS Version %s *****", self.version))
|
self:I(string.format("***** Starting MANTIS Version %s *****", self.version))
|
||||||
|
|
||||||
--- FSM Functions ---
|
--- FSM Functions ---
|
||||||
@@ -879,7 +893,11 @@ do
|
|||||||
self.AcceptZones = AcceptZones or {}
|
self.AcceptZones = AcceptZones or {}
|
||||||
self.RejectZones = RejectZones or {}
|
self.RejectZones = RejectZones or {}
|
||||||
self.ConflictZones = ConflictZones or {}
|
self.ConflictZones = ConflictZones or {}
|
||||||
if #self.AcceptZones > 0 or #self.RejectZones > 0 or #self.ConflictZones > 0 then
|
self.AcceptZonesNo = UTILS.TableLength(self.AcceptZones)
|
||||||
|
self.RejectZonesNo = UTILS.TableLength(self.RejectZones)
|
||||||
|
self.ConflictZonesNo = UTILS.TableLength(self.ConflictZones)
|
||||||
|
self:T(string.format("AcceptZonesNo = %d | RejectZonesNo = %d | ConflictZonesNo = %d",self.AcceptZonesNo,self.RejectZonesNo,self.ConflictZonesNo))
|
||||||
|
if self.AcceptZonesNo > 0 or self.RejectZonesNo > 0 or self.ConflictZonesNo > 0 then
|
||||||
self.usezones = true
|
self.usezones = true
|
||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
@@ -1271,7 +1289,8 @@ do
|
|||||||
self:T(self.lid.."_CheckCoordinateInZones")
|
self:T(self.lid.."_CheckCoordinateInZones")
|
||||||
local inzone = false
|
local inzone = false
|
||||||
-- acceptzones
|
-- acceptzones
|
||||||
if #self.AcceptZones > 0 then
|
self:T(string.format("AcceptZonesNo = %d | RejectZonesNo = %d | ConflictZonesNo = %d",self.AcceptZonesNo,self.RejectZonesNo,self.ConflictZonesNo))
|
||||||
|
if self.AcceptZonesNo > 0 then
|
||||||
for _,_zone in pairs(self.AcceptZones) do
|
for _,_zone in pairs(self.AcceptZones) do
|
||||||
local zone = _zone -- Core.Zone#ZONE
|
local zone = _zone -- Core.Zone#ZONE
|
||||||
if zone:IsCoordinateInZone(coord) then
|
if zone:IsCoordinateInZone(coord) then
|
||||||
@@ -1282,7 +1301,7 @@ do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- rejectzones
|
-- rejectzones
|
||||||
if #self.RejectZones > 0 and inzone then -- maybe in accept zone, but check the overlaps
|
if self.RejectZonesNo > 0 then
|
||||||
for _,_zone in pairs(self.RejectZones) do
|
for _,_zone in pairs(self.RejectZones) do
|
||||||
local zone = _zone -- Core.Zone#ZONE
|
local zone = _zone -- Core.Zone#ZONE
|
||||||
if zone:IsCoordinateInZone(coord) then
|
if zone:IsCoordinateInZone(coord) then
|
||||||
@@ -1293,7 +1312,7 @@ do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- conflictzones
|
-- conflictzones
|
||||||
if #self.ConflictZones > 0 and not inzone then -- if not already accepted, might be in conflict zones
|
if self.ConflictZonesNo > 0 then
|
||||||
for _,_zone in pairs(self.ConflictZones) do
|
for _,_zone in pairs(self.ConflictZones) do
|
||||||
local zone = _zone -- Core.Zone#ZONE
|
local zone = _zone -- Core.Zone#ZONE
|
||||||
if zone:IsCoordinateInZone(coord) then
|
if zone:IsCoordinateInZone(coord) then
|
||||||
@@ -1359,6 +1378,7 @@ do
|
|||||||
end
|
end
|
||||||
-- check accept/reject zones
|
-- check accept/reject zones
|
||||||
local zonecheck = true
|
local zonecheck = true
|
||||||
|
self:T("self.usezones = "..tostring(self.usezones))
|
||||||
if self.usezones then
|
if self.usezones then
|
||||||
-- DONE
|
-- DONE
|
||||||
zonecheck = self:_CheckCoordinateInZones(coord)
|
zonecheck = self:_CheckCoordinateInZones(coord)
|
||||||
@@ -1798,7 +1818,7 @@ do
|
|||||||
if self.Shorad and self.Shorad.ActiveGroups and self.Shorad.ActiveGroups[name] then
|
if self.Shorad and self.Shorad.ActiveGroups and self.Shorad.ActiveGroups[name] then
|
||||||
activeshorad = true
|
activeshorad = true
|
||||||
end
|
end
|
||||||
if IsInZone and not suppressed and not activeshorad then --check any target in zone and not currently managed by SEAD
|
if IsInZone and (not suppressed) and (not activeshorad) then --check any target in zone and not currently managed by SEAD
|
||||||
if samgroup:IsAlive() then
|
if samgroup:IsAlive() then
|
||||||
-- switch on SAM
|
-- switch on SAM
|
||||||
local switch = false
|
local switch = false
|
||||||
@@ -1830,7 +1850,7 @@ do
|
|||||||
-- link in to SHORAD if available
|
-- link in to SHORAD if available
|
||||||
-- DONE: Test integration fully
|
-- DONE: Test integration fully
|
||||||
if self.ShoradLink and (Distance < self.ShoradActDistance or Distance < blind ) then -- don't give SHORAD position away too early
|
if self.ShoradLink and (Distance < self.ShoradActDistance or Distance < blind ) then -- don't give SHORAD position away too early
|
||||||
local Shorad = self.Shorad
|
local Shorad = self.Shorad --Functional.Shorad#SHORAD
|
||||||
local radius = self.checkradius
|
local radius = self.checkradius
|
||||||
local ontime = self.ShoradTime
|
local ontime = self.ShoradTime
|
||||||
Shorad:WakeUpShorad(name, radius, ontime)
|
Shorad:WakeUpShorad(name, radius, ontime)
|
||||||
@@ -2110,7 +2130,7 @@ do
|
|||||||
if self.debug and self.verbose then
|
if self.debug and self.verbose then
|
||||||
self:I(self.lid .. "Status Report")
|
self:I(self.lid .. "Status Report")
|
||||||
for _name,_state in pairs(self.SamStateTracker) do
|
for _name,_state in pairs(self.SamStateTracker) do
|
||||||
self:I(string.format("Site %s\tStatus %s",_name,_state))
|
self:I(string.format("Site %s | Status %s",_name,_state))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local interval = self.detectinterval * -1
|
local interval = self.detectinterval * -1
|
||||||
|
|||||||
@@ -321,7 +321,9 @@ function SCORING:New( GameName, SavePath, AutoSave )
|
|||||||
-- Create the CSV file.
|
-- Create the CSV file.
|
||||||
self.AutoSavePath = SavePath
|
self.AutoSavePath = SavePath
|
||||||
self.AutoSave = AutoSave or true
|
self.AutoSave = AutoSave or true
|
||||||
self:OpenCSV( GameName )
|
if self.AutoSave == true then
|
||||||
|
self:OpenCSV( GameName )
|
||||||
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@@ -985,6 +987,7 @@ function SCORING:_EventOnHit( Event )
|
|||||||
local TargetUnitCoalition = nil
|
local TargetUnitCoalition = nil
|
||||||
local TargetUnitCategory = nil
|
local TargetUnitCategory = nil
|
||||||
local TargetUnitType = nil
|
local TargetUnitType = nil
|
||||||
|
local TargetIsScenery = false
|
||||||
|
|
||||||
if Event.IniDCSUnit then
|
if Event.IniDCSUnit then
|
||||||
|
|
||||||
@@ -1025,6 +1028,12 @@ function SCORING:_EventOnHit( Event )
|
|||||||
TargetCategory = Event.TgtCategory
|
TargetCategory = Event.TgtCategory
|
||||||
TargetType = Event.TgtTypeName
|
TargetType = Event.TgtTypeName
|
||||||
|
|
||||||
|
-- Scenery hit
|
||||||
|
if (not TargetCategory) and TargetUNIT ~= nil and TargetUnit:IsInstanceOf("SCENERY") then
|
||||||
|
TargetCategory = Unit.Category.STRUCTURE
|
||||||
|
TargetIsScenery = true
|
||||||
|
end
|
||||||
|
|
||||||
TargetUnitCoalition = _SCORINGCoalition[TargetCoalition]
|
TargetUnitCoalition = _SCORINGCoalition[TargetCoalition]
|
||||||
TargetUnitCategory = _SCORINGCategory[TargetCategory]
|
TargetUnitCategory = _SCORINGCategory[TargetCategory]
|
||||||
TargetUnitType = TargetType
|
TargetUnitType = TargetType
|
||||||
@@ -1117,17 +1126,22 @@ function SCORING:_EventOnHit( Event )
|
|||||||
MESSAGE.Type.Update )
|
MESSAGE.Type.Update )
|
||||||
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
||||||
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
: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. " ..
|
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " ..
|
||||||
"Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
|
"Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
|
||||||
MESSAGE.Type.Update )
|
MESSAGE.Type.Update )
|
||||||
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
||||||
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
: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
|
end
|
||||||
self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
|
self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
|
||||||
end
|
end
|
||||||
else -- A scenery object was hit.
|
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 )
|
MESSAGE.Type.Update )
|
||||||
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
||||||
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
||||||
@@ -1923,7 +1937,7 @@ function SCORING:ScoreCSV( PlayerName, TargetPlayerName, ScoreType, ScoreTimes,
|
|||||||
TargetUnitType = TargetUnitType or ""
|
TargetUnitType = TargetUnitType or ""
|
||||||
TargetUnitName = TargetUnitName 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.CSVFile:write(
|
||||||
'"' .. self.GameName .. '"' .. ',' ..
|
'"' .. self.GameName .. '"' .. ',' ..
|
||||||
'"' .. self.RunTime .. '"' .. ',' ..
|
'"' .. self.RunTime .. '"' .. ',' ..
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -3153,7 +3153,7 @@ end
|
|||||||
-- @param #WAREHOUSE self
|
-- @param #WAREHOUSE self
|
||||||
-- @return Core.Point#COORDINATE The coordinate of the warehouse.
|
-- @return Core.Point#COORDINATE The coordinate of the warehouse.
|
||||||
function WAREHOUSE:GetCoordinate()
|
function WAREHOUSE:GetCoordinate()
|
||||||
return self.warehouse:GetCoordinate()
|
return self.warehouse:GetCoord()
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get 3D vector of warehouse static.
|
--- Get 3D vector of warehouse static.
|
||||||
@@ -4247,6 +4247,16 @@ function WAREHOUSE:_AssetItemInfo(asset)
|
|||||||
self:T3({Template=asset.template})
|
self:T3({Template=asset.template})
|
||||||
end
|
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.
|
--- On after "NewAsset" event. A new asset has been added to the warehouse stock.
|
||||||
-- @param #WAREHOUSE self
|
-- @param #WAREHOUSE self
|
||||||
-- @param #string From From state.
|
-- @param #string From From state.
|
||||||
@@ -5965,6 +5975,10 @@ function WAREHOUSE:_SpawnAssetGroundNaval(alias, asset, request, spawnzone, late
|
|||||||
template.y = coord.z
|
template.y = coord.z
|
||||||
template.alt = coord.y
|
template.alt = coord.y
|
||||||
|
|
||||||
|
if self.ValidateAndRepositionGroundUnits then
|
||||||
|
UTILS.ValidateAndRepositionGroundUnits(template.units)
|
||||||
|
end
|
||||||
|
|
||||||
-- Spawn group.
|
-- Spawn group.
|
||||||
local group=_DATABASE:Spawn(template) --Wrapper.Group#GROUP
|
local group=_DATABASE:Spawn(template) --Wrapper.Group#GROUP
|
||||||
|
|
||||||
@@ -6893,7 +6907,7 @@ function WAREHOUSE:_CheckConquered()
|
|||||||
for _,_unit in pairs(units) do
|
for _,_unit in pairs(units) do
|
||||||
local unit=_unit --Wrapper.Unit#UNIT
|
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.
|
-- 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
|
if unit:IsGround() and unit:IsAlive() and distance <= radius then
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/MarkerOps_Base.lua' )
|
|||||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/TextAndSound.lua' )
|
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/TextAndSound.lua' )
|
||||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/Pathline.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/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/Object.lua' )
|
||||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Identifiable.lua' )
|
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Identifiable.lua' )
|
||||||
@@ -186,4 +187,11 @@ __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_Zone.lua' )
|
||||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Capture_Dispatcher.lua' )
|
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Capture_Dispatcher.lua' )
|
||||||
|
|
||||||
|
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Beacons.lua' )
|
||||||
|
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Point.lua' )
|
||||||
|
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Procedure.lua' )
|
||||||
|
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/FlightPlan.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' )
|
__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\\TextAndSound.lua' )
|
||||||
__Moose.Include( 'Core\\Condition.lua' )
|
__Moose.Include( 'Core\\Condition.lua' )
|
||||||
__Moose.Include( 'Core\\ClientMenu.lua' )
|
__Moose.Include( 'Core\\ClientMenu.lua' )
|
||||||
|
__Moose.Include( 'Core\\Vector.lua' )
|
||||||
|
|
||||||
__Moose.Include( 'Wrapper\\Object.lua' )
|
__Moose.Include( 'Wrapper\\Object.lua' )
|
||||||
__Moose.Include( 'Wrapper\\Identifiable.lua' )
|
__Moose.Include( 'Wrapper\\Identifiable.lua' )
|
||||||
@@ -115,7 +116,6 @@ __Moose.Include( 'Ops\\Operation.lua' )
|
|||||||
__Moose.Include( 'Ops\\FlightControl.lua' )
|
__Moose.Include( 'Ops\\FlightControl.lua' )
|
||||||
__Moose.Include( 'Ops\\PlayerRecce.lua' )
|
__Moose.Include( 'Ops\\PlayerRecce.lua' )
|
||||||
__Moose.Include( 'Ops\\EasyGCICAP.lua' )
|
__Moose.Include( 'Ops\\EasyGCICAP.lua' )
|
||||||
__Moose.Include( 'Ops\\EasyA2G.lua' )
|
|
||||||
|
|
||||||
__Moose.Include( 'AI\\AI_Balancer.lua' )
|
__Moose.Include( 'AI\\AI_Balancer.lua' )
|
||||||
__Moose.Include( 'AI\\AI_Air.lua' )
|
__Moose.Include( 'AI\\AI_Air.lua' )
|
||||||
@@ -178,4 +178,9 @@ __Moose.Include( 'Tasking\\Task_Cargo_Dispatcher.lua' )
|
|||||||
__Moose.Include( 'Tasking\\Task_Capture_Zone.lua' )
|
__Moose.Include( 'Tasking\\Task_Capture_Zone.lua' )
|
||||||
__Moose.Include( 'Tasking\\Task_Capture_Dispatcher.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' )
|
__Moose.Include( 'Globals.lua' )
|
||||||
|
|||||||
395
Moose Development/Moose/Navigation/Beacons.lua
Normal file
395
Moose Development/Moose/Navigation/Beacons.lua
Normal file
@@ -0,0 +1,395 @@
|
|||||||
|
--- **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 NAVIGATION_Beacons.png
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- 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 desinged 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.
|
||||||
|
--
|
||||||
|
-- # 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.0.4"
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- 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 BECAONS 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 BECAONS 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
|
||||||
|
|
||||||
|
--- 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`.
|
||||||
|
-- @return #table Table of beacons. Each element is of type #BEACON.Beacon.
|
||||||
|
function BEACONS:GetBeacons(TypeID)
|
||||||
|
|
||||||
|
local beacons={}
|
||||||
|
|
||||||
|
for _,_beacon in pairs(self.beacons) do
|
||||||
|
local bc=_beacon --#BEACONS.Beacon
|
||||||
|
|
||||||
|
if TypeID==nil or TypeID==bc.type 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
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
481
Moose Development/Moose/Navigation/FlightPlan.lua
Normal file
481
Moose Development/Moose/Navigation/FlightPlan.lua
Normal file
@@ -0,0 +1,481 @@
|
|||||||
|
--- **NAVIGATION** - Flight Plan.
|
||||||
|
--
|
||||||
|
-- **Main Features:**
|
||||||
|
--
|
||||||
|
-- * Manage navigation aids
|
||||||
|
-- * VOR, NDB
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ## Example Missions:
|
||||||
|
--
|
||||||
|
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Ops%20-%20FlightPlan).
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Author: **funkyfranky**
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
-- @module Navigation.FlightPlan
|
||||||
|
-- @image NAVIGATION_FlightPlan.png
|
||||||
|
|
||||||
|
|
||||||
|
--- FLIGHTPLAN class.
|
||||||
|
-- @type FLIGHTPLAN
|
||||||
|
-- @field #string ClassName Name of the class.
|
||||||
|
-- @field #number verbose Verbosity of output.
|
||||||
|
-- @field #table fixes Navigation fixes.
|
||||||
|
-- @field Core.Pathline#PATHLINE pathline Pathline of the plan.
|
||||||
|
-- @field Wrapper.Airbase#AIRBASE departureAirbase Departure airbase.
|
||||||
|
-- @field Wrapper.Airbase#AIRBASE destinationAirbase Destination airbase.
|
||||||
|
-- @field #number altitudeCruiseMin Minimum cruise altitude in feet MSL.
|
||||||
|
-- @field #number altitudeCruiseMax Maximum cruise altitude in feet MSL.
|
||||||
|
-- @extends Core.Pathline#PATHLINE
|
||||||
|
|
||||||
|
--- *Life is what happens to us while we are making other plans.* -- Allen Saunders
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- # The FLIGHTPLAN Concept
|
||||||
|
--
|
||||||
|
-- This class has a great concept!
|
||||||
|
--
|
||||||
|
-- # Basic Setup
|
||||||
|
--
|
||||||
|
-- A new `FLIGHTPLAN` object can be created with the @{#FLIGHTPLAN.New}() function.
|
||||||
|
--
|
||||||
|
-- myFlightplan=FLIGHTPLAN:New("Plan A")
|
||||||
|
-- myFleet:SetPortZone(ZonePort1stFleet)
|
||||||
|
-- myFleet:Start()
|
||||||
|
--
|
||||||
|
-- A fleet needs a *port zone*, which is set via the @{#FLIGHTPLAN.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 @{#FLIGHTPLAN.Start}() function. If the fleet is not started, it will not process any requests.
|
||||||
|
--
|
||||||
|
-- @field #FLIGHTPLAN
|
||||||
|
FLIGHTPLAN = {
|
||||||
|
ClassName = "FLIGHTPLAN",
|
||||||
|
verbose = 0,
|
||||||
|
fixes = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Type of flightplan.
|
||||||
|
-- @type FLIGHTPLAN.Type
|
||||||
|
-- @field #string IFRH Instrument Flying Rules High Altitude.
|
||||||
|
-- @field #string IFRL Instrument Flying Rules Low Altitude.
|
||||||
|
-- @field #string VFR Visual Flight Rules.
|
||||||
|
FLIGHTPLAN.Type={
|
||||||
|
IFRH = "IFR High",
|
||||||
|
IFRL = "IFR Low",
|
||||||
|
VFR = "VFR",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- FLIGHTPLAN class version.
|
||||||
|
-- @field #string version
|
||||||
|
FLIGHTPLAN.version="0.0.1"
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- ToDo list
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- TODO: How to connect SID, STAR, ENROUTE, TRANSITION, APPROACH. Typical flightplan SID --> ENROUTE --> STAR --> APPROACH
|
||||||
|
-- TODO: Add approach.
|
||||||
|
-- DONE: How to handle the FLIGHTGROUP:_LandAtAirBase
|
||||||
|
-- TODO: Do we always need a holding pattern? https://www.faa.gov/air_traffic/publications/atpubs/aip_html/part2_enr_section_1.5.html#:~:text=If%20no%20holding%20pattern%20is,than%20that%20desired%20by%20ATC.
|
||||||
|
-- DOEN: Read from MSFS file.
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Constructor
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Create a new FLIGHTPLAN instance.
|
||||||
|
-- @param #FLIGHTPLAN self
|
||||||
|
-- @param #string Name Name of this flight plan.
|
||||||
|
-- @return #FLIGHTPLAN self
|
||||||
|
function FLIGHTPLAN:New(Name)
|
||||||
|
|
||||||
|
-- Inherit everything from BASE class.
|
||||||
|
self=BASE:Inherit(self, PATHLINE:New(Name)) -- #FLIGHTPLAN
|
||||||
|
|
||||||
|
-- Set alias.
|
||||||
|
self.alias=tostring(Name)
|
||||||
|
|
||||||
|
-- Set some string id for output to DCS.log file.
|
||||||
|
self.lid=string.format("FLIGHTPLAN %s | ", self.alias)
|
||||||
|
|
||||||
|
--self.pathline=PATHLINE:New(Name)
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:I(self.lid..string.format("Created FLIGHTPLAN!"))
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create a new FLIGHTPLAN instance from another FLIGHTPLAN acting as blue print.
|
||||||
|
-- The newly created flight plan is deep copied from the given one.
|
||||||
|
-- @param #FLIGHTPLAN self
|
||||||
|
-- @param #FLIGHTPLAN FlightPlan Blue print of the flight plan to copy.
|
||||||
|
-- @return #FLIGHTPLAN self
|
||||||
|
function FLIGHTPLAN:NewFromFlightPlan(FlightPlan)
|
||||||
|
self=UTILS.DeepCopy(FlightPlan)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Create a new FLIGHTPLAN instance from a given file.
|
||||||
|
-- Currently, the file has to be an MSFS 2020 .pln file as, *e.g.*, exported from [Navigraph](https://navigraph.com/).
|
||||||
|
--
|
||||||
|
-- **Note** that the flight plan does only cover the departure, enroute and arrival portions but **not the approach** part!
|
||||||
|
-- @param #FLIGHTPLAN self
|
||||||
|
-- @param #string FileName Full path to file.
|
||||||
|
-- @return #FLIGHTPLAN self
|
||||||
|
function FLIGHTPLAN:NewFromFile(FileName)
|
||||||
|
|
||||||
|
if UTILS.FileExists(FileName) then
|
||||||
|
|
||||||
|
self=FLIGHTPLAN._ReadFileMSFS(FileName)
|
||||||
|
|
||||||
|
else
|
||||||
|
error(string.format("ERROR: File not found! File name=%s", tostring(FileName)))
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- User Functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Add navigation fix to the flight plan.
|
||||||
|
-- @param #FLIGHTPLAN self
|
||||||
|
-- @param Navigation.Point#NAVPOINT NavFix The nav fix.
|
||||||
|
-- @return #FLIGHTPLAN self
|
||||||
|
function FLIGHTPLAN:AddNavFix(NavFix)
|
||||||
|
|
||||||
|
table.insert(self.fixes, NavFix)
|
||||||
|
|
||||||
|
local point=self:AddPointFromVec3(NavFix.vector:GetVec3(true))
|
||||||
|
|
||||||
|
point.navpoint=NavFix
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Set departure airbase.
|
||||||
|
-- @param #FLIGHTPLAN self
|
||||||
|
-- @param #string AirbaseName Name of the airbase or AIRBASE object.
|
||||||
|
-- @return #FLIGHTPLAN self
|
||||||
|
function FLIGHTPLAN:SetDepartureAirbase(AirbaseName)
|
||||||
|
|
||||||
|
self.departureAirbase=AIRBASE:FindByName(AirbaseName)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set destination airbase.
|
||||||
|
-- @param #FLIGHTPLAN self
|
||||||
|
-- @param #string AirbaseName Name of the airbase or AIRBASE object.
|
||||||
|
-- @return #FLIGHTPLAN self
|
||||||
|
function FLIGHTPLAN:SetDestinationAirbase(AirbaseName)
|
||||||
|
|
||||||
|
self.destinationAirbase=AIRBASE:FindByName(AirbaseName)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Set cruise altitude.
|
||||||
|
-- @param #FLIGHTPLAN self
|
||||||
|
-- @param #number AltMin Minimum altitude in feet MSL.
|
||||||
|
-- @param #number AltMax Maximum altitude in feet MSL. Default is `AltMin`.
|
||||||
|
-- @return #FLIGHTPLAN self
|
||||||
|
function FLIGHTPLAN:SetCruiseAltitude(AltMin, AltMax)
|
||||||
|
|
||||||
|
self.altitudeCruiseMin=AltMin
|
||||||
|
self.altitudeCruiseMax=AltMax or self.altitudeCruiseMin
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set cruise speed.
|
||||||
|
-- @param #FLIGHTPLAN self
|
||||||
|
-- @param #number SpeedMin Minimum speed in knots.
|
||||||
|
-- @param #number SpeedMax Maximum speed in knots. Default is `SpeedMin`.
|
||||||
|
-- @return #FLIGHTPLAN self
|
||||||
|
function FLIGHTPLAN:SetCruiseSpeed(SpeedMin, SpeedMax)
|
||||||
|
|
||||||
|
self.speedCruiseMin=SpeedMin
|
||||||
|
self.speedCruiseMax=SpeedMax or self.speedCruiseMin
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get the name of this flight plan.
|
||||||
|
-- @param #FLIGHTPLAN self
|
||||||
|
-- @return #string The name.
|
||||||
|
function FLIGHTPLAN:GetName()
|
||||||
|
return self.alias
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get cruise altitude. This returns a random altitude between the set min/max cruise altitudes.
|
||||||
|
-- @param #FLIGHTPLAN self
|
||||||
|
-- @return #number Cruise altitude in feet MSL.
|
||||||
|
function FLIGHTPLAN:GetCruiseAltitude()
|
||||||
|
|
||||||
|
local alt=10000
|
||||||
|
if self.altitudeCruiseMin and self.altitudeCruiseMax then
|
||||||
|
alt=math.random(self.altitudeCruiseMin, self.altitudeCruiseMax)
|
||||||
|
elseif self.altitudeCruiseMin then
|
||||||
|
alt=self.altitudeCruiseMin
|
||||||
|
elseif self.altitudeCruiseMax then
|
||||||
|
alt=self.altitudeCruiseMax
|
||||||
|
end
|
||||||
|
|
||||||
|
return alt
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get cruise speed. This returns a random speed between the set min/max cruise speeds.
|
||||||
|
-- @param #FLIGHTPLAN self
|
||||||
|
-- @return #number Cruise speed in knots.
|
||||||
|
function FLIGHTPLAN:GetCruiseSpeed()
|
||||||
|
|
||||||
|
local speed=250
|
||||||
|
|
||||||
|
if self.speedCruiseMin and self.speedCruiseMax then
|
||||||
|
speed=math.random(self.speedCruiseMin, self.speedCruiseMax)
|
||||||
|
elseif self.speedCruiseMin then
|
||||||
|
speed=self.speedCruiseMin
|
||||||
|
elseif self.altitudeCruiseMax then
|
||||||
|
speed=self.speedCruiseMax
|
||||||
|
end
|
||||||
|
|
||||||
|
return speed
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Private Functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Read flight plan from a given MSFS 2020 .plt file.
|
||||||
|
-- @param #string FileName Name of the file.
|
||||||
|
-- @return #FLIGHTPLAN The flight plan.
|
||||||
|
function FLIGHTPLAN._ReadFileMSFS(FileName)
|
||||||
|
|
||||||
|
local function readfile(filename)
|
||||||
|
|
||||||
|
local lines = {}
|
||||||
|
|
||||||
|
-- Open file in read binary mode.
|
||||||
|
local file=assert(io.open(filename, "rb"), string.format("File not found! File name = %s", tostring(filename)))
|
||||||
|
|
||||||
|
for line in file:lines() do
|
||||||
|
lines[#lines+1] = line
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Close file.
|
||||||
|
file:close()
|
||||||
|
|
||||||
|
-- Return data
|
||||||
|
return lines
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- This function returns an XML element, i.e. the string between <...> and </...>.
|
||||||
|
local function getXMLelement(line)
|
||||||
|
local element=string.match(line, ">(.+)<")
|
||||||
|
return element
|
||||||
|
end
|
||||||
|
|
||||||
|
--- This function returns Latitude and Longitude
|
||||||
|
local function getLatLong(line)
|
||||||
|
local latlong=getXMLelement(line)
|
||||||
|
-- The format is "N41° 38' 20.00",E41° 33' 19.00",+000000.00" so we still need to process that.
|
||||||
|
local lat,long=string.match(latlong, "(.+),(.+),")
|
||||||
|
return lat,long
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Read data from file.
|
||||||
|
local data=readfile(FileName)
|
||||||
|
|
||||||
|
local flightplan={}
|
||||||
|
local waypoints={}
|
||||||
|
local wp=nil
|
||||||
|
|
||||||
|
local gotwaypoint=false
|
||||||
|
for i,line in pairs(data) do
|
||||||
|
|
||||||
|
|
||||||
|
--print(line)
|
||||||
|
|
||||||
|
-- Title
|
||||||
|
if string.find(line, "<Title>") then
|
||||||
|
flightplan.title=getXMLelement(line)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Departure ICAO
|
||||||
|
if string.find(line, "<DepartureID>") then
|
||||||
|
flightplan.departureICAO=getXMLelement(line)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Destination ICAO
|
||||||
|
if string.find(line, "<DestinationID>") then
|
||||||
|
flightplan.destinationICAO=getXMLelement(line)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- FPType
|
||||||
|
if string.find(line, "<FPType>") then
|
||||||
|
flightplan.plantype=getXMLelement(line)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Route type
|
||||||
|
if string.find(line, "<RouteType>") then
|
||||||
|
flightplan.routetype=getXMLelement(line)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Cruise alt in feet
|
||||||
|
if string.find(line, "<CruisingAlt>") then
|
||||||
|
flightplan.altCruise=getXMLelement(line)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Departure LLA
|
||||||
|
if string.find(line, "<DepartureLLA>") then
|
||||||
|
local lat,long=getLatLong(line)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Destination LLA
|
||||||
|
if string.find(line, "<DestinationLLA>") then
|
||||||
|
local lat,long=getLatLong(line)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Departure Name
|
||||||
|
if string.find(line, "<DepartureName>") then
|
||||||
|
local DepartureName=getXMLelement(line)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- DestinationName
|
||||||
|
if string.find(line, "<DestinationName>") then
|
||||||
|
local DestinationName=getXMLelement(line)
|
||||||
|
end
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Waypoint stuff
|
||||||
|
---
|
||||||
|
|
||||||
|
-- New waypoint starts.
|
||||||
|
if string.find(line, "ATCWaypoint id") then
|
||||||
|
|
||||||
|
--Get string inside quotes " and ".
|
||||||
|
local wpid=string.match(line, [["(.+)"]])
|
||||||
|
|
||||||
|
-- Create a new wp table.
|
||||||
|
wp={}
|
||||||
|
|
||||||
|
-- Set waypoint name.
|
||||||
|
wp.name=wpid
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Waypoint info ends.
|
||||||
|
if string.find(line, "</ATCWaypoint>") then
|
||||||
|
-- This is the end of the waypoint.
|
||||||
|
|
||||||
|
-- Add info to waypoints table.
|
||||||
|
table.insert(waypoints, wp)
|
||||||
|
|
||||||
|
-- Set waypoint to nil. We create an empty table if the next wp starts.
|
||||||
|
wp=nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Waypoint type (Airport, Intersection, NDB, VORTAC)
|
||||||
|
if string.find(line, "<ATCWaypointType>") then
|
||||||
|
local wptype=getXMLelement(line)
|
||||||
|
wp.type=wptype
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Waypoint position.
|
||||||
|
if string.find(line, "<WorldPosition>") then
|
||||||
|
wp.lat, wp.long=getLatLong(line)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Runway should exist for initial and final WP if it is an airport.
|
||||||
|
if string.find(line, "RunwayNumberFP") then
|
||||||
|
wp.runway=getXMLelement(line)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Runway designator: LEFT, RIGHT, CENTER
|
||||||
|
if string.find(line, "RunwayDesignatorFP") then
|
||||||
|
wp.runwayDesignator=getXMLelement(line)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Segment is Departure
|
||||||
|
if string.find(line, "<DepartureFP>") then
|
||||||
|
wp.segment="Departure"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Segment is Arrival
|
||||||
|
if string.find(line, "<ArrivalFP>") then
|
||||||
|
wp.segment="Arrival"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Segment is Enroute
|
||||||
|
if string.find(line, "<ATCAirway>") then
|
||||||
|
wp.segment="Enroute"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Approach type: VORDME, LOCALIZER
|
||||||
|
if string.find(line, "ApproachTypeFP") then
|
||||||
|
flightplan.approachtype=getXMLelement(line)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Approach type suffic: Z
|
||||||
|
if string.find(line, "SuffixFP") then
|
||||||
|
local SuffixFP=getXMLelement(line)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
for key, value in pairs(flightplan) do
|
||||||
|
env.info(string.format("Flightplan %s=%s", key, tostring(value)))
|
||||||
|
end
|
||||||
|
|
||||||
|
env.info(string.format("Number of waypoints=%d", #waypoints))
|
||||||
|
for i,wp in pairs(waypoints) do
|
||||||
|
env.info(string.format("Waypoint name=%s type=%s segment=%s runway=%s lat=%s long=%s", wp.name, wp.type, tostring(wp.segment), tostring(wp.runway)..tostring(wp.runwayDesignator or ""), wp.lat, wp.long))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Create a new flightplan.
|
||||||
|
local fp=FLIGHTPLAN:New(flightplan.title)
|
||||||
|
|
||||||
|
-- Set cruise altitude.
|
||||||
|
fp:SetCruiseAltitude(flightplan.altCruise)
|
||||||
|
|
||||||
|
-- Set departure and destination airports.
|
||||||
|
fp:SetDepartureAirbase(flightplan.departureICAO)
|
||||||
|
fp:SetDestinationAirbase(flightplan.destinationICAO)
|
||||||
|
|
||||||
|
--TODO: Remove first and last waypoint if they are identical to the departure/destination airport!
|
||||||
|
|
||||||
|
for i,wp in pairs(waypoints) do
|
||||||
|
|
||||||
|
-- Create a navpoint.
|
||||||
|
local navpoint=NAVPOINT:NewFromLLDMS(wp.name, wp.type, wp.lat, wp.long)
|
||||||
|
|
||||||
|
navpoint:SetAltMin(flightplan.altCruise)
|
||||||
|
|
||||||
|
-- Add point to flightplan.
|
||||||
|
-- TODO: section departure, enroute, arrival.
|
||||||
|
fp:AddNavFix(navpoint)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return fp
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
587
Moose Development/Moose/Navigation/Point.lua
Normal file
587
Moose Development/Moose/Navigation/Point.lua
Normal file
@@ -0,0 +1,587 @@
|
|||||||
|
--- **NAVIGATION** - Navigation Airspace Points, Fixes and Aids.
|
||||||
|
--
|
||||||
|
-- **Main Features:**
|
||||||
|
--
|
||||||
|
-- * Stuff
|
||||||
|
-- * More Stuff
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ## 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 NAVIGATION_Point.png
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- 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.0.1"
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- 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
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- 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 compusory 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.0.1"
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- 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.
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
333
Moose Development/Moose/Navigation/Procedure.lua
Normal file
333
Moose Development/Moose/Navigation/Procedure.lua
Normal file
@@ -0,0 +1,333 @@
|
|||||||
|
--- **NAVIGATION** - Prodedures for Departure (*e.g.* SID), Enroute, Arrival (*e.g.* STAR) and Approach.
|
||||||
|
--
|
||||||
|
-- **Main Features:**
|
||||||
|
--
|
||||||
|
-- * Stuff
|
||||||
|
-- * More Stuff
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ## Example Missions:
|
||||||
|
--
|
||||||
|
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Navigation%20-%20Template).
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Author: **funkyfranky**
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
-- @module Navigation.Procedure
|
||||||
|
-- @image NAVIGATION_Procedure.png
|
||||||
|
|
||||||
|
|
||||||
|
--- APPROACH class.
|
||||||
|
-- @type APPROACH
|
||||||
|
-- @field #string ClassName Name of the class.
|
||||||
|
-- @field #number verbose Verbosity of output.
|
||||||
|
-- @field #string apptype Approach type (ILS, VOR, LOC).
|
||||||
|
-- @field Wrapper.Airbase#AIRBASE airbase Airbase of this approach.
|
||||||
|
-- @field Wrapper.Airbase#AIRBASE.Runway runway Runway of this approach.
|
||||||
|
-- @field Navigation.Point#NAVAID navaid Primary navigation aid.
|
||||||
|
-- @field #number wpcounter Running number counting the waypoints to generate its UID.
|
||||||
|
-- @list <#APPROACH.Waypoint> path Path of approach consisting of waypoints.
|
||||||
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
--- *A fleet of British ships at war are the best negotiators.* -- Horatio Nelson
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- # The APPROACH Concept
|
||||||
|
--
|
||||||
|
-- A typical approach has (up to) four segments. It starts with the initial approach segment, followed by the intermediate approach segment, followed
|
||||||
|
-- by the final approach segment. In case something goes wrong during the final approach, the missed approach segment kicks in.
|
||||||
|
--
|
||||||
|
-- The initial approach segment starts at the initial approach fix (IAF). The segment can contain multiple other fixes, that need to be passed.
|
||||||
|
-- Note, that an approach procedure can have more than one intitial approach segment and IAF.
|
||||||
|
--
|
||||||
|
-- The intermediate approach segment starts at the intermediate fix (IF). The intermediate approach segment blends the initial approach segment into the final approach segment.
|
||||||
|
-- It is the segment in which aircraft configuration, speed, and positioning adjustments are made for entry into the final approach segment.
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- https://en.wikipedia.org/wiki/Visual_approach
|
||||||
|
-- https://en.wikipedia.org/wiki/Instrument_approach
|
||||||
|
--
|
||||||
|
-- # Basic Setup
|
||||||
|
--
|
||||||
|
-- A new `APPROACH` object can be created with the @{#APPROACH.New}() function.
|
||||||
|
--
|
||||||
|
-- myTemplate=APPROACH:New()
|
||||||
|
-- myTemplate:SetXYZ(X, Y, Z)
|
||||||
|
--
|
||||||
|
-- This is how it works.
|
||||||
|
--
|
||||||
|
-- @field #APPROACH
|
||||||
|
APPROACH = {
|
||||||
|
ClassName = "APPROACH",
|
||||||
|
verbose = 0,
|
||||||
|
wpcounter = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Type of approach.
|
||||||
|
-- @type APPROACH.Type
|
||||||
|
-- @field #string VFR Visual Flight Rules.
|
||||||
|
-- @field #string VOR VOR
|
||||||
|
-- @field #string NDB NDB
|
||||||
|
APPROACH.Type={
|
||||||
|
VFR="VFR",
|
||||||
|
VOR="VOR",
|
||||||
|
ILS="ILS",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
--- Setments of approach.
|
||||||
|
-- @type APPROACH.Segment
|
||||||
|
-- @field #string INITIAL Initial approach segment.
|
||||||
|
-- @field #string INTERMEDIATE Intermediate approach segment.
|
||||||
|
-- @field #string FINAL Final approach segment.
|
||||||
|
-- @field #string MISSED Missed approach segment.
|
||||||
|
APPROACH.Segment={
|
||||||
|
INITIAL="Initial",
|
||||||
|
INTERMEDIATE="Intermediate",
|
||||||
|
FINAL="Final",
|
||||||
|
MISSED="Missed",
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Waypoint of the approach.
|
||||||
|
-- @type APPROACH.Waypoint
|
||||||
|
-- @field #number uid Unique ID of the point.
|
||||||
|
-- @field #string segment The segment this point belongs to.
|
||||||
|
-- @field Navigation.Point#NAVFIX navfix The navigation fix that determines the coordinates of this point.
|
||||||
|
|
||||||
|
--- APPROACH class version.
|
||||||
|
-- @field #string version
|
||||||
|
APPROACH.version="0.0.1"
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- ToDo list
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- TODO: A lot...
|
||||||
|
-- Initial approach segment --> Intermediate approach segment: starts at IF --> Final approach segment
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Constructor
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Create a new APPROACH class instance.
|
||||||
|
-- @param #APPROACH self
|
||||||
|
-- @param #string Type Type of approach (ILS, VOR, LOC).
|
||||||
|
-- @param Wrapper.Airbase#AIRBASE Airbase The airbase or name of the airbase.
|
||||||
|
-- @param Wrapper.Airbase#AIRBASE.Runway Runway The runway or name of the runway.
|
||||||
|
-- @return #APPROACH self
|
||||||
|
function APPROACH:New(Type, Airbase, Runway)
|
||||||
|
|
||||||
|
-- Inherit everything from BASE class.
|
||||||
|
self=BASE:Inherit(self, BASE:New()) -- #APPROACH
|
||||||
|
|
||||||
|
-- Set approach type.
|
||||||
|
-- TODO: Check if this is a valid/known approach type.
|
||||||
|
self.apptype=Type
|
||||||
|
|
||||||
|
if type(Airbase)=="string" then
|
||||||
|
self.airbase=AIRBASE:FindByName(Airbase)
|
||||||
|
else
|
||||||
|
self.airbase=Airbase
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(Runway)=="string" then
|
||||||
|
self.runway=self.airbase:GetRunwayByName(Runway)
|
||||||
|
else
|
||||||
|
self.runway=Runway
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:I("Created new approach for airbase %s: type=%s, runway=%s", self.airbase:GetName(), self.apptype, self.runway.name)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- User Functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Set the primary navigation aid used in the approach.
|
||||||
|
-- @param #APPROACH self
|
||||||
|
-- @param Navigation.Point#NAVAID NavAid The NAVAID.
|
||||||
|
-- @return #APPROACH self
|
||||||
|
function APPROACH:SetNavAid(NavAid)
|
||||||
|
|
||||||
|
self.navaid=NavAid
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Add a waypoint to the path of the approach.
|
||||||
|
-- @param #APPROACH self
|
||||||
|
-- @param Navigation.Point#NAVFIX NavFix The navigation fix.
|
||||||
|
-- @param #string Segment The approach segment this fix belongs to.
|
||||||
|
-- @return #APPROACH.Waypoint The waypoint data table.
|
||||||
|
function APPROACH:AddNavFix(NavFix, Segment)
|
||||||
|
|
||||||
|
self.wpcounter=self.wpcounter+1
|
||||||
|
|
||||||
|
local point={} --#APPROACH.Waypoint
|
||||||
|
point.uid=self.wpcounter
|
||||||
|
point.segment=Segment
|
||||||
|
point.navfix=NavFix
|
||||||
|
|
||||||
|
table.insert(self.path, point)
|
||||||
|
|
||||||
|
return point
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Private Functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Add private functions here.
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- DEPARTURE class.
|
||||||
|
-- @type DEPARTURE
|
||||||
|
-- @field #string ClassName Name of the class.
|
||||||
|
-- @field #number verbose Verbosity of output.
|
||||||
|
-- @field #string apptype DEPARTURE type (ILS, VOR, LOC).
|
||||||
|
-- @field Wrapper.Airbase#AIRBASE airbase Airbase of this DEPARTURE.
|
||||||
|
-- @field Wrapper.Airbase#AIRBASE.Runway runway Runway of this DEPARTURE.
|
||||||
|
-- @field Navigation.Point#NAVAID navaid Primary navigation aid.
|
||||||
|
-- @field #number wpcounter Running number counting the waypoints to generate its UID.
|
||||||
|
-- @list <#DEPARTURE.Waypoint> path Path of DEPARTURE consisting of waypoints.
|
||||||
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
--- *A fleet of British ships at war are the best negotiators.* -- Horatio Nelson
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- # The DEPARTURE Concept
|
||||||
|
--
|
||||||
|
-- Bla.
|
||||||
|
--
|
||||||
|
-- @field #DEPARTURE
|
||||||
|
DEPARTURE = {
|
||||||
|
ClassName = "DEPARTURE",
|
||||||
|
verbose = 0,
|
||||||
|
wpcounter = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Type of DEPARTURE.
|
||||||
|
-- @type DEPARTURE.Type
|
||||||
|
-- @field #string VOR VOR
|
||||||
|
-- @field #string NDB NDB
|
||||||
|
DEPARTURE.Type={
|
||||||
|
VOR="VOR",
|
||||||
|
ILS="ILS",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
--- Setments of DEPARTURE.
|
||||||
|
-- @type DEPARTURE.Segment
|
||||||
|
-- @field #string INITIAL Initial DEPARTURE segment.
|
||||||
|
-- @field #string INTERMEDIATE Intermediate DEPARTURE segment.
|
||||||
|
-- @field #string FINAL Final DEPARTURE segment.
|
||||||
|
-- @field #string MISSED Missed DEPARTURE segment.
|
||||||
|
DEPARTURE.Segment={
|
||||||
|
INITIAL="Initial",
|
||||||
|
INTERMEDIATE="Intermediate",
|
||||||
|
FINAL="Final",
|
||||||
|
MISSED="Missed",
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Waypoint of the DEPARTURE.
|
||||||
|
-- @type DEPARTURE.Waypoint
|
||||||
|
-- @field #number uid Unique ID of the point.
|
||||||
|
-- @field #string segment The segment this point belongs to.
|
||||||
|
-- @field Navigation.Point#NAVFIX navfix The navigation fix that determines the coordinates of this point.
|
||||||
|
|
||||||
|
--- DEPARTURE class version.
|
||||||
|
-- @field #string version
|
||||||
|
DEPARTURE.version="0.0.1"
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- ToDo list
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- TODO: A lot...
|
||||||
|
-- Initial DEPARTURE segment --> Intermediate DEPARTURE segment: starts at IF --> Final DEPARTURE segment
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Constructor
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Create a new DEPARTURE class instance.
|
||||||
|
-- @param #DEPARTURE self
|
||||||
|
-- @param Wrapper.Airbase#AIRBASE Airbase The airbase or name of the airbase.
|
||||||
|
-- @param Wrapper.Airbase#AIRBASE.Runway Runway The runway or name of the runway.
|
||||||
|
-- @param #string Type Type of DEPARTURE (ILS, VOR, LOC).
|
||||||
|
-- @return #DEPARTURE self
|
||||||
|
function DEPARTURE:New(Airbase, Runway)
|
||||||
|
|
||||||
|
-- Inherit everything from BASE class.
|
||||||
|
self=BASE:Inherit(self, BASE:New()) -- #DEPARTURE
|
||||||
|
|
||||||
|
if type(Airbase)=="string" then
|
||||||
|
self.airbase=AIRBASE:FindByName(Airbase)
|
||||||
|
else
|
||||||
|
self.airbase=Airbase
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(Runway)=="string" then
|
||||||
|
self.runway=self.airbase:GetRunwayByName(Runway)
|
||||||
|
else
|
||||||
|
self.runway=Runway
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- User Functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Set the primary navigation aid used in the DEPARTURE.
|
||||||
|
-- @param #DEPARTURE self
|
||||||
|
-- @param Navigation.Point#NAVAID NavAid The NAVAID.
|
||||||
|
-- @return #DEPARTURE self
|
||||||
|
function DEPARTURE:SetNavAid(NavAid)
|
||||||
|
|
||||||
|
self.navaid=NavAid
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Add a waypoint to the path of the DEPARTURE.
|
||||||
|
-- @param #DEPARTURE self
|
||||||
|
-- @param Navigation.Point#NAVFIX NavFix The navigation fix.
|
||||||
|
-- @param #string Segment The DEPARTURE segment this fix belongs to.
|
||||||
|
-- @return #DEPARTURE.Waypoint The waypoint data.
|
||||||
|
function DEPARTURE:AddWaypoint(NavFix, Segment)
|
||||||
|
|
||||||
|
self.wpcounter=self.wpcounter+1
|
||||||
|
|
||||||
|
local point={} --#DEPARTURE.Waypoint
|
||||||
|
point.uid=self.wpcounter
|
||||||
|
point.segment=Segment
|
||||||
|
point.navfix=NavFix
|
||||||
|
|
||||||
|
table.insert(self.path, point)
|
||||||
|
|
||||||
|
return point
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Private Functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Add DEPARTURE private functions here.
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
330
Moose Development/Moose/Navigation/Radios.lua
Normal file
330
Moose Development/Moose/Navigation/Radios.lua
Normal file
@@ -0,0 +1,330 @@
|
|||||||
|
--- **NAVIGATION** - Airbase radios.
|
||||||
|
--
|
||||||
|
-- **Main Features:**
|
||||||
|
--
|
||||||
|
-- * Get radio frequencies of airbases
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ## 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 NAVIGATION_Radios.png
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- 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 desinged 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.
|
||||||
|
--
|
||||||
|
-- # 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>\radios.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.
|
||||||
|
|
||||||
|
--- 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.0.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()
|
||||||
|
|
||||||
|
for _,_radio in pairs(RadioTable) do
|
||||||
|
local radio=_radio --#RADIOS.Radio
|
||||||
|
|
||||||
|
--UTILS.PrintTableToLog(radio)
|
||||||
|
--UTILS.PrintTableToLog(radio.callsign)
|
||||||
|
|
||||||
|
-- The table structure of callsign is a bit awkward. We need to get the airbase name.
|
||||||
|
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
|
||||||
|
|
||||||
|
--UTILS.PrintTableToLog(radio.callsign)
|
||||||
|
|
||||||
|
radio.name=self:_GetAirbaseName(airbasenames, radio.name)
|
||||||
|
|
||||||
|
radio.airbase=AIRBASE:FindByName(radio.name)
|
||||||
|
|
||||||
|
if radio.airbase then
|
||||||
|
radio.coordinate=radio.airbase:GetCoordinate()
|
||||||
|
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
|
||||||
|
|
||||||
|
--- 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
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
108
Moose Development/Moose/Navigation/Template.lua
Normal file
108
Moose Development/Moose/Navigation/Template.lua
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
--- **NAVIGATION** - Template.
|
||||||
|
--
|
||||||
|
-- **Main Features:**
|
||||||
|
--
|
||||||
|
-- * Stuff
|
||||||
|
-- * More Stuff
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ## Example Missions:
|
||||||
|
--
|
||||||
|
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Navigation%20-%20Template).
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Author: **funkyfranky**
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
-- @module Navigation.Template
|
||||||
|
-- @image NAVIGATION_Template.png
|
||||||
|
|
||||||
|
|
||||||
|
--- TEMPLATE class.
|
||||||
|
-- @type TEMPLATE
|
||||||
|
-- @field #string ClassName Name of the class.
|
||||||
|
-- @field #number verbose Verbosity of output.
|
||||||
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
--- *A fleet of British ships at war are the best negotiators.* -- Horatio Nelson
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- # The TEMPLATE Concept
|
||||||
|
--
|
||||||
|
-- The TEMPLATE class has a great concept!
|
||||||
|
--
|
||||||
|
-- # Basic Setup
|
||||||
|
--
|
||||||
|
-- A new `TEMPLATE` object can be created with the @{#TEMPLATE.New}() function.
|
||||||
|
--
|
||||||
|
-- myTemplate=TEMPLATE:New()
|
||||||
|
-- myTemplate:SetXYZ(X, Y, Z)
|
||||||
|
--
|
||||||
|
-- This is how it works.
|
||||||
|
--
|
||||||
|
-- @field #TEMPLATE
|
||||||
|
TEMPLATE = {
|
||||||
|
ClassName = "TEMPLATE",
|
||||||
|
verbose = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Type of navaid
|
||||||
|
-- @type TEMPLATE.Type
|
||||||
|
-- @field #string VOR VOR
|
||||||
|
-- @field #string NDB NDB
|
||||||
|
TEMPLATE.TYPE={
|
||||||
|
VOR="VOR",
|
||||||
|
NDB="NDB",
|
||||||
|
}
|
||||||
|
|
||||||
|
--- TEMPLATE class version.
|
||||||
|
-- @field #string version
|
||||||
|
TEMPLATE.version="0.0.1"
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- ToDo list
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- TODO: A lot...
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Constructor
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Create a new TEMPLATE class instance.
|
||||||
|
-- @param #TEMPLATE self
|
||||||
|
-- @return #TEMPLATE self
|
||||||
|
function TEMPLATE:New()
|
||||||
|
|
||||||
|
-- Inherit everything from SCENERY class.
|
||||||
|
self=BASE:Inherit(self, BASE:New()) -- #TEMPLATE
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- User Functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Set frequency.
|
||||||
|
-- @param #TEMPLATE self
|
||||||
|
-- @param #number Frequency Frequency in Hz.
|
||||||
|
-- @return #TEMPLATE self
|
||||||
|
function TEMPLATE:SetFrequency(Frequency)
|
||||||
|
|
||||||
|
self.frequency=Frequency
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Private Functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
296
Moose Development/Moose/Navigation/Towns.lua
Normal file
296
Moose Development/Moose/Navigation/Towns.lua
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
--- **NAVIGATION** - Beacons of the map/theatre.
|
||||||
|
--
|
||||||
|
-- **Main Features:**
|
||||||
|
--
|
||||||
|
-- * Find towns of map
|
||||||
|
-- * Road and rail connections
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ## 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 NAVIGATION_Towns.png
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- 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 desinged 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.
|
||||||
|
--
|
||||||
|
-- # 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>\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.0.1"
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- ToDo list
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- TODO: A lot...
|
||||||
|
-- TODO: Road connection
|
||||||
|
-- TODO: Rail connection
|
||||||
|
-- TODO: 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 connection between two towns.
|
||||||
|
-- @param #TOWNS self
|
||||||
|
-- @param #TOWNS.Town townA The town data structure.
|
||||||
|
-- @param #TOWNS.Town townB The town data structure.
|
||||||
|
-- @return #table Table containing path coordinates.
|
||||||
|
function TOWNS:GetConnectionRoad(townA, townB)
|
||||||
|
|
||||||
|
local path=townA.coordRoad:GetPathOnRoad(townB.coordRoad)
|
||||||
|
|
||||||
|
return path
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Find closest town to a given coordinate.
|
||||||
|
-- @param #TOWNS self
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate The reference coordinate.
|
||||||
|
-- @return #TOWNS.Town The closest town.
|
||||||
|
function TOWNS:GetClosestTown(Coordinate)
|
||||||
|
|
||||||
|
local Town=nil --#TOWNS.Town
|
||||||
|
local distmin=math.huge
|
||||||
|
|
||||||
|
for _,_town in pairs(self.towns) do
|
||||||
|
local town=_town --#TOWNS.Town
|
||||||
|
|
||||||
|
local dist=Coordinate:Get2DDistance(town.coordinate)
|
||||||
|
|
||||||
|
if dist<distmin then
|
||||||
|
distmin=dist
|
||||||
|
Town=town
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return Town
|
||||||
|
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
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -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 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 #number noccupied Number of flights on this patrol point.
|
||||||
-- @field Wrapper.Marker#MARKER marker F10 marker.
|
-- @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.
|
--- Patrol zone.
|
||||||
-- @type AIRWING.PatrolZone
|
-- @type AIRWING.PatrolZone
|
||||||
@@ -187,13 +189,14 @@ AIRWING = {
|
|||||||
|
|
||||||
--- AIRWING class version.
|
--- AIRWING class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
AIRWING.version="0.9.6"
|
AIRWING.version="0.9.7"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- ToDo list
|
-- ToDo list
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- TODO: Check that airbase has enough parking spots if a request is BIG.
|
-- 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 in air ==> Needs WAREHOUSE update.
|
||||||
-- DONE: Spawn hot.
|
-- DONE: Spawn hot.
|
||||||
-- DONE: Make special request to transfer squadrons to anther airwing (or warehouse).
|
-- DONE: Make special request to transfer squadrons to anther airwing (or warehouse).
|
||||||
@@ -807,13 +810,22 @@ function AIRWING:_PatrolPointMarkerText(point)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- Update marker of the patrol point.
|
--- Update marker of the patrol point.
|
||||||
|
-- @param #AIRWING self
|
||||||
-- @param #AIRWING.PatrolData point Patrol point table.
|
-- @param #AIRWING.PatrolData point Patrol point table.
|
||||||
function AIRWING:UpdatePatrolPointMarker(point)
|
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",
|
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.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
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -821,7 +833,7 @@ end
|
|||||||
--- Create a new generic patrol point.
|
--- Create a new generic patrol point.
|
||||||
-- @param #AIRWING self
|
-- @param #AIRWING self
|
||||||
-- @param #string Type Patrol point type, e.g. "CAP" or "AWACS". Default "Unknown".
|
-- @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 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 Heading Heading in degrees. Default random (0, 360] degrees.
|
||||||
-- @param #number LegLength Length of race-track orbit in NM. Default 15 NM.
|
-- @param #number LegLength Length of race-track orbit in NM. Default 15 NM.
|
||||||
@@ -830,14 +842,16 @@ end
|
|||||||
-- @return #AIRWING.PatrolData Patrol point table.
|
-- @return #AIRWING.PatrolData Patrol point table.
|
||||||
function AIRWING:NewPatrolPoint(Type, Coordinate, Altitude, Speed, Heading, LegLength, RefuelSystem)
|
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
|
local patrolpoint={} --#AIRWING.PatrolData
|
||||||
patrolpoint.type=Type or "Unknown"
|
patrolpoint.type=Type or "Unknown"
|
||||||
patrolpoint.coord=Coordinate or self:GetCoordinate():Translate(UTILS.NMToMeters(math.random(10, 15)), math.random(360))
|
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.heading=Heading or math.random(360)
|
||||||
patrolpoint.leg=LegLength or 15
|
patrolpoint.leg=LegLength or 15
|
||||||
patrolpoint.altitude=Altitude or math.random(10,20)*1000
|
patrolpoint.altitude=Altitude or math.random(10,20)*1000
|
||||||
@@ -847,7 +861,7 @@ function AIRWING:NewPatrolPoint(Type, Coordinate, Altitude, Speed, Heading, LegL
|
|||||||
|
|
||||||
if self.markpoints then
|
if self.markpoints then
|
||||||
patrolpoint.marker=MARKER:New(Coordinate, "New Patrol Point"):ToAll()
|
patrolpoint.marker=MARKER:New(Coordinate, "New Patrol Point"):ToAll()
|
||||||
AIRWING.UpdatePatrolPointMarker(patrolpoint)
|
self:UpdatePatrolPointMarker(patrolpoint)
|
||||||
end
|
end
|
||||||
|
|
||||||
return patrolpoint
|
return patrolpoint
|
||||||
@@ -855,7 +869,7 @@ end
|
|||||||
|
|
||||||
--- Add a patrol Point for CAP missions.
|
--- Add a patrol Point for CAP missions.
|
||||||
-- @param #AIRWING self
|
-- @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 Altitude Orbit altitude in feet.
|
||||||
-- @param #number Speed Orbit speed in knots.
|
-- @param #number Speed Orbit speed in knots.
|
||||||
-- @param #number Heading Heading in degrees.
|
-- @param #number Heading Heading in degrees.
|
||||||
@@ -872,7 +886,7 @@ end
|
|||||||
|
|
||||||
--- Add a patrol Point for RECON missions.
|
--- Add a patrol Point for RECON missions.
|
||||||
-- @param #AIRWING self
|
-- @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 Altitude Orbit altitude in feet.
|
||||||
-- @param #number Speed Orbit speed in knots.
|
-- @param #number Speed Orbit speed in knots.
|
||||||
-- @param #number Heading Heading in degrees.
|
-- @param #number Heading Heading in degrees.
|
||||||
@@ -889,7 +903,7 @@ end
|
|||||||
|
|
||||||
--- Add a patrol Point for TANKER missions.
|
--- Add a patrol Point for TANKER missions.
|
||||||
-- @param #AIRWING self
|
-- @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 Altitude Orbit altitude in feet.
|
||||||
-- @param #number Speed Orbit speed in knots.
|
-- @param #number Speed Orbit speed in knots.
|
||||||
-- @param #number Heading Heading in degrees.
|
-- @param #number Heading Heading in degrees.
|
||||||
@@ -907,7 +921,7 @@ end
|
|||||||
|
|
||||||
--- Add a patrol Point for AWACS missions.
|
--- Add a patrol Point for AWACS missions.
|
||||||
-- @param #AIRWING self
|
-- @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 Altitude Orbit altitude in feet.
|
||||||
-- @param #number Speed Orbit speed in knots.
|
-- @param #number Speed Orbit speed in knots.
|
||||||
-- @param #number Heading Heading in degrees.
|
-- @param #number Heading Heading in degrees.
|
||||||
@@ -974,6 +988,46 @@ function AIRWING:SetTakeoffAir()
|
|||||||
return self
|
return self
|
||||||
end
|
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.
|
--- Set despawn after landing. Aircraft will be despawned after the landing event.
|
||||||
-- Can help to avoid DCS AI taxiing issues.
|
-- Can help to avoid DCS AI taxiing issues.
|
||||||
-- @param #AIRWING self
|
-- @param #AIRWING self
|
||||||
@@ -1136,6 +1190,10 @@ function AIRWING:_GetPatrolData(PatrolPoints, RefuelSystem)
|
|||||||
|
|
||||||
for _,_patrolpoint in pairs(PatrolPoints) do
|
for _,_patrolpoint in pairs(PatrolPoints) do
|
||||||
local patrolpoint=_patrolpoint --#AIRWING.PatrolData
|
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
|
if (RefuelSystem and patrolpoint.refuelsystem and RefuelSystem==patrolpoint.refuelsystem) or RefuelSystem==nil or patrolpoint.refuelsystem==nil then
|
||||||
return patrolpoint
|
return patrolpoint
|
||||||
end
|
end
|
||||||
@@ -1195,7 +1253,7 @@ function AIRWING:CheckCAP()
|
|||||||
|
|
||||||
patrol.noccupied=patrol.noccupied+1
|
patrol.noccupied=patrol.noccupied+1
|
||||||
|
|
||||||
if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol) end
|
if self.markpoints then self:UpdatePatrolPointMarker(patrol) end
|
||||||
|
|
||||||
self:AddMission(missionCAP)
|
self:AddMission(missionCAP)
|
||||||
|
|
||||||
@@ -1247,7 +1305,7 @@ function AIRWING:CheckRECON()
|
|||||||
|
|
||||||
patrol.noccupied=patrol.noccupied+1
|
patrol.noccupied=patrol.noccupied+1
|
||||||
|
|
||||||
if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol) end
|
if self.markpoints then self:UpdatePatrolPointMarker(patrol) end
|
||||||
|
|
||||||
self:AddMission(missionRECON)
|
self:AddMission(missionRECON)
|
||||||
|
|
||||||
@@ -1292,7 +1350,7 @@ function AIRWING:CheckTANKER()
|
|||||||
|
|
||||||
patrol.noccupied=patrol.noccupied+1
|
patrol.noccupied=patrol.noccupied+1
|
||||||
|
|
||||||
if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol) end
|
if self.markpoints then self:UpdatePatrolPointMarker(patrol) end
|
||||||
|
|
||||||
self:AddMission(mission)
|
self:AddMission(mission)
|
||||||
|
|
||||||
@@ -1311,7 +1369,7 @@ function AIRWING:CheckTANKER()
|
|||||||
|
|
||||||
patrol.noccupied=patrol.noccupied+1
|
patrol.noccupied=patrol.noccupied+1
|
||||||
|
|
||||||
if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol) end
|
if self.markpoints then self:UpdatePatrolPointMarker(patrol) end
|
||||||
|
|
||||||
self:AddMission(mission)
|
self:AddMission(mission)
|
||||||
|
|
||||||
@@ -1349,7 +1407,7 @@ function AIRWING:CheckAWACS()
|
|||||||
|
|
||||||
patrol.noccupied=patrol.noccupied+1
|
patrol.noccupied=patrol.noccupied+1
|
||||||
|
|
||||||
if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol) end
|
if self.markpoints then self:UpdatePatrolPointMarker(patrol) end
|
||||||
|
|
||||||
self:AddMission(mission)
|
self:AddMission(mission)
|
||||||
|
|
||||||
@@ -1464,7 +1522,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()))
|
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
|
if self.UseConnectedOpsAwacs and self.ConnectedOpsAwacs then
|
||||||
self.ConnectedOpsAwacs:__FlightOnMission(2,FlightGroup,Mission)
|
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
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -397,6 +397,7 @@ AUFTRAG = {
|
|||||||
conditionPush = {},
|
conditionPush = {},
|
||||||
conditionSuccessSet = false,
|
conditionSuccessSet = false,
|
||||||
conditionFailureSet = false,
|
conditionFailureSet = false,
|
||||||
|
repeatDelay = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Global mission counter.
|
--- Global mission counter.
|
||||||
@@ -1320,13 +1321,19 @@ end
|
|||||||
-- @param #number Altitude Orbit altitude in feet. Default is y component of `Coordinate`.
|
-- @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 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 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.
|
-- @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
|
-- @return #AUFTRAG self
|
||||||
function AUFTRAG:NewTANKER(Coordinate, Altitude, Speed, Heading, Leg, RefuelSystem)
|
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.
|
-- 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 TANKER.
|
||||||
mission.type=AUFTRAG.Type.TANKER
|
mission.type=AUFTRAG.Type.TANKER
|
||||||
@@ -1428,7 +1435,7 @@ function AUFTRAG:NewCAP(ZoneCAP, Altitude, Speed, Coordinate, Heading, Leg, Targ
|
|||||||
mission:_SetLogID()
|
mission:_SetLogID()
|
||||||
|
|
||||||
-- DCS task parameters:
|
-- DCS task parameters:
|
||||||
mission.engageZone=ZoneCAP
|
mission.engageZone=ZoneCAP or Coordinate
|
||||||
mission.engageTargetTypes=TargetTypes or {"Air"}
|
mission.engageTargetTypes=TargetTypes or {"Air"}
|
||||||
|
|
||||||
-- Mission options:
|
-- Mission options:
|
||||||
@@ -1715,6 +1722,42 @@ function AUFTRAG:NewSEAD(Target, Altitude)
|
|||||||
return mission
|
return mission
|
||||||
end
|
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.
|
--- **[AIR]** Create a STRIKE mission. Flight will attack the closest map object to the specified coordinate.
|
||||||
-- @param #AUFTRAG self
|
-- @param #AUFTRAG self
|
||||||
-- @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 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.
|
||||||
@@ -1752,8 +1795,9 @@ end
|
|||||||
-- @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 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 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 #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
|
-- @return #AUFTRAG self
|
||||||
function AUFTRAG:NewBOMBING(Target, Altitude, EngageWeaponType)
|
function AUFTRAG:NewBOMBING(Target, Altitude, EngageWeaponType, Divebomb)
|
||||||
|
|
||||||
local mission=AUFTRAG:New(AUFTRAG.Type.BOMBING)
|
local mission=AUFTRAG:New(AUFTRAG.Type.BOMBING)
|
||||||
|
|
||||||
@@ -1770,6 +1814,7 @@ function AUFTRAG:NewBOMBING(Target, Altitude, EngageWeaponType)
|
|||||||
mission.missionFraction=0.5
|
mission.missionFraction=0.5
|
||||||
mission.optionROE=ENUMS.ROE.OpenFire
|
mission.optionROE=ENUMS.ROE.OpenFire
|
||||||
mission.optionROT=ENUMS.ROT.NoReaction -- No reaction is better.
|
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.
|
-- Evaluate result after 5 min. We might need time until the bombs have dropped and targets have been detroyed.
|
||||||
mission.dTevaluate=5*60
|
mission.dTevaluate=5*60
|
||||||
@@ -2279,8 +2324,9 @@ end
|
|||||||
-- @param #number Speed Speed in knots.
|
-- @param #number Speed Speed in knots.
|
||||||
-- @param #number Altitude Altitude in feet. Only for airborne units. Default 2000 feet ASL.
|
-- @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 #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
|
-- @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)
|
local mission=AUFTRAG:New(AUFTRAG.Type.CAPTUREZONE)
|
||||||
|
|
||||||
@@ -2294,6 +2340,7 @@ function AUFTRAG:NewCAPTUREZONE(OpsZone, Coalition, Speed, Altitude, Formation)
|
|||||||
mission.optionROE=ENUMS.ROE.ReturnFire
|
mission.optionROE=ENUMS.ROE.ReturnFire
|
||||||
mission.optionROT=ENUMS.ROT.PassiveDefense
|
mission.optionROT=ENUMS.ROT.PassiveDefense
|
||||||
mission.optionAlarm=ENUMS.AlarmState.Auto
|
mission.optionAlarm=ENUMS.AlarmState.Auto
|
||||||
|
mission.StayInZoneTime = StayInZoneTime
|
||||||
|
|
||||||
mission.missionFraction=0.1
|
mission.missionFraction=0.1
|
||||||
mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or nil
|
mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or nil
|
||||||
@@ -2966,6 +3013,16 @@ function AUFTRAG:SetRepeat(Nrepeat)
|
|||||||
return self
|
return self
|
||||||
end
|
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 Nrepeat 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.
|
--- **[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 #AUFTRAG self
|
||||||
-- @param #number Nrepeat Number of repeats. Default 0.
|
-- @param #number Nrepeat Number of repeats. Default 0.
|
||||||
@@ -3961,6 +4018,23 @@ function AUFTRAG:IsOver()
|
|||||||
return over
|
return over
|
||||||
end
|
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.
|
--- Check if mission is NOT over.
|
||||||
-- @param #AUFTRAG self
|
-- @param #AUFTRAG self
|
||||||
-- @return #boolean If true, mission is NOT over yet.
|
-- @return #boolean If true, mission is NOT over yet.
|
||||||
@@ -4765,6 +4839,8 @@ end
|
|||||||
-- @return #boolean If `true`, all groups are done with the mission.
|
-- @return #boolean If `true`, all groups are done with the mission.
|
||||||
function AUFTRAG:CheckGroupsDone()
|
function AUFTRAG:CheckGroupsDone()
|
||||||
|
|
||||||
|
local fsmState = self:GetState()
|
||||||
|
|
||||||
-- Check status of all OPS groups.
|
-- Check status of all OPS groups.
|
||||||
for groupname,data in pairs(self.groupdata) do
|
for groupname,data in pairs(self.groupdata) do
|
||||||
local groupdata=data --#AUFTRAG.GroupData
|
local groupdata=data --#AUFTRAG.GroupData
|
||||||
@@ -4822,6 +4898,11 @@ function AUFTRAG:CheckGroupsDone()
|
|||||||
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()))
|
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
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if (self:IsStarted() or self:IsExecuting()) and (fsmState == AUFTRAG.Status.STARTED or fsmState == AUFTRAG.Status.EXECUTING) and self:CountOpsGroups()>0 then
|
||||||
|
self:T(self.lid..string.format("CheckGroupsDone: Mission is STARTED state %s [FSM=%s] and count of alive OPSGROUP > zero. Mission NOT DONE!", self.status, self:GetState()))
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@@ -5160,7 +5241,7 @@ function AUFTRAG:onafterSuccess(From, Event, To)
|
|||||||
|
|
||||||
-- Repeat mission.
|
-- 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: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
|
else
|
||||||
|
|
||||||
@@ -5202,7 +5283,7 @@ function AUFTRAG:onafterFailed(From, Event, To)
|
|||||||
|
|
||||||
-- Repeat mission.
|
-- 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: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
|
else
|
||||||
|
|
||||||
@@ -6110,7 +6191,7 @@ function AUFTRAG:GetDCSMissionTask()
|
|||||||
|
|
||||||
local coords = self.engageTarget:GetCoordinates()
|
local coords = self.engageTarget:GetCoordinates()
|
||||||
for _, coord in pairs(coords) do
|
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)
|
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)
|
table.insert(DCStasks, DCStask)
|
||||||
end
|
end
|
||||||
@@ -6150,8 +6231,16 @@ function AUFTRAG:GetDCSMissionTask()
|
|||||||
-----------------
|
-----------------
|
||||||
-- CAP Mission --
|
-- 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)
|
table.insert(self.enrouteTasks, DCStask)
|
||||||
|
|
||||||
@@ -6305,9 +6394,35 @@ function AUFTRAG:GetDCSMissionTask()
|
|||||||
-- Add enroute task SEAD. Disabled that here because the group enganges everything on its route.
|
-- Add enroute task SEAD. Disabled that here because the group enganges everything on its route.
|
||||||
--local DCStask=CONTROLLABLE.EnRouteTaskSEAD(nil, self.TargetType)
|
--local DCStask=CONTROLLABLE.EnRouteTaskSEAD(nil, self.TargetType)
|
||||||
--table.insert(self.enrouteTasks, DCStask)
|
--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
|
elseif self.type==AUFTRAG.Type.STRIKE then
|
||||||
|
|
||||||
--------------------
|
--------------------
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Author: **applevangelist**
|
-- ### Author: **applevangelist**
|
||||||
-- @date Last Update Jan 2025
|
-- @date Last Update July 2025
|
||||||
-- @module Ops.AWACS
|
-- @module Ops.AWACS
|
||||||
-- @image OPS_AWACS.jpg
|
-- @image OPS_AWACS.jpg
|
||||||
|
|
||||||
@@ -237,7 +237,7 @@ do
|
|||||||
-- -- Callsign will be "Focus". We'll be a Angels 30, doing 300 knots, orbit leg to 88deg with a length of 25nm.
|
-- -- 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)
|
-- testawacs:SetAwacsDetails(CALLSIGN.AWACS.Focus,1,30,300,88,25)
|
||||||
-- -- Set up SRS on port 5010 - change the below to your path and port
|
-- -- 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"
|
-- -- 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"))
|
-- 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.
|
-- -- Our CAP flight will have the callsign "Ford", we want 4 AI planes, Time-On-Station is four hours, doing 300 kn IAS.
|
||||||
@@ -255,7 +255,7 @@ do
|
|||||||
-- -- The CAP station zone is called "Fremont". We will be on 255 AM. Note the Orbit Zone is given as *nil* in the `New()`-Statement
|
-- -- 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 )
|
-- 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
|
-- -- 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"
|
-- -- 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"))
|
-- 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.
|
-- -- Our CAP flight will have the callsign "Ford", we want 4 AI planes, Time-On-Station is four hours, doing 300 kn IAS.
|
||||||
@@ -509,7 +509,7 @@ do
|
|||||||
-- @field #AWACS
|
-- @field #AWACS
|
||||||
AWACS = {
|
AWACS = {
|
||||||
ClassName = "AWACS", -- #string
|
ClassName = "AWACS", -- #string
|
||||||
version = "0.2.71", -- #string
|
version = "0.2.73", -- #string
|
||||||
lid = "", -- #string
|
lid = "", -- #string
|
||||||
coalition = coalition.side.BLUE, -- #number
|
coalition = coalition.side.BLUE, -- #number
|
||||||
coalitiontxt = "blue", -- #string
|
coalitiontxt = "blue", -- #string
|
||||||
@@ -1123,7 +1123,7 @@ function AWACS:New(Name,AirWing,Coalition,AirbaseName,AwacsOrbit,OpsZone,Station
|
|||||||
self.EscortMissionReplacement = {}
|
self.EscortMissionReplacement = {}
|
||||||
|
|
||||||
-- SRS
|
-- SRS
|
||||||
self.PathToSRS = "C:\\Program Files\\DCS-SimpleRadio-Standalone"
|
self.PathToSRS = "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio"
|
||||||
self.Gender = "female"
|
self.Gender = "female"
|
||||||
self.Culture = "en-GB"
|
self.Culture = "en-GB"
|
||||||
self.Voice = nil
|
self.Voice = nil
|
||||||
@@ -1242,6 +1242,8 @@ function AWACS:New(Name,AirWing,Coalition,AirbaseName,AwacsOrbit,OpsZone,Station
|
|||||||
self:AddTransition("*", "Intercept", "*")
|
self:AddTransition("*", "Intercept", "*")
|
||||||
self:AddTransition("*", "InterceptSuccess", "*")
|
self:AddTransition("*", "InterceptSuccess", "*")
|
||||||
self:AddTransition("*", "InterceptFailure", "*")
|
self:AddTransition("*", "InterceptFailure", "*")
|
||||||
|
self:AddTransition("*", "VIDSuccess", "*")
|
||||||
|
self:AddTransition("*", "VIDFailure", "*")
|
||||||
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
|
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
|
||||||
|
|
||||||
|
|
||||||
@@ -1365,18 +1367,38 @@ function AWACS:New(Name,AirWing,Coalition,AirbaseName,AwacsOrbit,OpsZone,Station
|
|||||||
-- @param #string To To state.
|
-- @param #string To To state.
|
||||||
|
|
||||||
--- On After "InterceptSuccess" event. Intercept successful.
|
--- On After "InterceptSuccess" event. Intercept successful.
|
||||||
-- @function [parent=#AWACS] OnAfterIntercept
|
-- @function [parent=#AWACS] OnAfterInterceptSuccess
|
||||||
-- @param #AWACS self
|
-- @param #AWACS self
|
||||||
-- @param #string From From state.
|
-- @param #string From From state.
|
||||||
-- @param #string Event Event.
|
-- @param #string Event Event.
|
||||||
-- @param #string To To state.
|
-- @param #string To To state.
|
||||||
|
|
||||||
--- On After "InterceptFailure" event. Intercept failure.
|
--- On After "InterceptFailure" event. Intercept failure.
|
||||||
-- @function [parent=#AWACS] OnAfterIntercept
|
-- @function [parent=#AWACS] OnAfterInterceptFailure
|
||||||
-- @param #AWACS self
|
-- @param #AWACS self
|
||||||
-- @param #string From From state.
|
-- @param #string From From state.
|
||||||
-- @param #string Event Event.
|
-- @param #string Event Event.
|
||||||
-- @param #string To To state.
|
-- @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
|
return self
|
||||||
end
|
end
|
||||||
@@ -1574,6 +1596,16 @@ function AWACS:SetLocale(Locale)
|
|||||||
return self
|
return self
|
||||||
end
|
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.
|
--- [User] Set the max mission range flights can be away from their home base.
|
||||||
-- @param #AWACS self
|
-- @param #AWACS self
|
||||||
-- @param #number NM Distance in nautical miles
|
-- @param #number NM Distance in nautical miles
|
||||||
@@ -1999,7 +2031,9 @@ function AWACS:SetAdditionalZone(Zone, Draw)
|
|||||||
self.BorderZone = Zone
|
self.BorderZone = Zone
|
||||||
if self.debug then
|
if self.debug then
|
||||||
Zone:DrawZone(self.coalition,{1,0.64,0},1,{1,0.64,0},0.2,1,true)
|
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
|
elseif Draw then
|
||||||
Zone:DrawZone(self.coalition,{1,0.64,0},1,{1,0.64,0},0.2,1,true)
|
Zone:DrawZone(self.coalition,{1,0.64,0},1,{1,0.64,0},0.2,1,true)
|
||||||
end
|
end
|
||||||
@@ -2019,7 +2053,9 @@ function AWACS:SetRejectionZone(Zone,Draw)
|
|||||||
--MARKER:New(Zone:GetCoordinate(),"Rejection Zone"):ToAll()
|
--MARKER:New(Zone:GetCoordinate(),"Rejection Zone"):ToAll()
|
||||||
elseif self.debug then
|
elseif self.debug then
|
||||||
Zone:DrawZone(self.coalition,{1,0.64,0},1,{1,0.64,0},0.2,1,true)
|
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
|
end
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -2091,7 +2127,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.
|
--- [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 #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 Gender Defaults to "male"
|
||||||
-- @param #string Culture Defaults to "en-US"
|
-- @param #string Culture Defaults to "en-US"
|
||||||
-- @param #number Port Defaults to 5002
|
-- @param #number Port Defaults to 5002
|
||||||
@@ -2104,7 +2140,7 @@ end
|
|||||||
-- @return #AWACS self
|
-- @return #AWACS self
|
||||||
function AWACS:SetSRS(PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,AccessKey,Backend)
|
function AWACS:SetSRS(PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,AccessKey,Backend)
|
||||||
self:T(self.lid.."SetSRS")
|
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.Gender = Gender or MSRS.gender or "male"
|
||||||
self.Culture = Culture or MSRS.culture or "en-US"
|
self.Culture = Culture or MSRS.culture or "en-US"
|
||||||
self.Port = Port or MSRS.port or 5002
|
self.Port = Port or MSRS.port or 5002
|
||||||
@@ -3263,12 +3299,14 @@ function AWACS:_VID(Group,Declaration)
|
|||||||
local vidpos = self.gettext:GetEntry("VIDPOS",self.locale)
|
local vidpos = self.gettext:GetEntry("VIDPOS",self.locale)
|
||||||
text = string.format(vidpos,Callsign,self.callsigntxt, Declaration)
|
text = string.format(vidpos,Callsign,self.callsigntxt, Declaration)
|
||||||
self:T(text)
|
self:T(text)
|
||||||
|
self:__VIDSuccess(3,GID,group,cluster)
|
||||||
else
|
else
|
||||||
-- too far away
|
-- too far away
|
||||||
self:T("Contact VID not close enough")
|
self:T("Contact VID not close enough")
|
||||||
local vidneg = self.gettext:GetEntry("VIDNEG",self.locale)
|
local vidneg = self.gettext:GetEntry("VIDNEG",self.locale)
|
||||||
text = string.format(vidneg,Callsign,self.callsigntxt)
|
text = string.format(vidneg,Callsign,self.callsigntxt)
|
||||||
self:T(text)
|
self:T(text)
|
||||||
|
self:__VIDFailure(3,GID,group,cluster)
|
||||||
end
|
end
|
||||||
self:_NewRadioEntry(text,text,GID,Outcome,true,true,false,true)
|
self:_NewRadioEntry(text,text,GID,Outcome,true,true,false,true)
|
||||||
end
|
end
|
||||||
@@ -4070,10 +4108,14 @@ function AWACS:_CreateAnchorStackFromMarker(Name,Coord)
|
|||||||
if self.debug then
|
if self.debug then
|
||||||
AnchorStackOne.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true)
|
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())
|
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
|
else
|
||||||
local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
|
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
|
end
|
||||||
|
|
||||||
self.AnchorStacks:Push(AnchorStackOne,newname)
|
self.AnchorStacks:Push(AnchorStackOne,newname)
|
||||||
@@ -4116,10 +4158,14 @@ function AWACS:_CreateAnchorStack()
|
|||||||
--self.AnchorStacks:Flush()
|
--self.AnchorStacks:Flush()
|
||||||
AnchorStackOne.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true)
|
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())
|
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
|
else
|
||||||
local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
|
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
|
end
|
||||||
self.AnchorStacks:Push(AnchorStackOne,newname)
|
self.AnchorStacks:Push(AnchorStackOne,newname)
|
||||||
else
|
else
|
||||||
@@ -4143,10 +4189,14 @@ function AWACS:_CreateAnchorStack()
|
|||||||
if self.debug then
|
if self.debug then
|
||||||
AnchorStackOne.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true)
|
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())
|
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
|
else
|
||||||
local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
|
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
|
end
|
||||||
self.AnchorStacks:Push(AnchorStackOne,newname)
|
self.AnchorStacks:Push(AnchorStackOne,newname)
|
||||||
end
|
end
|
||||||
@@ -5078,10 +5128,14 @@ function AWACS:AddCAPAirWing(AirWing,Zone)
|
|||||||
if self.debug then
|
if self.debug then
|
||||||
AnchorStackOne.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true)
|
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())
|
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
|
else
|
||||||
local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
|
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
|
end
|
||||||
self.AnchorStacks:Push(AnchorStackOne,newname)
|
self.AnchorStacks:Push(AnchorStackOne,newname)
|
||||||
AirWing.HasOwnStation = true
|
AirWing.HasOwnStation = true
|
||||||
@@ -5924,23 +5978,35 @@ function AWACS:onafterStart(From, Event, To)
|
|||||||
self.OpsZone:DrawZone(self.coalition,{1,0,0},1,{1,0,0},0.2,5,true)
|
self.OpsZone:DrawZone(self.coalition,{1,0,0},1,{1,0,0},0.2,5,true)
|
||||||
local AOCoordString = self.AOCoordinate:ToStringLLDDM()
|
local AOCoordString = self.AOCoordinate:ToStringLLDDM()
|
||||||
local Rocktag = string.format("FEZ: %s\nBulls Coordinate: %s",self.AOName,AOCoordString)
|
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)
|
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())
|
local stationtag = string.format("Station: %s\nCoordinate: %s",self.StationZoneName,self.StationZone:GetCoordinate():ToStringLLDDM())
|
||||||
if not self.GCI then
|
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)
|
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
|
end
|
||||||
else
|
else
|
||||||
local AOCoordString = self.AOCoordinate:ToStringLLDDM()
|
local AOCoordString = self.AOCoordinate:ToStringLLDDM()
|
||||||
local Rocktag = string.format("FEZ: %s\nBulls Coordinate: %s",self.AOName,AOCoordString)
|
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
|
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
|
end
|
||||||
local stationtag = string.format("Station: %s\nCoordinate: %s",self.StationZoneName,self.StationZone:GetCoordinate():ToStringLLDDM())
|
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
|
end
|
||||||
|
|
||||||
if not self.GCI then
|
if not self.GCI then
|
||||||
|
|||||||
@@ -599,7 +599,80 @@ function BRIGADE:onafterStatus(From, Event, To)
|
|||||||
text=text..string.format("\n* %s: spawned=%s", asset.spawngroupname, tostring(asset.spawned))
|
text=text..string.format("\n* %s: spawned=%s", asset.spawngroupname, tostring(asset.spawned))
|
||||||
end
|
end
|
||||||
self:I(self.lid..text)
|
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
|
end
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -25,7 +25,7 @@
|
|||||||
-- @module Ops.CTLD
|
-- @module Ops.CTLD
|
||||||
-- @image OPS_CTLD.jpg
|
-- @image OPS_CTLD.jpg
|
||||||
|
|
||||||
-- Last Update May 2025
|
-- Last Update Oct 2025
|
||||||
|
|
||||||
|
|
||||||
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -147,6 +147,7 @@ CTLD_CARGO = {
|
|||||||
Location = ZONE:New(Location)
|
Location = ZONE:New(Location)
|
||||||
end
|
end
|
||||||
self.Location = Location
|
self.Location = Location
|
||||||
|
self.NoMoveToZone = false
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -783,6 +784,7 @@ do
|
|||||||
-- my_ctld:AddCratesCargo("Humvee",{"Humvee"},CTLD_CARGO.Enum.VEHICLE,2,2775,10)
|
-- my_ctld:AddCratesCargo("Humvee",{"Humvee"},CTLD_CARGO.Enum.VEHICLE,2,2775,10)
|
||||||
-- -- additionally, you can limit **where** the stock is available (one location only!) - this one is available in a zone called "Vehicle Store".
|
-- -- additionally, you can limit **where** the stock is available (one location only!) - this one is available in a zone called "Vehicle Store".
|
||||||
-- my_ctld:AddCratesCargo("Humvee",{"Humvee"},CTLD_CARGO.Enum.VEHICLE,2,2775,10,nil,nil,"Vehicle Store")
|
-- my_ctld:AddCratesCargo("Humvee",{"Humvee"},CTLD_CARGO.Enum.VEHICLE,2,2775,10,nil,nil,"Vehicle Store")
|
||||||
|
-- -- Tip: if you want the spawned/built group NOT to move to a MOVE zone, replace AddCratesCargo with AddCratesCargoNoMove (same parameters).
|
||||||
--
|
--
|
||||||
-- -- add infantry unit called "Forward Ops Base" using template "FOB", of type FOB, size 4, i.e. needs four crates to be build:
|
-- -- add infantry unit called "Forward Ops Base" using template "FOB", of type FOB, size 4, i.e. needs four crates to be build:
|
||||||
-- my_ctld:AddCratesCargo("Forward Ops Base",{"FOB"},CTLD_CARGO.Enum.FOB,4)
|
-- my_ctld:AddCratesCargo("Forward Ops Base",{"FOB"},CTLD_CARGO.Enum.FOB,4)
|
||||||
@@ -867,7 +869,9 @@ do
|
|||||||
-- my_ctld.TroopUnloadDistHoverHook = 5 -- When hovering, unload troops this far behind the Chinook
|
-- my_ctld.TroopUnloadDistHoverHook = 5 -- When hovering, unload troops this far behind the Chinook
|
||||||
-- my_ctld.showstockinmenuitems = false -- When set to true, the menu lines will also show the remaining items in stock (that is, if you set any), downside is that the menu for all will be build every 30 seconds anew.
|
-- my_ctld.showstockinmenuitems = false -- When set to true, the menu lines will also show the remaining items in stock (that is, if you set any), downside is that the menu for all will be build every 30 seconds anew.
|
||||||
-- my_ctld.onestepmenu = false -- When set to true, the menu will create Drop and build, Get and load, Pack and remove, Pack and load, Pack. it will be a 1 step solution.
|
-- my_ctld.onestepmenu = false -- When set to true, the menu will create Drop and build, Get and load, Pack and remove, Pack and load, Pack. it will be a 1 step solution.
|
||||||
--
|
-- my_ctld.VehicleMoveFormation = AI.Task.VehicleFormation.VEE -- When a group moves to a MOVE zone, then it takes this formation. Can be a table of formations, which are then randomly chosen. Defaults to "Vee".
|
||||||
|
-- my_ctld.validateAndRepositionUnits = false -- Uses Disposition and other logic to find better ground positions for ground units avoiding trees, water, roads, runways, map scenery, statics and other units in the area. (Default is false)
|
||||||
|
--
|
||||||
-- ## 2.1 CH-47 Chinook support
|
-- ## 2.1 CH-47 Chinook support
|
||||||
--
|
--
|
||||||
-- The Chinook comes with the option to use the ground crew menu to load and unload cargo into the Helicopter itself for better immersion. As well, it can sling-load cargo from ground. The cargo you can actually **create**
|
-- The Chinook comes with the option to use the ground crew menu to load and unload cargo into the Helicopter itself for better immersion. As well, it can sling-load cargo from ground. The cargo you can actually **create**
|
||||||
@@ -1294,6 +1298,7 @@ CTLD = {
|
|||||||
LoadedGroupsTable = {},
|
LoadedGroupsTable = {},
|
||||||
keeploadtable = true,
|
keeploadtable = true,
|
||||||
allowCATransport = false,
|
allowCATransport = false,
|
||||||
|
VehicleMoveFormation = AI.Task.VehicleFormation.VEE,
|
||||||
}
|
}
|
||||||
|
|
||||||
------------------------------
|
------------------------------
|
||||||
@@ -1393,6 +1398,7 @@ CTLD.UnitTypeCapabilities = {
|
|||||||
["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64, length = 25, cargoweightlimit = 19000}, -- 19t cargo, 64 paratroopers.
|
["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64, length = 25, cargoweightlimit = 19000}, -- 19t cargo, 64 paratroopers.
|
||||||
--Actually it's longer, but the center coord is off-center of the model.
|
--Actually it's longer, but the center coord is off-center of the model.
|
||||||
["UH-60L"] = {type="UH-60L", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats
|
["UH-60L"] = {type="UH-60L", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats
|
||||||
|
["UH-60L_DAP"] = {type="UH-60L_DAP", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 16, cargoweightlimit = 500}, -- UH-60L DAP is an attack helo but can do limited CSAR and CTLD
|
||||||
["MH-60R"] = {type="MH-60R", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats
|
["MH-60R"] = {type="MH-60R", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats
|
||||||
["SH-60B"] = {type="SH-60B", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats
|
["SH-60B"] = {type="SH-60B", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats
|
||||||
["AH-64D_BLK_II"] = {type="AH-64D_BLK_II", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 17, cargoweightlimit = 200}, -- 2 ppl **outside** the helo
|
["AH-64D_BLK_II"] = {type="AH-64D_BLK_II", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 17, cargoweightlimit = 200}, -- 2 ppl **outside** the helo
|
||||||
@@ -1414,7 +1420,7 @@ CTLD.FixedWingTypes = {
|
|||||||
|
|
||||||
--- CTLD class version.
|
--- CTLD class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
CTLD.version="1.3.34"
|
CTLD.version="1.3.38"
|
||||||
|
|
||||||
--- Instantiate a new CTLD.
|
--- Instantiate a new CTLD.
|
||||||
-- @param #CTLD self
|
-- @param #CTLD self
|
||||||
@@ -1481,6 +1487,7 @@ function CTLD:New(Coalition, Prefixes, Alias)
|
|||||||
self:AddTransition("*", "CratesRepaired", "*") -- CTLD repair event.
|
self:AddTransition("*", "CratesRepaired", "*") -- CTLD repair event.
|
||||||
self:AddTransition("*", "CratesBuildStarted", "*") -- CTLD build event.
|
self:AddTransition("*", "CratesBuildStarted", "*") -- CTLD build event.
|
||||||
self:AddTransition("*", "CratesRepairStarted", "*") -- CTLD repair event.
|
self:AddTransition("*", "CratesRepairStarted", "*") -- CTLD repair event.
|
||||||
|
self:AddTransition("*", "CratesPacked", "*") -- CTLD repack event.
|
||||||
self:AddTransition("*", "HelicopterLost", "*") -- CTLD lost event.
|
self:AddTransition("*", "HelicopterLost", "*") -- CTLD lost event.
|
||||||
self:AddTransition("*", "Load", "*") -- CTLD load event.
|
self:AddTransition("*", "Load", "*") -- CTLD load event.
|
||||||
self:AddTransition("*", "Loaded", "*") -- CTLD load event.
|
self:AddTransition("*", "Loaded", "*") -- CTLD load event.
|
||||||
@@ -1551,15 +1558,20 @@ function CTLD:New(Coalition, Prefixes, Alias)
|
|||||||
self.smokedistance = 2000
|
self.smokedistance = 2000
|
||||||
self.movetroopstowpzone = true
|
self.movetroopstowpzone = true
|
||||||
self.movetroopsdistance = 5000
|
self.movetroopsdistance = 5000
|
||||||
|
self.returntroopstobase = true -- if set to false, troops would stay after deployment inside a load zone.
|
||||||
self.troopdropzoneradius = 100
|
self.troopdropzoneradius = 100
|
||||||
|
|
||||||
|
self.VehicleMoveFormation = AI.Task.VehicleFormation.VEE
|
||||||
|
|
||||||
-- added support Hercules Mod
|
-- added support Hercules Mod
|
||||||
self.enableHercules = false -- deprecated
|
self.enableHercules = false -- deprecated
|
||||||
self.enableFixedWing = false
|
self.enableFixedWing = false
|
||||||
self.FixedMinAngels = 165 -- for troop/cargo drop via chute
|
self.FixedMinAngels = 165 -- for troop/cargo drop via chute
|
||||||
self.FixedMaxAngels = 2000 -- for troop/cargo drop via chute
|
self.FixedMaxAngels = 2000 -- for troop/cargo drop via chute
|
||||||
self.FixedMaxSpeed = 77 -- 280 kph or 150kn eq 77 mps
|
self.FixedMaxSpeed = 77 -- 280 kph or 150kn eq 77 mps
|
||||||
|
|
||||||
|
self.validateAndRepositionUnits = false -- 280 kph or 150kn eq 77 mps
|
||||||
|
|
||||||
-- message suppression
|
-- message suppression
|
||||||
self.suppressmessages = false
|
self.suppressmessages = false
|
||||||
|
|
||||||
@@ -1759,6 +1771,17 @@ function CTLD:New(Coalition, Prefixes, Alias)
|
|||||||
-- @param Wrapper.Unit#UNIT Unit Unit Object.
|
-- @param Wrapper.Unit#UNIT Unit Unit Object.
|
||||||
-- @param Wrapper.Group#GROUP Vehicle The #GROUP object of the vehicle or FOB repaired.
|
-- @param Wrapper.Group#GROUP Vehicle The #GROUP object of the vehicle or FOB repaired.
|
||||||
-- @return #CTLD self
|
-- @return #CTLD self
|
||||||
|
|
||||||
|
--- FSM Function OnBeforeCratesPacked.
|
||||||
|
-- @function [parent=#CTLD] OnBeforeCratesPacked
|
||||||
|
-- @param #CTLD self
|
||||||
|
-- @param #string From State.
|
||||||
|
-- @param #string Event Trigger.
|
||||||
|
-- @param #string To State.
|
||||||
|
-- @param Wrapper.Group#GROUP Group Group Object.
|
||||||
|
-- @param Wrapper.Unit#UNIT Unit Unit Object.
|
||||||
|
-- @param #CTLD_CARGO Cargo Cargo crate that was repacked.
|
||||||
|
-- @return #CTLD self
|
||||||
|
|
||||||
--- FSM Function OnBeforeTroopsRTB.
|
--- FSM Function OnBeforeTroopsRTB.
|
||||||
-- @function [parent=#CTLD] OnBeforeTroopsRTB
|
-- @function [parent=#CTLD] OnBeforeTroopsRTB
|
||||||
@@ -1846,6 +1869,7 @@ function CTLD:New(Coalition, Prefixes, Alias)
|
|||||||
-- @param #string To State.
|
-- @param #string To State.
|
||||||
-- @param Wrapper.Group#GROUP Group Group Object.
|
-- @param Wrapper.Group#GROUP Group Group Object.
|
||||||
-- @param Wrapper.Unit#UNIT Unit Unit Object.
|
-- @param Wrapper.Unit#UNIT Unit Unit Object.
|
||||||
|
-- @param CargoName The name of the cargo being built.
|
||||||
-- @return #CTLD self
|
-- @return #CTLD self
|
||||||
|
|
||||||
--- FSM Function OnAfterCratesRepairStarted. Info event that a repair has been started.
|
--- FSM Function OnAfterCratesRepairStarted. Info event that a repair has been started.
|
||||||
@@ -1888,6 +1912,17 @@ function CTLD:New(Coalition, Prefixes, Alias)
|
|||||||
-- @param Wrapper.Unit#UNIT Unit Unit Object.
|
-- @param Wrapper.Unit#UNIT Unit Unit Object.
|
||||||
-- @param Wrapper.Group#GROUP Vehicle The #GROUP object of the vehicle or FOB repaired.
|
-- @param Wrapper.Group#GROUP Vehicle The #GROUP object of the vehicle or FOB repaired.
|
||||||
-- @return #CTLD self
|
-- @return #CTLD self
|
||||||
|
|
||||||
|
--- FSM Function OnAfterCratesPacked.
|
||||||
|
-- @function [parent=#CTLD] OnAfterCratesPacked
|
||||||
|
-- @param #CTLD self
|
||||||
|
-- @param #string From State.
|
||||||
|
-- @param #string Event Trigger.
|
||||||
|
-- @param #string To State.
|
||||||
|
-- @param Wrapper.Group#GROUP Group Group Object.
|
||||||
|
-- @param Wrapper.Unit#UNIT Unit Unit Object.
|
||||||
|
-- @param #CTLD_CARGO Cargo Cargo crate that was repacked.
|
||||||
|
-- @return #CTLD self
|
||||||
|
|
||||||
--- FSM Function OnAfterTroopsRTB.
|
--- FSM Function OnAfterTroopsRTB.
|
||||||
-- @function [parent=#CTLD] OnAfterTroopsRTB
|
-- @function [parent=#CTLD] OnAfterTroopsRTB
|
||||||
@@ -2075,6 +2110,9 @@ function CTLD:_EventHandler(EventData)
|
|||||||
local _group = event.IniGroup
|
local _group = event.IniGroup
|
||||||
local _unit = event.IniUnit
|
local _unit = event.IniUnit
|
||||||
self:_RefreshLoadCratesMenu(_group, _unit)
|
self:_RefreshLoadCratesMenu(_group, _unit)
|
||||||
|
if self:IsFixedWing(_unit) and self.enableFixedWing then
|
||||||
|
self:_RefreshDropCratesMenu(_group, _unit)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
elseif event.id == EVENTS.PlayerLeaveUnit or event.id == EVENTS.UnitLost then
|
elseif event.id == EVENTS.PlayerLeaveUnit or event.id == EVENTS.UnitLost then
|
||||||
-- remove from pilot table
|
-- remove from pilot table
|
||||||
@@ -2824,8 +2862,12 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop, pack)
|
|||||||
if cratedistance > self.CrateDistance then cratedistance = self.CrateDistance end
|
if cratedistance > self.CrateDistance then cratedistance = self.CrateDistance end
|
||||||
-- altered heading logic
|
-- altered heading logic
|
||||||
-- DONE: right standard deviation?
|
-- DONE: right standard deviation?
|
||||||
rheading = UTILS.RandomGaussian(0,30,-90,90,100)
|
if self:IsUnitInAir(Unit) and self:IsFixedWing(Unit) then
|
||||||
rheading = math.fmod((heading + rheading), 360)
|
rheading = math.random(20,60)
|
||||||
|
else
|
||||||
|
rheading = UTILS.RandomGaussian(0, 30, -90, 90, 100)
|
||||||
|
end
|
||||||
|
rheading=math.fmod((heading+rheading),360)
|
||||||
cratecoord = position:Translate(cratedistance,rheading)
|
cratecoord = position:Translate(cratedistance,rheading)
|
||||||
else
|
else
|
||||||
cratedistance = (row-1)*6
|
cratedistance = (row-1)*6
|
||||||
@@ -3290,6 +3332,7 @@ function CTLD:_LoadCratesNearby(Group, Unit)
|
|||||||
self:_RefreshLoadCratesMenu(Group, Unit)
|
self:_RefreshLoadCratesMenu(Group, Unit)
|
||||||
-- clean up real world crates
|
-- clean up real world crates
|
||||||
self:_CleanupTrackedCrates(crateidsloaded)
|
self:_CleanupTrackedCrates(crateidsloaded)
|
||||||
|
self:__CratesPickedUp(1, Group, Unit, loaded.Cargo)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
@@ -3636,7 +3679,7 @@ function CTLD:_UnloadTroops(Group, Unit)
|
|||||||
inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP)
|
inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP)
|
||||||
end
|
end
|
||||||
if inzone then
|
if inzone then
|
||||||
droppingatbase = true
|
droppingatbase = self.returntroopstobase
|
||||||
end
|
end
|
||||||
-- check for hover unload
|
-- check for hover unload
|
||||||
local hoverunload = self:IsCorrectHover(Unit) --if true we\'re hovering in parameters
|
local hoverunload = self:IsCorrectHover(Unit) --if true we\'re hovering in parameters
|
||||||
@@ -3700,6 +3743,7 @@ function CTLD:_UnloadTroops(Group, Unit)
|
|||||||
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
|
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
|
||||||
:InitDelayOff()
|
:InitDelayOff()
|
||||||
:InitSetUnitAbsolutePositions(Positions)
|
:InitSetUnitAbsolutePositions(Positions)
|
||||||
|
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
|
||||||
:OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end)
|
:OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end)
|
||||||
:SpawnFromVec2(randomcoord:GetVec2())
|
:SpawnFromVec2(randomcoord:GetVec2())
|
||||||
self:__TroopsDeployed(1, Group, Unit, self.DroppedTroops[self.TroopCounter],type)
|
self:__TroopsDeployed(1, Group, Unit, self.DroppedTroops[self.TroopCounter],type)
|
||||||
@@ -3962,7 +4006,7 @@ function CTLD:_BuildCrates(Group, Unit,Engineering,MultiDrop)
|
|||||||
local buildtimer = TIMER:New(self._BuildObjectFromCrates,self,Group,Unit,build,false,Group:GetCoordinate(),MultiDrop)
|
local buildtimer = TIMER:New(self._BuildObjectFromCrates,self,Group,Unit,build,false,Group:GetCoordinate(),MultiDrop)
|
||||||
buildtimer:Start(self.buildtime)
|
buildtimer:Start(self.buildtime)
|
||||||
self:_SendMessage(string.format("Build started, ready in %d seconds!",self.buildtime),15,false,Group)
|
self:_SendMessage(string.format("Build started, ready in %d seconds!",self.buildtime),15,false,Group)
|
||||||
self:__CratesBuildStarted(1,Group,Unit)
|
self:__CratesBuildStarted(1,Group,Unit,build.Name)
|
||||||
self:_RefreshDropTroopsMenu(Group,Unit)
|
self:_RefreshDropTroopsMenu(Group,Unit)
|
||||||
else
|
else
|
||||||
self:_BuildObjectFromCrates(Group,Unit,build,false,nil,MultiDrop)
|
self:_BuildObjectFromCrates(Group,Unit,build,false,nil,MultiDrop)
|
||||||
@@ -4004,6 +4048,7 @@ function CTLD:_PackCratesNearby(Group, Unit)
|
|||||||
_Group:Destroy() -- if a match is found destroy the Wrapper.Group#GROUP near the player
|
_Group:Destroy() -- if a match is found destroy the Wrapper.Group#GROUP near the player
|
||||||
self:_GetCrates(Group, Unit, _entry, nil, false, true) -- spawn the appropriate crates near the player
|
self:_GetCrates(Group, Unit, _entry, nil, false, true) -- spawn the appropriate crates near the player
|
||||||
self:_RefreshLoadCratesMenu(Group,Unit) -- call the refresher to show the crates in the menu
|
self:_RefreshLoadCratesMenu(Group,Unit) -- call the refresher to show the crates in the menu
|
||||||
|
self:__CratesPacked(1,Group,Unit,_entry)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -4145,11 +4190,13 @@ function CTLD:_BuildObjectFromCrates(Group,Unit,Build,Repair,RepairLocation,Mult
|
|||||||
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
|
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
|
||||||
--:InitRandomizeUnits(true,20,2)
|
--:InitRandomizeUnits(true,20,2)
|
||||||
:InitDelayOff()
|
:InitDelayOff()
|
||||||
|
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
|
||||||
:OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end)
|
:OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end)
|
||||||
:SpawnFromVec2(randomcoord)
|
:SpawnFromVec2(randomcoord)
|
||||||
else -- don't random position of e.g. SAM units build as FOB
|
else -- don't random position of e.g. SAM units build as FOB
|
||||||
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
|
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
|
||||||
:InitDelayOff()
|
:InitDelayOff()
|
||||||
|
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
|
||||||
:OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end)
|
:OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end)
|
||||||
:SpawnFromVec2(randomcoord)
|
:SpawnFromVec2(randomcoord)
|
||||||
end
|
end
|
||||||
@@ -4165,6 +4212,17 @@ function CTLD:_BuildObjectFromCrates(Group,Unit,Build,Repair,RepairLocation,Mult
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- (Internal) Function to get a vehicle formation for a moving group
|
||||||
|
-- @param #CTLD self
|
||||||
|
-- @return #string Formation
|
||||||
|
function CTLD:_GetVehicleFormation()
|
||||||
|
local VehicleMoveFormation = self.VehicleMoveFormation or AI.Task.VehicleFormation.VEE
|
||||||
|
if type(self.VehicleMoveFormation)=="table" then
|
||||||
|
VehicleMoveFormation = self.VehicleMoveFormation[math.random(1,#self.VehicleMoveFormation)]
|
||||||
|
end
|
||||||
|
return VehicleMoveFormation
|
||||||
|
end
|
||||||
|
|
||||||
--- (Internal) Function to move group to WP zone.
|
--- (Internal) Function to move group to WP zone.
|
||||||
-- @param #CTLD self
|
-- @param #CTLD self
|
||||||
-- @param Wrapper.Group#GROUP Group The Group to move.
|
-- @param Wrapper.Group#GROUP Group The Group to move.
|
||||||
@@ -4179,18 +4237,20 @@ function CTLD:_MoveGroupToZone(Group)
|
|||||||
-- yes, we can ;)
|
-- yes, we can ;)
|
||||||
local groupname = Group:GetName()
|
local groupname = Group:GetName()
|
||||||
local zonecoord = zone:GetRandomCoordinate(20,125) -- Core.Point#COORDINATE
|
local zonecoord = zone:GetRandomCoordinate(20,125) -- Core.Point#COORDINATE
|
||||||
local coordinate = zonecoord:GetVec2()
|
local formation = self:_GetVehicleFormation()
|
||||||
|
--local coordinate = zonecoord:GetVec2()
|
||||||
Group:SetAIOn()
|
Group:SetAIOn()
|
||||||
Group:OptionAlarmStateAuto()
|
Group:OptionAlarmStateAuto()
|
||||||
Group:OptionDisperseOnAttack(30)
|
Group:OptionDisperseOnAttack(30)
|
||||||
Group:OptionROEOpenFirePossible()
|
Group:OptionROEOpenFire()
|
||||||
Group:RouteToVec2(coordinate,5)
|
Group:RouteGroundTo(zonecoord,25,formation)
|
||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- (Internal) Housekeeping - Cleanup crates when build
|
--- (Internal) Housekeeping - Cleanup crates when build
|
||||||
-- @param #CTLD self
|
-- @param #CTLD self
|
||||||
|
--
|
||||||
-- @param #table Crates Table of #CTLD_CARGO objects near the unit.
|
-- @param #table Crates Table of #CTLD_CARGO objects near the unit.
|
||||||
-- @param #CTLD.Buildable Build Table build object.
|
-- @param #CTLD.Buildable Build Table build object.
|
||||||
-- @param #number Number Number of objects in Crates (found) to limit search.
|
-- @param #number Number Number of objects in Crates (found) to limit search.
|
||||||
@@ -4456,7 +4516,8 @@ function CTLD:_RefreshF10Menus()
|
|||||||
end
|
end
|
||||||
for _,cargoObj in pairs(self.Cargo_Crates) do
|
for _,cargoObj in pairs(self.Cargo_Crates) do
|
||||||
if not cargoObj.DontShowInMenu then
|
if not cargoObj.DontShowInMenu then
|
||||||
local txt = string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0)
|
local needed = cargoObj:GetCratesNeeded() or 1
|
||||||
|
local txt = string.format("%d crate%s %s (%dkg)",needed,needed==1 and "" or "s",cargoObj.Name,cargoObj.PerCrateMass or 0)
|
||||||
if cargoObj.Location then txt = txt.."[R]" end
|
if cargoObj.Location then txt = txt.."[R]" end
|
||||||
local stock = cargoObj:GetStock()
|
local stock = cargoObj:GetStock()
|
||||||
if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
|
if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
|
||||||
@@ -4467,7 +4528,8 @@ function CTLD:_RefreshF10Menus()
|
|||||||
end
|
end
|
||||||
for _,cargoObj in pairs(self.Cargo_Statics) do
|
for _,cargoObj in pairs(self.Cargo_Statics) do
|
||||||
if not cargoObj.DontShowInMenu then
|
if not cargoObj.DontShowInMenu then
|
||||||
local txt = string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0)
|
local needed = cargoObj:GetCratesNeeded() or 1
|
||||||
|
local txt = string.format("%d crate%s %s (%dkg)",needed,needed==1 and "" or "s",cargoObj.Name,cargoObj.PerCrateMass or 0)
|
||||||
if cargoObj.Location then txt = txt.."[R]" end
|
if cargoObj.Location then txt = txt.."[R]" end
|
||||||
local stock = cargoObj:GetStock()
|
local stock = cargoObj:GetStock()
|
||||||
if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
|
if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
|
||||||
@@ -4479,7 +4541,8 @@ function CTLD:_RefreshF10Menus()
|
|||||||
else
|
else
|
||||||
for _,cargoObj in pairs(self.Cargo_Crates) do
|
for _,cargoObj in pairs(self.Cargo_Crates) do
|
||||||
if not cargoObj.DontShowInMenu then
|
if not cargoObj.DontShowInMenu then
|
||||||
local txt = string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0)
|
local needed = cargoObj:GetCratesNeeded() or 1
|
||||||
|
local txt = string.format("%d crate%s %s (%dkg)",needed,needed==1 and "" or "s",cargoObj.Name,cargoObj.PerCrateMass or 0)
|
||||||
if cargoObj.Location then txt = txt.."[R]" end
|
if cargoObj.Location then txt = txt.."[R]" end
|
||||||
local stock = cargoObj:GetStock()
|
local stock = cargoObj:GetStock()
|
||||||
if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
|
if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
|
||||||
@@ -4490,7 +4553,8 @@ function CTLD:_RefreshF10Menus()
|
|||||||
end
|
end
|
||||||
for _,cargoObj in pairs(self.Cargo_Statics) do
|
for _,cargoObj in pairs(self.Cargo_Statics) do
|
||||||
if not cargoObj.DontShowInMenu then
|
if not cargoObj.DontShowInMenu then
|
||||||
local txt = string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0)
|
local needed = cargoObj:GetCratesNeeded() or 1
|
||||||
|
local txt = string.format("%d crate%s %s (%dkg)",needed,needed==1 and "" or "s",cargoObj.Name,cargoObj.PerCrateMass or 0)
|
||||||
if cargoObj.Location then txt = txt.."[R]" end
|
if cargoObj.Location then txt = txt.."[R]" end
|
||||||
local stock = cargoObj:GetStock()
|
local stock = cargoObj:GetStock()
|
||||||
if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
|
if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
|
||||||
@@ -4502,14 +4566,15 @@ function CTLD:_RefreshF10Menus()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if self.usesubcats then
|
if self.usesubcats == true then
|
||||||
local subcatmenus = {}
|
local subcatmenus = {}
|
||||||
for catName, _ in pairs(self.subcats) do
|
for catName, _ in pairs(self.subcats) do
|
||||||
subcatmenus[catName] = MENU_GROUP:New(_group, catName, cratesmenu) -- fixed variable case
|
subcatmenus[catName] = MENU_GROUP:New(_group, catName, cratesmenu) -- fixed variable case
|
||||||
end
|
end
|
||||||
for _, cargoObj in pairs(self.Cargo_Crates) do
|
for _, cargoObj in pairs(self.Cargo_Crates) do
|
||||||
if not cargoObj.DontShowInMenu then
|
if not cargoObj.DontShowInMenu then
|
||||||
local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0)
|
local needed = cargoObj:GetCratesNeeded() or 1
|
||||||
|
local txt = string.format("%d crate%s %s (%dkg)", needed, needed==1 and "" or "s", cargoObj.Name, cargoObj.PerCrateMass or 0)
|
||||||
if cargoObj.Location then txt = txt.."[R]" end
|
if cargoObj.Location then txt = txt.."[R]" end
|
||||||
local stock = cargoObj:GetStock()
|
local stock = cargoObj:GetStock()
|
||||||
if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
|
if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
|
||||||
@@ -4518,7 +4583,8 @@ function CTLD:_RefreshF10Menus()
|
|||||||
end
|
end
|
||||||
for _, cargoObj in pairs(self.Cargo_Statics) do
|
for _, cargoObj in pairs(self.Cargo_Statics) do
|
||||||
if not cargoObj.DontShowInMenu then
|
if not cargoObj.DontShowInMenu then
|
||||||
local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0)
|
local needed = cargoObj:GetCratesNeeded() or 1
|
||||||
|
local txt = string.format("%d crate%s %s (%dkg)", needed, needed==1 and "" or "s", cargoObj.Name, cargoObj.PerCrateMass or 0)
|
||||||
if cargoObj.Location then txt = txt.."[R]" end
|
if cargoObj.Location then txt = txt.."[R]" end
|
||||||
local stock = cargoObj:GetStock()
|
local stock = cargoObj:GetStock()
|
||||||
if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
|
if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
|
||||||
@@ -4528,7 +4594,8 @@ function CTLD:_RefreshF10Menus()
|
|||||||
else
|
else
|
||||||
for _, cargoObj in pairs(self.Cargo_Crates) do
|
for _, cargoObj in pairs(self.Cargo_Crates) do
|
||||||
if not cargoObj.DontShowInMenu then
|
if not cargoObj.DontShowInMenu then
|
||||||
local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0)
|
local needed = cargoObj:GetCratesNeeded() or 1
|
||||||
|
local txt = string.format("%d crate%s %s (%dkg)", needed, needed==1 and "" or "s", cargoObj.Name, cargoObj.PerCrateMass or 0)
|
||||||
if cargoObj.Location then txt = txt.."[R]" end
|
if cargoObj.Location then txt = txt.."[R]" end
|
||||||
local stock = cargoObj:GetStock()
|
local stock = cargoObj:GetStock()
|
||||||
if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
|
if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
|
||||||
@@ -4537,7 +4604,8 @@ function CTLD:_RefreshF10Menus()
|
|||||||
end
|
end
|
||||||
for _, cargoObj in pairs(self.Cargo_Statics) do
|
for _, cargoObj in pairs(self.Cargo_Statics) do
|
||||||
if not cargoObj.DontShowInMenu then
|
if not cargoObj.DontShowInMenu then
|
||||||
local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0)
|
local needed = cargoObj:GetCratesNeeded() or 1
|
||||||
|
local txt = string.format("%d crate%s %s (%dkg)", needed, needed==1 and "" or "s", cargoObj.Name, cargoObj.PerCrateMass or 0)
|
||||||
if cargoObj.Location then txt = txt.."[R]" end
|
if cargoObj.Location then txt = txt.."[R]" end
|
||||||
local stock = cargoObj:GetStock()
|
local stock = cargoObj:GetStock()
|
||||||
if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
|
if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
|
||||||
@@ -4888,7 +4956,17 @@ function CTLD:_UnloadSingleCrateSet(Group, Unit, setIndex)
|
|||||||
cObj:SetWasDropped(true)
|
cObj:SetWasDropped(true)
|
||||||
cObj:SetHasMoved(true)
|
cObj:SetHasMoved(true)
|
||||||
end
|
end
|
||||||
|
local cname = crateObj:GetName() or "Unknown"
|
||||||
|
local count = #chunk
|
||||||
|
if needed > 1 then
|
||||||
|
if count == needed then
|
||||||
|
self:_SendMessage(string.format("Dropped %d %s.", 1, cname), 10, false, Group)
|
||||||
|
else
|
||||||
|
self:_SendMessage(string.format("Dropped %d/%d crate(s) of %s.", count, needed, cname), 15, false, Group)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self:_SendMessage(string.format("Dropped %d %s(s).", count, cname), 10, false, Group)
|
||||||
|
end
|
||||||
-- Rebuild the cargo list to remove the dropped crates
|
-- Rebuild the cargo list to remove the dropped crates
|
||||||
local loadedData = self.Loaded_Cargo[unitName]
|
local loadedData = self.Loaded_Cargo[unitName]
|
||||||
if loadedData and loadedData.Cargo then
|
if loadedData and loadedData.Cargo then
|
||||||
@@ -5007,8 +5085,10 @@ function CTLD:_RefreshDropCratesMenu(Group, Unit)
|
|||||||
--------------------------------------------------------------------
|
--------------------------------------------------------------------
|
||||||
local mAll=MENU_GROUP:New(Group,"Drop ALL crates",dropCratesMenu)
|
local mAll=MENU_GROUP:New(Group,"Drop ALL crates",dropCratesMenu)
|
||||||
MENU_GROUP_COMMAND:New(Group,"Drop",mAll,self._UnloadCrates,self,Group,Unit)
|
MENU_GROUP_COMMAND:New(Group,"Drop",mAll,self._UnloadCrates,self,Group,Unit)
|
||||||
MENU_GROUP_COMMAND:New(Group,"Drop and build",mAll,self._DropAndBuild,self,Group,Unit)
|
if not ( self:IsUnitInAir(Unit) and self:IsFixedWing(Unit) ) then
|
||||||
|
MENU_GROUP_COMMAND:New(Group,"Drop and build",mAll,self._DropAndBuild,self,Group,Unit)
|
||||||
|
end
|
||||||
|
|
||||||
self.CrateGroupList=self.CrateGroupList or{}
|
self.CrateGroupList=self.CrateGroupList or{}
|
||||||
self.CrateGroupList[Unit:GetName()]={}
|
self.CrateGroupList[Unit:GetName()]={}
|
||||||
|
|
||||||
@@ -5029,7 +5109,9 @@ function CTLD:_RefreshDropCratesMenu(Group, Unit)
|
|||||||
local setIndex=#self.CrateGroupList[Unit:GetName()]
|
local setIndex=#self.CrateGroupList[Unit:GetName()]
|
||||||
local mSet=MENU_GROUP:New(Group,label,dropCratesMenu)
|
local mSet=MENU_GROUP:New(Group,label,dropCratesMenu)
|
||||||
MENU_GROUP_COMMAND:New(Group,"Drop",mSet,self._UnloadSingleCrateSet,self,Group,Unit,setIndex)
|
MENU_GROUP_COMMAND:New(Group,"Drop",mSet,self._UnloadSingleCrateSet,self,Group,Unit,setIndex)
|
||||||
|
if not ( self:IsUnitInAir(Unit) and self:IsFixedWing(Unit) ) then
|
||||||
MENU_GROUP_COMMAND:New(Group,"Drop and build",mSet,self._DropSingleAndBuild,self,Group,Unit,setIndex)
|
MENU_GROUP_COMMAND:New(Group,"Drop and build",mSet,self._DropSingleAndBuild,self,Group,Unit,setIndex)
|
||||||
|
end
|
||||||
i=i+needed
|
i=i+needed
|
||||||
else
|
else
|
||||||
local chunk={}
|
local chunk={}
|
||||||
@@ -5063,7 +5145,7 @@ function CTLD:_UnloadSingleTroopByID(Group, Unit, chunkID)
|
|||||||
inzone, zonename, zone, distance = self:IsUnitInZone(Unit, CTLD.CargoZoneType.SHIP)
|
inzone, zonename, zone, distance = self:IsUnitInZone(Unit, CTLD.CargoZoneType.SHIP)
|
||||||
end
|
end
|
||||||
if inzone then
|
if inzone then
|
||||||
droppingatbase = true
|
droppingatbase = self.returntroopstobase
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then
|
if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then
|
||||||
@@ -5148,6 +5230,7 @@ function CTLD:_UnloadSingleTroopByID(Group, Unit, chunkID)
|
|||||||
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template, alias)
|
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template, alias)
|
||||||
:InitDelayOff()
|
:InitDelayOff()
|
||||||
:InitSetUnitAbsolutePositions(Positions)
|
:InitSetUnitAbsolutePositions(Positions)
|
||||||
|
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
|
||||||
:OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end)
|
:OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end)
|
||||||
:SpawnFromVec2(randomcoord:GetVec2())
|
:SpawnFromVec2(randomcoord:GetVec2())
|
||||||
self:__TroopsDeployed(1, Group, Unit, self.DroppedTroops[self.TroopCounter], cType)
|
self:__TroopsDeployed(1, Group, Unit, self.DroppedTroops[self.TroopCounter], cType)
|
||||||
@@ -5156,6 +5239,8 @@ function CTLD:_UnloadSingleTroopByID(Group, Unit, chunkID)
|
|||||||
foundCargo:SetWasDropped(true)
|
foundCargo:SetWasDropped(true)
|
||||||
if cType == CTLD_CARGO.Enum.ENGINEERS then
|
if cType == CTLD_CARGO.Enum.ENGINEERS then
|
||||||
self.Engineers = self.Engineers + 1
|
self.Engineers = self.Engineers + 1
|
||||||
|
local grpname = self.DroppedTroops[self.TroopCounter]:GetName()
|
||||||
|
self.EngineersInField[self.Engineers] = CTLD_ENGINEERING:New(name, grpname)
|
||||||
self:_SendMessage(string.format("Dropped Engineers %s into action!", name), 10, false, Group)
|
self:_SendMessage(string.format("Dropped Engineers %s into action!", name), 10, false, Group)
|
||||||
else
|
else
|
||||||
self:_SendMessage(string.format("Dropped Troops %s into action!", name), 10, false, Group)
|
self:_SendMessage(string.format("Dropped Troops %s into action!", name), 10, false, Group)
|
||||||
@@ -5352,6 +5437,52 @@ function CTLD:AddCratesCargo(Name,Templates,Type,NoCrates,PerCrateMass,Stock,Sub
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Identical to AddCratesCargo, but registers the cargo so the spawned/built group does not move to MOVE zones.
|
||||||
|
--- User function - Add *generic* crate-type loadable as cargo. This type will create crates that need to be loaded, moved, dropped and built.
|
||||||
|
-- @param #CTLD self
|
||||||
|
-- @param #string Name Unique name of this type of cargo. E.g. "Humvee".
|
||||||
|
-- @param #table Templates Table of #string names of late activated Wrapper.Group#GROUP building this cargo.
|
||||||
|
-- @param #CTLD_CARGO.Enum Type Type of cargo. I.e. VEHICLE or FOB. VEHICLE will move to destination zones when dropped/build, FOB stays put.
|
||||||
|
-- @param #number NoCrates Number of crates needed to build this cargo.
|
||||||
|
-- @param #number PerCrateMass Mass in kg of each crate
|
||||||
|
-- @param #number Stock Number of buildable groups in stock. Nil for unlimited.
|
||||||
|
-- @param #string SubCategory Name of sub-category (optional).
|
||||||
|
-- @param #boolean DontShowInMenu (optional) If set to "true" this won't show up in the menu.
|
||||||
|
-- @param Core.Zone#ZONE Location (optional) If set, the cargo item is **only** available here. Can be a #ZONE object or the name of a zone as #string.
|
||||||
|
-- @param #string UnitTypes Unit type names (optional). If set, only these unit types can pick up the cargo, e.g. "UH-1H" or {"UH-1H","OH58D"}.
|
||||||
|
-- @param #string Category Static category name (optional). If set, spawn cargo crate with an alternate category type, e.g. "Cargos".
|
||||||
|
-- @param #string TypeName Static type name (optional). If set, spawn cargo crate with an alternate type shape, e.g. "iso_container".
|
||||||
|
-- @param #string ShapeName Static shape name (optional). If set, spawn cargo crate with an alternate type sub-shape, e.g. "iso_container_cargo".
|
||||||
|
-- @return #CTLD self
|
||||||
|
function CTLD:AddCratesCargoNoMove(Name,Templates,Type,NoCrates,PerCrateMass,Stock,SubCategory,DontShowInMenu,Location,UnitTypes,Category,TypeName,ShapeName)
|
||||||
|
self:T(self.lid .. " AddCratesCargoNoMove")
|
||||||
|
if not self:_CheckTemplates(Templates) then
|
||||||
|
self:E(self.lid .. "Crates Cargo for " .. Name .. " has missing template(s)!" )
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
self.CargoCounter = self.CargoCounter + 1
|
||||||
|
local cargo = CTLD_CARGO:New(self.CargoCounter,Name,Templates,Type,false,false,NoCrates,nil,nil,PerCrateMass,Stock,SubCategory,DontShowInMenu,Location)
|
||||||
|
cargo.NoMoveToZone = true
|
||||||
|
if UnitTypes then
|
||||||
|
cargo:AddUnitTypeName(UnitTypes)
|
||||||
|
end
|
||||||
|
cargo:SetStaticTypeAndShape("Cargos",self.basetype)
|
||||||
|
if TypeName then
|
||||||
|
cargo:SetStaticTypeAndShape(Category,TypeName,ShapeName)
|
||||||
|
end
|
||||||
|
table.insert(self.Cargo_Crates,cargo)
|
||||||
|
self.templateToCargoName = self.templateToCargoName or {}
|
||||||
|
if type(Templates)=="table" then
|
||||||
|
for _,t in pairs(Templates) do self.templateToCargoName[t] = Name end
|
||||||
|
else
|
||||||
|
self.templateToCargoName[Templates] = Name
|
||||||
|
end
|
||||||
|
self.nomovetozone_names = self.nomovetozone_names or {}
|
||||||
|
self.nomovetozone_names[Name] = true
|
||||||
|
if SubCategory and self.usesubcats ~= true then self.usesubcats=true end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- User function - Add *generic* static-type loadable as cargo. This type will create cargo that needs to be loaded, moved and dropped.
|
--- User function - Add *generic* static-type loadable as cargo. This type will create cargo that needs to be loaded, moved and dropped.
|
||||||
-- @param #CTLD self
|
-- @param #CTLD self
|
||||||
-- @param #string Name Unique name of this type of cargo as set in the mission editor (note: UNIT name!), e.g. "Ammunition-1".
|
-- @param #string Name Unique name of this type of cargo as set in the mission editor (note: UNIT name!), e.g. "Ammunition-1".
|
||||||
@@ -5596,8 +5727,14 @@ function CTLD:AddCTLDZone(Name, Type, Color, Active, HasBeacon, Shiplength, Ship
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local ctldzone = {} -- #CTLD.CargoZone
|
local exists = true
|
||||||
|
local ctldzone = self:GetCTLDZone(Name, Type) -- #CTLD.CargoZone
|
||||||
|
if not ctldzone then
|
||||||
|
exists = false
|
||||||
|
ctldzone = {}
|
||||||
|
end
|
||||||
|
|
||||||
ctldzone.active = Active or false
|
ctldzone.active = Active or false
|
||||||
ctldzone.color = Color or SMOKECOLOR.Red
|
ctldzone.color = Color or SMOKECOLOR.Red
|
||||||
ctldzone.name = Name or "NONE"
|
ctldzone.name = Name or "NONE"
|
||||||
@@ -5623,11 +5760,56 @@ function CTLD:AddCTLDZone(Name, Type, Color, Active, HasBeacon, Shiplength, Ship
|
|||||||
ctldzone.shiplength = Shiplength or 100
|
ctldzone.shiplength = Shiplength or 100
|
||||||
ctldzone.shipwidth = Shipwidth or 10
|
ctldzone.shipwidth = Shipwidth or 10
|
||||||
end
|
end
|
||||||
|
|
||||||
self:AddZone(ctldzone)
|
if not exists then
|
||||||
|
self:AddZone(ctldzone)
|
||||||
|
end
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- User function - find #CTLD.CargoZone zone by name.
|
||||||
|
-- @param #CTLD self
|
||||||
|
-- @param #string Name Name of this zone.
|
||||||
|
-- @param #string Type Type of this zone, #CTLD.CargoZoneType
|
||||||
|
-- @return #CTLD.CargoZone self
|
||||||
|
function CTLD:GetCTLDZone(Name, Type)
|
||||||
|
|
||||||
|
if Type == CTLD.CargoZoneType.LOAD then
|
||||||
|
for _, z in pairs(self.pickupZones) do
|
||||||
|
if z.name == Name then
|
||||||
|
return z
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif Type == CTLD.CargoZoneType.DROP then
|
||||||
|
for _, z in pairs(self.dropOffZones) do
|
||||||
|
if z.name == Name then
|
||||||
|
return z
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif Type == CTLD.CargoZoneType.SHIP then
|
||||||
|
for _, z in pairs(self.shipZones) do
|
||||||
|
if z.name == Name then
|
||||||
|
return z
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif Type == CTLD.CargoZoneType.BEACON then
|
||||||
|
for _, z in pairs(self.droppedBeacons) do
|
||||||
|
if z.name == Name then
|
||||||
|
return z
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for _, z in pairs(self.wpZones) do
|
||||||
|
if z.name == Name then
|
||||||
|
return z
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
--- User function - Creates and adds a #CTLD.CargoZone zone for this CTLD instance from an Airbase or FARP name.
|
--- User function - Creates and adds a #CTLD.CargoZone zone for this CTLD instance from an Airbase or FARP name.
|
||||||
-- Zones of type LOAD: Players load crates and troops here.
|
-- Zones of type LOAD: Players load crates and troops here.
|
||||||
-- Zones of type DROP: Players can drop crates here. Note that troops can be unloaded anywhere.
|
-- Zones of type DROP: Players can drop crates here. Note that troops can be unloaded anywhere.
|
||||||
@@ -5971,16 +6153,22 @@ function CTLD:SmokeZoneNearBy(Unit, Flare)
|
|||||||
for index,cargozone in pairs(zones[i]) do
|
for index,cargozone in pairs(zones[i]) do
|
||||||
local CZone = cargozone --#CTLD.CargoZone
|
local CZone = cargozone --#CTLD.CargoZone
|
||||||
local zonename = CZone.name
|
local zonename = CZone.name
|
||||||
local zone = nil
|
local zone = nil -- Core.Zone#ZONE_RADIUS
|
||||||
|
local airbasezone = false
|
||||||
if i == 4 then
|
if i == 4 then
|
||||||
zone = UNIT:FindByName(zonename)
|
zone = UNIT:FindByName(zonename)
|
||||||
else
|
else
|
||||||
zone = ZONE:FindByName(zonename)
|
zone = ZONE:FindByName(zonename)
|
||||||
if not zone then
|
if not zone then
|
||||||
zone = AIRBASE:FindByName(zonename):GetZone()
|
zone = AIRBASE:FindByName(zonename):GetZone()
|
||||||
|
airbasezone = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local zonecoord = zone:GetCoordinate()
|
local zonecoord = zone:GetCoordinate()
|
||||||
|
-- Avoid smoke/flares on runways
|
||||||
|
if (i==1 or 1==3) and airbasezone==true and zone:IsInstanceOf("ZONE_BASE") then
|
||||||
|
zonecoord = zone:GetRandomCoordinate(inner,outer,{land.SurfaceType.LAND})
|
||||||
|
end
|
||||||
if zonecoord then
|
if zonecoord then
|
||||||
local active = CZone.active
|
local active = CZone.active
|
||||||
local color = CZone.color
|
local color = CZone.color
|
||||||
@@ -6852,6 +7040,7 @@ end
|
|||||||
local alias = string.format("%s-%d", _template, math.random(1,100000))
|
local alias = string.format("%s-%d", _template, math.random(1,100000))
|
||||||
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
|
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
|
||||||
:InitRandomizeUnits(randompositions,20,2)
|
:InitRandomizeUnits(randompositions,20,2)
|
||||||
|
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
|
||||||
:InitDelayOff()
|
:InitDelayOff()
|
||||||
:OnSpawnGroup(function(grp,TimeStamp) grp.spawntime = TimeStamp or timer.getTime() end,TimeStamp)
|
:OnSpawnGroup(function(grp,TimeStamp) grp.spawntime = TimeStamp or timer.getTime() end,TimeStamp)
|
||||||
:SpawnFromVec2(randomcoord)
|
:SpawnFromVec2(randomcoord)
|
||||||
@@ -7005,12 +7194,14 @@ end
|
|||||||
if canmove then
|
if canmove then
|
||||||
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
|
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
|
||||||
:InitRandomizeUnits(true,20,2)
|
:InitRandomizeUnits(true,20,2)
|
||||||
|
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
|
||||||
:InitDelayOff()
|
:InitDelayOff()
|
||||||
:OnSpawnGroup(function(grp,TimeStamp) grp.spawntime = TimeStamp or timer.getTime() end,TimeStamp)
|
:OnSpawnGroup(function(grp,TimeStamp) grp.spawntime = TimeStamp or timer.getTime() end,TimeStamp)
|
||||||
:SpawnFromVec2(randomcoord)
|
:SpawnFromVec2(randomcoord)
|
||||||
else -- don't random position of e.g. SAM units build as FOB
|
else -- don't random position of e.g. SAM units build as FOB
|
||||||
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
|
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
|
||||||
:InitDelayOff()
|
:InitDelayOff()
|
||||||
|
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
|
||||||
:OnSpawnGroup(function(grp,TimeStamp) grp.spawntime = TimeStamp or timer.getTime() end,TimeStamp)
|
:OnSpawnGroup(function(grp,TimeStamp) grp.spawntime = TimeStamp or timer.getTime() end,TimeStamp)
|
||||||
:SpawnFromVec2(randomcoord)
|
:SpawnFromVec2(randomcoord)
|
||||||
end
|
end
|
||||||
@@ -7080,6 +7271,16 @@ end
|
|||||||
local filepath = self.filepath
|
local filepath = self.filepath
|
||||||
self:__Save(interval,filepath,filename)
|
self:__Save(interval,filepath,filename)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if type(self.VehicleMoveFormation) == "table" then
|
||||||
|
local Formations = {}
|
||||||
|
for _,_formation in pairs(self.VehicleMoveFormation) do
|
||||||
|
table.insert(Formations,_formation)
|
||||||
|
end
|
||||||
|
self.VehicleMoveFormation = nil
|
||||||
|
self.VehicleMoveFormation = Formations
|
||||||
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -7350,8 +7551,11 @@ end
|
|||||||
-- @return #CTLD self
|
-- @return #CTLD self
|
||||||
function CTLD:onafterCratesBuild(From, Event, To, Group, Unit, Vehicle)
|
function CTLD:onafterCratesBuild(From, Event, To, Group, Unit, Vehicle)
|
||||||
self:T({From, Event, To})
|
self:T({From, Event, To})
|
||||||
if self.movetroopstowpzone then
|
if self.movetroopstowpzone and Vehicle then
|
||||||
self:_MoveGroupToZone(Vehicle)
|
local cg = self:GetGenericCargoObjectFromGroupName(Vehicle:GetName())
|
||||||
|
if not (cg and (cg.NoMoveToZone or (self.nomovetozone_names and self.nomovetozone_names[cg:GetName()]))) then
|
||||||
|
self:_MoveGroupToZone(Vehicle)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ COHORT = {
|
|||||||
|
|
||||||
--- COHORT class version.
|
--- COHORT class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
COHORT.version="0.3.6"
|
COHORT.version="0.3.7"
|
||||||
|
|
||||||
--- Global variable to store the unique(!) cohort names
|
--- Global variable to store the unique(!) cohort names
|
||||||
_COHORTNAMES={}
|
_COHORTNAMES={}
|
||||||
@@ -100,6 +100,7 @@ _COHORTNAMES={}
|
|||||||
-- DONE: Create FLOTILLA class.
|
-- DONE: Create FLOTILLA class.
|
||||||
-- DONE: Added check for properties.
|
-- DONE: Added check for properties.
|
||||||
-- DONE: Make general so that PLATOON and SQUADRON can inherit this class.
|
-- DONE: Make general so that PLATOON and SQUADRON can inherit this class.
|
||||||
|
-- DONE: Better setting of call signs.
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- Constructor
|
-- Constructor
|
||||||
@@ -515,10 +516,12 @@ end
|
|||||||
-- @param #COHORT self
|
-- @param #COHORT self
|
||||||
-- @param #number Callsign Callsign from CALLSIGN.Aircraft, e.g. "Chevy" for CALLSIGN.Aircraft.CHEVY.
|
-- @param #number Callsign Callsign from CALLSIGN.Aircraft, e.g. "Chevy" for CALLSIGN.Aircraft.CHEVY.
|
||||||
-- @param #number Index Callsign index, Chevy-**1**.
|
-- @param #number Index Callsign index, Chevy-**1**.
|
||||||
|
-- @param #string CallsignString (optional) Set this for tasks like TANKER, AWACS or KIOWA and the like, which have special names. E.g. "Darkstar" or "Roughneck".
|
||||||
-- @return #COHORT self
|
-- @return #COHORT self
|
||||||
function COHORT:SetCallsign(Callsign, Index)
|
function COHORT:SetCallsign(Callsign, Index, CallsignString)
|
||||||
self.callsignName=Callsign
|
self.callsignName=Callsign
|
||||||
self.callsignIndex=Index
|
self.callsignIndex=Index
|
||||||
|
self.callsignClearName=CallsignString
|
||||||
self.callsign={}
|
self.callsign={}
|
||||||
self.callsign.NumberSquad=Callsign
|
self.callsign.NumberSquad=Callsign
|
||||||
self.callsign.NumberGroup=Index
|
self.callsign.NumberGroup=Index
|
||||||
@@ -679,7 +682,16 @@ end
|
|||||||
function COHORT:GetCallsign(Asset)
|
function COHORT:GetCallsign(Asset)
|
||||||
|
|
||||||
if self.callsignName then
|
if self.callsignName then
|
||||||
|
--[[
|
||||||
|
["callsign"] =
|
||||||
|
{
|
||||||
|
[2] = 1,
|
||||||
|
["name"] = "Darkstar11",
|
||||||
|
[3] = 1,
|
||||||
|
[1] = 5,
|
||||||
|
[4] = "Darkstar11",
|
||||||
|
}, -- end of ["callsign"]
|
||||||
|
]]
|
||||||
Asset.callsign={}
|
Asset.callsign={}
|
||||||
|
|
||||||
for i=1,Asset.nunits do
|
for i=1,Asset.nunits do
|
||||||
@@ -695,12 +707,16 @@ function COHORT:GetCallsign(Asset)
|
|||||||
else
|
else
|
||||||
self.callsigncounter=self.callsigncounter+1
|
self.callsigncounter=self.callsigncounter+1
|
||||||
end
|
end
|
||||||
|
callsign["name"] = self.callsignClearName or UTILS.GetCallsignName(self.callsignName) or "None"
|
||||||
|
callsign["name"] = string.format("%s%d%d",callsign["name"],callsign[2],callsign[3])
|
||||||
|
callsign[4] = callsign["name"]
|
||||||
|
|
||||||
Asset.callsign[i]=callsign
|
Asset.callsign[i]=callsign
|
||||||
|
|
||||||
self:T3({callsign=callsign})
|
self:T3({callsign=callsign})
|
||||||
|
|
||||||
--TODO: there is also a table entry .name, which is a string.
|
--DONE: there is also a table entry .name, which is a string.
|
||||||
|
--UTILS.PrintTableToLog(callsign)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ COMMANDER = {
|
|||||||
awacsZones = {},
|
awacsZones = {},
|
||||||
tankerZones = {},
|
tankerZones = {},
|
||||||
limitMission = {},
|
limitMission = {},
|
||||||
|
maxMissionsAssignPerCycle = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- COMMANDER class version.
|
--- COMMANDER class version.
|
||||||
@@ -1535,6 +1536,8 @@ function COMMANDER:CheckMissionQueue()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local missionsAssigned = 0
|
||||||
|
|
||||||
-- Loop over missions in queue.
|
-- Loop over missions in queue.
|
||||||
for _,_mission in pairs(self.missionqueue) do
|
for _,_mission in pairs(self.missionqueue) do
|
||||||
local mission=_mission --Ops.Auftrag#AUFTRAG
|
local mission=_mission --Ops.Auftrag#AUFTRAG
|
||||||
@@ -1594,9 +1597,12 @@ function COMMANDER:CheckMissionQueue()
|
|||||||
-- Recruited assets but no requested escort available. Unrecruit assets!
|
-- Recruited assets but no requested escort available. Unrecruit assets!
|
||||||
LEGION.UnRecruitAssets(assets, mission)
|
LEGION.UnRecruitAssets(assets, mission)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Only ONE mission is assigned.
|
missionsAssigned = missionsAssigned + 1
|
||||||
return
|
if missionsAssigned >= (self.maxMissionsAssignPerCycle or 1) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
@@ -1611,6 +1617,16 @@ function COMMANDER:CheckMissionQueue()
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set how many missions can be assigned in a single status iteration. (eg. This is useful for persistent missions where you need to load all AUFTRAGs on mission start and then change it back to default)
|
||||||
|
--- Warning: Increasing this value will increase the number of missions started per iteration and thus may lead to performance issues if too many missions are started at once.
|
||||||
|
-- @param #COMMANDER self
|
||||||
|
-- @param #number Number of missions assigned per status iteration. Default is 1.
|
||||||
|
-- @return #COMMANDER self.
|
||||||
|
function COMMANDER:SetMaxMissionsAssignPerCycle(MaxMissionsAssignPerCycle)
|
||||||
|
self.maxMissionsAssignPerCycle = MaxMissionsAssignPerCycle or 1
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Get cohorts.
|
--- Get cohorts.
|
||||||
-- @param #COMMANDER self
|
-- @param #COMMANDER self
|
||||||
-- @param #table Legions Special legions.
|
-- @param #table Legions Special legions.
|
||||||
@@ -1670,9 +1686,12 @@ function COMMANDER:_GetCohorts(Legions, Cohorts, Operation)
|
|||||||
for _,_legion in pairs(Legions or {}) do
|
for _,_legion in pairs(Legions or {}) do
|
||||||
local legion=_legion --Ops.Legion#LEGION
|
local legion=_legion --Ops.Legion#LEGION
|
||||||
|
|
||||||
-- Check that runway is operational.
|
-- Check that runway is operational.
|
||||||
local Runway=legion:IsAirwing() and legion:IsRunwayOperational() or true
|
local Runway=true
|
||||||
|
if legion:IsAirwing() then
|
||||||
|
Runway=legion:IsRunwayOperational() and legion.airbase and legion.airbase:GetCoalition() == legion:GetCoalition()
|
||||||
|
end
|
||||||
|
|
||||||
-- Legion has to be running.
|
-- Legion has to be running.
|
||||||
if legion:IsRunning() and Runway then
|
if legion:IsRunning() and Runway then
|
||||||
|
|
||||||
@@ -1703,9 +1722,12 @@ function COMMANDER:_GetCohorts(Legions, Cohorts, Operation)
|
|||||||
for _,_legion in pairs(self.legions) do
|
for _,_legion in pairs(self.legions) do
|
||||||
local legion=_legion --Ops.Legion#LEGION
|
local legion=_legion --Ops.Legion#LEGION
|
||||||
|
|
||||||
-- Check that runway is operational.
|
-- Check that runway is operational.
|
||||||
local Runway=legion:IsAirwing() and legion:IsRunwayOperational() or true
|
local Runway=true
|
||||||
|
if legion:IsAirwing() then
|
||||||
|
Runway=legion:IsRunwayOperational() and legion.airbase and legion.airbase:GetCoalition() == legion:GetCoalition()
|
||||||
|
end
|
||||||
|
|
||||||
-- Legion has to be running.
|
-- Legion has to be running.
|
||||||
if legion:IsRunning() and Runway then
|
if legion:IsRunning() and Runway then
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
-------------------------------------------------------------------------
|
-------------------------------------------------------------------------
|
||||||
-- Easy CAP/GCI Class, based on OPS classes
|
-- Easy CAP/GCI Class, based on OPS classes
|
||||||
-------------------------------------------------------------------------
|
-------------------------------------------------------------------------
|
||||||
-- Documentation
|
--
|
||||||
|
-- ## Documentation:
|
||||||
--
|
--
|
||||||
-- https://flightcontrol-master.github.io/MOOSE_DOCS_DEVELOP/Documentation/Ops.EasyGCICAP.html
|
-- https://flightcontrol-master.github.io/MOOSE_DOCS_DEVELOP/Documentation/Ops.EasyGCICAP.html
|
||||||
--
|
--
|
||||||
|
-- ## Example Missions:
|
||||||
|
--
|
||||||
|
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Ops/EasyGCICAP).
|
||||||
|
--
|
||||||
-------------------------------------------------------------------------
|
-------------------------------------------------------------------------
|
||||||
-- Date: September 2023
|
-- Date: September 2023
|
||||||
-- Last Update: July 2024
|
-- Last Update: Aug 2025
|
||||||
-------------------------------------------------------------------------
|
-------------------------------------------------------------------------
|
||||||
--
|
--
|
||||||
--- **Ops** - Easy GCI & CAP Manager
|
--- **Ops** - Easy GCI & CAP Manager
|
||||||
@@ -71,6 +76,10 @@
|
|||||||
-- @field #boolean DespawnAfterHolding
|
-- @field #boolean DespawnAfterHolding
|
||||||
-- @field #list<Ops.Auftrag#AUFTRAG> ListOfAuftrag
|
-- @field #list<Ops.Auftrag#AUFTRAG> ListOfAuftrag
|
||||||
-- @field #string defaulttakeofftype Take off type
|
-- @field #string defaulttakeofftype Take off type
|
||||||
|
-- @field #number FuelLowThreshold
|
||||||
|
-- @field #number FuelCriticalThreshold
|
||||||
|
-- @field #boolean showpatrolpointmarks
|
||||||
|
-- @field #table EngageTargetTypes
|
||||||
-- @extends Core.Fsm#FSM
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
--- *“Airspeed, altitude, and brains. Two are always needed to successfully complete the flight.”* -- Unknown.
|
--- *“Airspeed, altitude, and brains. Two are always needed to successfully complete the flight.”* -- Unknown.
|
||||||
@@ -226,6 +235,10 @@ EASYGCICAP = {
|
|||||||
DespawnAfterHolding = true,
|
DespawnAfterHolding = true,
|
||||||
ListOfAuftrag = {},
|
ListOfAuftrag = {},
|
||||||
defaulttakeofftype = "hot",
|
defaulttakeofftype = "hot",
|
||||||
|
FuelLowThreshold = 25,
|
||||||
|
FuelCriticalThreshold = 10,
|
||||||
|
showpatrolpointmarks = false,
|
||||||
|
EngageTargetTypes = {"Air"},
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Internal Squadron data type
|
--- Internal Squadron data type
|
||||||
@@ -258,10 +271,11 @@ EASYGCICAP = {
|
|||||||
-- @field #number Speed
|
-- @field #number Speed
|
||||||
-- @field #number Heading
|
-- @field #number Heading
|
||||||
-- @field #number LegLength
|
-- @field #number LegLength
|
||||||
|
-- @field Core.Zone#ZONE_BASE Zone
|
||||||
|
|
||||||
--- EASYGCICAP class version.
|
--- EASYGCICAP class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
EASYGCICAP.version="0.1.21"
|
EASYGCICAP.version="0.1.30"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- TODO list
|
-- TODO list
|
||||||
@@ -315,6 +329,10 @@ function EASYGCICAP:New(Alias, AirbaseName, Coalition, EWRName)
|
|||||||
self.DespawnAfterHolding = true
|
self.DespawnAfterHolding = true
|
||||||
self.ListOfAuftrag = {}
|
self.ListOfAuftrag = {}
|
||||||
self.defaulttakeofftype = "hot"
|
self.defaulttakeofftype = "hot"
|
||||||
|
self.FuelLowThreshold = 25
|
||||||
|
self.FuelCriticalThreshold = 10
|
||||||
|
self.showpatrolpointmarks = false
|
||||||
|
self.EngageTargetTypes = {"Air"}
|
||||||
|
|
||||||
-- Set some string id for output to DCS.log file.
|
-- Set some string id for output to DCS.log file.
|
||||||
self.lid=string.format("EASYGCICAP %s | ", self.alias)
|
self.lid=string.format("EASYGCICAP %s | ", self.alias)
|
||||||
@@ -339,6 +357,63 @@ end
|
|||||||
-- Functions
|
-- Functions
|
||||||
-------------------------------------------------------------------------
|
-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Get a specific managed AirWing by name
|
||||||
|
-- @param #EASYGCICAP self
|
||||||
|
-- @param #string AirbaseName Airbase name of the home of this wing.
|
||||||
|
-- @return Ops.AirWing#AIRWING Airwing or nil if not found
|
||||||
|
function EASYGCICAP:GetAirwing(AirbaseName)
|
||||||
|
self:T(self.lid.."GetAirwing")
|
||||||
|
if self.wings[AirbaseName] then
|
||||||
|
return self.wings[AirbaseName][1]
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get a table of all managed AirWings
|
||||||
|
-- @param #EASYGCICAP self
|
||||||
|
-- @return #table Table of Ops.AirWing#AIRWING Airwings
|
||||||
|
function EASYGCICAP:GetAirwingTable()
|
||||||
|
self:T(self.lid.."GetAirwingTable")
|
||||||
|
local Wingtable = {}
|
||||||
|
for _,_object in pairs(self.wings or {}) do
|
||||||
|
table.insert(Wingtable,_object[1])
|
||||||
|
end
|
||||||
|
return Wingtable
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set "fuel low" threshold for CAP and INTERCEPT flights.
|
||||||
|
-- @param #EASYGCICAP self
|
||||||
|
-- @param #number Percent RTB if fuel at this percent. Values: 1..100, defaults to 25.
|
||||||
|
-- @return #EASYGCICAP self
|
||||||
|
function EASYGCICAP:SetFuelLow(Percent)
|
||||||
|
self:T(self.lid.."SetFuelLow")
|
||||||
|
self.FuelLowThreshold = Percent or 25
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set markers on the map for Patrol Points.
|
||||||
|
-- @param #EASYGCICAP self
|
||||||
|
-- @param #boolean onoff Set to true to switch markers on.
|
||||||
|
-- @return #EASYGCICAP self
|
||||||
|
function EASYGCICAP:ShowPatrolPointMarkers(onoff)
|
||||||
|
if onoff then
|
||||||
|
self.showpatrolpointmarks = true
|
||||||
|
else
|
||||||
|
self.showpatrolpointmarks = false
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set "fuel critical" threshold for CAP and INTERCEPT flights.
|
||||||
|
-- @param #EASYGCICAP self
|
||||||
|
-- @param #number Percent RTB if fuel at this percent. Values: 1..100, defaults to 10.
|
||||||
|
-- @return #EASYGCICAP self
|
||||||
|
function EASYGCICAP:SetFuelCritical(Percent)
|
||||||
|
self:T(self.lid.."SetFuelCritical")
|
||||||
|
self.FuelCriticalThreshold = Percent or 10
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Set CAP formation.
|
--- Set CAP formation.
|
||||||
-- @param #EASYGCICAP self
|
-- @param #EASYGCICAP self
|
||||||
-- @param #number Formation Formation to fly, defaults to ENUMS.Formation.FixedWing.FingerFour.Group
|
-- @param #number Formation Formation to fly, defaults to ENUMS.Formation.FixedWing.FingerFour.Group
|
||||||
@@ -359,7 +434,7 @@ function EASYGCICAP:SetTankerAndAWACSInvisible(Switch)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Count alive missions in our internal stack.
|
--- (internal) Count alive missions in our internal stack.
|
||||||
-- @param #EASYGCICAP self
|
-- @param #EASYGCICAP self
|
||||||
-- @return #number count
|
-- @return #number count
|
||||||
function EASYGCICAP:_CountAliveAuftrags()
|
function EASYGCICAP:_CountAliveAuftrags()
|
||||||
@@ -536,6 +611,17 @@ function EASYGCICAP:SetCapStartTimeVariation(Start, End)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Set which target types CAP flights will prefer to engage, defaults to {"Air"}
|
||||||
|
-- @param #EASYGCICAP self
|
||||||
|
-- @param #table types Table of comma separated #string entries, defaults to {"Air"} (everything that flies and is not a weapon). Useful other options are e.g. {"Bombers"}, {"Fighters"},
|
||||||
|
-- or {"Helicopters"} or combinations like {"Bombers", "Fighters", "UAVs"}. See [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_enum_attributes).
|
||||||
|
-- @return #EASYGCICAP self
|
||||||
|
function EASYGCICAP:SetCAPEngageTargetTypes(types)
|
||||||
|
self.EngageTargetTypes = types or {"Air"}
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Add an AirWing to the manager
|
--- Add an AirWing to the manager
|
||||||
-- @param #EASYGCICAP self
|
-- @param #EASYGCICAP self
|
||||||
-- @param #string Airbasename
|
-- @param #string Airbasename
|
||||||
@@ -582,6 +668,13 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias)
|
|||||||
local DespawnAfterLanding = self.DespawnAfterLanding
|
local DespawnAfterLanding = self.DespawnAfterLanding
|
||||||
local DespawnAfterHolding = self.DespawnAfterHolding
|
local DespawnAfterHolding = self.DespawnAfterHolding
|
||||||
|
|
||||||
|
-- Check STATIC name
|
||||||
|
local check = STATIC:FindByName(Airbasename,false) or UNIT:FindByName(Airbasename)
|
||||||
|
if check == nil then
|
||||||
|
MESSAGE:New(self.lid.."There's no warehouse static on the map (wrong naming?) for airbase "..tostring(Airbasename).."!",30,"CHECK"):ToAllIf(self.debug):ToLog()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
-- Create Airwing
|
-- Create Airwing
|
||||||
local CAP_Wing = AIRWING:New(Airbasename,Alias)
|
local CAP_Wing = AIRWING:New(Airbasename,Alias)
|
||||||
CAP_Wing:SetVerbosityLevel(0)
|
CAP_Wing:SetVerbosityLevel(0)
|
||||||
@@ -591,6 +684,10 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias)
|
|||||||
CAP_Wing:SetRespawnAfterDestroyed()
|
CAP_Wing:SetRespawnAfterDestroyed()
|
||||||
CAP_Wing:SetNumberCAP(self.capgrouping)
|
CAP_Wing:SetNumberCAP(self.capgrouping)
|
||||||
CAP_Wing:SetCapCloseRaceTrack(true)
|
CAP_Wing:SetCapCloseRaceTrack(true)
|
||||||
|
|
||||||
|
if self.showpatrolpointmarks then
|
||||||
|
CAP_Wing:ShowPatrolPointMarkers(true)
|
||||||
|
end
|
||||||
|
|
||||||
if self.capOptionVaryStartTime then
|
if self.capOptionVaryStartTime then
|
||||||
CAP_Wing:SetCapStartTimeVariation(self.capOptionVaryStartTime,self.capOptionVaryEndTime)
|
CAP_Wing:SetCapStartTimeVariation(self.capOptionVaryStartTime,self.capOptionVaryEndTime)
|
||||||
@@ -621,6 +718,9 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias)
|
|||||||
local engagerange = self.engagerange
|
local engagerange = self.engagerange
|
||||||
local GoZoneSet = self.GoZoneSet
|
local GoZoneSet = self.GoZoneSet
|
||||||
local NoGoZoneSet = self.NoGoZoneSet
|
local NoGoZoneSet = self.NoGoZoneSet
|
||||||
|
local FuelLow = self.FuelLowThreshold or 25
|
||||||
|
local FuelCritical = self.FuelCriticalThreshold or 10
|
||||||
|
local EngageTypes = self.EngageTargetTypes or {"Air"}
|
||||||
|
|
||||||
function CAP_Wing:onbeforeFlightOnMission(From, Event, To, Flightgroup, Mission)
|
function CAP_Wing:onbeforeFlightOnMission(From, Event, To, Flightgroup, Mission)
|
||||||
local flightgroup = Flightgroup -- Ops.FlightGroup#FLIGHTGROUP
|
local flightgroup = Flightgroup -- Ops.FlightGroup#FLIGHTGROUP
|
||||||
@@ -632,10 +732,15 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias)
|
|||||||
flightgroup:SetDestinationbase(AIRBASE:FindByName(Airbasename))
|
flightgroup:SetDestinationbase(AIRBASE:FindByName(Airbasename))
|
||||||
flightgroup:GetGroup():CommandEPLRS(true,5)
|
flightgroup:GetGroup():CommandEPLRS(true,5)
|
||||||
flightgroup:GetGroup():SetOptionRadarUsingForContinousSearch()
|
flightgroup:GetGroup():SetOptionRadarUsingForContinousSearch()
|
||||||
|
flightgroup:GetGroup():SetOptionLandingOverheadBreak()
|
||||||
if Mission.type ~= AUFTRAG.Type.TANKER and Mission.type ~= AUFTRAG.Type.AWACS and Mission.type ~= AUFTRAG.Type.RECON then
|
if Mission.type ~= AUFTRAG.Type.TANKER and Mission.type ~= AUFTRAG.Type.AWACS and Mission.type ~= AUFTRAG.Type.RECON then
|
||||||
flightgroup:SetDetection(true)
|
flightgroup:SetDetection(true)
|
||||||
flightgroup:SetEngageDetectedOn(engagerange,{"Air"},GoZoneSet,NoGoZoneSet)
|
flightgroup:SetEngageDetectedOn(engagerange,EngageTypes,GoZoneSet,NoGoZoneSet)
|
||||||
flightgroup:SetOutOfAAMRTB()
|
flightgroup:SetOutOfAAMRTB()
|
||||||
|
flightgroup:SetFuelLowRTB(true)
|
||||||
|
flightgroup:SetFuelLowThreshold(FuelLow)
|
||||||
|
flightgroup:SetFuelCriticalRTB(true)
|
||||||
|
flightgroup:SetFuelCriticalThreshold(FuelCritical)
|
||||||
if CapFormation then
|
if CapFormation then
|
||||||
flightgroup:GetGroup():SetOption(AI.Option.Air.id.FORMATION,CapFormation)
|
flightgroup:GetGroup():SetOption(AI.Option.Air.id.FORMATION,CapFormation)
|
||||||
end
|
end
|
||||||
@@ -674,24 +779,30 @@ end
|
|||||||
--- Add a CAP patrol point to a Wing
|
--- Add a CAP patrol point to a Wing
|
||||||
-- @param #EASYGCICAP self
|
-- @param #EASYGCICAP self
|
||||||
-- @param #string AirbaseName Name of the Wing's airbase
|
-- @param #string AirbaseName Name of the Wing's airbase
|
||||||
-- @param Core.Point#COORDINATE Coordinate.
|
-- @param Core.Point#COORDINATE Coordinate. Can be handed as a Core.Zone#ZONE object (e.g. in case you want the point to align with a moving zone).
|
||||||
-- @param #number Altitude Defaults to 25000 feet ASL.
|
-- @param #number Altitude Defaults to 25000 feet ASL.
|
||||||
-- @param #number Speed Defaults to 300 knots TAS.
|
-- @param #number Speed Defaults to 300 knots TAS.
|
||||||
-- @param #number Heading Defaults to 90 degrees (East).
|
-- @param #number Heading Defaults to 90 degrees (East).
|
||||||
-- @param #number LegLength Defaults to 15 NM.
|
-- @param #number LegLength Defaults to 15 NM.
|
||||||
-- @return #EASYGCICAP self
|
-- @return #EASYGCICAP self
|
||||||
function EASYGCICAP:AddPatrolPointCAP(AirbaseName,Coordinate,Altitude,Speed,Heading,LegLength)
|
function EASYGCICAP:AddPatrolPointCAP(AirbaseName,Coordinate,Altitude,Speed,Heading,LegLength)
|
||||||
self:T(self.lid.."AddPatrolPointCAP "..Coordinate:ToStringLLDDM())
|
self:T(self.lid.."AddPatrolPointCAP")--..Coordinate:ToStringLLDDM())
|
||||||
local EntryCAP = {} -- #EASYGCICAP.CapPoint
|
local coordinate = Coordinate
|
||||||
|
local EntryCAP = {} -- #EASYGCICAP.CapPoint
|
||||||
|
if Coordinate:IsInstanceOf("ZONE_BASE") then
|
||||||
|
-- adjust coordinate and get the coordinate from the zone
|
||||||
|
coordinate = Coordinate:GetCoordinate()
|
||||||
|
EntryCAP.Zone = Coordinate
|
||||||
|
end
|
||||||
EntryCAP.AirbaseName = AirbaseName
|
EntryCAP.AirbaseName = AirbaseName
|
||||||
EntryCAP.Coordinate = Coordinate
|
EntryCAP.Coordinate = coordinate
|
||||||
EntryCAP.Altitude = Altitude or 25000
|
EntryCAP.Altitude = Altitude or 25000
|
||||||
EntryCAP.Speed = Speed or 300
|
EntryCAP.Speed = Speed or 300
|
||||||
EntryCAP.Heading = Heading or 90
|
EntryCAP.Heading = Heading or 90
|
||||||
EntryCAP.LegLength = LegLength or 15
|
EntryCAP.LegLength = LegLength or 15
|
||||||
self.ManagedCP[#self.ManagedCP+1] = EntryCAP
|
self.ManagedCP[#self.ManagedCP+1] = EntryCAP
|
||||||
if self.debug then
|
if self.debug then
|
||||||
local mark = MARKER:New(Coordinate,self.lid.."Patrol Point"):ToAll()
|
local mark = MARKER:New(coordinate,self.lid.."Patrol Point"):ToAll()
|
||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -699,7 +810,7 @@ end
|
|||||||
--- Add a RECON patrol point to a Wing
|
--- Add a RECON patrol point to a Wing
|
||||||
-- @param #EASYGCICAP self
|
-- @param #EASYGCICAP self
|
||||||
-- @param #string AirbaseName Name of the Wing's airbase
|
-- @param #string AirbaseName Name of the Wing's airbase
|
||||||
-- @param Core.Point#COORDINATE Coordinate.
|
-- @param Core.Point#COORDINATE Coordinate. Can be handed as a Core.Zone#ZONE object (e.g. in case you want the point to align with a moving zone).
|
||||||
-- @param #number Altitude Defaults to 25000 feet.
|
-- @param #number Altitude Defaults to 25000 feet.
|
||||||
-- @param #number Speed Defaults to 300 knots.
|
-- @param #number Speed Defaults to 300 knots.
|
||||||
-- @param #number Heading Defaults to 90 degrees (East).
|
-- @param #number Heading Defaults to 90 degrees (East).
|
||||||
@@ -724,7 +835,7 @@ end
|
|||||||
--- Add a TANKER patrol point to a Wing
|
--- Add a TANKER patrol point to a Wing
|
||||||
-- @param #EASYGCICAP self
|
-- @param #EASYGCICAP self
|
||||||
-- @param #string AirbaseName Name of the Wing's airbase
|
-- @param #string AirbaseName Name of the Wing's airbase
|
||||||
-- @param Core.Point#COORDINATE Coordinate.
|
-- @param Core.Point#COORDINATE Coordinate. Can be handed as a Core.Zone#ZONE object (e.g. in case you want the point to align with a moving zone).
|
||||||
-- @param #number Altitude Defaults to 25000 feet.
|
-- @param #number Altitude Defaults to 25000 feet.
|
||||||
-- @param #number Speed Defaults to 300 knots.
|
-- @param #number Speed Defaults to 300 knots.
|
||||||
-- @param #number Heading Defaults to 90 degrees (East).
|
-- @param #number Heading Defaults to 90 degrees (East).
|
||||||
@@ -749,7 +860,7 @@ end
|
|||||||
--- Add an AWACS patrol point to a Wing
|
--- Add an AWACS patrol point to a Wing
|
||||||
-- @param #EASYGCICAP self
|
-- @param #EASYGCICAP self
|
||||||
-- @param #string AirbaseName Name of the Wing's airbase
|
-- @param #string AirbaseName Name of the Wing's airbase
|
||||||
-- @param Core.Point#COORDINATE Coordinate.
|
-- @param Core.Point#COORDINATE Coordinate. Can be handed as a Core.Zone#ZONE object (e.g. in case you want the point to align with a moving zone).
|
||||||
-- @param #number Altitude Defaults to 25000 feet.
|
-- @param #number Altitude Defaults to 25000 feet.
|
||||||
-- @param #number Speed Defaults to 300 knots.
|
-- @param #number Speed Defaults to 300 knots.
|
||||||
-- @param #number Heading Defaults to 90 degrees (East).
|
-- @param #number Heading Defaults to 90 degrees (East).
|
||||||
@@ -778,6 +889,11 @@ function EASYGCICAP:_SetTankerPatrolPoints()
|
|||||||
self:T(self.lid.."_SetTankerPatrolPoints")
|
self:T(self.lid.."_SetTankerPatrolPoints")
|
||||||
for _,_data in pairs(self.ManagedTK) do
|
for _,_data in pairs(self.ManagedTK) do
|
||||||
local data = _data --#EASYGCICAP.CapPoint
|
local data = _data --#EASYGCICAP.CapPoint
|
||||||
|
self:T("Airbasename = "..data.AirbaseName)
|
||||||
|
if not self.wings[data.AirbaseName] then
|
||||||
|
MESSAGE:New(self.lid.."You are trying to create a TANKER point for which there is no wing! "..tostring(data.AirbaseName),30,"CHECK"):ToAllIf(self.debug):ToLog()
|
||||||
|
return
|
||||||
|
end
|
||||||
local Wing = self.wings[data.AirbaseName][1] -- Ops.Airwing#AIRWING
|
local Wing = self.wings[data.AirbaseName][1] -- Ops.Airwing#AIRWING
|
||||||
local Coordinate = data.Coordinate
|
local Coordinate = data.Coordinate
|
||||||
local Altitude = data.Altitude
|
local Altitude = data.Altitude
|
||||||
@@ -797,6 +913,11 @@ function EASYGCICAP:_SetAwacsPatrolPoints()
|
|||||||
self:T(self.lid.."_SetAwacsPatrolPoints")
|
self:T(self.lid.."_SetAwacsPatrolPoints")
|
||||||
for _,_data in pairs(self.ManagedEWR) do
|
for _,_data in pairs(self.ManagedEWR) do
|
||||||
local data = _data --#EASYGCICAP.CapPoint
|
local data = _data --#EASYGCICAP.CapPoint
|
||||||
|
self:T("Airbasename = "..data.AirbaseName)
|
||||||
|
if not self.wings[data.AirbaseName] then
|
||||||
|
MESSAGE:New(self.lid.."You are trying to create an AWACS point for which there is no wing! "..tostring(data.AirbaseName),30,"CHECK"):ToAllIf(self.debug):ToLog()
|
||||||
|
return
|
||||||
|
end
|
||||||
local Wing = self.wings[data.AirbaseName][1] -- Ops.Airwing#AIRWING
|
local Wing = self.wings[data.AirbaseName][1] -- Ops.Airwing#AIRWING
|
||||||
local Coordinate = data.Coordinate
|
local Coordinate = data.Coordinate
|
||||||
local Altitude = data.Altitude
|
local Altitude = data.Altitude
|
||||||
@@ -816,13 +937,23 @@ function EASYGCICAP:_SetCAPPatrolPoints()
|
|||||||
self:T(self.lid.."_SetCAPPatrolPoints")
|
self:T(self.lid.."_SetCAPPatrolPoints")
|
||||||
for _,_data in pairs(self.ManagedCP) do
|
for _,_data in pairs(self.ManagedCP) do
|
||||||
local data = _data --#EASYGCICAP.CapPoint
|
local data = _data --#EASYGCICAP.CapPoint
|
||||||
|
self:T("Airbasename = "..data.AirbaseName)
|
||||||
|
if not self.wings[data.AirbaseName] then
|
||||||
|
MESSAGE:New(self.lid.."You are trying to create a CAP point for which there is no wing! "..tostring(data.AirbaseName),30,"CHECK"):ToAllIf(self.debug):ToLog()
|
||||||
|
return
|
||||||
|
end
|
||||||
local Wing = self.wings[data.AirbaseName][1] -- Ops.Airwing#AIRWING
|
local Wing = self.wings[data.AirbaseName][1] -- Ops.Airwing#AIRWING
|
||||||
local Coordinate = data.Coordinate
|
local Coordinate = data.Coordinate
|
||||||
local Altitude = data.Altitude
|
local Altitude = data.Altitude
|
||||||
local Speed = data.Speed
|
local Speed = data.Speed
|
||||||
local Heading = data.Heading
|
local Heading = data.Heading
|
||||||
local LegLength = data.LegLength
|
local LegLength = data.LegLength
|
||||||
Wing:AddPatrolPointCAP(Coordinate,Altitude,Speed,Heading,LegLength)
|
local Zone = _data.Zone
|
||||||
|
if Zone then
|
||||||
|
Wing:AddPatrolPointCAP(Zone,Altitude,Speed,Heading,LegLength)
|
||||||
|
else
|
||||||
|
Wing:AddPatrolPointCAP(Coordinate,Altitude,Speed,Heading,LegLength)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -835,6 +966,11 @@ function EASYGCICAP:_SetReconPatrolPoints()
|
|||||||
self:T(self.lid.."_SetReconPatrolPoints")
|
self:T(self.lid.."_SetReconPatrolPoints")
|
||||||
for _,_data in pairs(self.ManagedREC) do
|
for _,_data in pairs(self.ManagedREC) do
|
||||||
local data = _data --#EASYGCICAP.CapPoint
|
local data = _data --#EASYGCICAP.CapPoint
|
||||||
|
self:T("Airbasename = "..data.AirbaseName)
|
||||||
|
if not self.wings[data.AirbaseName] then
|
||||||
|
MESSAGE:New(self.lid.."You are trying to create a RECON point for which there is no wing! "..tostring(data.AirbaseName),30,"CHECK"):ToAllIf(self.debug):ToLog()
|
||||||
|
return
|
||||||
|
end
|
||||||
local Wing = self.wings[data.AirbaseName][1] -- Ops.Airwing#AIRWING
|
local Wing = self.wings[data.AirbaseName][1] -- Ops.Airwing#AIRWING
|
||||||
local Coordinate = data.Coordinate
|
local Coordinate = data.Coordinate
|
||||||
local Altitude = data.Altitude
|
local Altitude = data.Altitude
|
||||||
@@ -883,7 +1019,7 @@ end
|
|||||||
-- @param #string SquadName Squadron name - must be unique!
|
-- @param #string SquadName Squadron name - must be unique!
|
||||||
-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi
|
-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi
|
||||||
-- @param #number AirFrames Number of available airframes, e.g. 20.
|
-- @param #number AirFrames Number of available airframes, e.g. 20.
|
||||||
-- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE
|
-- @param #string Skill (optional) Skill level, e.g. AI.Skill.AVERAGE
|
||||||
-- @param #string Modex (optional) Modex to be used,e.g. 402.
|
-- @param #string Modex (optional) Modex to be used,e.g. 402.
|
||||||
-- @param #string Livery (optional) Livery name to be used.
|
-- @param #string Livery (optional) Livery name to be used.
|
||||||
-- @return #EASYGCICAP self
|
-- @return #EASYGCICAP self
|
||||||
@@ -1088,7 +1224,9 @@ function EASYGCICAP:_AddTankerSquadron(TemplateName, SquadName, AirbaseName, Air
|
|||||||
Squadron_One:SetSkill(Skill or AI.Skill.AVERAGE)
|
Squadron_One:SetSkill(Skill or AI.Skill.AVERAGE)
|
||||||
Squadron_One:SetMissionRange(self.missionrange)
|
Squadron_One:SetMissionRange(self.missionrange)
|
||||||
Squadron_One:SetRadio(Frequency,Modulation)
|
Squadron_One:SetRadio(Frequency,Modulation)
|
||||||
Squadron_One:AddTacanChannel(TACAN,TACAN)
|
if TACAN then
|
||||||
|
Squadron_One:AddTacanChannel(TACAN,TACAN)
|
||||||
|
end
|
||||||
|
|
||||||
local wing = self.wings[AirbaseName][1] -- Ops.Airwing#AIRWING
|
local wing = self.wings[AirbaseName][1] -- Ops.Airwing#AIRWING
|
||||||
|
|
||||||
@@ -1172,19 +1310,19 @@ end
|
|||||||
-- @return #boolean assigned
|
-- @return #boolean assigned
|
||||||
-- @return #number leftover
|
-- @return #number leftover
|
||||||
function EASYGCICAP:_TryAssignIntercept(ReadyFlightGroups,InterceptAuftrag,Group,WingSize)
|
function EASYGCICAP:_TryAssignIntercept(ReadyFlightGroups,InterceptAuftrag,Group,WingSize)
|
||||||
self:I("_TryAssignIntercept for size "..WingSize or 1)
|
self:T("_TryAssignIntercept for size "..WingSize or 1)
|
||||||
local assigned = false
|
local assigned = false
|
||||||
local wingsize = WingSize or 1
|
local wingsize = WingSize or 1
|
||||||
local mindist = 0
|
local mindist = 0
|
||||||
local disttable = {}
|
local disttable = {}
|
||||||
if Group and Group:IsAlive() then
|
if Group and Group:IsAlive() then
|
||||||
local gcoord = Group:GetCoordinate() or COORDINATE:New(0,0,0)
|
local gcoord = Group:GetCoordinate() or COORDINATE:New(0,0,0)
|
||||||
self:I(self.lid..string.format("Assignment for %s",Group:GetName()))
|
self:T(self.lid..string.format("Assignment for %s",Group:GetName()))
|
||||||
for _name,_FG in pairs(ReadyFlightGroups or {}) do
|
for _name,_FG in pairs(ReadyFlightGroups or {}) do
|
||||||
local FG = _FG -- Ops.FlightGroup#FLIGHTGROUP
|
local FG = _FG -- Ops.FlightGroup#FLIGHTGROUP
|
||||||
local fcoord = FG:GetCoordinate()
|
local fcoord = FG:GetCoordinate()
|
||||||
local dist = math.floor(UTILS.Round(fcoord:Get2DDistance(gcoord)/1000,1))
|
local dist = math.floor(UTILS.Round(fcoord:Get2DDistance(gcoord)/1000,1))
|
||||||
self:I(self.lid..string.format("FG %s Distance %dkm",_name,dist))
|
self:T(self.lid..string.format("FG %s Distance %dkm",_name,dist))
|
||||||
disttable[#disttable+1] = { FG=FG, dist=dist}
|
disttable[#disttable+1] = { FG=FG, dist=dist}
|
||||||
if dist>mindist then mindist=dist end
|
if dist>mindist then mindist=dist end
|
||||||
end
|
end
|
||||||
@@ -1201,7 +1339,7 @@ function EASYGCICAP:_TryAssignIntercept(ReadyFlightGroups,InterceptAuftrag,Group
|
|||||||
local cm = FG:GetMissionCurrent()
|
local cm = FG:GetMissionCurrent()
|
||||||
if cm then cm:Cancel() end
|
if cm then cm:Cancel() end
|
||||||
wingsize = wingsize - 1
|
wingsize = wingsize - 1
|
||||||
self:I(self.lid..string.format("Assigned to FG %s Distance %dkm",FG:GetName(),_entry.dist))
|
self:T(self.lid..string.format("Assigned to FG %s Distance %dkm",FG:GetName(),_entry.dist))
|
||||||
if wingsize == 0 then
|
if wingsize == 0 then
|
||||||
assigned = true
|
assigned = true
|
||||||
break
|
break
|
||||||
@@ -1231,7 +1369,7 @@ function EASYGCICAP:_AssignIntercept(Cluster)
|
|||||||
local conflictzoneset = self.ConflictZoneSet
|
local conflictzoneset = self.ConflictZoneSet
|
||||||
local ReadyFlightGroups = self.ReadyFlightGroups
|
local ReadyFlightGroups = self.ReadyFlightGroups
|
||||||
|
|
||||||
-- Aircraft?
|
-- Aircraft?
|
||||||
if Cluster.ctype ~= INTEL.Ctype.AIRCRAFT then return end
|
if Cluster.ctype ~= INTEL.Ctype.AIRCRAFT then return end
|
||||||
-- Threatlevel 0..10
|
-- Threatlevel 0..10
|
||||||
local contact = self.Intel:GetHighestThreatContact(Cluster)
|
local contact = self.Intel:GetHighestThreatContact(Cluster)
|
||||||
@@ -1276,6 +1414,10 @@ function EASYGCICAP:_AssignIntercept(Cluster)
|
|||||||
local data = _data -- #EASYGCICAP.CapPoint
|
local data = _data -- #EASYGCICAP.CapPoint
|
||||||
local name = data.AirbaseName
|
local name = data.AirbaseName
|
||||||
local zonecoord = data.Coordinate
|
local zonecoord = data.Coordinate
|
||||||
|
if data.Zone then
|
||||||
|
-- refresh coordinate in case we have a (moving) zone
|
||||||
|
zonecoord = data.Zone:GetCoordinate()
|
||||||
|
end
|
||||||
local airwing = wings[name][1]
|
local airwing = wings[name][1]
|
||||||
local coa = AIRBASE:FindByName(name):GetCoalition()
|
local coa = AIRBASE:FindByName(name):GetCoalition()
|
||||||
local samecoalitionab = coa == self.coalition and true or false
|
local samecoalitionab = coa == self.coalition and true or false
|
||||||
@@ -1377,7 +1519,7 @@ function EASYGCICAP:_StartIntel()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------
|
-------------------------------------------------------------------------
|
||||||
-- FSM Functions
|
-- TODO FSM Functions
|
||||||
-------------------------------------------------------------------------
|
-------------------------------------------------------------------------
|
||||||
|
|
||||||
--- (Internal) FSM Function onafterStart
|
--- (Internal) FSM Function onafterStart
|
||||||
@@ -1473,7 +1615,7 @@ function EASYGCICAP:onafterStatus(From,Event,To)
|
|||||||
local engage = FG:IsEngaging()
|
local engage = FG:IsEngaging()
|
||||||
local hasmissiles = FG:IsOutOfMissiles() == nil and true or false
|
local hasmissiles = FG:IsOutOfMissiles() == nil and true or false
|
||||||
local ready = hasmissiles and FG:IsFuelGood() and FG:IsAirborne()
|
local ready = hasmissiles and FG:IsFuelGood() and FG:IsAirborne()
|
||||||
--self:I(string.format("Flightgroup %s Engaging = %s Ready = %s",tostring(name),tostring(engage),tostring(ready)))
|
--self:T(string.format("Flightgroup %s Engaging = %s Ready = %s",tostring(name),tostring(engage),tostring(ready)))
|
||||||
if ready then
|
if ready then
|
||||||
self.ReadyFlightGroups[name] = FG
|
self.ReadyFlightGroups[name] = FG
|
||||||
end
|
end
|
||||||
@@ -1508,5 +1650,8 @@ end
|
|||||||
function EASYGCICAP:onafterStop(From,Event,To)
|
function EASYGCICAP:onafterStop(From,Event,To)
|
||||||
self:T({From,Event,To})
|
self:T({From,Event,To})
|
||||||
self.Intel:Stop()
|
self.Intel:Stop()
|
||||||
|
for _,_wing in pairs(self.wings or {}) do
|
||||||
|
_wing:Stop()
|
||||||
|
end
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -779,6 +779,61 @@ function FLIGHTGROUP:SetJettisonWeapons(Switch)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set the aircraft to land straight in.
|
||||||
|
-- @param #FLIGHTGROUP self
|
||||||
|
-- @return #FLIGHTGROUP self
|
||||||
|
function FLIGHTGROUP:SetOptionLandingStraightIn()
|
||||||
|
self.OptionLandingStraightIn = true
|
||||||
|
if self:GetGroup():IsAlive() then
|
||||||
|
self:GetGroup():SetOptionLandingStraightIn()
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the aircraft to land in pairs.
|
||||||
|
-- @param #FLIGHTGROUP self
|
||||||
|
-- @return #FLIGHTGROUP self
|
||||||
|
function FLIGHTGROUP:SetOptionLandingForcePair()
|
||||||
|
self.OptionLandingForcePair = true
|
||||||
|
if self:GetGroup():IsAlive() then
|
||||||
|
self:GetGroup():SetOptionLandingForcePair()
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the aircraft to NOT land in pairs.
|
||||||
|
-- @param #FLIGHTGROUP self
|
||||||
|
-- @return #FLIGHTGROUP self
|
||||||
|
function FLIGHTGROUP:SetOptionLandingRestrictPair()
|
||||||
|
self.OptionLandingRestrictPair = true
|
||||||
|
if self:GetGroup():IsAlive() then
|
||||||
|
self:GetGroup():SetOptionLandingRestrictPair()
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the aircraft to land after overhead break.
|
||||||
|
-- @param #FLIGHTGROUP self
|
||||||
|
-- @return #FLIGHTGROUP self
|
||||||
|
function FLIGHTGROUP:SetOptionLandingOverheadBreak()
|
||||||
|
self.OptionLandingOverheadBreak = true
|
||||||
|
if self:GetGroup():IsAlive() then
|
||||||
|
self:GetGroup():SetOptionLandingOverheadBreak()
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- [HELICOPTER] Set the aircraft to prefer takeoff and landing vertically.
|
||||||
|
-- @param #FLIGHTGROUP self
|
||||||
|
-- @return #FLIGHTGROUP self
|
||||||
|
function FLIGHTGROUP:SetOptionPreferVertical()
|
||||||
|
self.OptionPreferVertical = true
|
||||||
|
if self:GetGroup():IsAlive() then
|
||||||
|
self:GetGroup():OptionPreferVerticalLanding()
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Set if group is ready for taxi/takeoff if controlled by a `FLIGHTCONTROL`.
|
--- Set if group is ready for taxi/takeoff if controlled by a `FLIGHTCONTROL`.
|
||||||
-- @param #FLIGHTGROUP self
|
-- @param #FLIGHTGROUP self
|
||||||
-- @param #boolean ReadyTO If `true`, flight is ready for takeoff.
|
-- @param #boolean ReadyTO If `true`, flight is ready for takeoff.
|
||||||
@@ -2002,6 +2057,9 @@ function FLIGHTGROUP:onafterElementAirborne(From, Event, To, Element)
|
|||||||
|
|
||||||
-- Debug info.
|
-- Debug info.
|
||||||
self:T2(self.lid..string.format("Element airborne %s", Element.name))
|
self:T2(self.lid..string.format("Element airborne %s", Element.name))
|
||||||
|
|
||||||
|
-- Set parking spot to free. Also for FC. This is usually done after taxiing but doing it here in case the group is teleported.
|
||||||
|
self:_SetElementParkingFree(Element)
|
||||||
|
|
||||||
-- Set element status.
|
-- Set element status.
|
||||||
self:_UpdateStatus(Element, OPSGROUP.ElementStatus.AIRBORNE)
|
self:_UpdateStatus(Element, OPSGROUP.ElementStatus.AIRBORNE)
|
||||||
@@ -3076,7 +3134,7 @@ function FLIGHTGROUP:onbeforeLandAtAirbase(From, Event, To, airbase)
|
|||||||
local Tsuspend=nil
|
local Tsuspend=nil
|
||||||
|
|
||||||
if airbase==nil then
|
if airbase==nil then
|
||||||
self:T(self.lid.."ERROR: Airbase is nil in LandAtAirase() call!")
|
self:T(self.lid.."ERROR: Airbase is nil in LandAtAirbase() call!")
|
||||||
allowed=false
|
allowed=false
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -4494,6 +4552,11 @@ function FLIGHTGROUP:GetParkingSpot(element, maxdist, airbase)
|
|||||||
-- Airbase.
|
-- Airbase.
|
||||||
airbase=airbase or self:GetClosestAirbase()
|
airbase=airbase or self:GetClosestAirbase()
|
||||||
|
|
||||||
|
if airbase == nil then
|
||||||
|
self:T(self.lid.."No airbase found for element "..element.name)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
-- Parking table of airbase.
|
-- Parking table of airbase.
|
||||||
local parking=airbase.parking --:GetParkingSpotsTable()
|
local parking=airbase.parking --:GetParkingSpotsTable()
|
||||||
|
|
||||||
@@ -4604,10 +4667,12 @@ function FLIGHTGROUP:GetParking(airbase)
|
|||||||
local coords={}
|
local coords={}
|
||||||
for clientname, client in pairs(clients) do
|
for clientname, client in pairs(clients) do
|
||||||
local template=_DATABASE:GetGroupTemplateFromUnitName(clientname)
|
local template=_DATABASE:GetGroupTemplateFromUnitName(clientname)
|
||||||
local units=template.units
|
if template then
|
||||||
for i,unit in pairs(units) do
|
local units=template.units
|
||||||
local coord=COORDINATE:New(unit.x, unit.alt, unit.y)
|
for i,unit in pairs(units) do
|
||||||
coords[unit.name]=coord
|
local coord=COORDINATE:New(unit.x, unit.alt, unit.y)
|
||||||
|
coords[unit.name]=coord
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return coords
|
return coords
|
||||||
@@ -4964,7 +5029,7 @@ function FLIGHTGROUP:_UpdateMenu(delay)
|
|||||||
|
|
||||||
-- Message to group.
|
-- Message to group.
|
||||||
MESSAGE:New(text, 5):ToGroup(self.group)
|
MESSAGE:New(text, 5):ToGroup(self.group)
|
||||||
self:I(self.lid..text)
|
self:T(self.lid..text)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get current position of player.
|
-- Get current position of player.
|
||||||
|
|||||||
@@ -662,6 +662,15 @@ function LEGION:CheckMissionQueue()
|
|||||||
if mission:IsNotOver() and mission:IsReadyToCancel() then
|
if mission:IsNotOver() and mission:IsReadyToCancel() then
|
||||||
mission:Cancel()
|
mission:Cancel()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Housekeeping
|
||||||
|
local TNow = timer.getTime()
|
||||||
|
if mission:IsOver() and mission:IsNotRepeatable() and mission.DeletionTimstamp == nil then
|
||||||
|
mission.DeletionTimstamp = TNow
|
||||||
|
end
|
||||||
|
if mission.DeletionTimstamp ~= nil and TNow - mission.DeletionTimstamp > 1800 then
|
||||||
|
mission = nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check that runway is operational and that carrier is not recovering.
|
-- Check that runway is operational and that carrier is not recovering.
|
||||||
@@ -761,7 +770,7 @@ function LEGION:CheckMissionQueue()
|
|||||||
-- Reduce number of reinforcements.
|
-- Reduce number of reinforcements.
|
||||||
if reinforce then
|
if reinforce then
|
||||||
mission.reinforce=mission.reinforce-#assets
|
mission.reinforce=mission.reinforce-#assets
|
||||||
self:I(self.lid..string.format("Reinforced with N=%d Nreinforce=%d", #assets, mission.reinforce))
|
self:T(self.lid..string.format("Reinforced with N=%d Nreinforce=%d", #assets, mission.reinforce))
|
||||||
end
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@@ -1823,6 +1832,7 @@ function LEGION:_CreateFlightGroup(asset)
|
|||||||
---
|
---
|
||||||
|
|
||||||
opsgroup=ARMYGROUP:New(asset.spawngroupname)
|
opsgroup=ARMYGROUP:New(asset.spawngroupname)
|
||||||
|
opsgroup:SetValidateAndRepositionGroundUnits(self.ValidateAndRepositionGroundUnits)
|
||||||
|
|
||||||
elseif self:IsFleet() then
|
elseif self:IsFleet() then
|
||||||
|
|
||||||
@@ -2513,9 +2523,12 @@ function LEGION._GetCohorts(Legions, Cohorts, Operation, OpsQueue)
|
|||||||
for _,_legion in pairs(Legions or {}) do
|
for _,_legion in pairs(Legions or {}) do
|
||||||
local legion=_legion --Ops.Legion#LEGION
|
local legion=_legion --Ops.Legion#LEGION
|
||||||
|
|
||||||
-- Check that runway is operational.
|
-- Check that runway is operational.
|
||||||
local Runway=legion:IsAirwing() and legion:IsRunwayOperational() or true
|
local Runway=true
|
||||||
|
if legion:IsAirwing() then
|
||||||
|
Runway=legion:IsRunwayOperational() and legion.airbase and legion.airbase:GetCoalition() == legion:GetCoalition()
|
||||||
|
end
|
||||||
|
|
||||||
-- Legion has to be running.
|
-- Legion has to be running.
|
||||||
if legion:IsRunning() and Runway then
|
if legion:IsRunning() and Runway then
|
||||||
|
|
||||||
|
|||||||
@@ -445,6 +445,7 @@ OPSGROUP.TaskType={
|
|||||||
-- @field Wrapper.Marker#MARKER marker Marker on the F10 map.
|
-- @field Wrapper.Marker#MARKER marker Marker on the F10 map.
|
||||||
-- @field #string formation Ground formation. Similar to action but on/off road.
|
-- @field #string formation Ground formation. Similar to action but on/off road.
|
||||||
-- @field #number missionUID Mission UID (Auftragsnr) this waypoint belongs to.
|
-- @field #number missionUID Mission UID (Auftragsnr) this waypoint belongs to.
|
||||||
|
-- @field Navigation.FlightPlan#FLIGHTPLAN flightplan Flightplan this waypoint belongs to.
|
||||||
|
|
||||||
--- Cargo Carrier status.
|
--- Cargo Carrier status.
|
||||||
-- @type OPSGROUP.CarrierStatus
|
-- @type OPSGROUP.CarrierStatus
|
||||||
@@ -573,6 +574,11 @@ function OPSGROUP:New(group)
|
|||||||
|
|
||||||
-- Set DCS group and controller.
|
-- Set DCS group and controller.
|
||||||
self.dcsgroup=self:GetDCSGroup()
|
self.dcsgroup=self:GetDCSGroup()
|
||||||
|
|
||||||
|
if not self.dcsgroup then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
self.controller=self.dcsgroup:getController()
|
self.controller=self.dcsgroup:getController()
|
||||||
|
|
||||||
-- Category.
|
-- Category.
|
||||||
@@ -4631,7 +4637,12 @@ function OPSGROUP:_UpdateTask(Task, Mission)
|
|||||||
self:T(self.lid..string.format("Zone %s captured ==> Task DONE!", zoneCurr:GetName()))
|
self:T(self.lid..string.format("Zone %s captured ==> Task DONE!", zoneCurr:GetName()))
|
||||||
|
|
||||||
-- Task done.
|
-- Task done.
|
||||||
self:TaskDone(Task)
|
if Task.StayInZoneTime then
|
||||||
|
local stay = Task.StayInZoneTime
|
||||||
|
self:__TaskDone(stay,Task)
|
||||||
|
else
|
||||||
|
self:TaskDone(Task)
|
||||||
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
-- Current zone NOT captured yet ==> Find Target
|
-- Current zone NOT captured yet ==> Find Target
|
||||||
@@ -5589,10 +5600,13 @@ function OPSGROUP:onafterUnpauseMission(From, Event, To)
|
|||||||
-- Debug info.
|
-- Debug info.
|
||||||
self:T(self.lid..string.format("Unpausing mission %s [%s]", mission:GetName(), mission:GetType()))
|
self:T(self.lid..string.format("Unpausing mission %s [%s]", mission:GetName(), mission:GetType()))
|
||||||
|
|
||||||
|
-- Set state of mission, e.g. for not teleporting again
|
||||||
|
mission.unpaused=true
|
||||||
|
|
||||||
-- Start mission.
|
-- Start mission.
|
||||||
self:MissionStart(mission)
|
self:MissionStart(mission)
|
||||||
|
|
||||||
-- Remove mission from
|
-- Remove mission from pausedmissions queue
|
||||||
for i,mid in pairs(self.pausedmissions) do
|
for i,mid in pairs(self.pausedmissions) do
|
||||||
--self:T(self.lid..string.format("Checking paused mission", mid))
|
--self:T(self.lid..string.format("Checking paused mission", mid))
|
||||||
if mid==mission.auftragsnummer then
|
if mid==mission.auftragsnummer then
|
||||||
@@ -5727,7 +5741,7 @@ function OPSGROUP:onafterMissionDone(From, Event, To, Mission)
|
|||||||
-- Decrease patrol data.
|
-- Decrease patrol data.
|
||||||
if Mission.patroldata then
|
if Mission.patroldata then
|
||||||
Mission.patroldata.noccupied=Mission.patroldata.noccupied-1
|
Mission.patroldata.noccupied=Mission.patroldata.noccupied-1
|
||||||
AIRWING.UpdatePatrolPointMarker(Mission.patroldata)
|
AIRWING.UpdatePatrolPointMarker(self,Mission.patroldata)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Switch auto engage detected off. This IGNORES that engage detected had been activated for the group!
|
-- Switch auto engage detected off. This IGNORES that engage detected had been activated for the group!
|
||||||
@@ -6232,7 +6246,7 @@ function OPSGROUP:RouteToMission(mission, delay)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Check if group is mobile. Note that some immobile units report a speed of 1 m/s = 3.6 km/h.
|
-- Check if group is mobile. Note that some immobile units report a speed of 1 m/s = 3.6 km/h.
|
||||||
if self.speedMax<=3.6 or mission.teleport then
|
if (self.speedMax<=3.6 or mission.teleport) and not mission.unpaused then
|
||||||
|
|
||||||
-- Teleport to waypoint coordinate. Mission will not be paused.
|
-- Teleport to waypoint coordinate. Mission will not be paused.
|
||||||
self:Teleport(waypointcoord, nil, true)
|
self:Teleport(waypointcoord, nil, true)
|
||||||
@@ -7531,7 +7545,7 @@ end
|
|||||||
function OPSGROUP:onafterElementDead(From, Event, To, Element)
|
function OPSGROUP:onafterElementDead(From, Event, To, Element)
|
||||||
|
|
||||||
-- Debug info.
|
-- Debug info.
|
||||||
self:I(self.lid..string.format("Element dead %s at t=%.3f", Element.name, timer.getTime()))
|
self:T(self.lid..string.format("Element dead %s at t=%.3f", Element.name, timer.getTime()))
|
||||||
|
|
||||||
-- Set element status.
|
-- Set element status.
|
||||||
self:_UpdateStatus(Element, OPSGROUP.ElementStatus.DEAD)
|
self:_UpdateStatus(Element, OPSGROUP.ElementStatus.DEAD)
|
||||||
@@ -7844,8 +7858,13 @@ function OPSGROUP:_Spawn(Delay, Template)
|
|||||||
-- Debug output.
|
-- Debug output.
|
||||||
self:T2({Template=Template})
|
self:T2({Template=Template})
|
||||||
|
|
||||||
|
if self:IsArmygroup() and self.ValidateAndRepositionGroundUnits then
|
||||||
|
UTILS.ValidateAndRepositionGroundUnits(Template.units)
|
||||||
|
end
|
||||||
|
|
||||||
-- Spawn new group.
|
-- Spawn new group.
|
||||||
self.group=_DATABASE:Spawn(Template)
|
self.group=_DATABASE:Spawn(Template)
|
||||||
|
self.group:SetValidateAndRepositionGroundUnits(self.ValidateAndRepositionGroundUnits)
|
||||||
--local countryID=self.group:GetCountry()
|
--local countryID=self.group:GetCountry()
|
||||||
--local categoryID=self.group:GetCategory()
|
--local categoryID=self.group:GetCategory()
|
||||||
--local dcsgroup=coalition.addGroup(countryID, categoryID, Template)
|
--local dcsgroup=coalition.addGroup(countryID, categoryID, Template)
|
||||||
@@ -8082,7 +8101,7 @@ function OPSGROUP:onafterStop(From, Event, To)
|
|||||||
_DATABASE.FLIGHTGROUPS[self.groupname]=nil
|
_DATABASE.FLIGHTGROUPS[self.groupname]=nil
|
||||||
|
|
||||||
-- Debug output.
|
-- Debug output.
|
||||||
self:I(self.lid.."STOPPED! Unhandled events, cleared scheduler and removed from _DATABASE")
|
self:T(self.lid.."STOPPED! Unhandled events, cleared scheduler and removed from _DATABASE")
|
||||||
end
|
end
|
||||||
|
|
||||||
--- On after "OutOfAmmo" event.
|
--- On after "OutOfAmmo" event.
|
||||||
@@ -11349,9 +11368,9 @@ function OPSGROUP:_SimpleTaskFunction(Function, uid)
|
|||||||
return DCSTask
|
return DCSTask
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Enhance waypoint table.
|
--- Enhanced waypoint table.
|
||||||
-- @param #OPSGROUP self
|
-- @param #OPSGROUP self
|
||||||
-- @param #OPSGROUP.Waypoint Waypoint data.
|
-- @param #OPSGROUP.Waypoint waypoint Waypoint data.
|
||||||
-- @return #OPSGROUP.Waypoint Modified waypoint data.
|
-- @return #OPSGROUP.Waypoint Modified waypoint data.
|
||||||
function OPSGROUP:_CreateWaypoint(waypoint)
|
function OPSGROUP:_CreateWaypoint(waypoint)
|
||||||
|
|
||||||
@@ -12779,7 +12798,7 @@ function OPSGROUP:_UpdatePosition()
|
|||||||
self.positionLast=self.position or self:GetVec3()
|
self.positionLast=self.position or self:GetVec3()
|
||||||
self.headingLast=self.heading or self:GetHeading()
|
self.headingLast=self.heading or self:GetHeading()
|
||||||
self.orientXLast=self.orientX or self:GetOrientationX()
|
self.orientXLast=self.orientX or self:GetOrientationX()
|
||||||
self.velocityLast=self.velocity or self.group:GetVelocityMPS()
|
self.velocityLast=self.velocity or self.group:GetVelocityMPS()
|
||||||
|
|
||||||
-- Current state.
|
-- Current state.
|
||||||
self.position=self:GetVec3()
|
self.position=self:GetVec3()
|
||||||
@@ -13607,20 +13626,24 @@ end
|
|||||||
-- @return Core.Point#COORDINATE The coordinate of the object.
|
-- @return Core.Point#COORDINATE The coordinate of the object.
|
||||||
function OPSGROUP:_CoordinateFromObject(Object)
|
function OPSGROUP:_CoordinateFromObject(Object)
|
||||||
|
|
||||||
|
env.info("FF coordfrom object")
|
||||||
if Object then
|
if Object then
|
||||||
if Object:IsInstanceOf("COORDINATE") then
|
if VECTOR._IsVector(Object) then
|
||||||
|
env.info("FF VECTOR")
|
||||||
|
return Object:GetCoordinate()
|
||||||
|
elseif Object:IsInstanceOf("COORDINATE") then
|
||||||
return Object
|
return Object
|
||||||
else
|
else
|
||||||
if Object:IsInstanceOf("POSITIONABLE") or Object:IsInstanceOf("ZONE_BASE") then
|
if Object:IsInstanceOf("POSITIONABLE") or Object:IsInstanceOf("ZONE_BASE") then
|
||||||
self:T(self.lid.."WARNING: Coordinate is not a COORDINATE but a POSITIONABLE or ZONE. Trying to get coordinate")
|
self:E(self.lid.."WARNING: Coordinate is not a COORDINATE but a POSITIONABLE or ZONE. Trying to get coordinate")
|
||||||
local coord=Object:GetCoordinate()
|
local coord=Object:GetCoordinate()
|
||||||
return coord
|
return coord
|
||||||
else
|
else
|
||||||
self:T(self.lid.."ERROR: Coordinate is neither a COORDINATE nor any POSITIONABLE or ZONE!")
|
self:E(self.lid.."ERROR: Coordinate is neither a COORDINATE nor any POSITIONABLE or ZONE!")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
self:T(self.lid.."ERROR: Object passed is nil!")
|
self:E(self.lid.."ERROR: Object passed is nil!")
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -13952,6 +13975,15 @@ function OPSGROUP:_GetDetectedTarget()
|
|||||||
return targetgroup, targetdist
|
return targetgroup, targetdist
|
||||||
end
|
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 OPSGROUP:SetValidateAndRepositionGroundUnits(Enabled)
|
||||||
|
self.ValidateAndRepositionGroundUnits = Enabled
|
||||||
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -1544,7 +1544,7 @@ end
|
|||||||
-- @param #PLAYERRECCE self
|
-- @param #PLAYERRECCE self
|
||||||
-- @param #number Frequency Frequency to be used. Can also be given as a table of multiple frequencies, e.g. 271 or {127,251}. There needs to be exactly the same number of modulations!
|
-- @param #number Frequency Frequency to be used. Can also be given as a table of multiple frequencies, e.g. 271 or {127,251}. There needs to be exactly the same number of modulations!
|
||||||
-- @param #number Modulation Modulation to be used. Can also be given as a table of multiple modulations, e.g. radio.modulation.AM or {radio.modulation.FM,radio.modulation.AM}. There needs to be exactly the same number of frequencies!
|
-- @param #number Modulation Modulation to be used. Can also be given as a table of multiple modulations, e.g. radio.modulation.AM or {radio.modulation.FM,radio.modulation.AM}. There needs to be exactly the same number of frequencies!
|
||||||
-- @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 (Optional) Defaults to "male"
|
-- @param #string Gender (Optional) Defaults to "male"
|
||||||
-- @param #string Culture (Optional) Defaults to "en-US"
|
-- @param #string Culture (Optional) Defaults to "en-US"
|
||||||
-- @param #number Port (Optional) Defaults to 5002
|
-- @param #number Port (Optional) Defaults to 5002
|
||||||
@@ -1556,7 +1556,7 @@ end
|
|||||||
-- @return #PLAYERRECCE self
|
-- @return #PLAYERRECCE self
|
||||||
function PLAYERRECCE:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,Backend)
|
function PLAYERRECCE:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,Backend)
|
||||||
self:T(self.lid.."SetSRS")
|
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.Gender = Gender or MSRS.gender or "male" --
|
||||||
self.Culture = Culture or MSRS.culture or "en-US" --
|
self.Culture = Culture or MSRS.culture or "en-US" --
|
||||||
self.Port = Port or MSRS.port or 5002 --
|
self.Port = Port or MSRS.port or 5002 --
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
-- ===
|
-- ===
|
||||||
-- @module Ops.PlayerTask
|
-- @module Ops.PlayerTask
|
||||||
-- @image OPS_PlayerTask.jpg
|
-- @image OPS_PlayerTask.jpg
|
||||||
-- @date Last Update April 2025
|
-- @date Last Update May 2025
|
||||||
|
|
||||||
|
|
||||||
do
|
do
|
||||||
@@ -98,7 +98,7 @@ PLAYERTASK = {
|
|||||||
|
|
||||||
--- PLAYERTASK class version.
|
--- PLAYERTASK class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
PLAYERTASK.version="0.1.26"
|
PLAYERTASK.version="0.1.28"
|
||||||
|
|
||||||
--- Generic task condition.
|
--- Generic task condition.
|
||||||
-- @type PLAYERTASK.Condition
|
-- @type PLAYERTASK.Condition
|
||||||
@@ -387,6 +387,14 @@ function PLAYERTASK:_CheckCaptureOpsZoneSuccess(OpsZone, CaptureSquadGroupNamePr
|
|||||||
return OpsZone:GetOwner() == Coalition and isClientInZone and isCaptureGroupInZone
|
return OpsZone:GetOwner() == Coalition and isClientInZone and isCaptureGroupInZone
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- [User] Override this function in order to implement custom logic if a player can join a task or not.
|
||||||
|
-- @param #PLAYERTASK self
|
||||||
|
-- @param Wrapper.Group#GROUP Group
|
||||||
|
-- @param Wrapper.Client#CLIENT Client
|
||||||
|
-- @return #boolean Outcome True if player can join the task, false if not
|
||||||
|
function PLAYERTASK:CanJoinTask(Group, Client)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
--- [Internal] Add a PLAYERTASKCONTROLLER for this task
|
--- [Internal] Add a PLAYERTASKCONTROLLER for this task
|
||||||
-- @param #PLAYERTASK self
|
-- @param #PLAYERTASK self
|
||||||
@@ -1227,7 +1235,10 @@ function PLAYERTASK:onafterFailed(From, Event, To)
|
|||||||
self.TargetMarker:Remove()
|
self.TargetMarker:Remove()
|
||||||
end
|
end
|
||||||
self.FinalState = "Failed"
|
self.FinalState = "Failed"
|
||||||
self:__Done(-1)
|
if self.TaskController then
|
||||||
|
self.TaskController:__TaskFailed(-1,self)
|
||||||
|
end
|
||||||
|
self:__Done(-1.5)
|
||||||
end
|
end
|
||||||
if self.TaskController.Scoring then
|
if self.TaskController.Scoring then
|
||||||
local clients,count = self:GetClientObjects()
|
local clients,count = self:GetClientObjects()
|
||||||
@@ -1440,9 +1451,9 @@ do
|
|||||||
-- taskmanager:AddRejectZone(ZONE:FindByName("RejectZone"))
|
-- taskmanager:AddRejectZone(ZONE:FindByName("RejectZone"))
|
||||||
--
|
--
|
||||||
-- -- Set up using SRS for messaging
|
-- -- Set up using SRS for messaging
|
||||||
-- local hereSRSPath = "C:\\Program Files\\DCS-SimpleRadio-Standalone"
|
-- local hereSRSPath = "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio"
|
||||||
-- local hereSRSPort = 5002
|
-- local hereSRSPort = 5002
|
||||||
-- -- local hereSRSGoogle = "C:\\Program Files\\DCS-SimpleRadio-Standalone\\yourkey.json"
|
-- -- local hereSRSGoogle = "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio\\yourkey.json"
|
||||||
-- taskmanager:SetSRS({130,255},{radio.modulation.AM,radio.modulation.AM},hereSRSPath,"female","en-GB",hereSRSPort,"Microsoft Hazel Desktop",0.7,hereSRSGoogle)
|
-- taskmanager:SetSRS({130,255},{radio.modulation.AM,radio.modulation.AM},hereSRSPath,"female","en-GB",hereSRSPort,"Microsoft Hazel Desktop",0.7,hereSRSGoogle)
|
||||||
--
|
--
|
||||||
-- -- Controller will announce itself under these broadcast frequencies, handy to use cold-start frequencies here of your aircraft
|
-- -- Controller will announce itself under these broadcast frequencies, handy to use cold-start frequencies here of your aircraft
|
||||||
@@ -1951,7 +1962,7 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter)
|
|||||||
self.taskinfomenu = false
|
self.taskinfomenu = false
|
||||||
self.activehasinfomenu = false
|
self.activehasinfomenu = false
|
||||||
self.MenuName = nil
|
self.MenuName = nil
|
||||||
self.menuitemlimit = 5
|
self.menuitemlimit = 6
|
||||||
self.holdmenutime = 30
|
self.holdmenutime = 30
|
||||||
|
|
||||||
self.MarkerReadOnly = false
|
self.MarkerReadOnly = false
|
||||||
@@ -2581,7 +2592,7 @@ function PLAYERTASKCONTROLLER:SetMenuOptions(InfoMenu,ItemLimit,HoldTime)
|
|||||||
if self.activehasinfomenu then
|
if self.activehasinfomenu then
|
||||||
self:EnableTaskInfoMenu()
|
self:EnableTaskInfoMenu()
|
||||||
end
|
end
|
||||||
self.menuitemlimit = ItemLimit or 5
|
self.menuitemlimit = ItemLimit+1 or 6
|
||||||
self.holdmenutime = HoldTime or 30
|
self.holdmenutime = HoldTime or 30
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -3486,7 +3497,7 @@ end
|
|||||||
-- @param #PLAYERTASKCONTROLLER self
|
-- @param #PLAYERTASKCONTROLLER self
|
||||||
-- @param Ops.PlayerTask#PLAYERTASK PlayerTask
|
-- @param Ops.PlayerTask#PLAYERTASK PlayerTask
|
||||||
-- @param #boolean Silent If true, make no "has new task" announcement
|
-- @param #boolean Silent If true, make no "has new task" announcement
|
||||||
-- @param #boolen TaskFilter If true, apply the white/black-list task filters here, also
|
-- @param #boolean TaskFilter If true, apply the white/black-list task filters here, also
|
||||||
-- @return #PLAYERTASKCONTROLLER self
|
-- @return #PLAYERTASKCONTROLLER self
|
||||||
-- @usage
|
-- @usage
|
||||||
-- Example to create a PLAYERTASK of type CTLD and give Players 10 minutes to complete:
|
-- Example to create a PLAYERTASK of type CTLD and give Players 10 minutes to complete:
|
||||||
@@ -3530,6 +3541,16 @@ function PLAYERTASKCONTROLLER:AddPlayerTaskToQueue(PlayerTask,Silent,TaskFilter)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- [User] Override this function in order to implement custom logic if a player can join a task or not.
|
||||||
|
-- @param #PLAYERTASKCONTROLLER self
|
||||||
|
-- @param Ops.PlayerTask#PLAYERTASK Task
|
||||||
|
-- @param Wrapper.Group#GROUP Group
|
||||||
|
-- @param Wrapper.Client#CLIENT Client
|
||||||
|
-- @return #boolean Outcome True if player can join the task, false if not
|
||||||
|
function PLAYERTASKCONTROLLER:CanJoinTask(Task, Group, Client)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
--- [Internal] Join a player to a task
|
--- [Internal] Join a player to a task
|
||||||
-- @param #PLAYERTASKCONTROLLER self
|
-- @param #PLAYERTASKCONTROLLER self
|
||||||
-- @param Ops.PlayerTask#PLAYERTASK Task
|
-- @param Ops.PlayerTask#PLAYERTASK Task
|
||||||
@@ -3540,6 +3561,15 @@ end
|
|||||||
function PLAYERTASKCONTROLLER:_JoinTask(Task, Force, Group, Client)
|
function PLAYERTASKCONTROLLER:_JoinTask(Task, Force, Group, Client)
|
||||||
self:T({Force, Group, Client})
|
self:T({Force, Group, Client})
|
||||||
self:T(self.lid.."_JoinTask")
|
self:T(self.lid.."_JoinTask")
|
||||||
|
|
||||||
|
if not self:CanJoinTask(Task, Group, Client) then
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
if not Task:CanJoinTask(Group, Client) then
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
local force = false
|
local force = false
|
||||||
if type(Force) == "boolean" then
|
if type(Force) == "boolean" then
|
||||||
force = Force
|
force = Force
|
||||||
@@ -4606,7 +4636,7 @@ end
|
|||||||
-- @param #PLAYERTASKCONTROLLER self
|
-- @param #PLAYERTASKCONTROLLER self
|
||||||
-- @param #number Frequency Frequency to be used. Can also be given as a table of multiple frequencies, e.g. 271 or {127,251}. There needs to be exactly the same number of modulations!
|
-- @param #number Frequency Frequency to be used. Can also be given as a table of multiple frequencies, e.g. 271 or {127,251}. There needs to be exactly the same number of modulations!
|
||||||
-- @param #number Modulation Modulation to be used. Can also be given as a table of multiple modulations, e.g. radio.modulation.AM or {radio.modulation.FM,radio.modulation.AM}. There needs to be exactly the same number of frequencies!
|
-- @param #number Modulation Modulation to be used. Can also be given as a table of multiple modulations, e.g. radio.modulation.AM or {radio.modulation.FM,radio.modulation.AM}. There needs to be exactly the same number of frequencies!
|
||||||
-- @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 (Optional) Defaults to "male"
|
-- @param #string Gender (Optional) Defaults to "male"
|
||||||
-- @param #string Culture (Optional) Defaults to "en-US"
|
-- @param #string Culture (Optional) Defaults to "en-US"
|
||||||
-- @param #number Port (Optional) Defaults to 5002
|
-- @param #number Port (Optional) Defaults to 5002
|
||||||
@@ -4620,7 +4650,7 @@ end
|
|||||||
-- @return #PLAYERTASKCONTROLLER self
|
-- @return #PLAYERTASKCONTROLLER self
|
||||||
function PLAYERTASKCONTROLLER:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,AccessKey,Coordinate,Backend)
|
function PLAYERTASKCONTROLLER:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,AccessKey,Coordinate,Backend)
|
||||||
self:T(self.lid.."SetSRS")
|
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.Gender = Gender or MSRS.gender or "male" --
|
||||||
self.Culture = Culture or MSRS.culture or "en-US" --
|
self.Culture = Culture or MSRS.culture or "en-US" --
|
||||||
self.Port = Port or MSRS.port or 5002 --
|
self.Port = Port or MSRS.port or 5002 --
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ end
|
|||||||
|
|
||||||
--- Checks if a point is contained within the circle.
|
--- Checks if a point is contained within the circle.
|
||||||
-- @param #table point The point to check
|
-- @param #table point The point to check
|
||||||
-- @return #bool True if the point is contained, false otherwise
|
-- @return #boolean True if the point is contained, false otherwise
|
||||||
function CIRCLE:ContainsPoint(point)
|
function CIRCLE:ContainsPoint(point)
|
||||||
if ((point.x - self.CenterVec2.x) ^ 2 + (point.y - self.CenterVec2.y) ^ 2) ^ 0.5 <= self.Radius then
|
if ((point.x - self.CenterVec2.x) ^ 2 + (point.y - self.CenterVec2.y) ^ 2) ^ 0.5 <= self.Radius then
|
||||||
return true
|
return true
|
||||||
@@ -226,6 +226,11 @@ end
|
|||||||
--- Returns a random Vec2 within the circle.
|
--- Returns a random Vec2 within the circle.
|
||||||
-- @return #table The random Vec2
|
-- @return #table The random Vec2
|
||||||
function CIRCLE:GetRandomVec2()
|
function CIRCLE:GetRandomVec2()
|
||||||
|
|
||||||
|
math.random()
|
||||||
|
math.random()
|
||||||
|
math.random()
|
||||||
|
|
||||||
local angle = math.random() * 2 * math.pi
|
local angle = math.random() * 2 * math.pi
|
||||||
|
|
||||||
local rx = math.random(0, self.Radius) * math.cos(angle) + self.CenterVec2.x
|
local rx = math.random(0, self.Radius) * math.cos(angle) + self.CenterVec2.x
|
||||||
@@ -237,6 +242,11 @@ end
|
|||||||
--- Returns a random Vec2 on the border of the circle.
|
--- Returns a random Vec2 on the border of the circle.
|
||||||
-- @return #table The random Vec2
|
-- @return #table The random Vec2
|
||||||
function CIRCLE:GetRandomVec2OnBorder()
|
function CIRCLE:GetRandomVec2OnBorder()
|
||||||
|
|
||||||
|
math.random()
|
||||||
|
math.random()
|
||||||
|
math.random()
|
||||||
|
|
||||||
local angle = math.random() * 2 * math.pi
|
local angle = math.random() * 2 * math.pi
|
||||||
|
|
||||||
local rx = self.Radius * math.cos(angle) + self.CenterVec2.x
|
local rx = self.Radius * math.cos(angle) + self.CenterVec2.x
|
||||||
|
|||||||
@@ -352,6 +352,7 @@ end
|
|||||||
--- Returns a random Vec2 within the polygon. The Vec2 is weighted by the areas of the triangles that make up the polygon.
|
--- Returns a random Vec2 within the polygon. The Vec2 is weighted by the areas of the triangles that make up the polygon.
|
||||||
-- @return #table The random Vec2
|
-- @return #table The random Vec2
|
||||||
function POLYGON:GetRandomVec2()
|
function POLYGON:GetRandomVec2()
|
||||||
|
|
||||||
local weights = {}
|
local weights = {}
|
||||||
for _, triangle in pairs(self.Triangles) do
|
for _, triangle in pairs(self.Triangles) do
|
||||||
weights[triangle] = triangle.SurfaceArea / self.SurfaceArea
|
weights[triangle] = triangle.SurfaceArea / self.SurfaceArea
|
||||||
|
|||||||
@@ -73,6 +73,11 @@ end
|
|||||||
-- @param #table points The points of the triangle, or 3 other points if you're just using the TRIANGLE class without an object of it
|
-- @param #table points The points of the triangle, or 3 other points if you're just using the TRIANGLE class without an object of it
|
||||||
-- @return #table The random Vec2
|
-- @return #table The random Vec2
|
||||||
function TRIANGLE:GetRandomVec2(points)
|
function TRIANGLE:GetRandomVec2(points)
|
||||||
|
|
||||||
|
math.random()
|
||||||
|
math.random()
|
||||||
|
math.random()
|
||||||
|
|
||||||
points = points or self.Points
|
points = points or self.Points
|
||||||
local pt = {math.random(), math.random()}
|
local pt = {math.random(), math.random()}
|
||||||
table.sort(pt)
|
table.sort(pt)
|
||||||
|
|||||||
@@ -443,28 +443,32 @@ MSRS.Voices = {
|
|||||||
["en_AU_Standard_B"] = 'en-AU-Standard-B', -- [2] MALE
|
["en_AU_Standard_B"] = 'en-AU-Standard-B', -- [2] MALE
|
||||||
["en_AU_Standard_C"] = 'en-AU-Standard-C', -- [3] FEMALE
|
["en_AU_Standard_C"] = 'en-AU-Standard-C', -- [3] FEMALE
|
||||||
["en_AU_Standard_D"] = 'en-AU-Standard-D', -- [4] MALE
|
["en_AU_Standard_D"] = 'en-AU-Standard-D', -- [4] MALE
|
||||||
["en_IN_Standard_A"] = 'en-IN-Standard-A', -- [5] FEMALE
|
-- IN
|
||||||
["en_IN_Standard_B"] = 'en-IN-Standard-B', -- [6] MALE
|
["en_IN_Standard_A"] = 'en-IN-Standard-A', -- Female
|
||||||
["en_IN_Standard_C"] = 'en-IN-Standard-C', -- [7] MALE
|
["en_IN_Standard_B"] = 'en-IN-Standard-B', -- Male
|
||||||
["en_IN_Standard_D"] = 'en-IN-Standard-D', -- [8] FEMALE
|
["en_IN_Standard_C"] = 'en-IN-Standard-C', -- Male
|
||||||
|
["en_IN_Standard_D"] = 'en-IN-Standard-D', -- Female
|
||||||
|
["en_IN_Standard_E"] = 'en-IN-Standard-E', -- Female
|
||||||
|
["en_IN_Standard_F"] = 'en-IN-Standard-F', -- Male
|
||||||
-- 2025 changes
|
-- 2025 changes
|
||||||
["en_GB_Standard_A"] = 'en-GB-Standard-N', -- [9] FEMALE
|
["en_GB_Standard_A"] = 'en-GB-Standard-A', -- Female
|
||||||
["en_GB_Standard_B"] = 'en-GB-Standard-O', -- [10] MALE
|
["en_GB_Standard_B"] = 'en-GB-Standard-B', -- Male
|
||||||
["en_GB_Standard_C"] = 'en-GB-Standard-N', -- [11] FEMALE
|
["en_GB_Standard_C"] = 'en-GB-Standard-C', -- Female
|
||||||
["en_GB_Standard_D"] = 'en-GB-Standard-O', -- [12] MALE
|
["en_GB_Standard_D"] = 'en-GB-Standard-D', -- Male
|
||||||
["en_GB_Standard_F"] = 'en-GB-Standard-N', -- [13] FEMALE
|
["en_GB_Standard_F"] = 'en-GB-Standard-F', -- Female
|
||||||
["en_GB_Standard_O"] = 'en-GB-Standard-O', -- [12] MALE
|
["en_GB_Standard_N"] = 'en-GB-Standard-N', -- Female
|
||||||
["en_GB_Standard_N"] = 'en-GB-Standard-N', -- [13] FEMALE
|
["en_GB_Standard_O"] = 'en-GB-Standard-O', -- Male
|
||||||
["en_US_Standard_A"] = 'en-US-Standard-A', -- [14] MALE
|
-- US
|
||||||
["en_US_Standard_B"] = 'en-US-Standard-B', -- [15] MALE
|
["en_US_Standard_A"] = 'en-US-Standard-A', -- Male
|
||||||
["en_US_Standard_C"] = 'en-US-Standard-C', -- [16] FEMALE
|
["en_US_Standard_B"] = 'en-US-Standard-B', -- Male
|
||||||
["en_US_Standard_D"] = 'en-US-Standard-D', -- [17] MALE
|
["en_US_Standard_C"] = 'en-US-Standard-C', -- Female
|
||||||
["en_US_Standard_E"] = 'en-US-Standard-E', -- [18] FEMALE
|
["en_US_Standard_D"] = 'en-US-Standard-D', -- Male
|
||||||
["en_US_Standard_F"] = 'en-US-Standard-F', -- [19] FEMALE
|
["en_US_Standard_E"] = 'en-US-Standard-E', -- Female
|
||||||
["en_US_Standard_G"] = 'en-US-Standard-G', -- [20] FEMALE
|
["en_US_Standard_F"] = 'en-US-Standard-F', -- Female
|
||||||
["en_US_Standard_H"] = 'en-US-Standard-H', -- [21] FEMALE
|
["en_US_Standard_G"] = 'en-US-Standard-G', -- Female
|
||||||
["en_US_Standard_I"] = 'en-US-Standard-I', -- [22] MALE
|
["en_US_Standard_H"] = 'en-US-Standard-H', -- Female
|
||||||
["en_US_Standard_J"] = 'en-US-Standard-J', -- [23] MALE
|
["en_US_Standard_I"] = 'en-US-Standard-I', -- Male
|
||||||
|
["en_US_Standard_J"] = 'en-US-Standard-J', -- Male
|
||||||
-- 2025 catalog changes
|
-- 2025 catalog changes
|
||||||
["fr_FR_Standard_A"] = "fr-FR-Standard-F", -- Female
|
["fr_FR_Standard_A"] = "fr-FR-Standard-F", -- Female
|
||||||
["fr_FR_Standard_B"] = "fr-FR-Standard-G", -- Male
|
["fr_FR_Standard_B"] = "fr-FR-Standard-G", -- Male
|
||||||
@@ -474,14 +478,15 @@ MSRS.Voices = {
|
|||||||
["fr_FR_Standard_G"] = "fr-FR-Standard-G", -- Male
|
["fr_FR_Standard_G"] = "fr-FR-Standard-G", -- Male
|
||||||
["fr_FR_Standard_F"] = "fr-FR-Standard-F", -- Female
|
["fr_FR_Standard_F"] = "fr-FR-Standard-F", -- Female
|
||||||
-- 2025 catalog changes
|
-- 2025 catalog changes
|
||||||
["de_DE_Standard_A"] = "de-DE-Standard-G", -- Female
|
["de_DE_Standard_A"] = 'de-DE-Standard-A', -- Female
|
||||||
["de_DE_Standard_B"] = "de-DE-Standard-H", -- Male
|
["de_DE_Standard_B"] = 'de-DE-Standard-B', -- Male
|
||||||
["de_DE_Standard_C"] = "de-DE-Standard-G", -- Female
|
["de_DE_Standard_C"] = 'de-DE-Standard-C', -- Female
|
||||||
["de_DE_Standard_D"] = "de-DE-Standard-H", -- Male
|
["de_DE_Standard_D"] = 'de-DE-Standard-D', -- Male
|
||||||
["de_DE_Standard_E"] = "de-DE-Standard-H", -- Male
|
["de_DE_Standard_E"] = 'de-DE-Standard-E', -- Male
|
||||||
["de_DE_Standard_F"] = "de-DE-Standard-G", -- Female
|
["de_DE_Standard_F"] = 'de-DE-Standard-F', -- Female
|
||||||
["de_DE_Standard_H"] = "de-DE-Standard-H", -- Male
|
["de_DE_Standard_G"] = 'de-DE-Standard-G', -- Female
|
||||||
["de_DE_Standard_G"] = "de-DE-Standard-G", -- Female
|
["de_DE_Standard_H"] = 'de-DE-Standard-H', -- Male
|
||||||
|
-- ES
|
||||||
["es_ES_Standard_A"] = "es-ES-Standard-E", -- Female
|
["es_ES_Standard_A"] = "es-ES-Standard-E", -- Female
|
||||||
["es_ES_Standard_B"] = "es-ES-Standard-F", -- Male
|
["es_ES_Standard_B"] = "es-ES-Standard-F", -- Male
|
||||||
["es_ES_Standard_C"] = "es-ES-Standard-E", -- Female
|
["es_ES_Standard_C"] = "es-ES-Standard-E", -- Female
|
||||||
@@ -497,32 +502,36 @@ MSRS.Voices = {
|
|||||||
["it_IT_Standard_F"] = "it-IT-Standard-F", -- Male
|
["it_IT_Standard_F"] = "it-IT-Standard-F", -- Male
|
||||||
},
|
},
|
||||||
Wavenet = {
|
Wavenet = {
|
||||||
["en_AU_Wavenet_A"] = 'en-AU-Wavenet-A', -- [1] FEMALE
|
["en_AU_Wavenet_A"] = 'en-AU-Wavenet-A', -- Female
|
||||||
["en_AU_Wavenet_B"] = 'en-AU-Wavenet-B', -- [2] MALE
|
["en_AU_Wavenet_B"] = 'en-AU-Wavenet-B', -- Male
|
||||||
["en_AU_Wavenet_C"] = 'en-AU-Wavenet-C', -- [3] FEMALE
|
["en_AU_Wavenet_C"] = 'en-AU-Wavenet-C', -- Female
|
||||||
["en_AU_Wavenet_D"] = 'en-AU-Wavenet-D', -- [4] MALE
|
["en_AU_Wavenet_D"] = 'en-AU-Wavenet-D', -- Male
|
||||||
["en_IN_Wavenet_A"] = 'en-IN-Wavenet-A', -- [5] FEMALE
|
-- IN
|
||||||
["en_IN_Wavenet_B"] = 'en-IN-Wavenet-B', -- [6] MALE
|
["en_IN_Wavenet_A"] = 'en-IN-Wavenet-A', -- Female
|
||||||
["en_IN_Wavenet_C"] = 'en-IN-Wavenet-C', -- [7] MALE
|
["en_IN_Wavenet_B"] = 'en-IN-Wavenet-B', -- Male
|
||||||
["en_IN_Wavenet_D"] = 'en-IN-Wavenet-D', -- [8] FEMALE
|
["en_IN_Wavenet_C"] = 'en-IN-Wavenet-C', -- Male
|
||||||
|
["en_IN_Wavenet_D"] = 'en-IN-Wavenet-D', -- Female
|
||||||
|
["en_IN_Wavenet_E"] = 'en-IN-Wavenet-E', -- Female
|
||||||
|
["en_IN_Wavenet_F"] = 'en-IN-Wavenet-F', -- Male
|
||||||
-- 2025 changes
|
-- 2025 changes
|
||||||
["en_GB_Wavenet_A"] = 'en-GB-Wavenet-N', -- [9] FEMALE
|
["en_GB_Wavenet_A"] = 'en-GB-Wavenet-A', -- [9] FEMALE
|
||||||
["en_GB_Wavenet_B"] = 'en-GB-Wavenet-O', -- [10] MALE
|
["en_GB_Wavenet_B"] = 'en-GB-Wavenet-B', -- [10] MALE
|
||||||
["en_GB_Wavenet_C"] = 'en-GB-Wavenet-N', -- [11] FEMALE
|
["en_GB_Wavenet_C"] = 'en-GB-Wavenet-C', -- [11] FEMALE
|
||||||
["en_GB_Wavenet_D"] = 'en-GB-Wavenet-O', -- [12] MALE
|
["en_GB_Wavenet_D"] = 'en-GB-Wavenet-D', -- [12] MALE
|
||||||
["en_GB_Wavenet_F"] = 'en-GB-Wavenet-N', -- [13] FEMALE
|
["en_GB_Wavenet_F"] = 'en-GB-Wavenet-F', -- [13] FEMALE
|
||||||
["en_GB_Wavenet_O"] = 'en-GB-Wavenet-O', -- [12] MALE
|
["en_GB_Wavenet_O"] = 'en-GB-Wavenet-O', -- [12] MALE
|
||||||
["en_GB_Wavenet_N"] = 'en-GB-Wavenet-N', -- [13] FEMALE
|
["en_GB_Wavenet_N"] = 'en-GB-Wavenet-N', -- [13] FEMALE
|
||||||
["en_US_Wavenet_A"] = 'en-US-Wavenet-N', -- [14] MALE
|
-- US
|
||||||
["en_US_Wavenet_B"] = 'en-US-Wavenet-B', -- [15] MALE
|
["en_US_Wavenet_A"] = 'en-US-Wavenet-A', -- Male
|
||||||
["en_US_Wavenet_C"] = 'en-US-Wavenet-C', -- [16] FEMALE
|
["en_US_Wavenet_B"] = 'en-US-Wavenet-B', -- Male
|
||||||
["en_US_Wavenet_D"] = 'en-US-Wavenet-D', -- [17] MALE
|
["en_US_Wavenet_C"] = 'en-US-Wavenet-C', -- Female
|
||||||
["en_US_Wavenet_E"] = 'en-US-Wavenet-E', -- [18] FEMALE
|
["en_US_Wavenet_D"] = 'en-US-Wavenet-D', -- Male
|
||||||
["en_US_Wavenet_F"] = 'en-US-Wavenet-F', -- [19] FEMALE
|
["en_US_Wavenet_E"] = 'en-US-Wavenet-E', -- Female
|
||||||
["en_US_Wavenet_G"] = 'en-US-Wavenet-G', -- [20] FEMALE
|
["en_US_Wavenet_F"] = 'en-US-Wavenet-F', -- Female
|
||||||
["en_US_Wavenet_H"] = 'en-US-Wavenet-H', -- [21] FEMALE
|
["en_US_Wavenet_G"] = 'en-US-Wavenet-G', -- Female
|
||||||
["en_US_Wavenet_I"] = 'en-US-Wavenet-I', -- [22] MALE
|
["en_US_Wavenet_H"] = 'en-US-Wavenet-H', -- Female
|
||||||
["en_US_Wavenet_J"] = 'en-US-Wavenet-J', -- [23] MALE
|
["en_US_Wavenet_I"] = 'en-US-Wavenet-I', -- Male
|
||||||
|
["en_US_Wavenet_J"] = 'en-US-Wavenet-J', -- Male
|
||||||
-- 2025 catalog changes
|
-- 2025 catalog changes
|
||||||
["fr_FR_Wavenet_A"] = "fr-FR-Wavenet-F", -- Female
|
["fr_FR_Wavenet_A"] = "fr-FR-Wavenet-F", -- Female
|
||||||
["fr_FR_Wavenet_B"] = "fr-FR-Wavenet-G", -- Male
|
["fr_FR_Wavenet_B"] = "fr-FR-Wavenet-G", -- Male
|
||||||
@@ -532,14 +541,15 @@ MSRS.Voices = {
|
|||||||
["fr_FR_Wavenet_G"] = "fr-FR-Wavenet-G", -- Male
|
["fr_FR_Wavenet_G"] = "fr-FR-Wavenet-G", -- Male
|
||||||
["fr_FR_Wavenet_F"] = "fr-FR-Wavenet-F", -- Female
|
["fr_FR_Wavenet_F"] = "fr-FR-Wavenet-F", -- Female
|
||||||
-- 2025 catalog changes
|
-- 2025 catalog changes
|
||||||
["de_DE_Wavenet_A"] = "de-DE-Wavenet-G", -- Female
|
["de_DE_Wavenet_A"] = 'de-DE-Wavenet-A', -- Female
|
||||||
["de_DE_Wavenet_B"] = "de-DE-Wavenet-H", -- Male
|
["de_DE_Wavenet_B"] = 'de-DE-Wavenet-B', -- Male
|
||||||
["de_DE_Wavenet_C"] = "de-DE-Wavenet-G", -- Female
|
["de_DE_Wavenet_C"] = 'de-DE-Wavenet-C', -- Female
|
||||||
["de_DE_Wavenet_D"] = "de-DE-Wavenet-H", -- Male
|
["de_DE_Wavenet_D"] = 'de-DE-Wavenet-D', -- Male
|
||||||
["de_DE_Wavenet_E"] = "de-DE-Wavenet-H", -- Male
|
["de_DE_Wavenet_E"] = 'de-DE-Wavenet-E', -- Male
|
||||||
["de_DE_Wavenet_F"] = "de-DE-Wavenet-G", -- Female
|
["de_DE_Wavenet_F"] = 'de-DE-Wavenet-F', -- Female
|
||||||
["de_DE_Wavenet_H"] = "de-DE-Wavenet-H", -- Male
|
["de_DE_Wavenet_G"] = 'de-DE-Wavenet-G', -- Female
|
||||||
["de_DE_Wavenet_G"] = "de-DE-Wavenet-G", -- Female
|
["de_DE_Wavenet_H"] = 'de-DE-Wavenet-H', -- Male
|
||||||
|
-- ES
|
||||||
["es_ES_Wavenet_B"] = "es-ES-Wavenet-E", -- Male
|
["es_ES_Wavenet_B"] = "es-ES-Wavenet-E", -- Male
|
||||||
["es_ES_Wavenet_C"] = "es-ES-Wavenet-F", -- Female
|
["es_ES_Wavenet_C"] = "es-ES-Wavenet-F", -- Female
|
||||||
["es_ES_Wavenet_D"] = "es-ES-Wavenet-E", -- Female
|
["es_ES_Wavenet_D"] = "es-ES-Wavenet-E", -- Female
|
||||||
@@ -553,6 +563,134 @@ MSRS.Voices = {
|
|||||||
["it_IT_Wavenet_E"] = "it-IT-Wavenet-E", -- Female
|
["it_IT_Wavenet_E"] = "it-IT-Wavenet-E", -- Female
|
||||||
["it_IT_Wavenet_F"] = "it-IT-Wavenet-F", -- Male
|
["it_IT_Wavenet_F"] = "it-IT-Wavenet-F", -- Male
|
||||||
} ,
|
} ,
|
||||||
|
Chirp3HD = {
|
||||||
|
["en_GB_Chirp3_HD_Aoede"] = 'en-GB-Chirp3-HD-Aoede', -- Female
|
||||||
|
["en_GB_Chirp3_HD_Charon"] = 'en-GB-Chirp3-HD-Charon', -- Male
|
||||||
|
["en_GB_Chirp3_HD_Fenrir"] = 'en-GB-Chirp3-HD-Fenrir', -- Male
|
||||||
|
["en_GB_Chirp3_HD_Kore"] = 'en-GB-Chirp3-HD-Kore', -- Female
|
||||||
|
["en_GB_Chirp3_HD_Leda"] = 'en-GB-Chirp3-HD-Leda', -- Female
|
||||||
|
["en_GB_Chirp3_HD_Orus"] = 'en-GB-Chirp3-HD-Orus', -- Male
|
||||||
|
["en_GB_Chirp3_HD_Puck"] = 'en-GB-Chirp3-HD-Puck', -- Male
|
||||||
|
["en_GB_Chirp3_HD_Zephyr"] = 'en-GB-Chirp3-HD-Zephyr', -- Female
|
||||||
|
--["de_DE_Chirp3_HD_Aoede"] = 'de-DE-Chirp3-HD-Aoede', -- Female (Datenfehler im Original)
|
||||||
|
["en_US_Chirp3_HD_Charon"] = 'en-US-Chirp3-HD-Charon', -- Male
|
||||||
|
["en_US_Chirp3_HD_Fenrir"] = 'en-US-Chirp3-HD-Fenrir', -- Male
|
||||||
|
["en_US_Chirp3_HD_Kore"] = 'en-US-Chirp3-HD-Kore', -- Female
|
||||||
|
["en_US_Chirp3_HD_Leda"] = 'en-US-Chirp3-HD-Leda', -- Female
|
||||||
|
["en_US_Chirp3_HD_Orus"] = 'en-US-Chirp3-HD-Orus', -- Male
|
||||||
|
["en_US_Chirp3_HD_Puck"] = 'en-US-Chirp3-HD-Puck', -- Male
|
||||||
|
--["de_DE_Chirp3_HD_Zephyr"] = 'de-DE-Chirp3-HD-Zephyr', -- Female (Datenfehler im Original)
|
||||||
|
-- DE
|
||||||
|
["de_DE_Chirp3_HD_Aoede"] = 'de-DE-Chirp3-HD-Aoede', -- Female
|
||||||
|
["de_DE_Chirp3_HD_Charon"] = 'de-DE-Chirp3-HD-Charon', -- Male
|
||||||
|
["de_DE_Chirp3_HD_Fenrir"] = 'de-DE-Chirp3-HD-Fenrir', -- Male
|
||||||
|
["de_DE_Chirp3_HD_Kore"] = 'de-DE-Chirp3-HD-Kore', -- Female
|
||||||
|
["de_DE_Chirp3_HD_Leda"] = 'de-DE-Chirp3-HD-Leda', -- Female
|
||||||
|
["de_DE_Chirp3_HD_Orus"] = 'de-DE-Chirp3-HD-Orus', -- Male
|
||||||
|
["de_DE_Chirp3_HD_Puck"] = 'de-DE-Chirp3-HD-Puck', -- Male
|
||||||
|
["de_DE_Chirp3_HD_Zephyr"] = 'de-DE-Chirp3-HD-Zephyr', -- Female
|
||||||
|
-- AU
|
||||||
|
["en_AU_Chirp3_HD_Aoede"] = 'en-AU-Chirp3-HD-Aoede', -- Female
|
||||||
|
["en_AU_Chirp3_HD_Charon"] = 'en-AU-Chirp3-HD-Charon', -- Male
|
||||||
|
["en_AU_Chirp3_HD_Fenrir"] = 'en-AU-Chirp3-HD-Fenrir', -- Male
|
||||||
|
["en_AU_Chirp3_HD_Kore"] = 'en-AU-Chirp3-HD-Kore', -- Female
|
||||||
|
["en_AU_Chirp3_HD_Leda"] = 'en-AU-Chirp3-HD-Leda', -- Female
|
||||||
|
["en_AU_Chirp3_HD_Orus"] = 'en-AU-Chirp3-HD-Orus', -- Male
|
||||||
|
["en_AU_Chirp3_HD_Puck"] = 'en-AU-Chirp3-HD-Puck', -- Male
|
||||||
|
["en_AU_Chirp3_HD_Zephyr"] = 'en-AU-Chirp3-HD-Zephyr', -- Female
|
||||||
|
-- IN
|
||||||
|
["en_IN_Chirp3_HD_Aoede"] = 'en-IN-Chirp3-HD-Aoede', -- Female
|
||||||
|
["en_IN_Chirp3_HD_Charon"] = 'en-IN-Chirp3-HD-Charon', -- Male
|
||||||
|
["en_IN_Chirp3_HD_Fenrir"] = 'en-IN-Chirp3-HD-Fenrir', -- Male
|
||||||
|
["en_IN_Chirp3_HD_Kore"] = 'en-IN-Chirp3-HD-Kore', -- Female
|
||||||
|
["en_IN_Chirp3_HD_Leda"] = 'en-IN-Chirp3-HD-Leda', -- Female
|
||||||
|
["en_IN_Chirp3_HD_Orus"] = 'en-IN-Chirp3-HD-Orus', -- Male
|
||||||
|
},
|
||||||
|
ChirpHD = {
|
||||||
|
["en_US_Chirp_HD_D"] = 'en-US-Chirp-HD-D', -- Male
|
||||||
|
["en_US_Chirp_HD_F"] = 'en-US-Chirp-HD-F', -- Female
|
||||||
|
["en_US_Chirp_HD_O"] = 'en-US-Chirp-HD-O', -- Female
|
||||||
|
-- DE
|
||||||
|
["de_DE_Chirp_HD_D"] = 'de-DE-Chirp-HD-D', -- Male
|
||||||
|
["de_DE_Chirp_HD_F"] = 'de-DE-Chirp-HD-F', -- Female
|
||||||
|
["de_DE_Chirp_HD_O"] = 'de-DE-Chirp-HD-O', -- Female
|
||||||
|
-- AU
|
||||||
|
["en_AU_Chirp_HD_D"] = 'en-AU-Chirp-HD-D', -- Male
|
||||||
|
["en_AU_Chirp_HD_F"] = 'en-AU-Chirp-HD-F', -- Female
|
||||||
|
["en_AU_Chirp_HD_O"] = 'en-AU-Chirp-HD-O', -- Female
|
||||||
|
-- IN
|
||||||
|
["en_IN_Chirp_HD_D"] = 'en-IN-Chirp-HD-D', -- Male
|
||||||
|
["en_IN_Chirp_HD_F"] = 'en-IN-Chirp-HD-F', -- Female
|
||||||
|
["en_IN_Chirp_HD_O"] = 'en-IN-Chirp-HD-O', -- Female
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Neural2 = {
|
||||||
|
["en_GB_Neural2_A"] = 'en-GB-Neural2-A', -- Female
|
||||||
|
["en_GB_Neural2_B"] = 'en-GB-Neural2-B', -- Male
|
||||||
|
["en_GB_Neural2_C"] = 'en-GB-Neural2-C', -- Female
|
||||||
|
["en_GB_Neural2_D"] = 'en-GB-Neural2-D', -- Male
|
||||||
|
["en_GB_Neural2_F"] = 'en-GB-Neural2-F', -- Female
|
||||||
|
["en_GB_Neural2_N"] = 'en-GB-Neural2-N', -- Female
|
||||||
|
["en_GB_Neural2_O"] = 'en-GB-Neural2-O', -- Male
|
||||||
|
-- US
|
||||||
|
["en_US_Neural2_A"] = 'en-US-Neural2-A', -- Male
|
||||||
|
["en_US_Neural2_C"] = 'en-US-Neural2-C', -- Female
|
||||||
|
["en_US_Neural2_D"] = 'en-US-Neural2-D', -- Male
|
||||||
|
["en_US_Neural2_E"] = 'en-US-Neural2-E', -- Female
|
||||||
|
["en_US_Neural2_F"] = 'en-US-Neural2-F', -- Female
|
||||||
|
["en_US_Neural2_G"] = 'en-US-Neural2-G', -- Female
|
||||||
|
["en_US_Neural2_H"] = 'en-US-Neural2-H', -- Female
|
||||||
|
["en_US_Neural2_I"] = 'en-US-Neural2-I', -- Male
|
||||||
|
["en_US_Neural2_J"] = 'en-US-Neural2-J', -- Male
|
||||||
|
-- DE
|
||||||
|
["de_DE_Neural2_G"] = 'de-DE-Neural2-G', -- Female
|
||||||
|
["de_DE_Neural2_H"] = 'de-DE-Neural2-H', -- Male
|
||||||
|
-- AU
|
||||||
|
["en_AU_Neural2_A"] = 'en-AU-Neural2-A', -- Female
|
||||||
|
["en_AU_Neural2_B"] = 'en-AU-Neural2-B', -- Male
|
||||||
|
["en_AU_Neural2_C"] = 'en-AU-Neural2-C', -- Female
|
||||||
|
["en_AU_Neural2_D"] = 'en-AU-Neural2-D', -- Male
|
||||||
|
-- IN
|
||||||
|
["en_IN_Neural2_A"] = 'en-IN-Neural2-A', -- Female
|
||||||
|
["en_IN_Neural2_B"] = 'en-IN-Neural2-B', -- Male
|
||||||
|
["en_IN_Neural2_C"] = 'en-IN-Neural2-C', -- Male
|
||||||
|
["en_IN_Neural2_D"] = 'en-IN-Neural2-D', -- Female
|
||||||
|
},
|
||||||
|
News = {
|
||||||
|
["en_GB_News_G"] = 'en-GB-News-G', -- Female
|
||||||
|
["en_GB_News_H"] = 'en-GB-News-H', -- Female
|
||||||
|
["en_GB_News_I"] = 'en-GB-News-I', -- Female
|
||||||
|
["en_GB_News_J"] = 'en-GB-News-J', -- Male
|
||||||
|
["en_GB_News_K"] = 'en-GB-News-K', -- Male
|
||||||
|
["en_GB_News_L"] = 'en-GB-News-L', -- Male
|
||||||
|
["en_GB_News_M"] = 'en-GB-News-M', -- Male
|
||||||
|
-- US
|
||||||
|
["en_US_News_K"] = 'en-US-News-K', -- Female
|
||||||
|
["en_US_News_L"] = 'en-US-News-L', -- Female
|
||||||
|
["en_US_News_N"] = 'en-US-News-N', -- Male
|
||||||
|
-- AU
|
||||||
|
["en_AU_News_E"] = 'en-AU-News-E', -- Female
|
||||||
|
["en_AU_News_F"] = 'en-AU-News-F', -- Female
|
||||||
|
["en_AU_News_G"] = 'en-AU-News-G', -- Male
|
||||||
|
},
|
||||||
|
Casual = {
|
||||||
|
["en_US_Casual_K"] = 'en-US-Casual-K', -- Male
|
||||||
|
},
|
||||||
|
Polyglot = {
|
||||||
|
["en_US_Polyglot_1"] = 'en-US-Polyglot-1', -- Male
|
||||||
|
["de_DE_Polyglot_1"] = 'de-DE-Polyglot-1', -- Male
|
||||||
|
["en_AU_Polyglot_1"] = 'en-AU-Polyglot-1', -- Male
|
||||||
|
},
|
||||||
|
Studio = {
|
||||||
|
-- Englisch (UK) - Studio
|
||||||
|
["en_GB_Studio_B"] = 'en-GB-Studio-B', -- Male
|
||||||
|
["en_GB_Studio_C"] = 'en-GB-Studio-C', -- Female
|
||||||
|
-- Englisch (USA) - Studio
|
||||||
|
["en_US_Studio_O"] = 'en-US-Studio-O', -- Female
|
||||||
|
["en_US_Studio_Q"] = 'en-US-Studio-Q', -- Male
|
||||||
|
-- DE
|
||||||
|
["de_DE_Studio_B"] = 'de-DE-Studio-B', -- Male
|
||||||
|
["de_DE_Studio_C"] = 'de-DE-Studio-C', -- Female
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -632,7 +770,7 @@ end
|
|||||||
-- set the path to the exe file via @{#MSRS.SetPath}.
|
-- set the path to the exe file via @{#MSRS.SetPath}.
|
||||||
--
|
--
|
||||||
-- @param #MSRS self
|
-- @param #MSRS self
|
||||||
-- @param #string Path Path to SRS directory. Default `C:\\Program Files\\DCS-SimpleRadio-Standalone`.
|
-- @param #string Path Path to SRS directory. Default `C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio`.
|
||||||
-- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. Can also be given as a #table of multiple frequencies.
|
-- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. Can also be given as a #table of multiple frequencies.
|
||||||
-- @param #number Modulation Radio modulation: 0=AM (default), 1=FM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators. Can also be given as a #table of multiple modulations.
|
-- @param #number Modulation Radio modulation: 0=AM (default), 1=FM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators. Can also be given as a #table of multiple modulations.
|
||||||
-- @param #string Backend Backend used: `MSRS.Backend.SRSEXE` (default) or `MSRS.Backend.GRPC`.
|
-- @param #string Backend Backend used: `MSRS.Backend.SRSEXE` (default) or `MSRS.Backend.GRPC`.
|
||||||
@@ -767,13 +905,13 @@ end
|
|||||||
|
|
||||||
--- Set path to SRS install directory. More precisely, path to where the `DCS-SR-ExternalAudio.exe` is located.
|
--- Set path to SRS install directory. More precisely, path to where the `DCS-SR-ExternalAudio.exe` is located.
|
||||||
-- @param #MSRS self
|
-- @param #MSRS self
|
||||||
-- @param #string Path Path to the directory, where the sound file is located. Default is `C:\\Program Files\\DCS-SimpleRadio-Standalone`.
|
-- @param #string Path Path to the directory, where the sound file is located. Default is `C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio`.
|
||||||
-- @return #MSRS self
|
-- @return #MSRS self
|
||||||
function MSRS:SetPath(Path)
|
function MSRS:SetPath(Path)
|
||||||
self:F( {Path=Path} )
|
self:F( {Path=Path} )
|
||||||
|
|
||||||
-- Set path.
|
-- Set path.
|
||||||
self.path=Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone"
|
self.path=Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio"
|
||||||
|
|
||||||
-- Remove (back)slashes.
|
-- Remove (back)slashes.
|
||||||
local n=1 ; local nmax=1000
|
local n=1 ; local nmax=1000
|
||||||
@@ -1817,7 +1955,7 @@ end
|
|||||||
--
|
--
|
||||||
-- -- Moose MSRS default Config
|
-- -- Moose MSRS default Config
|
||||||
-- MSRS_Config = {
|
-- MSRS_Config = {
|
||||||
-- Path = "C:\\Program Files\\DCS-SimpleRadio-Standalone", -- Path to SRS install directory.
|
-- Path = "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio", -- Path to SRS install directory.
|
||||||
-- Port = 5002, -- Port of SRS server. Default 5002.
|
-- Port = 5002, -- Port of SRS server. Default 5002.
|
||||||
-- Backend = "srsexe", -- Interface to SRS: "srsexe" or "grpc".
|
-- Backend = "srsexe", -- Interface to SRS: "srsexe" or "grpc".
|
||||||
-- Frequency = {127, 243}, -- Default frequences. Must be a table 1..n entries!
|
-- Frequency = {127, 243}, -- Default frequences. Must be a table 1..n entries!
|
||||||
@@ -1837,7 +1975,7 @@ end
|
|||||||
-- -- Google Cloud
|
-- -- Google Cloud
|
||||||
-- gcloud = {
|
-- gcloud = {
|
||||||
-- voice = "en-GB-Standard-A", -- The Google Cloud voice to use (see https://cloud.google.com/text-to-speech/docs/voices).
|
-- voice = "en-GB-Standard-A", -- The Google Cloud voice to use (see https://cloud.google.com/text-to-speech/docs/voices).
|
||||||
-- credentials="C:\\Program Files\\DCS-SimpleRadio-Standalone\\yourfilename.json", -- Full path to credentials JSON file (only for SRS-TTS.exe backend)
|
-- credentials="C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio\\yourfilename.json", -- Full path to credentials JSON file (only for SRS-TTS.exe backend)
|
||||||
-- key="Your access Key", -- Google API access key (only for DCS-gRPC backend)
|
-- key="Your access Key", -- Google API access key (only for DCS-gRPC backend)
|
||||||
-- },
|
-- },
|
||||||
-- -- Amazon Web Service
|
-- -- Amazon Web Service
|
||||||
@@ -1905,7 +2043,7 @@ function MSRS:LoadConfigFile(Path,Filename)
|
|||||||
|
|
||||||
local Self = self or MSRS --#MSRS
|
local Self = self or MSRS --#MSRS
|
||||||
|
|
||||||
Self.path = MSRS_Config.Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone"
|
Self.path = MSRS_Config.Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio"
|
||||||
Self.port = MSRS_Config.Port or 5002
|
Self.port = MSRS_Config.Port or 5002
|
||||||
Self.backend = MSRS_Config.Backend or MSRS.Backend.SRSEXE
|
Self.backend = MSRS_Config.Backend or MSRS.Backend.SRSEXE
|
||||||
Self.frequencies = MSRS_Config.Frequency or {127,243}
|
Self.frequencies = MSRS_Config.Frequency or {127,243}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -805,6 +805,33 @@ UTILS.kg2lbs = function( kg )
|
|||||||
return kg * 2.20462
|
return kg * 2.20462
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Convert latitude or longitude from degrees, minutes, seconds (DMS) to decimal degrees (DD).
|
||||||
|
-- @param #number Degrees Degrees in grad.
|
||||||
|
-- @param #number Minutes Minutes.
|
||||||
|
-- @param #number Seconds Seconds.
|
||||||
|
-- @return #number Latitude or Longitude in decimal degrees.
|
||||||
|
UTILS.LLDMSToDD = function(Degrees, Minutes, Seconds)
|
||||||
|
|
||||||
|
local dd=tonumber(Degrees or 0) + tonumber(Minutes or 0)/60 + tonumber(Seconds or 0)/3600
|
||||||
|
|
||||||
|
return dd
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Convert latitude or longitude from degrees, minutes, seconds (DMS) given in text form to decimal degrees (DD).
|
||||||
|
-- @param #string LatOrLongString Latitude or longitude passed as ttring in format `DD°MM'SS.SS"`.
|
||||||
|
-- @return #number Latitude or Longitude in decimal degrees.
|
||||||
|
UTILS.LLDMSstringToDD = function(LatOrLongString)
|
||||||
|
|
||||||
|
local hem=string.match(LatOrLongString, "(%a)")
|
||||||
|
local Degrees=string.match(LatOrLongString, "(%d+)°")
|
||||||
|
local Minutes=string.match(LatOrLongString, "(%d+)'")
|
||||||
|
local Seconds=string.match(LatOrLongString, "([%d\.]+)\"")
|
||||||
|
|
||||||
|
local dd=UTILS.LLDMSToDD(Degrees, Minutes, Seconds)
|
||||||
|
|
||||||
|
return dd
|
||||||
|
end
|
||||||
|
|
||||||
--[[acc:
|
--[[acc:
|
||||||
in DM: decimal point of minutes.
|
in DM: decimal point of minutes.
|
||||||
In DMS: decimal point of seconds.
|
In DMS: decimal point of seconds.
|
||||||
@@ -1525,6 +1552,22 @@ function UTILS.Vec2Add(a, b)
|
|||||||
return {x=a.x+b.x, y=a.y+b.y}
|
return {x=a.x+b.x, y=a.y+b.y}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Multiply 2D vector by a scalar value.
|
||||||
|
-- @param DCS#Vec2 a Vector in 2D with x, y components.
|
||||||
|
-- @param #number c Scalar value.
|
||||||
|
-- @return DCS#Vec2 Vector
|
||||||
|
function UTILS.Vec2Mult(a, c)
|
||||||
|
return {x=c*a.x, y=c*a.y}
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Multiply 3D vector by a scalar value.
|
||||||
|
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
||||||
|
-- @param #number c Scalar value.
|
||||||
|
-- @return DCS#Vec3 Vector
|
||||||
|
function UTILS.VecMult(a, c)
|
||||||
|
return {x=c*a.x, y=c*a.y, z=c*a.z}
|
||||||
|
end
|
||||||
|
|
||||||
--- Calculate the angle between two 3D vectors.
|
--- Calculate the angle between two 3D vectors.
|
||||||
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
||||||
-- @param DCS#Vec3 b Vector in 3D with x, y, z components.
|
-- @param DCS#Vec3 b Vector in 3D with x, y, z components.
|
||||||
@@ -1545,6 +1588,24 @@ function UTILS.VecAngle(a, b)
|
|||||||
return math.deg(alpha)
|
return math.deg(alpha)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Calculate the angle between two 3D vectors.
|
||||||
|
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
||||||
|
-- @param DCS#Vec3 b Vector in 3D with x, y, z components.
|
||||||
|
-- @return #number Angle alpha between and b in degrees.
|
||||||
|
function UTILS.VecAngleSigned(a, b)
|
||||||
|
|
||||||
|
local a=UTILS.VecSubstract(s1.p2.vec3, s1.p1.vec3)
|
||||||
|
|
||||||
|
local b=UTILS.VecSubstract(s2.p2.vec3, s2.p1.vec3)
|
||||||
|
|
||||||
|
local h1=UTILS.VecHdg(a)
|
||||||
|
local h2=UTILS.VecHdg(b)
|
||||||
|
|
||||||
|
local angle=h1-h2 --UTILS.VecAngle(a, b)
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
--- Calculate "heading" of a 3D vector in the X-Z plane.
|
--- Calculate "heading" of a 3D vector in the X-Z plane.
|
||||||
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
||||||
-- @return #number Heading in degrees in [0,360).
|
-- @return #number Heading in degrees in [0,360).
|
||||||
@@ -1567,6 +1628,20 @@ function UTILS.Vec2Hdg(a)
|
|||||||
return h
|
return h
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Calculate the difference between two "heading", i.e. angles in [0,360) deg.
|
||||||
|
-- @param DCS#Vec3 a Vector a.
|
||||||
|
-- @param DCS#Vec3 a Vector b.
|
||||||
|
-- @return #number Heading difference in degrees.
|
||||||
|
function UTILS.VecHdgDiff(a, b)
|
||||||
|
|
||||||
|
local ha=math.deg(math.atan2(a.z, a.x))
|
||||||
|
local hb=math.deg(math.atan2(b.z, b.x))
|
||||||
|
|
||||||
|
local angle=ha-hb
|
||||||
|
|
||||||
|
return angle
|
||||||
|
end
|
||||||
|
|
||||||
--- Calculate the difference between two "heading", i.e. angles in [0,360) deg.
|
--- Calculate the difference between two "heading", i.e. angles in [0,360) deg.
|
||||||
-- @param #number h1 Heading one.
|
-- @param #number h1 Heading one.
|
||||||
-- @param #number h2 Heading two.
|
-- @param #number h2 Heading two.
|
||||||
@@ -1586,12 +1661,12 @@ function UTILS.HdgDiff(h1, h2)
|
|||||||
return math.abs(delta)
|
return math.abs(delta)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns the heading from one vec3 to another vec3.
|
--- Returns the heading from one vec2/vec3 to another vec2/vec3.
|
||||||
-- @param DCS#Vec3 a From vec3.
|
-- @param DCS#Vec3 a From Vec2 or Vec3.
|
||||||
-- @param DCS#Vec3 b To vec3.
|
-- @param DCS#Vec3 b To Vec2 or Vec3.
|
||||||
-- @return #number Heading in degrees.
|
-- @return #number Heading in degrees.
|
||||||
function UTILS.HdgTo(a, b)
|
function UTILS.HdgTo(a, b)
|
||||||
local dz=b.z-a.z
|
local dz=(b.z or b.y) - (a.z or a.y)
|
||||||
local dx=b.x-a.x
|
local dx=b.x-a.x
|
||||||
local heading=math.deg(math.atan2(dz, dx))
|
local heading=math.deg(math.atan2(dz, dx))
|
||||||
if heading < 0 then
|
if heading < 0 then
|
||||||
@@ -1913,6 +1988,13 @@ end
|
|||||||
function UTILS.GetReportingName(Typename)
|
function UTILS.GetReportingName(Typename)
|
||||||
|
|
||||||
local typename = string.lower(Typename)
|
local typename = string.lower(Typename)
|
||||||
|
|
||||||
|
-- special cases - Shark and Manstay have "A-50" in the name
|
||||||
|
if string.find(typename,"ka-50",1,true) then
|
||||||
|
return "Shark"
|
||||||
|
elseif string.find(typename,"a-50",1,true) then
|
||||||
|
return "Mainstay"
|
||||||
|
end
|
||||||
|
|
||||||
for name, value in pairs(ENUMS.ReportingName.NATO) do
|
for name, value in pairs(ENUMS.ReportingName.NATO) do
|
||||||
local svalue = string.lower(value)
|
local svalue = string.lower(value)
|
||||||
@@ -2323,6 +2405,16 @@ function UTILS.IsLoadingDoorOpen( unit_name )
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if type_name == "UH-60L_DAP" and (unit:getDrawArgumentValue(401) == 1 or unit:getDrawArgumentValue(402) == 1) then
|
||||||
|
BASE:T(unit_name .. " cargo door is open")
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if type_name == "UH-60L_DAP" and (unit:getDrawArgumentValue(38) > 0 or unit:getDrawArgumentValue(400) == 1 ) then
|
||||||
|
BASE:T(unit_name .. " front door(s) are open")
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
if type_name == "AH-64D_BLK_II" then
|
if type_name == "AH-64D_BLK_II" then
|
||||||
BASE:T(unit_name .. " front door(s) are open")
|
BASE:T(unit_name .. " front door(s) are open")
|
||||||
return true -- no doors on this one ;)
|
return true -- no doors on this one ;)
|
||||||
@@ -3210,6 +3302,64 @@ function UTILS.BearingToCardinal(Heading)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Adjust given heading so that is is in [0, 360).
|
||||||
|
-- @param #number Heading The heading in degrees.
|
||||||
|
-- @return #number Adjust heading in [0,360).
|
||||||
|
function UTILS.AdjustHeading360(Heading)
|
||||||
|
|
||||||
|
while Heading>=360 or Heading<0 do
|
||||||
|
if Heading>=360 then
|
||||||
|
Heading=Heading-360
|
||||||
|
elseif Heading<0 then
|
||||||
|
Heading=Heading+360
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return Heading
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Transfroms a given 2D vector 3D.
|
||||||
|
-- This takes care of ED's different conventions for 2D and 3D coordinate systems.
|
||||||
|
-- @param DCS#Vec2 Vec Vector to be transformed. Can be any table/object that has at least x and y and optionally a z component.
|
||||||
|
-- @param #boolean OnSurface If `true`, new vector's y-component (alt) is at surface height. Otherwise, it is set to 0.
|
||||||
|
-- @return DCS#Vec3 Vector in 3D with x-, y- and z-components.
|
||||||
|
function UTILS.VecTo3D(Vec, OnSurface)
|
||||||
|
|
||||||
|
local vec={x=0, y=0, z=0} --DCS#Vec3
|
||||||
|
if Vec.z then
|
||||||
|
-- Vector is 3D already ==> Nothing to do.
|
||||||
|
vec.x=Vec.x
|
||||||
|
vec.y=Vec.y
|
||||||
|
vec.z=Vec.z
|
||||||
|
else
|
||||||
|
-- Vector is 2D
|
||||||
|
vec.x=Vec.x
|
||||||
|
vec.y=OnSurface and land.getHeight({x=Vec.x, y=Vec.y}) or 0
|
||||||
|
vec.z=Vec.y
|
||||||
|
end
|
||||||
|
|
||||||
|
return vec
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Transfroms a given 3D (or 2D) vector to 2D.
|
||||||
|
-- This takes care of ED's different conventions for 2D and 3D coordinate systems.
|
||||||
|
-- @param DCS#Vec3 Vec Vector to be transformed. Can be any table/object that has at least x and y and optionally a z component.
|
||||||
|
-- @return DCS#Vec2 Vector in 2D with x- and y-components.
|
||||||
|
function UTILS.VecTo2D(Vec)
|
||||||
|
|
||||||
|
local vec={x=0, y=0} --DCS#Vec2
|
||||||
|
|
||||||
|
if Vec.z then
|
||||||
|
vec.x=Vec.x
|
||||||
|
vec.y=Vec.z
|
||||||
|
else
|
||||||
|
vec.x=Vec.x
|
||||||
|
vec.y=Vec.y
|
||||||
|
end
|
||||||
|
|
||||||
|
return vec
|
||||||
|
end
|
||||||
|
|
||||||
--- Create a BRAA NATO call string BRAA between two GROUP objects
|
--- Create a BRAA NATO call string BRAA between two GROUP objects
|
||||||
-- @param Wrapper.Group#GROUP FromGrp GROUP object
|
-- @param Wrapper.Group#GROUP FromGrp GROUP object
|
||||||
-- @param Wrapper.Group#GROUP ToGrp GROUP object
|
-- @param Wrapper.Group#GROUP ToGrp GROUP object
|
||||||
@@ -4120,6 +4270,45 @@ function UTILS.LCGRandom()
|
|||||||
return UTILS.lcg.seed / UTILS.lcg.m
|
return UTILS.lcg.seed / UTILS.lcg.m
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Create a table of grid-points for n points.
|
||||||
|
-- @param #number startVec2 Starting DCS#Vec2 map coordinate, e.g. `{x=63598575,y=-63598575}`
|
||||||
|
-- @param #number n Number of points to generate.
|
||||||
|
-- @param #number spacingX Horizonzal spacing (meters).
|
||||||
|
-- @param #number spacingY Vertical spacing (meters).
|
||||||
|
-- @return #table Grid Table of DCS#Vec2 entries.
|
||||||
|
function UTILS.GenerateGridPoints(startVec2, n, spacingX, spacingY)
|
||||||
|
local points = {}
|
||||||
|
local gridSize = math.ceil(math.sqrt(n))
|
||||||
|
local count = 0
|
||||||
|
local n = n or 1
|
||||||
|
local spacingX = spacingX or 100
|
||||||
|
local spacingY = spacingY or 100
|
||||||
|
local startX = startVec2.x or 100
|
||||||
|
local startY = startVec2.y or 100
|
||||||
|
|
||||||
|
for row = 0, gridSize - 1 do
|
||||||
|
for col = 0, gridSize - 1 do
|
||||||
|
if count >= n then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
local point = {
|
||||||
|
x = startX + (col * spacingX),
|
||||||
|
y = startY + (row * spacingY)
|
||||||
|
}
|
||||||
|
|
||||||
|
table.insert(points, point)
|
||||||
|
count = count + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if count >= n then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return points
|
||||||
|
end
|
||||||
|
|
||||||
--- Spawns a new FARP of a defined type and coalition and functional statics (fuel depot, ammo storage, tent, windsock) around that FARP to make it operational.
|
--- Spawns a new FARP of a defined type and coalition and functional statics (fuel depot, ammo storage, tent, windsock) around that FARP to make it operational.
|
||||||
-- Adds vehicles from template if given. Fills the FARP warehouse with liquids and known materiels.
|
-- Adds vehicles from template if given. Fills the FARP warehouse with liquids and known materiels.
|
||||||
-- References: [DCS Forum Topic](https://forum.dcs.world/topic/282989-farp-equipment-to-run-it)
|
-- References: [DCS Forum Topic](https://forum.dcs.world/topic/282989-farp-equipment-to-run-it)
|
||||||
@@ -4136,9 +4325,42 @@ end
|
|||||||
-- @param #string VehicleTemplate, template name for additional vehicles. Can be nil for no additional vehicles.
|
-- @param #string VehicleTemplate, template name for additional vehicles. Can be nil for no additional vehicles.
|
||||||
-- @param #number Liquids Tons of fuel to be added initially to the FARP. Defaults to 10 (tons). Set to 0 for no fill.
|
-- @param #number Liquids Tons of fuel to be added initially to the FARP. Defaults to 10 (tons). Set to 0 for no fill.
|
||||||
-- @param #number Equipment Number of equipment items per known item to be added initially to the FARP. Defaults to 10 (items). Set to 0 for no fill.
|
-- @param #number Equipment Number of equipment items per known item to be added initially to the FARP. Defaults to 10 (items). Set to 0 for no fill.
|
||||||
|
-- @param #number Airframes Number of helicopter airframes per known type in Ops.CSAR#CSAR.AircraftType to be added initially to the FARP. Set to 0 for no airframes.
|
||||||
|
-- @param #string F10Text Text to display on F10 map if given. Handy to post things like the ADF beacon Frequency, Callsign and ATC Frequency.
|
||||||
|
-- @param #boolean DynamicSpawns If true, allow Dynamic Spawns from this FARP.
|
||||||
|
-- @param #boolean HotStart If true and DynamicSpawns is true, allow hot starts for Dynamic Spawns from this FARP.
|
||||||
|
-- @param #number NumberPads If given, spawn this number of pads.
|
||||||
|
-- @param #number SpacingX For NumberPads > 1, space this many meters horizontally. Defaults to 100.
|
||||||
|
-- @param #number SpacingY For NumberPads > 1, space this many meters vertically. Defaults to 100.
|
||||||
-- @return #list<Wrapper.Static#STATIC> Table of spawned objects and vehicle object (if given).
|
-- @return #list<Wrapper.Static#STATIC> Table of spawned objects and vehicle object (if given).
|
||||||
-- @return #string ADFBeaconName Name of the ADF beacon, to be able to remove/stop it later.
|
-- @return #string ADFBeaconName Name of the ADF beacon, to be able to remove/stop it later.
|
||||||
function UTILS.SpawnFARPAndFunctionalStatics(Name,Coordinate,FARPType,Coalition,Country,CallSign,Frequency,Modulation,ADF,SpawnRadius,VehicleTemplate,Liquids,Equipment)
|
-- @return #number MarkerID ID of the F10 Text, to be able to remove it later.
|
||||||
|
function UTILS.SpawnFARPAndFunctionalStatics(Name,Coordinate,FARPType,Coalition,Country,CallSign,Frequency,Modulation,ADF,SpawnRadius,VehicleTemplate,Liquids,Equipment,Airframes,F10Text,DynamicSpawns,HotStart,NumberPads,SpacingX,SpacingY)
|
||||||
|
|
||||||
|
local function PopulateStorage(Name,liquids,equip,airframes)
|
||||||
|
local newWH = STORAGE:New(Name)
|
||||||
|
if liquids and liquids > 0 then
|
||||||
|
-- Storage fill-up
|
||||||
|
newWH:SetLiquid(STORAGE.Liquid.DIESEL,liquids) -- kgs to tons
|
||||||
|
newWH:SetLiquid(STORAGE.Liquid.GASOLINE,liquids)
|
||||||
|
newWH:SetLiquid(STORAGE.Liquid.JETFUEL,liquids)
|
||||||
|
newWH:SetLiquid(STORAGE.Liquid.MW50,liquids)
|
||||||
|
end
|
||||||
|
|
||||||
|
if equip and equip > 0 then
|
||||||
|
for cat,nitem in pairs(ENUMS.Storage.weapons) do
|
||||||
|
for name,item in pairs(nitem) do
|
||||||
|
newWH:SetItem(item,equip)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if airframes and airframes > 0 then
|
||||||
|
for typename in pairs (CSAR.AircraftType) do
|
||||||
|
newWH:SetItem(typename,airframes)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Set Defaults
|
-- Set Defaults
|
||||||
local farplocation = Coordinate
|
local farplocation = Coordinate
|
||||||
@@ -4152,18 +4374,43 @@ function UTILS.SpawnFARPAndFunctionalStatics(Name,Coordinate,FARPType,Coalition,
|
|||||||
local liquids = Liquids or 10
|
local liquids = Liquids or 10
|
||||||
liquids = liquids * 1000 -- tons to kg
|
liquids = liquids * 1000 -- tons to kg
|
||||||
local equip = Equipment or 10
|
local equip = Equipment or 10
|
||||||
|
local airframes = Airframes or 10
|
||||||
local statictypes = ENUMS.FARPObjectTypeNamesAndShape[farptype] or {TypeName="FARP", ShapeName="FARPS"}
|
local statictypes = ENUMS.FARPObjectTypeNamesAndShape[farptype] or {TypeName="FARP", ShapeName="FARPS"}
|
||||||
local STypeName = statictypes.TypeName
|
local STypeName = statictypes.TypeName
|
||||||
local SShapeName = statictypes.ShapeName
|
local SShapeName = statictypes.ShapeName
|
||||||
local Country = Country or (Coalition == coalition.side.BLUE and country.id.USA or country.id.RUSSIA)
|
local Country = Country or (Coalition == coalition.side.BLUE and country.id.USA or country.id.RUSSIA)
|
||||||
local ReturnObjects = {}
|
local ReturnObjects = {}
|
||||||
|
|
||||||
-- Spawn FARP
|
-- many FARPs
|
||||||
local newfarp = SPAWNSTATIC:NewFromType(STypeName,"Heliports",Country) -- "Invisible FARP" "FARP"
|
local NumberPads = NumberPads or 1
|
||||||
newfarp:InitShape(SShapeName) -- "invisiblefarp" "FARPS"
|
local SpacingX = SpacingX or 100
|
||||||
newfarp:InitFARP(callsign,freq,mod)
|
local SpacingY = SpacingY or 100
|
||||||
local spawnedfarp = newfarp:SpawnFromCoordinate(farplocation,0,Name)
|
local FarpVec2 = Coordinate:GetVec2()
|
||||||
table.insert(ReturnObjects,spawnedfarp)
|
|
||||||
|
if NumberPads > 1 then
|
||||||
|
local Grid = UTILS.GenerateGridPoints(FarpVec2, NumberPads, SpacingX, SpacingY)
|
||||||
|
for id,gridpoint in ipairs(Grid) do
|
||||||
|
-- Spawn FARP
|
||||||
|
local location = COORDINATE:NewFromVec2(gridpoint)
|
||||||
|
local newfarp = SPAWNSTATIC:NewFromType(STypeName,"Heliports",Country) -- "Invisible FARP" "FARP"
|
||||||
|
newfarp:InitShape(SShapeName) -- "invisiblefarp" "FARPS"
|
||||||
|
newfarp:InitFARP(callsign,freq,mod,DynamicSpawns,HotStart)
|
||||||
|
local spawnedfarp = newfarp:SpawnFromCoordinate(location,0,Name.."-"..id)
|
||||||
|
table.insert(ReturnObjects,spawnedfarp)
|
||||||
|
|
||||||
|
PopulateStorage(Name.."-"..id,liquids,equip,airframes)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Spawn FARP
|
||||||
|
local newfarp = SPAWNSTATIC:NewFromType(STypeName,"Heliports",Country) -- "Invisible FARP" "FARP"
|
||||||
|
newfarp:InitShape(SShapeName) -- "invisiblefarp" "FARPS"
|
||||||
|
newfarp:InitFARP(callsign,freq,mod,DynamicSpawns,HotStart)
|
||||||
|
local spawnedfarp = newfarp:SpawnFromCoordinate(farplocation,0,Name)
|
||||||
|
table.insert(ReturnObjects,spawnedfarp)
|
||||||
|
|
||||||
|
PopulateStorage(Name,liquids,equip,airframes)
|
||||||
|
end
|
||||||
|
|
||||||
-- Spawn Objects
|
-- Spawn Objects
|
||||||
local FARPStaticObjectsNato = {
|
local FARPStaticObjectsNato = {
|
||||||
["FUEL"] = { TypeName = "FARP Fuel Depot", ShapeName = "GSM Rus", Category = "Fortifications"},
|
["FUEL"] = { TypeName = "FARP Fuel Depot", ShapeName = "GSM Rus", Category = "Fortifications"},
|
||||||
@@ -4197,23 +4444,6 @@ function UTILS.SpawnFARPAndFunctionalStatics(Name,Coordinate,FARPType,Coalition,
|
|||||||
table.insert(ReturnObjects,spawnedvehicle)
|
table.insert(ReturnObjects,spawnedvehicle)
|
||||||
end
|
end
|
||||||
|
|
||||||
local newWH = STORAGE:New(Name)
|
|
||||||
if liquids and liquids > 0 then
|
|
||||||
-- Storage fill-up
|
|
||||||
newWH:SetLiquid(STORAGE.Liquid.DIESEL,liquids) -- kgs to tons
|
|
||||||
newWH:SetLiquid(STORAGE.Liquid.GASOLINE,liquids)
|
|
||||||
newWH:SetLiquid(STORAGE.Liquid.JETFUEL,liquids)
|
|
||||||
newWH:SetLiquid(STORAGE.Liquid.MW50,liquids)
|
|
||||||
end
|
|
||||||
|
|
||||||
if equip and equip > 0 then
|
|
||||||
for cat,nitem in pairs(ENUMS.Storage.weapons) do
|
|
||||||
for name,item in pairs(nitem) do
|
|
||||||
newWH:SetItem(item,equip)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local ADFName
|
local ADFName
|
||||||
if ADF and type(ADF) == "number" then
|
if ADF and type(ADF) == "number" then
|
||||||
local ADFFreq = ADF*1000 -- KHz to Hz
|
local ADFFreq = ADF*1000 -- KHz to Hz
|
||||||
@@ -4224,7 +4454,150 @@ function UTILS.SpawnFARPAndFunctionalStatics(Name,Coordinate,FARPType,Coalition,
|
|||||||
trigger.action.radioTransmission(Sound, vec3, 0, true, ADFFreq, 250, ADFName)
|
trigger.action.radioTransmission(Sound, vec3, 0, true, ADFFreq, 250, ADFName)
|
||||||
end
|
end
|
||||||
|
|
||||||
return ReturnObjects, ADFName
|
local MarkerID = nil
|
||||||
|
if F10Text then
|
||||||
|
local Color = {0,0,1}
|
||||||
|
if Coalition == coalition.side.RED then
|
||||||
|
Color = {1,0,0}
|
||||||
|
elseif Coalition == coalition.side.NEUTRAL then
|
||||||
|
Color = {0,1,0}
|
||||||
|
end
|
||||||
|
local Alpha = 0.75
|
||||||
|
local coordinate = Coordinate:Translate(600,0)
|
||||||
|
MarkerID = coordinate:TextToAll(F10Text,Coalition,Color,1,{1,1,1},Alpha,14,true)
|
||||||
|
end
|
||||||
|
|
||||||
|
return ReturnObjects, ADFName, MarkerID
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Spawn a MASH at a given coordinate, optionally, add an ADF Beacon.
|
||||||
|
-- @param #string Name Unique Name of the Mash.
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate Coordinate where to spawn the MASH. Can be given as a Core.Zone#ZONE object, in this case we take the center coordinate.
|
||||||
|
-- @param #number Country Country ID the MASH belongs to, e.g. country.id.USA or country.id.RUSSIA.
|
||||||
|
-- @param #number ADF (Optional) ADF Frequency in kHz (Kilohertz), if given activate an ADF Beacon at the location of the MASH.
|
||||||
|
-- @param #string Livery (Optional) The livery of the static CH-47, defaults to dark green.
|
||||||
|
-- @param #boolean DeployHelo (Optional) If true, deploy the helicopter static.
|
||||||
|
-- @param #number MASHRadio MASH Radio Frequency, defaults to 127.5.
|
||||||
|
-- @param #number MASHRadioModulation MASH Radio Modulation, defaults to radio.modulation.AM.
|
||||||
|
-- @param #number MASHCallsign Defaults to CALLSIGN.FARP.Berlin.
|
||||||
|
-- @param #table Templates (Optional) You can hand in your own template table of numbered(!) entries. Each entry consist of a relative(!) x,y position and data of a
|
||||||
|
-- static, shape_name is optional. Also, livery_id is optional, but is applied to the helicopter static only.
|
||||||
|
-- @return #table Table of Wrapper.Static#STATIC objects that were spawned.
|
||||||
|
-- @return #string ADFName Name of the ADF Beacon to remove it later.
|
||||||
|
-- @usage
|
||||||
|
-- -- MASH Template example, this one is the built in one used in the function:
|
||||||
|
-- MASHTemplates = {
|
||||||
|
-- [1]={category='Infantry',type='Soldier M4',shape_name='none',heading=0,x=0.000000,y=0.000000,},
|
||||||
|
-- [2]={category='Infantry',type='Soldier M4',shape_name='none',heading=0,x=0.313533,y=8.778935,},
|
||||||
|
-- [3]={category='Infantry',type='Soldier M4',shape_name='none',heading=0,x=16.303737,y=20.379671,},
|
||||||
|
-- [4]={category='Helicopters',type='CH-47Fbl1',shape_name='none',heading=0,x=-20.047735,y=-63.166179,livery_id = "us army dark green",},
|
||||||
|
-- [5]={category='Infantry',type='Soldier M4',shape_name='none',heading=0,x=26.650339,y=20.066138,},
|
||||||
|
-- [6]={category='Heliports',type='FARP_SINGLE_01',shape_name='FARP_SINGLE_01',heading=0,x=-25.432292,y=9.077099,},
|
||||||
|
-- [7]={category='Heliports',type='FARP_SINGLE_01',shape_name='FARP_SINGLE_01',heading=0,x=-12.717421,y=-3.216114,},
|
||||||
|
-- [8]={category='Heliports',type='FARP_SINGLE_01',shape_name='FARP_SINGLE_01',heading=0,x=-25.439281,y=-3.216114,},
|
||||||
|
-- [9]={category='Heliports',type='FARP_SINGLE_01',shape_name='FARP_SINGLE_01',heading=0,x=-12.717421,y=9.155603,},
|
||||||
|
-- [10]={category='Fortifications',type='TACAN_beacon',shape_name='none',heading=0,x=-2.329847,y=-16.579903,},
|
||||||
|
-- [11]={category='Fortifications',type='FARP Fuel Depot',shape_name='GSM Rus',heading=0,x=2.222011,y=4.487030,},
|
||||||
|
-- [12]={category='Fortifications',type='APFC fuel',shape_name='M92_APFCfuel',heading=0,x=3.614927,y=0.367838,},
|
||||||
|
-- [13]={category='Fortifications',type='Camouflage03',shape_name='M92_Camouflage03',heading=0,x=21.544148,y=21.998879,},
|
||||||
|
-- [14]={category='Fortifications',type='Container_generator',shape_name='M92_Container_generator',heading=0,x=20.989192,y=37.314334,},
|
||||||
|
-- [15]={category='Fortifications',type='FireExtinguisher02',shape_name='M92_FireExtinguisher02',heading=0,x=3.988003,y=8.362333,},
|
||||||
|
-- [16]={category='Fortifications',type='FireExtinguisher02',shape_name='M92_FireExtinguisher02',heading=0,x=-3.953195,y=12.945844,},
|
||||||
|
-- [17]={category='Fortifications',type='Windsock',shape_name='H-Windsock_RW',heading=0,x=-18.944173,y=-33.042196,},
|
||||||
|
-- [18]={category='Fortifications',type='Tent04',shape_name='M92_Tent04',heading=0,x=21.220671,y=30.247529,},
|
||||||
|
-- }
|
||||||
|
--
|
||||||
|
function UTILS.SpawnMASHStatics(Name,Coordinate,Country,ADF,Livery,DeployHelo,MASHRadio,MASHRadioModulation,MASHCallsign,Templates)
|
||||||
|
|
||||||
|
-- Basic objects table
|
||||||
|
|
||||||
|
local MASHTemplates = {
|
||||||
|
[1]={category='Infantry',type='Soldier M4',shape_name='none',heading=0,x=0.000000,y=0.000000,},
|
||||||
|
[2]={category='Infantry',type='Soldier M4',shape_name='none',heading=0,x=0.313533,y=8.778935,},
|
||||||
|
[3]={category='Infantry',type='Soldier M4',shape_name='none',heading=0,x=16.303737,y=20.379671,},
|
||||||
|
[4]={category='Helicopters',type='CH-47Fbl1',shape_name='none',heading=0,x=-20.047735,y=-63.166179,livery_id = "us army dark green",},
|
||||||
|
[5]={category='Infantry',type='Soldier M4',shape_name='none',heading=0,x=26.650339,y=20.066138,},
|
||||||
|
[6]={category='Heliports',type='FARP_SINGLE_01',shape_name='FARP_SINGLE_01',heading=0,x=-25.432292,y=9.077099,},
|
||||||
|
[7]={category='Heliports',type='FARP_SINGLE_01',shape_name='FARP_SINGLE_01',heading=0,x=-12.717421,y=-3.216114,},
|
||||||
|
[8]={category='Heliports',type='FARP_SINGLE_01',shape_name='FARP_SINGLE_01',heading=0,x=-25.439281,y=-3.216114,},
|
||||||
|
[9]={category='Heliports',type='FARP_SINGLE_01',shape_name='FARP_SINGLE_01',heading=0,x=-12.717421,y=9.155603,},
|
||||||
|
[10]={category='Fortifications',type='TACAN_beacon',shape_name='none',heading=0,x=-2.329847,y=-16.579903,},
|
||||||
|
[11]={category='Fortifications',type='FARP Fuel Depot',shape_name='GSM Rus',heading=0,x=2.222011,y=4.487030,},
|
||||||
|
[12]={category='Fortifications',type='APFC fuel',shape_name='M92_APFCfuel',heading=0,x=3.614927,y=0.367838,},
|
||||||
|
[13]={category='Fortifications',type='Camouflage03',shape_name='M92_Camouflage03',heading=0,x=21.544148,y=21.998879,},
|
||||||
|
[14]={category='Fortifications',type='Container_generator',shape_name='M92_Container_generator',heading=0,x=20.989192,y=37.314334,},
|
||||||
|
[15]={category='Fortifications',type='FireExtinguisher02',shape_name='M92_FireExtinguisher02',heading=0,x=3.988003,y=8.362333,},
|
||||||
|
[16]={category='Fortifications',type='FireExtinguisher02',shape_name='M92_FireExtinguisher02',heading=0,x=-3.953195,y=12.945844,},
|
||||||
|
[17]={category='Fortifications',type='Windsock',shape_name='H-Windsock_RW',heading=0,x=-18.944173,y=-33.042196,},
|
||||||
|
[18]={category='Fortifications',type='Tent04',shape_name='M92_Tent04',heading=0,x=21.220671,y=30.247529,},
|
||||||
|
}
|
||||||
|
|
||||||
|
if Templates then MASHTemplates=Templates end
|
||||||
|
|
||||||
|
-- locals
|
||||||
|
local name = Name or "Florence Nightingale"
|
||||||
|
local positionVec2
|
||||||
|
local positionVec3
|
||||||
|
local ReturnStatics = {}
|
||||||
|
local CountryID = Country or country.id.USA
|
||||||
|
local livery = "us army dark green"
|
||||||
|
local MASHRadio = MASHRadio or 127.5
|
||||||
|
local MASHRadioModulation = MASHRadioModulation or radio.modulation.AM
|
||||||
|
local MASHCallsign = MASHCallsign or CALLSIGN.FARP.Berlin
|
||||||
|
|
||||||
|
-- check for coordinate or zone
|
||||||
|
if type(Coordinate) == "table" then
|
||||||
|
if Coordinate:IsInstanceOf("COORDINATE") or Coordinate:IsInstanceOf("ZONE_BASE") then
|
||||||
|
positionVec2 = Coordinate:GetVec2()
|
||||||
|
positionVec3 = Coordinate:GetVec3()
|
||||||
|
end
|
||||||
|
else
|
||||||
|
BASE:E("Spawn MASH - no ZONE or COORDINATE handed!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- position
|
||||||
|
local BaseX = positionVec2.x
|
||||||
|
local BaseY = positionVec2.y
|
||||||
|
|
||||||
|
-- Statics
|
||||||
|
for id,object in pairs(MASHTemplates) do
|
||||||
|
local NewName = string.format("%s#%3d",name,id)
|
||||||
|
local vec2 = {x=BaseX+object.x,y=BaseY+object.y}
|
||||||
|
local Coordinate=COORDINATE:NewFromVec2(vec2)
|
||||||
|
local static = SPAWNSTATIC:NewFromType(object.type,object.category,CountryID)
|
||||||
|
if object.shape_name and object.shape_name ~= "none" then
|
||||||
|
static:InitShape(object.shape_name)
|
||||||
|
end
|
||||||
|
if object.category == "Helicopters" and DeployHelo == true then
|
||||||
|
if object.livery_id ~= nil then
|
||||||
|
livery = object.livery_id
|
||||||
|
end
|
||||||
|
static:InitLivery(livery)
|
||||||
|
local newstatic = static:SpawnFromCoordinate(Coordinate,object.heading,NewName)
|
||||||
|
table.insert(ReturnStatics,newstatic)
|
||||||
|
elseif object.category == "Heliports" then
|
||||||
|
static:InitFARP(MASHCallsign,MASHRadio,MASHRadioModulation,false,false)
|
||||||
|
local newstatic = static:SpawnFromCoordinate(Coordinate,object.heading,NewName)
|
||||||
|
table.insert(ReturnStatics,newstatic)
|
||||||
|
elseif object.category ~= "Helicopters" and object.category ~= "Heliports" then
|
||||||
|
local newstatic = static:SpawnFromCoordinate(Coordinate,object.heading,NewName)
|
||||||
|
table.insert(ReturnStatics,newstatic)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Beacon
|
||||||
|
local ADFName
|
||||||
|
if ADF and type(ADF) == "number" then
|
||||||
|
local ADFFreq = ADF*1000 -- KHz to Hz
|
||||||
|
local Sound = "l10n/DEFAULT/beacon.ogg"
|
||||||
|
ADFName = Name .. " ADF "..tostring(ADF).."KHz"
|
||||||
|
--BASE:I(string.format("Adding MASH Beacon %d KHz Name %s",ADF,ADFName))
|
||||||
|
trigger.action.radioTransmission(Sound, positionVec3, 0, true, ADFFreq, 250, ADFName)
|
||||||
|
end
|
||||||
|
|
||||||
|
return ReturnStatics, ADFName
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Converts a Vec2 to a Vec3.
|
--- Converts a Vec2 to a Vec3.
|
||||||
@@ -4421,3 +4794,449 @@ end
|
|||||||
function UTILS.Weather.StopFogAnimation()
|
function UTILS.Weather.StopFogAnimation()
|
||||||
return world.weather.setFogAnimation({})
|
return world.weather.setFogAnimation({})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Find a ME created zone by its name
|
||||||
|
function UTILS.GetEnvZone(name)
|
||||||
|
for _,v in ipairs(env.mission.triggers.zones) do
|
||||||
|
if v.name == name then
|
||||||
|
return v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- net.dostring_in
|
||||||
|
function UTILS.DoStringIn(State,DoString)
|
||||||
|
return net.dostring_in(State,DoString)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Show a picture on the screen to all
|
||||||
|
-- @param #string FileName File name of the picture
|
||||||
|
-- @param #number Duration Duration in seconds, defaults to 10
|
||||||
|
-- @param #boolean ClearView If true, clears the view before showing the picture, defaults to false
|
||||||
|
-- @param #number StartDelay Delay in seconds before showing the picture, defaults to 0
|
||||||
|
-- @param #number HorizontalAlign Horizontal alignment of the picture, 0: Left, 1: Center, 2: Right
|
||||||
|
-- @param #number VerticalAlign Vertical alignment of the picture, 0: Top, 1: Center, 2: Bottom
|
||||||
|
-- @param #number Size Size of the picture in percent, defaults to 100
|
||||||
|
-- @param #number SizeUnits Size units, 0 for % of original picture size, and 1 for % of window size
|
||||||
|
function UTILS.ShowPictureToAll(FilePath, Duration, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits)
|
||||||
|
ClearView = ClearView or false
|
||||||
|
StartDelay = StartDelay or 0
|
||||||
|
HorizontalAlign = HorizontalAlign or 1
|
||||||
|
VerticalAlign = VerticalAlign or 1
|
||||||
|
Size = Size or 100
|
||||||
|
SizeUnits = SizeUnits or 0
|
||||||
|
|
||||||
|
if ClearView then ClearView = "true" else ClearView = "false" end
|
||||||
|
|
||||||
|
net.dostring_in("mission", string.format("a_out_picture(\"%s\", %d, %s, %d, \"%d\", \"%d\", %d, \"%d\")", FilePath, Duration or 10, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits))
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Show a picture on the screen to Coalition
|
||||||
|
-- @param #number Coalition Coalition ID, can be coalition.side.BLUE, coalition.side.RED or coalition.side.NEUTRAL
|
||||||
|
-- @param #string FileName File name of the picture
|
||||||
|
-- @param #number Duration Duration in seconds, defaults to 10
|
||||||
|
-- @param #boolean ClearView If true, clears the view before showing the picture, defaults to false
|
||||||
|
-- @param #number StartDelay Delay in seconds before showing the picture, defaults to 0
|
||||||
|
-- @param #number HorizontalAlign Horizontal alignment of the picture, 0: Left, 1: Center, 2: Right
|
||||||
|
-- @param #number VerticalAlign Vertical alignment of the picture, 0: Top, 1: Center, 2: Bottom
|
||||||
|
-- @param #number Size Size of the picture in percent, defaults to 100
|
||||||
|
-- @param #number SizeUnits Size units, 0 for % of original picture size, and 1 for % of window size
|
||||||
|
function UTILS.ShowPictureToCoalition(Coalition, FilePath, Duration, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits)
|
||||||
|
ClearView = ClearView or false
|
||||||
|
StartDelay = StartDelay or 0
|
||||||
|
HorizontalAlign = HorizontalAlign or 1
|
||||||
|
VerticalAlign = VerticalAlign or 1
|
||||||
|
Size = Size or 100
|
||||||
|
SizeUnits = SizeUnits or 0
|
||||||
|
|
||||||
|
if ClearView then ClearView = "true" else ClearView = "false" end
|
||||||
|
|
||||||
|
local coalName = string.lower(UTILS.GetCoalitionName(Coalition))
|
||||||
|
|
||||||
|
net.dostring_in("mission", string.format("a_out_picture_s(\"%s\", \"%s\", %d, %s, %d, \"%d\", \"%d\", %d, \"%d\")", coalName, FilePath, Duration or 10, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits))
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Show a picture on the screen to Country
|
||||||
|
-- @param #number Country Country ID, can be country.id.USA, country.id.RUSSIA, etc.
|
||||||
|
-- @param #string FileName File name of the picture
|
||||||
|
-- @param #number Duration Duration in seconds, defaults to 10
|
||||||
|
-- @param #boolean ClearView If true, clears the view before showing the picture, defaults to false
|
||||||
|
-- @param #number StartDelay Delay in seconds before showing the picture, defaults to 0
|
||||||
|
-- @param #number HorizontalAlign Horizontal alignment of the picture, 0: Left, 1: Center, 2: Right
|
||||||
|
-- @param #number VerticalAlign Vertical alignment of the picture, 0: Top, 1: Center, 2: Bottom
|
||||||
|
-- @param #number Size Size of the picture in percent, defaults to 100
|
||||||
|
-- @param #number SizeUnits Size units, 0 for % of original picture size, and 1 for % of window size
|
||||||
|
function UTILS.ShowPictureToCountry(Country, FilePath, Duration, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits)
|
||||||
|
ClearView = ClearView or false
|
||||||
|
StartDelay = StartDelay or 0
|
||||||
|
HorizontalAlign = HorizontalAlign or 1
|
||||||
|
VerticalAlign = VerticalAlign or 1
|
||||||
|
Size = Size or 100
|
||||||
|
SizeUnits = SizeUnits or 0
|
||||||
|
|
||||||
|
if ClearView then ClearView = "true" else ClearView = "false" end
|
||||||
|
|
||||||
|
net.dostring_in("mission", string.format("a_out_picture_c(%d, \"%s\", %d, %s, %d, \"%d\", \"%d\", %d, \"%d\")", Country, FilePath, Duration or 10, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits))
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Show a picture on the screen to Group
|
||||||
|
-- @param Wrapper.Group#GROUP Group Group to show the picture to
|
||||||
|
-- @param #string FileName File name of the picture
|
||||||
|
-- @param #number Duration Duration in seconds, defaults to 10
|
||||||
|
-- @param #boolean ClearView If true, clears the view before showing the picture, defaults to false
|
||||||
|
-- @param #number StartDelay Delay in seconds before showing the picture, defaults to 0
|
||||||
|
-- @param #number HorizontalAlign Horizontal alignment of the picture, 0: Left, 1: Center, 2: Right
|
||||||
|
-- @param #number VerticalAlign Vertical alignment of the picture, 0: Top, 1: Center, 2: Bottom
|
||||||
|
-- @param #number Size Size of the picture in percent, defaults to 100
|
||||||
|
-- @param #number SizeUnits Size units, 0 for % of original picture size, and 1 for % of window size
|
||||||
|
function UTILS.ShowPictureToGroup(Group, FilePath, Duration, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits)
|
||||||
|
ClearView = ClearView or false
|
||||||
|
StartDelay = StartDelay or 0
|
||||||
|
HorizontalAlign = HorizontalAlign or 1
|
||||||
|
VerticalAlign = VerticalAlign or 1
|
||||||
|
Size = Size or 100
|
||||||
|
SizeUnits = SizeUnits or 0
|
||||||
|
|
||||||
|
if ClearView then ClearView = "true" else ClearView = "false" end
|
||||||
|
|
||||||
|
net.dostring_in("mission", string.format("a_out_picture_g(%d, \"%s\", %d, %s, %d, \"%d\", \"%d\", %d, \"%d\")", Group:GetID(), FilePath, Duration or 10, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits))
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Show a picture on the screen to Unit
|
||||||
|
-- @param Wrapper.Unit#UNIT Unit Unit to show the picture to
|
||||||
|
-- @param #string FileName File name of the picture
|
||||||
|
-- @param #number Duration Duration in seconds, defaults to 10
|
||||||
|
-- @param #boolean ClearView If true, clears the view before showing the picture, defaults to false
|
||||||
|
-- @param #number StartDelay Delay in seconds before showing the picture, defaults to 0
|
||||||
|
-- @param #number HorizontalAlign Horizontal alignment of the picture, 0: Left, 1: Center, 2: Right
|
||||||
|
-- @param #number VerticalAlign Vertical alignment of the picture, 0: Top, 1: Center, 2: Bottom
|
||||||
|
-- @param #number Size Size of the picture in percent, defaults to 100
|
||||||
|
-- @param #number SizeUnits Size units, 0 for % of original picture size, and 1 for % of window size
|
||||||
|
function UTILS.ShowPictureToUnit(Unit, FilePath, Duration, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits)
|
||||||
|
ClearView = ClearView or false
|
||||||
|
StartDelay = StartDelay or 0
|
||||||
|
HorizontalAlign = HorizontalAlign or 1
|
||||||
|
VerticalAlign = VerticalAlign or 1
|
||||||
|
Size = Size or 100
|
||||||
|
SizeUnits = SizeUnits or 0
|
||||||
|
|
||||||
|
if ClearView then ClearView = "true" else ClearView = "false" end
|
||||||
|
|
||||||
|
net.dostring_in("mission", string.format("a_out_picture_u(%d, \"%s\", %d, %s, %d, \"%d\", \"%d\", %d, \"%d\")", Unit:GetID(), FilePath, Duration or 10, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits))
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Load a mission file. This will replace the current mission with the one given carrying along the online clients.
|
||||||
|
-- @param #string FileName Mission filename
|
||||||
|
function UTILS.LoadMission(FileName)
|
||||||
|
net.dostring_in("mission", string.format("a_load_mission(\"%s\")", FileName))
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the mission briefing for a coalition.
|
||||||
|
-- @param #number Coalition Briefing coalition ID, can be coalition.side.BLUE, coalition.side.RED or coalition.side.NEUTRAL
|
||||||
|
-- @param #string Text Briefing text, can contain newlines, will be converted formatted properly for DCS
|
||||||
|
-- @param #string Picture Picture file path, can be a file in the DEFAULT folder inside the .miz
|
||||||
|
function UTILS.SetMissionBriefing(Coalition, Text, Picture)
|
||||||
|
Text = Text or ""
|
||||||
|
Text = Text:gsub("\n", "\\n")
|
||||||
|
Picture = Picture or ""
|
||||||
|
local coalName = string.lower(UTILS.GetCoalitionName(Coalition))
|
||||||
|
net.dostring_in("mission", string.format("a_set_briefing(\"%s\", \"%s\", \"%s\")", coalName, Picture, Text))
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Show a helper gate at a DCS#Vec3 position
|
||||||
|
-- @param DCS#Vec3 pos The position
|
||||||
|
-- @param #number heading Heading in degrees, can be 0..359 degrees
|
||||||
|
function UTILS.ShowHelperGate(pos, heading)
|
||||||
|
net.dostring_in("mission",string.format("a_show_helper_gate(%s, %s, %s, %f)", pos.x, pos.y, pos.z, math.rad(heading)))
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Show a helper gate for a unit.
|
||||||
|
-- @param Wrapper.Unit#UNIT Unit The unit to show the gate for
|
||||||
|
-- @param #number Flag Helper gate flag
|
||||||
|
function UTILS.ShowHelperGateForUnit(Unit, Flag)
|
||||||
|
net.dostring_in("mission",string.format("a_show_route_gates_for_unit(%d, \"%d\")", Unit:GetID(), Flag))
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the carrier illumination mode. -2: OFF, -1: AUTO, 0: NAVIGATION, 1: AC LAUNCH, 2: AC RECOVERY
|
||||||
|
-- @param #number UnitID Carrier unit ID ( UNIT:GetID() )
|
||||||
|
-- @param #number Mode Illumination mode, can be -2: OFF, -1: AUTO, 0: NAVIGATION, 1: AC LAUNCH, 2: AC RECOVERY
|
||||||
|
function UTILS.SetCarrierIlluminationMode(UnitID, Mode)
|
||||||
|
net.dostring_in("mission",string.format("a_set_carrier_illumination_mode(%d, %d)", UnitID, Mode))
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Shell a zone, zone must ME created
|
||||||
|
-- @param #string name The name of the ME created zone
|
||||||
|
-- @param #number power Equals kg of TNT, e.g. 75
|
||||||
|
-- @param #count Number of shells simulated
|
||||||
|
function UTILS.ShellZone(name, power, count)
|
||||||
|
local z = UTILS.GetEnvZone(name)
|
||||||
|
if z then
|
||||||
|
net.dostring_in("mission",string.format("a_shelling_zone(%d, %d, %d)", z.zoneId, power, count))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Remove objects from a zone, zone must ME created
|
||||||
|
-- @param #string name The name of the ME created zone
|
||||||
|
-- @param #number type Type of objects to remove can be 0:all, 1: trees, 2:objects
|
||||||
|
function UTILS.RemoveObjects(name, type)
|
||||||
|
local z = UTILS.GetEnvZone(name)
|
||||||
|
if z then
|
||||||
|
net.dostring_in("mission",string.format("a_remove_scene_objects(%d, %d)", z.zoneId, type))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Remove scenery objects from a zone, zone must ME created
|
||||||
|
-- @param #string name The name of the ME created zone
|
||||||
|
-- @param #number level Level of removal
|
||||||
|
function UTILS.DestroyScenery(name, level)
|
||||||
|
local z = UTILS.GetEnvZone(name)
|
||||||
|
if z then
|
||||||
|
net.dostring_in("mission",string.format("a_scenery_destruction_zone(%d, %d)", z.zoneId, level))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Search for clear zones in a given area. A powerful and efficient function using Disposition to find clear areas for spawning ground units avoiding trees, water and map scenery.
|
||||||
|
-- @param DCS##Vec3 Center position vector for the search area.
|
||||||
|
-- @param #number SearchRadius Radius of the search area.
|
||||||
|
-- @param #number PosRadius Required clear radius around each position.
|
||||||
|
-- @param #number NumPositions Number of positions to find.
|
||||||
|
-- @return #table A table of DCS#Vec2 positions that are clear of map objects within the given PosRadius.
|
||||||
|
function UTILS.GetSimpleZones(Vec3, SearchRadius, PosRadius, NumPositions)
|
||||||
|
return Disposition.getSimpleZones(Vec3, SearchRadius, PosRadius, NumPositions)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Search for clear ground spawn zones within this zone. A powerful and efficient function using Disposition to find clear areas for spawning ground units avoiding trees, water and map scenery.
|
||||||
|
-- @param Core.Zone#ZONE Zone to search.
|
||||||
|
-- @param #number (Optional) PosRadius Required clear radius around each position. (Default is math.min(Radius/10, 200))
|
||||||
|
-- @param #number (Optional) NumPositions Number of positions to find. (Default 50)
|
||||||
|
-- @return #table A table of DCS#Vec2 positions that are clear of map objects within the given PosRadius. nil if no clear positions are found.
|
||||||
|
function UTILS.GetClearZonePositions(Zone, PosRadius, NumPositions)
|
||||||
|
local radius = PosRadius or math.min(Zone:GetRadius()/10, 200)
|
||||||
|
local clearPositions = UTILS.GetSimpleZones(Zone:GetVec3(), Zone:GetRadius(), radius, NumPositions or 50)
|
||||||
|
if clearPositions and #clearPositions > 0 then
|
||||||
|
local validZones = {}
|
||||||
|
for _, vec2 in pairs(clearPositions) do
|
||||||
|
if Zone:IsVec2InZone(vec2) then
|
||||||
|
table.insert(validZones, vec2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #validZones > 0 then
|
||||||
|
return validZones, radius
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Search for a random clear ground spawn coordinate within this zone. A powerful and efficient function using Disposition to find clear areas for spawning ground units avoiding trees, water and map scenery.
|
||||||
|
-- @param Core.Zone#ZONE Zone to search.
|
||||||
|
-- @param #number PosRadius (Optional) Required clear radius around each position. (Default is math.min(Radius/10, 200))
|
||||||
|
-- @param #number NumPositions (Optional) Number of positions to find. (Default 50)
|
||||||
|
-- @return Core.Point#COORDINATE A random coordinate for a clear zone. nil if no clear positions are found.
|
||||||
|
-- @return #number Assigned radius for the found zones. nil if no clear positions are found.
|
||||||
|
function UTILS.GetRandomClearZoneCoordinate(Zone, PosRadius, NumPositions)
|
||||||
|
local clearPositions = UTILS.GetClearZonePositions(Zone, PosRadius, NumPositions)
|
||||||
|
if clearPositions and #clearPositions > 0 then
|
||||||
|
local randomPosition, radius = clearPositions[math.random(1, #clearPositions)]
|
||||||
|
return COORDINATE:NewFromVec2(randomPosition), radius
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Find the point on the radius of a circle closest to a point outside of the radius.
|
||||||
|
-- @param DCS#Vec2 Vec1 Simple Vec2 marking the middle of the circle.
|
||||||
|
-- @param #number Radius The radius of the circle.
|
||||||
|
-- @param DCS#Vec2 Vec2 Simple Vec2 marking the point outside of the circle.
|
||||||
|
-- @return DCS#Vec2 Vec2 point on the radius.
|
||||||
|
function UTILS.FindNearestPointOnCircle(Vec1,Radius,Vec2)
|
||||||
|
local r = Radius
|
||||||
|
local cx = Vec1.x or 1
|
||||||
|
local cy = Vec1.y or 1
|
||||||
|
local px = Vec2.x or 1
|
||||||
|
local py = Vec2.y or 1
|
||||||
|
|
||||||
|
-- Berechne den Vektor vom Mittelpunkt zum externen Punkt
|
||||||
|
local dx = px - cx
|
||||||
|
local dy = py - cy
|
||||||
|
|
||||||
|
-- Berechne die Länge des Vektors
|
||||||
|
local dist = math.sqrt(dx * dx + dy * dy)
|
||||||
|
|
||||||
|
-- Wenn der Punkt im Mittelpunkt liegt, wähle einen Punkt auf der X-Achse
|
||||||
|
if dist == 0 then
|
||||||
|
return {x=cx + r, y=cy}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Normalisiere den Vektor (richtungsweise Vektor mit Länge 1)
|
||||||
|
local norm_dx = dx / dist
|
||||||
|
local norm_dy = dy / dist
|
||||||
|
|
||||||
|
-- Berechne den Punkt auf dem Rand des Kreises
|
||||||
|
local qx = cx + r * norm_dx
|
||||||
|
local qy = cy + r * norm_dy
|
||||||
|
|
||||||
|
local shift_factor = 1
|
||||||
|
qx = qx + shift_factor * norm_dx
|
||||||
|
qy = qy + shift_factor * norm_dy
|
||||||
|
|
||||||
|
return {x=qx, y=qy}
|
||||||
|
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.
|
||||||
|
-- @param #table Positions A table of DCS#Vec2 or DCS#Vec3, can be a units table from the group template.
|
||||||
|
-- @param DCS#Vec2 Anchor (Optional) DCS#Vec2 or DCS#Vec3 as anchor point to calculate offset of the units.
|
||||||
|
-- @param #number MaxRadius (Optional) Max radius to search for valid ground locations in meters. Default is double the max radius of the units.
|
||||||
|
-- @param #number Spacing (Optional) Minimum spacing between units in meters. Default is 5% of the search radius or 5 meters, whichever is larger.
|
||||||
|
function UTILS.ValidateAndRepositionGroundUnits(Positions, Anchor, MaxRadius, Spacing)
|
||||||
|
local units = Positions
|
||||||
|
Anchor = Anchor or UTILS.GetCenterPoint(units)
|
||||||
|
local gPos = { x = Anchor.x, y = Anchor.z or Anchor.y }
|
||||||
|
local maxRadius = 0
|
||||||
|
local unitCount = 0
|
||||||
|
for _, unit in pairs(units) do
|
||||||
|
local pos = { x = unit.x, y = unit.z or unit.y }
|
||||||
|
local dist = UTILS.VecDist2D(pos, gPos)
|
||||||
|
if dist > maxRadius then
|
||||||
|
maxRadius = dist
|
||||||
|
end
|
||||||
|
unitCount = unitCount + 1
|
||||||
|
end
|
||||||
|
maxRadius = MaxRadius or math.max(maxRadius * 2, 10)
|
||||||
|
local spacing = Spacing or math.max(maxRadius * 0.05, 5)
|
||||||
|
if unitCount > 0 and maxRadius > 5 then
|
||||||
|
local spots = UTILS.GetSimpleZones(UTILS.Vec2toVec3(gPos), maxRadius, spacing, 1000)
|
||||||
|
if spots and #spots > 0 then
|
||||||
|
local validSpots = {}
|
||||||
|
for _, spot in pairs(spots) do -- Disposition sometimes returns points on roads, hence this filter.
|
||||||
|
if land.getSurfaceType(spot) == land.SurfaceType.LAND then
|
||||||
|
table.insert(validSpots, spot)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
spots = validSpots
|
||||||
|
end
|
||||||
|
|
||||||
|
local step = spacing
|
||||||
|
for _, unit in pairs(units) do
|
||||||
|
local pos = { x = unit.x, y = unit.z or unit.y }
|
||||||
|
local isOnLand = land.getSurfaceType(pos) == land.SurfaceType.LAND
|
||||||
|
local isValid = false
|
||||||
|
if spots and #spots > 0 then
|
||||||
|
local si = 1
|
||||||
|
local sid = 0
|
||||||
|
local closestDist = 100000000
|
||||||
|
local closestSpot
|
||||||
|
for _, spot in pairs(spots) do
|
||||||
|
local dist = UTILS.VecDist2D(pos, spot)
|
||||||
|
if dist < closestDist then
|
||||||
|
closestDist = dist
|
||||||
|
closestSpot = spot
|
||||||
|
sid = si
|
||||||
|
end
|
||||||
|
si = si + 1
|
||||||
|
end
|
||||||
|
if closestSpot then
|
||||||
|
if closestDist >= spacing then
|
||||||
|
pos = closestSpot
|
||||||
|
end
|
||||||
|
isValid = true
|
||||||
|
table.remove(spots, sid)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Failsafe calculation
|
||||||
|
if not isValid and not isOnLand then
|
||||||
|
|
||||||
|
local h = UTILS.HdgTo(pos, gPos)
|
||||||
|
local retries = 0
|
||||||
|
while not isValid and retries < 500 do
|
||||||
|
|
||||||
|
local dist = UTILS.VecDist2D(pos, gPos)
|
||||||
|
pos = UTILS.Vec2Translate(pos, step, h)
|
||||||
|
|
||||||
|
local skip = false
|
||||||
|
for _, unit2 in pairs(units) do
|
||||||
|
if unit ~= unit2 then
|
||||||
|
local pos2 = { x = unit2.x, y = unit2.z or unit2.y }
|
||||||
|
local dist2 = UTILS.VecDist2D(pos, pos2)
|
||||||
|
if dist2 < 12 then
|
||||||
|
isValid = false
|
||||||
|
skip = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not skip and dist > step and land.getSurfaceType(pos) == land.SurfaceType.LAND then
|
||||||
|
isValid = true
|
||||||
|
break
|
||||||
|
elseif dist <= step then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
retries = retries + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if isValid then
|
||||||
|
unit.x = pos.x
|
||||||
|
if unit.z then
|
||||||
|
unit.z = pos.y
|
||||||
|
else
|
||||||
|
unit.y = pos.y
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- This function uses Disposition and other fallback logic to find better ground positions for statics.
|
||||||
|
--- 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.
|
||||||
|
-- @param #table Positions A table of DCS#Vec2 or DCS#Vec3, can be a units table from the group template.
|
||||||
|
-- @param DCS#Vec2 Position DCS#Vec2 or DCS#Vec3 initial spawn location.
|
||||||
|
-- @param #number MaxRadius (Optional) Max radius to search for valid ground locations in meters. Default is double the max radius of the static.
|
||||||
|
-- @return DCS#Vec2 Initial Position if it's valid, else a valid spawn position. nil if no valid position found.
|
||||||
|
function UTILS.ValidateAndRepositionStatic(Country, Category, Type, Position, ShapeName, MaxRadius)
|
||||||
|
local coord = COORDINATE:NewFromVec2(Position)
|
||||||
|
local st = SPAWNSTATIC:NewFromType(Type, Category, Country)
|
||||||
|
if ShapeName then
|
||||||
|
st:InitShape(ShapeName)
|
||||||
|
end
|
||||||
|
local sName = "s-"..timer.getTime().."-"..math.random(1,10000)
|
||||||
|
local tempStatic = st:SpawnFromCoordinate(coord, 0, sName)
|
||||||
|
if tempStatic then
|
||||||
|
local sRadius = tempStatic:GetBoundingRadius(2) or 3
|
||||||
|
tempStatic:Destroy()
|
||||||
|
sRadius = sRadius * 0.5
|
||||||
|
MaxRadius = MaxRadius or math.max(sRadius * 10, 100)
|
||||||
|
local positions = UTILS.GetSimpleZones(coord:GetVec3(), MaxRadius, sRadius, 20)
|
||||||
|
if positions and #positions > 0 then
|
||||||
|
local closestSpot
|
||||||
|
local closestDist = math.huge
|
||||||
|
for _, spot in pairs(positions) do -- Disposition sometimes returns points on roads, hence this filter.
|
||||||
|
if land.getSurfaceType(spot) == land.SurfaceType.LAND then
|
||||||
|
local dist = UTILS.VecDist2D(Position, spot)
|
||||||
|
if dist < closestDist then
|
||||||
|
closestDist = dist
|
||||||
|
closestSpot = spot
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if closestSpot then
|
||||||
|
if closestDist >= sRadius then
|
||||||
|
return closestSpot
|
||||||
|
else
|
||||||
|
return Position
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
-- @field #AIRBASE.Runway runwayLanding Runway used for landing.
|
-- @field #AIRBASE.Runway runwayLanding Runway used for landing.
|
||||||
-- @field #AIRBASE.Runway runwayTakeoff Runway used for takeoff.
|
-- @field #AIRBASE.Runway runwayTakeoff Runway used for takeoff.
|
||||||
-- @field Wrapper.Storage#STORAGE storage The DCS warehouse storage.
|
-- @field Wrapper.Storage#STORAGE storage The DCS warehouse storage.
|
||||||
|
-- @field #table taxiways Taxiways stored as PATHLINEs.
|
||||||
-- @extends Wrapper.Positionable#POSITIONABLE
|
-- @extends Wrapper.Positionable#POSITIONABLE
|
||||||
|
|
||||||
--- Wrapper class to handle the DCS Airbase objects:
|
--- Wrapper class to handle the DCS Airbase objects:
|
||||||
@@ -78,6 +79,7 @@ AIRBASE = {
|
|||||||
[Airbase.Category.SHIP] = "Ship",
|
[Airbase.Category.SHIP] = "Ship",
|
||||||
},
|
},
|
||||||
activerwyno = nil,
|
activerwyno = nil,
|
||||||
|
taxiways = {},
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Enumeration to identify the airbases in the Caucasus region.
|
--- Enumeration to identify the airbases in the Caucasus region.
|
||||||
@@ -449,7 +451,6 @@ AIRBASE.TheChannel = {
|
|||||||
-- * AIRBASE.Syria.Al_Dumayr
|
-- * AIRBASE.Syria.Al_Dumayr
|
||||||
-- * AIRBASE.Syria.Al_Qusayr
|
-- * AIRBASE.Syria.Al_Qusayr
|
||||||
-- * AIRBASE.Syria.Aleppo
|
-- * AIRBASE.Syria.Aleppo
|
||||||
-- * AIRBASE.Syria.Amman
|
|
||||||
-- * AIRBASE.Syria.An_Nasiriyah
|
-- * AIRBASE.Syria.An_Nasiriyah
|
||||||
-- * AIRBASE.Syria.At_Tanf
|
-- * AIRBASE.Syria.At_Tanf
|
||||||
-- * AIRBASE.Syria.Bassel_Al_Assad
|
-- * AIRBASE.Syria.Bassel_Al_Assad
|
||||||
@@ -511,7 +512,7 @@ AIRBASE.TheChannel = {
|
|||||||
-- * AIRBASE.Syria.Wujah_Al_Hajar
|
-- * AIRBASE.Syria.Wujah_Al_Hajar
|
||||||
-- * AIRBASE.Syria.Ben_Gurion
|
-- * AIRBASE.Syria.Ben_Gurion
|
||||||
-- * AIRBASE.Syria.Hatzor
|
-- * AIRBASE.Syria.Hatzor
|
||||||
-- * AIRBASE.Syria.Palmashim
|
-- * AIRBASE.Syria.Palmachim
|
||||||
-- * AIRBASE.Syria.Tel_Nof
|
-- * AIRBASE.Syria.Tel_Nof
|
||||||
-- * AIRBASE.Syria.Marka
|
-- * AIRBASE.Syria.Marka
|
||||||
--
|
--
|
||||||
@@ -523,7 +524,6 @@ AIRBASE.Syria={
|
|||||||
["Al_Dumayr"] = "Al-Dumayr",
|
["Al_Dumayr"] = "Al-Dumayr",
|
||||||
["Al_Qusayr"] = "Al Qusayr",
|
["Al_Qusayr"] = "Al Qusayr",
|
||||||
["Aleppo"] = "Aleppo",
|
["Aleppo"] = "Aleppo",
|
||||||
["Amman"] = "Amman",
|
|
||||||
["An_Nasiriyah"] = "An Nasiriyah",
|
["An_Nasiriyah"] = "An Nasiriyah",
|
||||||
["At_Tanf"] = "At Tanf",
|
["At_Tanf"] = "At Tanf",
|
||||||
["Bassel_Al_Assad"] = "Bassel Al-Assad",
|
["Bassel_Al_Assad"] = "Bassel Al-Assad",
|
||||||
@@ -555,6 +555,7 @@ AIRBASE.Syria={
|
|||||||
["Kuweires"] = "Kuweires",
|
["Kuweires"] = "Kuweires",
|
||||||
["Lakatamia"] = "Lakatamia",
|
["Lakatamia"] = "Lakatamia",
|
||||||
["Larnaca"] = "Larnaca",
|
["Larnaca"] = "Larnaca",
|
||||||
|
["Marka"] = "Marka",
|
||||||
["Marj_Ruhayyil"] = "Marj Ruhayyil",
|
["Marj_Ruhayyil"] = "Marj Ruhayyil",
|
||||||
["Marj_as_Sultan_North"] = "Marj as Sultan North",
|
["Marj_as_Sultan_North"] = "Marj as Sultan North",
|
||||||
["Marj_as_Sultan_South"] = "Marj as Sultan South",
|
["Marj_as_Sultan_South"] = "Marj as Sultan South",
|
||||||
@@ -585,9 +586,8 @@ AIRBASE.Syria={
|
|||||||
["Wujah_Al_Hajar"] = "Wujah Al Hajar",
|
["Wujah_Al_Hajar"] = "Wujah Al Hajar",
|
||||||
["Ben_Gurion"] = "Ben Gurion",
|
["Ben_Gurion"] = "Ben Gurion",
|
||||||
["Hatzor"] = "Hatzor",
|
["Hatzor"] = "Hatzor",
|
||||||
["Palmashim"] = "Palmashim",
|
["Palmachim"] = "Palmachim",
|
||||||
["Tel_Nof"] = "Tel Nof",
|
["Tel_Nof"] = "Tel Nof",
|
||||||
["Marka"] = "Marka",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Airbases of the Mariana Islands map:
|
--- Airbases of the Mariana Islands map:
|
||||||
@@ -613,6 +613,35 @@ AIRBASE.MarianaIslands = {
|
|||||||
["Tinian_Intl"] = "Tinian Intl",
|
["Tinian_Intl"] = "Tinian Intl",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
--- Airbase of the Marianas WWII map
|
||||||
|
--
|
||||||
|
-- * AIRBASE.MarianaIslandsWWII.Agana
|
||||||
|
-- * AIRBASE.MarianaIslandsWWII.Airfield_3
|
||||||
|
-- * AIRBASE.MarianaIslandsWWII.Charon_Kanoa
|
||||||
|
-- * AIRBASE.MarianaIslandsWWII.Gurguan_Point
|
||||||
|
-- * AIRBASE.MarianaIslandsWWII.Isley
|
||||||
|
-- * AIRBASE.MarianaIslandsWWII.Kagman
|
||||||
|
-- * AIRBASE.MarianaIslandsWWII.Marpi
|
||||||
|
-- * AIRBASE.MarianaIslandsWWII.Orote
|
||||||
|
-- * AIRBASE.MarianaIslandsWWII.Pagan
|
||||||
|
-- * AIRBASE.MarianaIslandsWWII.Rota
|
||||||
|
-- * AIRBASE.MarianaIslandsWWII.Ushi
|
||||||
|
-- @field AIRBASE.MarianaIslandsWWII
|
||||||
|
AIRBASE.MarianaIslandsWWII =
|
||||||
|
{
|
||||||
|
["Agana"] = "Agana",
|
||||||
|
["Airfield_3"] = "Airfield 3",
|
||||||
|
["Charon_Kanoa"] = "Charon Kanoa",
|
||||||
|
["Gurguan_Point"] = "Gurguan Point",
|
||||||
|
["Isley"] = "Isley",
|
||||||
|
["Kagman"] = "Kagman",
|
||||||
|
["Marpi"] = "Marpi",
|
||||||
|
["Orote"] = "Orote",
|
||||||
|
["Pagan"] = "Pagan",
|
||||||
|
["Rota"] = "Rota",
|
||||||
|
["Ushi"] = "Ushi",
|
||||||
|
}
|
||||||
|
|
||||||
--- Airbases of the South Atlantic map:
|
--- Airbases of the South Atlantic map:
|
||||||
--
|
--
|
||||||
-- * AIRBASE.SouthAtlantic.Almirante_Schroeders
|
-- * AIRBASE.SouthAtlantic.Almirante_Schroeders
|
||||||
@@ -676,51 +705,56 @@ AIRBASE.SouthAtlantic={
|
|||||||
|
|
||||||
--- Airbases of the Sinai map:
|
--- Airbases of the Sinai map:
|
||||||
--
|
--
|
||||||
-- * AIRBASE.Sinai.Abu_Rudeis
|
-- * AIRBASE.SinaiMap.Abu_Rudeis
|
||||||
-- * AIRBASE.Sinai.Abu_Suwayr
|
-- * AIRBASE.SinaiMap.Abu_Suwayr
|
||||||
-- * AIRBASE.Sinai.Al_Bahr_al_Ahmar
|
-- * AIRBASE.SinaiMap.Al_Bahr_al_Ahmar
|
||||||
-- * AIRBASE.Sinai.Al_Ismailiyah
|
-- * AIRBASE.SinaiMap.Al_Ismailiyah
|
||||||
-- * AIRBASE.Sinai.Al_Khatatbah
|
-- * AIRBASE.SinaiMap.Al_Khatatbah
|
||||||
-- * AIRBASE.Sinai.Al_Mansurah
|
-- * AIRBASE.SinaiMap.Al_Mansurah
|
||||||
-- * AIRBASE.Sinai.Al_Rahmaniyah_Air_Base
|
-- * AIRBASE.SinaiMap.Al_Rahmaniyah_Air_Base
|
||||||
-- * AIRBASE.Sinai.As_Salihiyah
|
-- * AIRBASE.SinaiMap.As_Salihiyah
|
||||||
-- * AIRBASE.Sinai.AzZaqaziq
|
-- * AIRBASE.SinaiMap.AzZaqaziq
|
||||||
-- * AIRBASE.Sinai.Baluza
|
-- * AIRBASE.SinaiMap.Baluza
|
||||||
-- * AIRBASE.Sinai.Ben_Gurion
|
-- * AIRBASE.SinaiMap.Ben_Gurion
|
||||||
-- * AIRBASE.Sinai.Beni_Suef
|
-- * AIRBASE.SinaiMap.Beni_Suef
|
||||||
-- * AIRBASE.Sinai.Bilbeis_Air_Base
|
-- * AIRBASE.SinaiMap.Bilbeis_Air_Base
|
||||||
-- * AIRBASE.Sinai.Bir_Hasanah
|
-- * AIRBASE.SinaiMap.Bir_Hasanah
|
||||||
-- * AIRBASE.Sinai.Birma_Air_Base
|
-- * AIRBASE.SinaiMap.Birma_Air_Base
|
||||||
-- * AIRBASE.Sinai.Borj_El_Arab_International_Airport
|
-- * AIRBASE.SinaiMap.Borg_El_Arab_International_Airport
|
||||||
-- * AIRBASE.Sinai.Cairo_International_Airport
|
-- * AIRBASE.SinaiMap.Cairo_International_Airport
|
||||||
-- * AIRBASE.Sinai.Cairo_West
|
-- * AIRBASE.SinaiMap.Cairo_West
|
||||||
-- * AIRBASE.Sinai.Difarsuwar_Airfield
|
-- * AIRBASE.SinaiMap.Damascus_Intl
|
||||||
-- * AIRBASE.Sinai.El_Arish
|
-- * AIRBASE.SinaiMap.Difarsuwar_Airfield
|
||||||
-- * AIRBASE.Sinai.El_Gora
|
-- * AIRBASE.SinaiMap.El_Arish
|
||||||
-- * AIRBASE.Sinai.El_Minya
|
-- * AIRBASE.SinaiMap.El_Gora
|
||||||
-- * AIRBASE.Sinai.Fayed
|
-- * AIRBASE.SinaiMap.El_Minya
|
||||||
-- * AIRBASE.Sinai.Gebel_El_Basur_Air_Base
|
-- * AIRBASE.SinaiMap.Fayed
|
||||||
-- * AIRBASE.Sinai.Hatzerim
|
-- * AIRBASE.SinaiMap.Gebel_El_Basur_Air_Base
|
||||||
-- * AIRBASE.Sinai.Hatzor
|
-- * AIRBASE.SinaiMap.Hatzerim
|
||||||
-- * AIRBASE.Sinai.Hurghada_International_Airport
|
-- * AIRBASE.SinaiMap.Hatzor
|
||||||
-- * AIRBASE.Sinai.Inshas_Airbase
|
-- * AIRBASE.SinaiMap.Hurghada_International_Airport
|
||||||
-- * AIRBASE.Sinai.Jiyanklis_Air_Base
|
-- * AIRBASE.SinaiMap.Inshas_Airbase
|
||||||
-- * AIRBASE.Sinai.Kedem
|
-- * AIRBASE.SinaiMap.Jiyanklis_Air_Base
|
||||||
-- * AIRBASE.Sinai.Kibrit_Air_Base
|
-- * AIRBASE.SinaiMap.Kedem
|
||||||
-- * AIRBASE.Sinai.Kom_Awshim
|
-- * AIRBASE.SinaiMap.Kibrit_Air_Base
|
||||||
-- * AIRBASE.Sinai.Melez
|
-- * AIRBASE.SinaiMap.Kom_Awshim
|
||||||
-- * AIRBASE.Sinai.Nevatim
|
-- * AIRBASE.SinaiMap.Melez
|
||||||
-- * AIRBASE.Sinai.Ovda
|
-- * AIRBASE.SinaiMap.Mezzeh_Air_Base
|
||||||
-- * AIRBASE.Sinai.Palmachim
|
-- * AIRBASE.SinaiMap.Nevatim
|
||||||
-- * AIRBASE.Sinai.Quwaysina
|
-- * AIRBASE.SinaiMap.Ovda
|
||||||
-- * AIRBASE.Sinai.Ramon_Airbase
|
-- * AIRBASE.SinaiMap.Palmachim
|
||||||
-- * AIRBASE.Sinai.Ramon_International_Airport
|
-- * AIRBASE.SinaiMap.Quwaysina
|
||||||
-- * AIRBASE.Sinai.Sde_Dov
|
-- * AIRBASE.SinaiMap.Rafic_Hariri_Intl
|
||||||
-- * AIRBASE.Sinai.Sharm_El_Sheikh_International_Airport
|
-- * AIRBASE.SinaiMap.Ramat_David
|
||||||
-- * AIRBASE.Sinai.St_Catherine
|
-- * AIRBASE.SinaiMap.Ramon_Airbase
|
||||||
-- * AIRBASE.Sinai.Tel_Nof
|
-- * AIRBASE.SinaiMap.Ramon_International_Airport
|
||||||
-- * AIRBASE.Sinai.Wadi_Abu_Rish
|
-- * AIRBASE.SinaiMap.Sde_Dov
|
||||||
-- * AIRBASE.Sinai.Wadi_al_Jandali
|
-- * AIRBASE.SinaiMap.Sharm_El_Sheikh_International_Airport
|
||||||
|
-- * AIRBASE.SinaiMap.St_Catherine
|
||||||
|
-- * AIRBASE.SinaiMap.Tabuk
|
||||||
|
-- * AIRBASE.SinaiMap.Tel_Nof
|
||||||
|
-- * AIRBASE.SinaiMap.Wadi_Abu_Rish
|
||||||
|
-- * AIRBASE.SinaiMap.Wadi_al_Jandali
|
||||||
--
|
--
|
||||||
-- @field Sinai
|
-- @field Sinai
|
||||||
AIRBASE.Sinai = {
|
AIRBASE.Sinai = {
|
||||||
@@ -739,9 +773,10 @@ AIRBASE.Sinai = {
|
|||||||
["Bilbeis_Air_Base"] = "Bilbeis Air Base",
|
["Bilbeis_Air_Base"] = "Bilbeis Air Base",
|
||||||
["Bir_Hasanah"] = "Bir Hasanah",
|
["Bir_Hasanah"] = "Bir Hasanah",
|
||||||
["Birma_Air_Base"] = "Birma Air Base",
|
["Birma_Air_Base"] = "Birma Air Base",
|
||||||
["Borj_El_Arab_International_Airport"] = "Borj El Arab International Airport",
|
["Borg_El_Arab_International_Airport"] = "Borg El Arab International Airport",
|
||||||
["Cairo_International_Airport"] = "Cairo International Airport",
|
["Cairo_International_Airport"] = "Cairo International Airport",
|
||||||
["Cairo_West"] = "Cairo West",
|
["Cairo_West"] = "Cairo West",
|
||||||
|
["Damascus_Intl"] = "Damascus Intl",
|
||||||
["Difarsuwar_Airfield"] = "Difarsuwar Airfield",
|
["Difarsuwar_Airfield"] = "Difarsuwar Airfield",
|
||||||
["El_Arish"] = "El Arish",
|
["El_Arish"] = "El Arish",
|
||||||
["El_Gora"] = "El Gora",
|
["El_Gora"] = "El Gora",
|
||||||
@@ -757,15 +792,19 @@ AIRBASE.Sinai = {
|
|||||||
["Kibrit_Air_Base"] = "Kibrit Air Base",
|
["Kibrit_Air_Base"] = "Kibrit Air Base",
|
||||||
["Kom_Awshim"] = "Kom Awshim",
|
["Kom_Awshim"] = "Kom Awshim",
|
||||||
["Melez"] = "Melez",
|
["Melez"] = "Melez",
|
||||||
|
["Mezzeh_Air_Base"] = "Mezzeh Air Base",
|
||||||
["Nevatim"] = "Nevatim",
|
["Nevatim"] = "Nevatim",
|
||||||
["Ovda"] = "Ovda",
|
["Ovda"] = "Ovda",
|
||||||
["Palmachim"] = "Palmachim",
|
["Palmachim"] = "Palmachim",
|
||||||
["Quwaysina"] = "Quwaysina",
|
["Quwaysina"] = "Quwaysina",
|
||||||
|
["Rafic_Hariri_Intl"] = "Rafic Hariri Intl",
|
||||||
|
["Ramat_David"] = "Ramat David",
|
||||||
["Ramon_Airbase"] = "Ramon Airbase",
|
["Ramon_Airbase"] = "Ramon Airbase",
|
||||||
["Ramon_International_Airport"] = "Ramon International Airport",
|
["Ramon_International_Airport"] = "Ramon International Airport",
|
||||||
["Sde_Dov"] = "Sde Dov",
|
["Sde_Dov"] = "Sde Dov",
|
||||||
["Sharm_El_Sheikh_International_Airport"] = "Sharm El Sheikh International Airport",
|
["Sharm_El_Sheikh_International_Airport"] = "Sharm El Sheikh International Airport",
|
||||||
["St_Catherine"] = "St Catherine",
|
["St_Catherine"] = "St Catherine",
|
||||||
|
["Tabuk"] = "Tabuk",
|
||||||
["Tel_Nof"] = "Tel Nof",
|
["Tel_Nof"] = "Tel Nof",
|
||||||
["Wadi_Abu_Rish"] = "Wadi Abu Rish",
|
["Wadi_Abu_Rish"] = "Wadi Abu Rish",
|
||||||
["Wadi_al_Jandali"] = "Wadi al Jandali",
|
["Wadi_al_Jandali"] = "Wadi al Jandali",
|
||||||
@@ -830,6 +869,12 @@ AIRBASE.Kola = {
|
|||||||
["Enontekio"] = "Enontekio",
|
["Enontekio"] = "Enontekio",
|
||||||
["Evenes"] = "Evenes",
|
["Evenes"] = "Evenes",
|
||||||
["Hosio"] = "Hosio",
|
["Hosio"] = "Hosio",
|
||||||
|
["Kilpyavr"] = "Kilpyavr",
|
||||||
|
["Afrikanda"] = "Afrikanda",
|
||||||
|
["Kalevala"] = "Kalevala",
|
||||||
|
["Koshka_Yavr"] = "Koshka Yavr",
|
||||||
|
["Poduzhemye"] = "Poduzhemye",
|
||||||
|
["Luostari_Pechenga"] = "Luostari Pechenga",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Airbases of the Afghanistan map
|
--- Airbases of the Afghanistan map
|
||||||
@@ -890,37 +935,51 @@ AIRBASE.Afghanistan = {
|
|||||||
|
|
||||||
--- Airbases of the Iraq map
|
--- Airbases of the Iraq map
|
||||||
--
|
--
|
||||||
-- * AIRBASE.Iraq.Baghdad_International_Airport
|
|
||||||
-- * AIRBASE.Iraq.Sulaimaniyah_International_Airport
|
|
||||||
-- * AIRBASE.Iraq.Al_Sahra_Airport
|
|
||||||
-- * AIRBASE.Iraq.Erbil_International_Airpor
|
|
||||||
-- * AIRBASE.Iraq.Al_Taji_Airport
|
|
||||||
-- * AIRBASE.Iraq.Al_Asad_Airbase
|
-- * AIRBASE.Iraq.Al_Asad_Airbase
|
||||||
|
-- * AIRBASE.Iraq.Al_Kut_Airbase
|
||||||
|
-- * AIRBASE.Iraq.Al_Sahra_Airport
|
||||||
-- * AIRBASE.Iraq.Al_Salam_Airbase
|
-- * AIRBASE.Iraq.Al_Salam_Airbase
|
||||||
-- * AIRBASE.Iraq.Balad_Airbase
|
-- * AIRBASE.Iraq.Al_Taji_Airport
|
||||||
-- * AIRBASE.Iraq.Kirkuk_International_Airport
|
|
||||||
-- * AIRBASE.Iraq.Bashur_Airport
|
|
||||||
-- * AIRBASE.Iraq.Al_Taquddum_Airport
|
-- * AIRBASE.Iraq.Al_Taquddum_Airport
|
||||||
-- * AIRBASE.Iraq.Qayyarah_Airfield_West
|
-- * AIRBASE.Iraq.Baghdad_International_Airport
|
||||||
|
-- * AIRBASE.Iraq.Balad_Airbase
|
||||||
|
-- * AIRBASE.Iraq.Bashur_Airport
|
||||||
|
-- * AIRBASE.Iraq.Erbil_International_Airport
|
||||||
|
-- * AIRBASE.Iraq.Sulaimaniyah_International_Airport
|
||||||
|
-- * AIRBASE.Iraq.H2_Airbase
|
||||||
|
-- * AIRBASE.Iraq.H3_Main_Airbase
|
||||||
|
-- * AIRBASE.Iraq.H3_Northwest_Airbase
|
||||||
|
-- * AIRBASE.Iraq.H3_Southwest_Airbase
|
||||||
-- * AIRBASE.Iraq.K1_Base
|
-- * AIRBASE.Iraq.K1_Base
|
||||||
|
-- * AIRBASE.Iraq.Kirkuk_International_Airport
|
||||||
|
-- * AIRBASE.Iraq.Mosul_International_Airport
|
||||||
|
-- * AIRBASE.Iraq.Qayyarah_Airfield_West
|
||||||
|
-- * AIRBASE.Iraq.Sulaimaniyah_International_Airport
|
||||||
--
|
--
|
||||||
-- @field Iraq
|
-- @field Iraq
|
||||||
AIRBASE.Iraq = {
|
AIRBASE.Iraq = {
|
||||||
["Baghdad_International_Airport"] = "Baghdad International Airport",
|
|
||||||
["Sulaimaniyah_International_Airport"] = "Sulaimaniyah International Airport",
|
|
||||||
["Al_Sahra_Airport"] = "Al-Sahra Airport",
|
|
||||||
["Erbil_International_Airport"] = "Erbil International Airport",
|
|
||||||
["Al_Taji_Airport"] = "Al-Taji Airport",
|
|
||||||
["Al_Asad_Airbase"] = "Al-Asad Airbase",
|
["Al_Asad_Airbase"] = "Al-Asad Airbase",
|
||||||
|
["Al_Kut_Airport"] = "Al-Kut Airport",
|
||||||
|
["Al_Sahra_Airport"] = "Al-Sahra Airport",
|
||||||
["Al_Salam_Airbase"] = "Al-Salam Airbase",
|
["Al_Salam_Airbase"] = "Al-Salam Airbase",
|
||||||
["Balad_Airbase"] = "Balad Airbase",
|
["Al_Taji_Airport"] = "Al-Taji Airport",
|
||||||
["Kirkuk_International_Airport"] = "Kirkuk International Airport",
|
|
||||||
["Bashur_Airport"] = "Bashur Airport",
|
|
||||||
["Al_Taquddum_Airport"] = "Al-Taquddum Airport",
|
["Al_Taquddum_Airport"] = "Al-Taquddum Airport",
|
||||||
["Qayyarah_Airfield_West"] = "Qayyarah Airfield West",
|
["Baghdad_International_Airport"] = "Baghdad International Airport",
|
||||||
|
["Balad_Airbase"] = "Balad Airbase",
|
||||||
|
["Bashur_Airport"] = "Bashur Airport",
|
||||||
|
["Erbil_International_Airport"] = "Erbil International Airport",
|
||||||
|
["H2_Airbase"] = "H-2 Airbase",
|
||||||
|
["H3_Main_Airbase"] = "H-3 Main Airbase",
|
||||||
|
["H3_Northwest_Airbase"] = "H-3 Northwest Airbase",
|
||||||
|
["H3_Southwest_Airbase"] = "H-3 Southwest Airbase",
|
||||||
["K1_Base"] = "K1 Base",
|
["K1_Base"] = "K1 Base",
|
||||||
|
["Kirkuk_International_Airport"] = "Kirkuk International Airport",
|
||||||
|
["Mosul_International_Airport"] = "Mosul International Airport",
|
||||||
|
["Qayyarah_Airfield_West"] = "Qayyarah Airfield West",
|
||||||
|
["Sulaimaniyah_International_Airport"] = "Sulaimaniyah International Airport",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
--- Airbases of the Germany Cold War map
|
--- Airbases of the Germany Cold War map
|
||||||
-- * AIRBASE.GermanyCW.Airracing_Frankfurt
|
-- * AIRBASE.GermanyCW.Airracing_Frankfurt
|
||||||
-- * AIRBASE.GermanyCW.Airracing_Frankfurt
|
-- * AIRBASE.GermanyCW.Airracing_Frankfurt
|
||||||
@@ -1397,6 +1456,32 @@ AIRBASE.SpotStatus = {
|
|||||||
RESERVED="Reserved",
|
RESERVED="Reserved",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
--- ICAO Codes.
|
||||||
|
-- @type AIRBASE.ICAO
|
||||||
|
AIRBASE.ICAO = {
|
||||||
|
UGTB=AIRBASE.Caucasus.Tbilisi_Lochini,
|
||||||
|
UGKO=AIRBASE.Caucasus.Kutaisi,
|
||||||
|
UG5X=AIRBASE.Caucasus.Kobuleti,
|
||||||
|
UG24=AIRBASE.Caucasus.Soganlug,
|
||||||
|
UG27=AIRBASE.Caucasus.Vaziani,
|
||||||
|
UGSS=AIRBASE.Caucasus.Sukhumi_Babushara,
|
||||||
|
UG23=AIRBASE.Caucasus.Gudauta,
|
||||||
|
URSS=AIRBASE.Caucasus.Sochi_Adler,
|
||||||
|
URMO=AIRBASE.Caucasus.Beslan,
|
||||||
|
URMN=AIRBASE.Caucasus.Nalchik,
|
||||||
|
XRMF=AIRBASE.Caucasus.Mozdok,
|
||||||
|
URMM=AIRBASE.Caucasus.Mineralnye_Vody,
|
||||||
|
URKH=AIRBASE.Caucasus.Maykop_Khanskaya,
|
||||||
|
URKK=AIRBASE.Caucasus.Krasnodar_Pashkovsky,
|
||||||
|
URKL=AIRBASE.Caucasus.Krasnodar_Center,
|
||||||
|
URKG=AIRBASE.Caucasus.Gelendzhik,
|
||||||
|
URKN=AIRBASE.Caucasus.Novorossiysk,
|
||||||
|
URKW=AIRBASE.Caucasus.Krymsk,
|
||||||
|
URKA=AIRBASE.Caucasus.Anapa_Vityazevo,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Runway data.
|
--- Runway data.
|
||||||
-- @type AIRBASE.Runway
|
-- @type AIRBASE.Runway
|
||||||
-- @field #string name Runway name.
|
-- @field #string name Runway name.
|
||||||
@@ -1434,7 +1519,7 @@ function AIRBASE:Register(AirbaseName)
|
|||||||
self.descriptors=self:GetDesc()
|
self.descriptors=self:GetDesc()
|
||||||
|
|
||||||
-- Debug info.
|
-- Debug info.
|
||||||
--self:I({airbase=AirbaseName, descriptors=self.descriptors})
|
--self:T({airbase=AirbaseName, descriptors=self.descriptors})
|
||||||
|
|
||||||
-- Category.
|
-- Category.
|
||||||
self.category=self.descriptors and self.descriptors.category or Airbase.Category.AIRDROME
|
self.category=self.descriptors and self.descriptors.category or Airbase.Category.AIRDROME
|
||||||
@@ -1517,6 +1602,17 @@ end
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get the true airbase center as seen in the ME. The position returned by the dcs object is is wrong and often at the start of the runway.
|
||||||
|
-- @return DCS#Vec2 The center of the true center of the airbase if it contains runways, otherwise the default DCS object position.
|
||||||
|
function AIRBASE:GetVec2()
|
||||||
|
local runways = self:GetRunways()
|
||||||
|
if runways and #runways > 0 then
|
||||||
|
return runways[1].center:GetVec2()
|
||||||
|
end
|
||||||
|
return self:GetCoordinate():GetVec2()
|
||||||
|
end
|
||||||
|
|
||||||
--- Get the category of this airbase. This is only a debug function because DCS 2.9 incorrectly returns heliports as airdromes.
|
--- Get the category of this airbase. This is only a debug function because DCS 2.9 incorrectly returns heliports as airdromes.
|
||||||
-- @param #AIRBASE self
|
-- @param #AIRBASE self
|
||||||
function AIRBASE:_GetCategory()
|
function AIRBASE:_GetCategory()
|
||||||
@@ -1574,14 +1670,47 @@ end
|
|||||||
|
|
||||||
--- Find a AIRBASE in the _DATABASE using the name of an existing DCS Airbase.
|
--- Find a AIRBASE in the _DATABASE using the name of an existing DCS Airbase.
|
||||||
-- @param #AIRBASE self
|
-- @param #AIRBASE self
|
||||||
-- @param #string AirbaseName The Airbase Name.
|
-- @param #string AirbaseName The name of the airbase. Can also be the ICAO code or the airbase ID.
|
||||||
-- @return #AIRBASE self
|
-- @return #AIRBASE self
|
||||||
function AIRBASE:FindByName( AirbaseName )
|
function AIRBASE:FindByName( AirbaseName )
|
||||||
|
|
||||||
local AirbaseFound = _DATABASE:FindAirbase( AirbaseName )
|
local AirbaseFound = _DATABASE:FindAirbase( AirbaseName )
|
||||||
|
|
||||||
|
if not AirbaseFound then
|
||||||
|
AirbaseFound=self:FindByICAO(AirbaseName)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not AirbaseFound then
|
||||||
|
AirbaseFound=self:FindByID(AirbaseName)
|
||||||
|
end
|
||||||
|
|
||||||
return AirbaseFound
|
return AirbaseFound
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Find an AIRBASE in the _DATABASE using the [International Civil Aviation Organization](https://en.wikipedia.org/wiki/ICAO_airport_code) (ICAO) airport code.
|
||||||
|
-- The code consists of four characters. Typically, the first one or two letters of the ICAO code indicate the country and the remaining letters identify the airport.
|
||||||
|
--
|
||||||
|
-- **NOTE** that the ICAO code cannot be retrieved via the DCS API and has to be hard coded into the MOOSE code. Therefore, it is a rare occasion where
|
||||||
|
-- @param #AIRBASE self
|
||||||
|
-- @param #string AirbaseICAO The Airbase ICAO code.
|
||||||
|
-- @return #AIRBASE self
|
||||||
|
function AIRBASE:FindByICAO( AirbaseICAO )
|
||||||
|
|
||||||
|
if AirbaseICAO then
|
||||||
|
|
||||||
|
local name=AIRBASE.ICAO[AirbaseICAO]
|
||||||
|
|
||||||
|
env.info(string.format("FF ICAO=%s, Name=%s", tostring(AirbaseICAO), tostring(name)))
|
||||||
|
|
||||||
|
local Airbase=self:FindByName(name)
|
||||||
|
return Airbase
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
--- Find a AIRBASE in the _DATABASE by its ID.
|
--- Find a AIRBASE in the _DATABASE by its ID.
|
||||||
-- @param #AIRBASE self
|
-- @param #AIRBASE self
|
||||||
-- @param #number id Airbase ID.
|
-- @param #number id Airbase ID.
|
||||||
@@ -1894,6 +2023,129 @@ function AIRBASE:IsShip()
|
|||||||
return self.isShip
|
return self.isShip
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Taxi ways
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Add a taxiway from a given PATHLINE.
|
||||||
|
-- @param #AIRBASE self
|
||||||
|
-- @param Core.Pathline#PATHLINE TaxiPathline Pathline of the taxi way or name of pathline as #string.
|
||||||
|
-- @param #string Name Name of the taxi way, *e.g.* "Alpha", or "Alpha-Kilo". Default is name of pathline.
|
||||||
|
-- @return #AIRBASE self
|
||||||
|
function AIRBASE:AddTaxiway(TaxiPathline, Name)
|
||||||
|
|
||||||
|
-- If passed as string, get pathline.
|
||||||
|
if type(TaxiPathline)=="string" then
|
||||||
|
TaxiPathline=PATHLINE:FindByName(TaxiPathline)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set name.
|
||||||
|
Name=Name or TaxiPathline:GetName()
|
||||||
|
|
||||||
|
-- Create a deep copy.
|
||||||
|
local taxiway=UTILS.DeepCopy(TaxiPathline) --Core.Pathline#PATHLINE
|
||||||
|
|
||||||
|
-- Set name.
|
||||||
|
taxiway.name=Name
|
||||||
|
|
||||||
|
-- Add to taxiways.
|
||||||
|
self.taxiways[Name]=taxiway
|
||||||
|
|
||||||
|
|
||||||
|
--self:I(self.taxiways)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Find the shortest path using taxiways to get from given coodinates A to B on the airbase.
|
||||||
|
-- Note that the taxi ways have to be manually added with the `AIRBASE:AddTaxiway()` function.
|
||||||
|
-- @param #AIRBASE self
|
||||||
|
-- @param Core.Point#COORDINATE StartCoord Start coordinate.
|
||||||
|
-- @param Core.Point#COORDINATE EndCoord End coordinate.
|
||||||
|
-- @return Core.Pathline#PATHLINE Shortest path on taxiways from `StartCoord` to `EndCoord`.
|
||||||
|
-- @return #table Table of used taxi way pathlines.
|
||||||
|
function AIRBASE:FindTaxiwaysFromAtoB(StartCoord, EndCoord)
|
||||||
|
|
||||||
|
-- Create A* pathfinding.
|
||||||
|
local astar=ASTAR:New()
|
||||||
|
|
||||||
|
-- Add pathlines of taxiways of airport.
|
||||||
|
for _,_taxiway in pairs(self.taxiways) do
|
||||||
|
local taxiway=_taxiway --Core.Pathline#PATHLINE
|
||||||
|
|
||||||
|
astar:AddNodeFromPathlineName(taxiway)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set cost function.
|
||||||
|
astar:SetCostDist2D()
|
||||||
|
|
||||||
|
-- Set valid neighbours to be on the same pathline or at most 10 meters between nodes to jump from one pathline/taxiway to another.
|
||||||
|
astar:SetValidNeighbourPathline(10)
|
||||||
|
|
||||||
|
-- Set start and end coordinates.
|
||||||
|
astar:SetStartCoordinate(StartCoord)
|
||||||
|
astar:SetEndCoordinate(EndCoord)
|
||||||
|
|
||||||
|
-- Get pathline.
|
||||||
|
local taxipath, nodes=astar:GetPathline()
|
||||||
|
|
||||||
|
local taxiways=astar:GetPathlinesFromNodes(nodes)
|
||||||
|
|
||||||
|
return taxipath, taxiways
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get closest taxiway from a given reference coordinate.
|
||||||
|
-- @param #AIRBASE self
|
||||||
|
-- @param Core.Point#COORDINATE Coord Reference coordinate.
|
||||||
|
-- @return Core.Pathline#PATHLINE Taxiway.
|
||||||
|
-- @return #number Distance to taxiway in meters.
|
||||||
|
-- @return Core.Point#COORDINATE Coordinate on taxiway closest to reference coordinate.
|
||||||
|
-- @return Core.Pathline#PATHLINE.Segment Segment of the taxiway closest to the reference coordinate.
|
||||||
|
function AIRBASE:GetClosestTaxiway(Coord)
|
||||||
|
|
||||||
|
local taxipath=nil
|
||||||
|
local distmin=math.huge
|
||||||
|
local coordmin=nil
|
||||||
|
local segmin=nil
|
||||||
|
|
||||||
|
for name,_pathline in pairs(self.taxiways) do
|
||||||
|
local pathline=_pathline --Core.Pathline#PATHLINE
|
||||||
|
|
||||||
|
local coord, dist, segment=pathline:GetClosestPoint3D(Coord)
|
||||||
|
|
||||||
|
if dist<distmin then
|
||||||
|
taxipath=pathline
|
||||||
|
coordmin=coord
|
||||||
|
distmin=dist
|
||||||
|
segmin=segment
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return taxipath, distmin, coordmin, segmin
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Find the shortest path using taxiways to get from given parking spot to the starting point of a runway.
|
||||||
|
-- Note that the taxi ways have to be manually added with the `AIRBASE:AddTaxiway()` function.
|
||||||
|
-- @param #AIRBASE self
|
||||||
|
-- @param #AIRBASE.ParkingSpot ParkingSpot Parking spot.
|
||||||
|
-- @param #AIRBASE.Runway Runway The runway. If none is given, we take the active runway for takeoff.
|
||||||
|
-- @return Core.Pathline#PATHLINE Shortest path on taxiways from `StartCoord` to `EndCoord`.
|
||||||
|
-- @return #table Table of used taxi way pathlines.
|
||||||
|
function AIRBASE:FindTaxiwaysParkingToRunway(ParkingSpot, Runway)
|
||||||
|
|
||||||
|
Runway=Runway or self:GetActiveRunwayTakeoff()
|
||||||
|
|
||||||
|
local StartCoord=ParkingSpot.Coordinate
|
||||||
|
local EndCoord=Runway.position
|
||||||
|
|
||||||
|
local taxipath, taxiways=self:FindTaxiwaysFromAtoB(StartCoord,EndCoord)
|
||||||
|
|
||||||
|
return taxipath, taxiways
|
||||||
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- Parking
|
-- Parking
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -2601,6 +2853,7 @@ function AIRBASE:_InitRunways(IncludeInverse)
|
|||||||
|
|
||||||
--runway.name=string.format("%02d", tonumber(name))
|
--runway.name=string.format("%02d", tonumber(name))
|
||||||
runway.magheading=tonumber(runway.name)*10
|
runway.magheading=tonumber(runway.name)*10
|
||||||
|
runway.idx=runway.magheading
|
||||||
runway.heading=heading
|
runway.heading=heading
|
||||||
runway.width=width or 0
|
runway.width=width or 0
|
||||||
runway.length=length or 0
|
runway.length=length or 0
|
||||||
@@ -2913,6 +3166,7 @@ function AIRBASE:GetRunwayData(magvar, mark)
|
|||||||
local runway={} --#AIRBASE.Runway
|
local runway={} --#AIRBASE.Runway
|
||||||
runway.heading=hdg
|
runway.heading=hdg
|
||||||
runway.idx=idx
|
runway.idx=idx
|
||||||
|
runway.magheading=idx
|
||||||
runway.length=c1:Get2DDistance(c2)
|
runway.length=c1:Get2DDistance(c2)
|
||||||
runway.position=c1
|
runway.position=c1
|
||||||
runway.endpoint=c2
|
runway.endpoint=c2
|
||||||
@@ -2928,6 +3182,57 @@ function AIRBASE:GetRunwayData(magvar, mark)
|
|||||||
-- Add runway.
|
-- Add runway.
|
||||||
table.insert(runways, runway)
|
table.insert(runways, runway)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Look for identical (parallel) runways, e.g. 03L and 03R at Nellis.
|
||||||
|
local rpairs={}
|
||||||
|
for i,_ri in pairs(runways) do
|
||||||
|
local ri=_ri --#AIRBASE.Runway
|
||||||
|
for j,_rj in pairs(runways) do
|
||||||
|
local rj=_rj --#AIRBASE.Runway
|
||||||
|
if i<j then
|
||||||
|
if ri.name==rj.name then
|
||||||
|
rpairs[i]=j
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function isLeft(a, b, c)
|
||||||
|
--return ((b.x - a.x)*(c.z - a.z) - (b.z - a.z)*(c.x - a.x)) > 0
|
||||||
|
return ((b.z - a.z)*(c.x - a.x) - (b.x - a.x)*(c.z - a.z)) > 0
|
||||||
|
end
|
||||||
|
|
||||||
|
for i,j in pairs(rpairs) do
|
||||||
|
local ri=runways[i] --#AIRBASE.Runway
|
||||||
|
local rj=runways[j] --#AIRBASE.Runway
|
||||||
|
|
||||||
|
-- Draw arrow.
|
||||||
|
--ri.center:ArrowToAll(rj.center)
|
||||||
|
|
||||||
|
local c0=ri.position
|
||||||
|
|
||||||
|
-- Vector in the direction of the runway.
|
||||||
|
local a=UTILS.VecTranslate(c0, 1000, ri.heading)
|
||||||
|
|
||||||
|
-- Vector from runway i to runway j.
|
||||||
|
local b=UTILS.VecSubstract(rj.position, ri.position)
|
||||||
|
b=UTILS.VecAdd(ri.position, b)
|
||||||
|
|
||||||
|
-- Check if rj is left of ri.
|
||||||
|
local left=isLeft(c0, a, b)
|
||||||
|
|
||||||
|
--env.info(string.format("Found pair %s: i=%d, j=%d, left==%s", ri.name, i, j, tostring(left)))
|
||||||
|
|
||||||
|
if left then
|
||||||
|
ri.isLeft=false
|
||||||
|
rj.isLeft=true
|
||||||
|
else
|
||||||
|
ri.isLeft=true
|
||||||
|
rj.isLeft=false
|
||||||
|
end
|
||||||
|
|
||||||
|
--break
|
||||||
end
|
end
|
||||||
|
|
||||||
return runways
|
return runways
|
||||||
|
|||||||
@@ -168,16 +168,25 @@
|
|||||||
-- * @{#CONTROLLABLE.OptionAlarmStateGreen}
|
-- * @{#CONTROLLABLE.OptionAlarmStateGreen}
|
||||||
-- * @{#CONTROLLABLE.OptionAlarmStateRed}
|
-- * @{#CONTROLLABLE.OptionAlarmStateRed}
|
||||||
--
|
--
|
||||||
-- ## 5.4) Jettison weapons:
|
-- ## 5.4) [AIR] Jettison weapons:
|
||||||
--
|
--
|
||||||
-- * @{#CONTROLLABLE.OptionAllowJettisonWeaponsOnThreat}
|
-- * @{#CONTROLLABLE.OptionAllowJettisonWeaponsOnThreat}
|
||||||
-- * @{#CONTROLLABLE.OptionKeepWeaponsOnThreat}
|
-- * @{#CONTROLLABLE.OptionKeepWeaponsOnThreat}
|
||||||
--
|
--
|
||||||
-- ## 5.5) Air-2-Air missile attack range:
|
-- ## 5.5) [AIR] Air-2-Air missile attack range:
|
||||||
-- * @{#CONTROLLABLE.OptionAAAttackRange}(): Defines the usage of A2A missiles against possible targets.
|
-- * @{#CONTROLLABLE.OptionAAAttackRange}(): Defines the usage of A2A missiles against possible targets.
|
||||||
--
|
--
|
||||||
-- # 6) [GROUND] IR Maker Beacons for GROUPs and UNITs
|
-- # 6) [GROUND] IR Maker Beacons for GROUPs and UNITs
|
||||||
-- * @{#CONTROLLABLE:NewIRMarker}(): Create a blinking IR Marker on a GROUP or UNIT.
|
-- * @{#CONTROLLABLE:NewIRMarker}(): Create a blinking IR Marker on a GROUP or UNIT.
|
||||||
|
--
|
||||||
|
-- # 7) [HELICOPTER] Units prefer vertical landing and takeoffs:
|
||||||
|
-- * @{#CONTROLLABLE.OptionPreferVerticalLanding}(): Set aircraft to prefer vertical landing and takeoff.
|
||||||
|
--
|
||||||
|
-- # 8) [AIRCRAFT] Landing approach options
|
||||||
|
-- * @{#CONTROLLABLE.SetOptionLandingStraightIn}(): Landing approach straight in.
|
||||||
|
-- * @{#CONTROLLABLE.SetOptionLandingForcePair}(): Landing approach in pairs for groups > 1 unit.
|
||||||
|
-- * @{#CONTROLLABLE.SetOptionLandingRestrictPair}(): Landing approach single.
|
||||||
|
-- * @{#CONTROLLABLE.SetOptionLandingOverheadBreak}(): Landing approach overhead break.
|
||||||
--
|
--
|
||||||
-- @field #CONTROLLABLE
|
-- @field #CONTROLLABLE
|
||||||
CONTROLLABLE = {
|
CONTROLLABLE = {
|
||||||
@@ -1432,7 +1441,7 @@ end
|
|||||||
-- @param #number Speed The speed [m/s] flying when holding the position.
|
-- @param #number Speed The speed [m/s] flying when holding the position.
|
||||||
-- @return #CONTROLLABLE self
|
-- @return #CONTROLLABLE self
|
||||||
function CONTROLLABLE:TaskOrbitCircleAtVec2( Point, Altitude, Speed )
|
function CONTROLLABLE:TaskOrbitCircleAtVec2( Point, Altitude, Speed )
|
||||||
self:F2( { self.ControllableName, Point, Altitude, Speed } )
|
--self:F2( { self.ControllableName, Point, Altitude, Speed } )
|
||||||
|
|
||||||
local DCSTask = {
|
local DCSTask = {
|
||||||
id = 'Orbit',
|
id = 'Orbit',
|
||||||
@@ -3629,6 +3638,26 @@ function CONTROLLABLE:OptionROTPassiveDefense()
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Helicopter - prefer vertical landing.
|
||||||
|
-- @param #CONTROLLABLE self
|
||||||
|
-- @return #CONTROLLABLE self
|
||||||
|
function CONTROLLABLE:OptionPreferVerticalLanding()
|
||||||
|
self:F2( { self.ControllableName } )
|
||||||
|
|
||||||
|
local DCSControllable = self:GetDCSObject()
|
||||||
|
if DCSControllable then
|
||||||
|
local Controller = self:_GetController()
|
||||||
|
|
||||||
|
if self:IsAir() then
|
||||||
|
Controller:setOption( AI.Option.Air.id.PREFER_VERTICAL, true )
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
--- Can the CONTROLLABLE evade on enemy fire?
|
--- Can the CONTROLLABLE evade on enemy fire?
|
||||||
-- @param #CONTROLLABLE self
|
-- @param #CONTROLLABLE self
|
||||||
-- @return #boolean
|
-- @return #boolean
|
||||||
@@ -4134,7 +4163,7 @@ function CONTROLLABLE:OptionRestrictBurner( RestrictBurner )
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sets Controllable Option for A2A attack range for AIR FIGHTER units.
|
--- [AIR] Sets Controllable Option for A2A attack range for AIR FIGHTER units.
|
||||||
-- @param #CONTROLLABLE self
|
-- @param #CONTROLLABLE self
|
||||||
-- @param #number range Defines the range
|
-- @param #number range Defines the range
|
||||||
-- @return #CONTROLLABLE self
|
-- @return #CONTROLLABLE self
|
||||||
@@ -4159,6 +4188,66 @@ function CONTROLLABLE:OptionAAAttackRange( range )
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- [GROUND/AAA] Sets Controllable Option for Ground AAA minimum firing height.
|
||||||
|
-- @param #CONTROLLABLE self
|
||||||
|
-- @param #number meters The minimum height in meters.
|
||||||
|
-- @return #CONTROLLABLE self
|
||||||
|
function CONTROLLABLE:OptionAAAMinFiringHeightMeters(meters)
|
||||||
|
self:F2( { self.ControllableName } )
|
||||||
|
local meters = meters or 20
|
||||||
|
local DCSControllable = self:GetDCSObject()
|
||||||
|
if DCSControllable then
|
||||||
|
local Controller = self:_GetController()
|
||||||
|
if Controller then
|
||||||
|
if self:IsGround() then
|
||||||
|
self:SetOption(27, meters)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- [GROUND/AAA] Sets Controllable Option for Ground AAA maximum firing height.
|
||||||
|
-- @param #CONTROLLABLE self
|
||||||
|
-- @param #number meters The maximum height in meters.
|
||||||
|
-- @return #CONTROLLABLE self
|
||||||
|
function CONTROLLABLE:OptionAAAMaxFiringHeightMeters(meters)
|
||||||
|
self:F2( { self.ControllableName } )
|
||||||
|
local meters = meters or 1000
|
||||||
|
local DCSControllable = self:GetDCSObject()
|
||||||
|
if DCSControllable then
|
||||||
|
local Controller = self:_GetController()
|
||||||
|
if Controller then
|
||||||
|
if self:IsGround() then
|
||||||
|
self:SetOption(29, meters)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- [GROUND/AAA] Sets Controllable Option for Ground AAA minimum firing height.
|
||||||
|
-- @param #CONTROLLABLE self
|
||||||
|
-- @param #number feet The minimum height in feet.
|
||||||
|
-- @return #CONTROLLABLE self
|
||||||
|
function CONTROLLABLE:OptionAAAMinFiringHeightFeet(feet)
|
||||||
|
self:F2( { self.ControllableName } )
|
||||||
|
local feet = feet or 60
|
||||||
|
return self:OptionAAAMinFiringHeightMeters(UTILS.FeetToMeters(feet))
|
||||||
|
end
|
||||||
|
|
||||||
|
--- [GROUND/AAA] Sets Controllable Option for Ground AAA maximum firing height.
|
||||||
|
-- @param #CONTROLLABLE self
|
||||||
|
-- @param #number feet The maximum height in feet.
|
||||||
|
-- @return #CONTROLLABLE self
|
||||||
|
function CONTROLLABLE:OptionAAAMaxFiringHeightfeet(feet)
|
||||||
|
self:F2( { self.ControllableName } )
|
||||||
|
local feet = feet or 3000
|
||||||
|
return self:OptionAAAMaxFiringHeightMeters(UTILS.FeetToMeters(feet))
|
||||||
|
end
|
||||||
|
|
||||||
--- Defines the range at which a GROUND unit/group is allowed to use its weapons automatically.
|
--- Defines the range at which a GROUND unit/group is allowed to use its weapons automatically.
|
||||||
-- @param #CONTROLLABLE self
|
-- @param #CONTROLLABLE self
|
||||||
-- @param #number EngageRange Engage range limit in percent (a number between 0 and 100). Default 100.
|
-- @param #number EngageRange Engage range limit in percent (a number between 0 and 100). Default 100.
|
||||||
@@ -4183,6 +4272,50 @@ function CONTROLLABLE:OptionEngageRange( EngageRange )
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- [AIR] Set how the AI lands on an airfield. Here: Straight in.
|
||||||
|
-- @param #CONTROLLABLE self
|
||||||
|
-- @return #CONTROLLABLE self
|
||||||
|
function CONTROLLABLE:SetOptionLandingStraightIn()
|
||||||
|
self:F2( { self.ControllableName } )
|
||||||
|
if self:IsAir() then
|
||||||
|
self:SetOption("36","0")
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- [AIR] Set how the AI lands on an airfield. Here: In pairs (if > 1 aircraft in group)
|
||||||
|
-- @param #CONTROLLABLE self
|
||||||
|
-- @return #CONTROLLABLE self
|
||||||
|
function CONTROLLABLE:SetOptionLandingForcePair()
|
||||||
|
self:F2( { self.ControllableName } )
|
||||||
|
if self:IsAir() then
|
||||||
|
self:SetOption("36","1")
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- [AIR] Set how the AI lands on an airfield. Here: No landing in pairs.
|
||||||
|
-- @param #CONTROLLABLE self
|
||||||
|
-- @return #CONTROLLABLE self
|
||||||
|
function CONTROLLABLE:SetOptionLandingRestrictPair()
|
||||||
|
self:F2( { self.ControllableName } )
|
||||||
|
if self:IsAir() then
|
||||||
|
self:SetOption("36","2")
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- [AIR] Set how the AI lands on an airfield. Here: Overhead break.
|
||||||
|
-- @param #CONTROLLABLE self
|
||||||
|
-- @return #CONTROLLABLE self
|
||||||
|
function CONTROLLABLE:SetOptionLandingOverheadBreak()
|
||||||
|
self:F2( { self.ControllableName } )
|
||||||
|
if self:IsAir() then
|
||||||
|
self:SetOption("36","3")
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- [AIR] Set how the AI uses the onboard radar.
|
--- [AIR] Set how the AI uses the onboard radar.
|
||||||
-- @param #CONTROLLABLE self
|
-- @param #CONTROLLABLE self
|
||||||
-- @param #number Option Options are: `NEVER = 0, FOR_ATTACK_ONLY = 1,FOR_SEARCH_IF_REQUIRED = 2, FOR_CONTINUOUS_SEARCH = 3`
|
-- @param #number Option Options are: `NEVER = 0, FOR_ATTACK_ONLY = 1,FOR_SEARCH_IF_REQUIRED = 2, FOR_CONTINUOUS_SEARCH = 3`
|
||||||
|
|||||||
@@ -108,6 +108,8 @@ DYNAMICCARGO.State = {
|
|||||||
-- @type DYNAMICCARGO.AircraftTypes
|
-- @type DYNAMICCARGO.AircraftTypes
|
||||||
DYNAMICCARGO.AircraftTypes = {
|
DYNAMICCARGO.AircraftTypes = {
|
||||||
["CH-47Fbl1"] = "CH-47Fbl1",
|
["CH-47Fbl1"] = "CH-47Fbl1",
|
||||||
|
["Mi-8MTV2"] = "CH-47Fbl1",
|
||||||
|
["Mi-8MT"] = "CH-47Fbl1",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Helo types possible.
|
--- Helo types possible.
|
||||||
@@ -120,17 +122,30 @@ DYNAMICCARGO.AircraftDimensions = {
|
|||||||
["length"] = 11,
|
["length"] = 11,
|
||||||
["ropelength"] = 30,
|
["ropelength"] = 30,
|
||||||
},
|
},
|
||||||
|
["Mi-8MTV2"] = {
|
||||||
|
["width"] = 6,
|
||||||
|
["height"] = 6,
|
||||||
|
["length"] = 15,
|
||||||
|
["ropelength"] = 30,
|
||||||
|
},
|
||||||
|
["Mi-8MT"] = {
|
||||||
|
["width"] = 6,
|
||||||
|
["height"] = 6,
|
||||||
|
["length"] = 15,
|
||||||
|
["ropelength"] = 30,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
--- DYNAMICCARGO class version.
|
--- DYNAMICCARGO class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
DYNAMICCARGO.version="0.0.7"
|
DYNAMICCARGO.version="0.0.9"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- TODO list
|
-- TODO list
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- TODO: A lot...
|
-- TODO: A lot...
|
||||||
|
-- DONE: Added Mi-8 type and dimensions
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- Constructor
|
-- Constructor
|
||||||
|
|||||||
@@ -230,6 +230,8 @@ GROUP.Attribute = {
|
|||||||
GROUND_EWR="Ground_EWR",
|
GROUND_EWR="Ground_EWR",
|
||||||
GROUND_AAA="Ground_AAA",
|
GROUND_AAA="Ground_AAA",
|
||||||
GROUND_SAM="Ground_SAM",
|
GROUND_SAM="Ground_SAM",
|
||||||
|
GROUND_SHORAD="Ground_SHORAD",
|
||||||
|
GROUND_BALLISTICMISSILE="Ground_BallisticMissile",
|
||||||
GROUND_OTHER="Ground_OtherGround",
|
GROUND_OTHER="Ground_OtherGround",
|
||||||
NAVAL_AIRCRAFTCARRIER="Naval_AircraftCarrier",
|
NAVAL_AIRCRAFTCARRIER="Naval_AircraftCarrier",
|
||||||
NAVAL_WARSHIP="Naval_WarShip",
|
NAVAL_WARSHIP="Naval_WarShip",
|
||||||
@@ -2230,6 +2232,10 @@ function GROUP:Respawn( Template, Reset )
|
|||||||
|
|
||||||
--UTILS.PrintTableToLog(Template)
|
--UTILS.PrintTableToLog(Template)
|
||||||
|
|
||||||
|
if self.ValidateAndRepositionGroundUnits then
|
||||||
|
UTILS.ValidateAndRepositionGroundUnits(Template.units)
|
||||||
|
end
|
||||||
|
|
||||||
-- Spawn new group.
|
-- Spawn new group.
|
||||||
self:ScheduleOnce(0.1,_DATABASE.Spawn,_DATABASE,Template)
|
self:ScheduleOnce(0.1,_DATABASE.Spawn,_DATABASE,Template)
|
||||||
--_DATABASE:Spawn(Template)
|
--_DATABASE:Spawn(Template)
|
||||||
@@ -2638,6 +2644,8 @@ function GROUP:GetAttribute()
|
|||||||
local artillery=self:HasAttribute("Artillery")
|
local artillery=self:HasAttribute("Artillery")
|
||||||
local tank=self:HasAttribute("Old Tanks") or self:HasAttribute("Modern Tanks") or self:HasAttribute("Tanks")
|
local tank=self:HasAttribute("Old Tanks") or self:HasAttribute("Modern Tanks") or self:HasAttribute("Tanks")
|
||||||
local aaa=self:HasAttribute("AAA") and (not self:HasAttribute("SAM elements"))
|
local aaa=self:HasAttribute("AAA") and (not self:HasAttribute("SAM elements"))
|
||||||
|
local ballisticMissile=artillery and self:HasAttribute("SS_missile")
|
||||||
|
local shorad=self:HasAttribute("SR SAM")
|
||||||
local ewr=self:HasAttribute("EWR")
|
local ewr=self:HasAttribute("EWR")
|
||||||
local ifv=self:HasAttribute("IFV")
|
local ifv=self:HasAttribute("IFV")
|
||||||
local sam=self:HasAttribute("SAM elements") or self:HasAttribute("Optical Tracker")
|
local sam=self:HasAttribute("SAM elements") or self:HasAttribute("Optical Tracker")
|
||||||
@@ -2679,6 +2687,8 @@ function GROUP:GetAttribute()
|
|||||||
attribute=GROUP.Attribute.GROUND_SAM
|
attribute=GROUP.Attribute.GROUND_SAM
|
||||||
elseif aaa then
|
elseif aaa then
|
||||||
attribute=GROUP.Attribute.GROUND_AAA
|
attribute=GROUP.Attribute.GROUND_AAA
|
||||||
|
elseif artillery and ballisticMissile then
|
||||||
|
attribute=GROUP.Attribute.GROUND_BALLISTICMISSILE
|
||||||
elseif artillery then
|
elseif artillery then
|
||||||
attribute=GROUP.Attribute.GROUND_ARTILLERY
|
attribute=GROUP.Attribute.GROUND_ARTILLERY
|
||||||
elseif tank then
|
elseif tank then
|
||||||
@@ -3191,3 +3201,60 @@ function GROUP:IsAAA()
|
|||||||
end
|
end
|
||||||
return isAAA
|
return isAAA
|
||||||
end
|
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 #GROUP self
|
||||||
|
-- @param #boolean Enabled Enable/disable the feature.
|
||||||
|
function GROUP:SetValidateAndRepositionGroundUnits(Enabled)
|
||||||
|
self.ValidateAndRepositionGroundUnits = Enabled
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get the bounding box of the group combining UNIT:GetBoundingBox() units.
|
||||||
|
-- @param #GROUP self
|
||||||
|
-- @return DCS#Box3 The bounding box of the GROUP.
|
||||||
|
-- @return #nil The GROUP does not have any alive units.
|
||||||
|
function GROUP:GetBoundingBox()
|
||||||
|
local bbox = { min = { x = math.huge, y = math.huge, z = math.huge },
|
||||||
|
max = { x = -math.huge, y = -math.huge, z = -math.huge }
|
||||||
|
}
|
||||||
|
|
||||||
|
local Units = self:GetUnits() or {}
|
||||||
|
if #Units == 0 then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, unit in pairs(Units) do
|
||||||
|
if unit and unit:IsAlive() then
|
||||||
|
local ubox = unit:GetBoundingBox()
|
||||||
|
|
||||||
|
if ubox then
|
||||||
|
if ubox.min.x < bbox.min.x then
|
||||||
|
bbox.min.x = ubox.min.x
|
||||||
|
end
|
||||||
|
if ubox.min.y < bbox.min.y then
|
||||||
|
bbox.min.y = ubox.min.y
|
||||||
|
end
|
||||||
|
if ubox.min.z < bbox.min.z then
|
||||||
|
bbox.min.z = ubox.min.z
|
||||||
|
end
|
||||||
|
|
||||||
|
if ubox.max.x > bbox.max.x then
|
||||||
|
bbox.max.x = ubox.max.x
|
||||||
|
end
|
||||||
|
if ubox.max.y > bbox.max.y then
|
||||||
|
bbox.max.y = ubox.max.y
|
||||||
|
end
|
||||||
|
if ubox.max.z > bbox.max.z then
|
||||||
|
bbox.max.z = ubox.max.z
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return bbox
|
||||||
|
end
|
||||||
|
|||||||
@@ -246,18 +246,20 @@ end
|
|||||||
function POSITIONABLE:GetVec3()
|
function POSITIONABLE:GetVec3()
|
||||||
local DCSPositionable = self:GetDCSObject()
|
local DCSPositionable = self:GetDCSObject()
|
||||||
if DCSPositionable then
|
if DCSPositionable then
|
||||||
--local status, vec3 = pcall(
|
|
||||||
-- function()
|
|
||||||
-- local vec3 = DCSPositionable:getPoint()
|
|
||||||
-- return vec3
|
|
||||||
--end
|
|
||||||
--)
|
|
||||||
local vec3 = DCSPositionable:getPoint()
|
local vec3 = DCSPositionable:getPoint()
|
||||||
--if status then
|
|
||||||
return vec3
|
if not vec3 then
|
||||||
--else
|
local pos = DCSPositionable:getPosition()
|
||||||
--self:E( { "Cannot get Vec3 from DCS Object", Positionable = self, Alive = self:IsAlive() } )
|
if pos and pos.p then
|
||||||
--end
|
vec3 = pos.p
|
||||||
|
else
|
||||||
|
self:E( { "Cannot get the position from DCS Object for GetVec3", Positionable = self, Alive = self:IsAlive() } )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return vec3
|
||||||
|
|
||||||
end
|
end
|
||||||
-- ERROR!
|
-- ERROR!
|
||||||
self:E( { "Cannot get the Positionable DCS Object for GetVec3", Positionable = self, Alive = self:IsAlive() } )
|
self:E( { "Cannot get the Positionable DCS Object for GetVec3", Positionable = self, Alive = self:IsAlive() } )
|
||||||
@@ -359,15 +361,17 @@ function POSITIONABLE:GetCoord()
|
|||||||
-- Get the current position.
|
-- Get the current position.
|
||||||
local PositionableVec3 = self:GetVec3()
|
local PositionableVec3 = self:GetVec3()
|
||||||
|
|
||||||
if self.coordinate then
|
if PositionableVec3 then
|
||||||
-- Update COORDINATE from 3D vector.
|
if self.coordinate then
|
||||||
self.coordinate:UpdateFromVec3( PositionableVec3 )
|
-- Update COORDINATE from 3D vector.
|
||||||
else
|
self.coordinate:UpdateFromVec3( PositionableVec3 )
|
||||||
-- New COORDINATE.
|
else
|
||||||
self.coordinate = COORDINATE:NewFromVec3( PositionableVec3 )
|
-- New COORDINATE.
|
||||||
end
|
self.coordinate = COORDINATE:NewFromVec3( PositionableVec3 )
|
||||||
|
end
|
||||||
|
|
||||||
return self.coordinate
|
return self.coordinate
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Error message.
|
-- Error message.
|
||||||
@@ -388,13 +392,13 @@ function POSITIONABLE:GetCoordinate()
|
|||||||
|
|
||||||
-- Get the current position.
|
-- Get the current position.
|
||||||
local PositionableVec3 = self:GetVec3()
|
local PositionableVec3 = self:GetVec3()
|
||||||
|
if PositionableVec3 then
|
||||||
local coord=COORDINATE:NewFromVec3(PositionableVec3)
|
local coord=COORDINATE:NewFromVec3(PositionableVec3)
|
||||||
local heading = self:GetHeading()
|
local heading = self:GetHeading()
|
||||||
coord.Heading = heading
|
coord.Heading = heading
|
||||||
-- Return a new coordiante object.
|
-- Return a new coordiante object.
|
||||||
return coord
|
return coord
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Error message.
|
-- Error message.
|
||||||
|
|||||||
@@ -753,7 +753,7 @@ function STORAGE:LoadFromFile(Path,Filename)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
self:E("File for Liquids could not be found: "..tostring(Path).."\\"..tostring(Filename"_Liquids.csv"))
|
self:E("File for Liquids could not be found: "..tostring(Path).."\\"..tostring(Filename).."_Liquids.csv")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -773,7 +773,7 @@ function STORAGE:LoadFromFile(Path,Filename)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
self:E("File for Aircraft could not be found: "..tostring(Path).."\\"..tostring(Filename"_Aircraft.csv"))
|
self:E("File for Aircraft could not be found: "..tostring(Path).."\\"..tostring(Filename).."_Aircraft.csv")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -805,7 +805,7 @@ function STORAGE:LoadFromFile(Path,Filename)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
self:E("File for Weapons could not be found: "..tostring(Path).."\\"..tostring(Filename"_Weapons.csv"))
|
self:E("File for Weapons could not be found: "..tostring(Path).."\\"..tostring(Filename).."_Weapons.csv")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -377,6 +377,10 @@ function UNIT:ReSpawnAt(Coordinate, Heading)
|
|||||||
|
|
||||||
--self:T( SpawnGroupTemplate )
|
--self:T( SpawnGroupTemplate )
|
||||||
|
|
||||||
|
if self.ValidateAndRepositionGroundUnits then
|
||||||
|
UTILS.ValidateAndRepositionGroundUnits(SpawnGroupTemplate.units)
|
||||||
|
end
|
||||||
|
|
||||||
_DATABASE:Spawn(SpawnGroupTemplate)
|
_DATABASE:Spawn(SpawnGroupTemplate)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1924,3 +1928,28 @@ function UNIT:IsAAA()
|
|||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set the relative life points of a UNIT object
|
||||||
|
-- @param #UNIT self
|
||||||
|
-- @param #number Percent Percent to set, can be 0..100.
|
||||||
|
function UNIT:SetLife(Percent)
|
||||||
|
net.dostring_in("mission",string.format("a_unit_set_life_percentage(%d, %f)", self:GetID(), Percent))
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the carrier illumination mode. -2: OFF, -1: AUTO, 0: NAVIGATION, 1: AC LAUNCH, 2: AC RECOVERY
|
||||||
|
-- @param #UNIT self
|
||||||
|
-- @param #number Mode Illumination mode, can be -2: OFF, -1: AUTO, 0: NAVIGATION, 1: AC LAUNCH, 2: AC RECOVERY
|
||||||
|
function UNIT:SetCarrierIlluminationMode(Mode)
|
||||||
|
UTILS.SetCarrierIlluminationMode(self:GetID(), Mode)
|
||||||
|
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 #UNIT self
|
||||||
|
-- @param #boolean Enabled Enable/disable the feature.
|
||||||
|
function UNIT:SetValidateAndRepositionGroundUnits(Enabled)
|
||||||
|
self.ValidateAndRepositionGroundUnits = Enabled
|
||||||
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user