mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-10-29 16:58:06 +00:00
Compare commits
98 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e6f25f17c | ||
|
|
0cd6a59ce4 | ||
|
|
91a445961b | ||
|
|
749e9b7e08 | ||
|
|
905d29b279 | ||
|
|
5f97299cb2 | ||
|
|
60b75a4c8f | ||
|
|
0868286f27 | ||
|
|
ffcc46cb2d | ||
|
|
21bcd64c9d | ||
|
|
943b68f38f | ||
|
|
256b93087f | ||
|
|
7ed0d7eec3 | ||
|
|
9b8154246f | ||
|
|
c360759e49 | ||
|
|
d8b80aab1a | ||
|
|
b96ebc1872 | ||
|
|
6896dc155a | ||
|
|
1060d63808 | ||
|
|
88b6540f5b | ||
|
|
302e785f32 | ||
|
|
1507cc0b42 | ||
|
|
240307ef94 | ||
|
|
90c74bd82c | ||
|
|
9414096de7 | ||
|
|
07c3be9d6a | ||
|
|
9869dbd95a | ||
|
|
49b702106a | ||
|
|
8eb09beb96 | ||
|
|
b402a99a25 | ||
|
|
ad8938cd74 | ||
|
|
ee409c45a0 | ||
|
|
9d5da3388d | ||
|
|
9e138aa149 | ||
|
|
cf94c4d043 | ||
|
|
63b3807cf6 | ||
|
|
f3b6b27521 | ||
|
|
ff656182e8 | ||
|
|
a85ef6e769 | ||
|
|
0bb8c56ea9 | ||
|
|
5f108aec23 | ||
|
|
bf6683f993 | ||
|
|
b7a5e8dd85 | ||
|
|
57294aeaf5 | ||
|
|
20bfb8b08f | ||
|
|
5585a8992c | ||
|
|
d92a20050c | ||
|
|
c9a91d0683 | ||
|
|
ae6716ac01 | ||
|
|
34285b26ae | ||
|
|
5341e5faee | ||
|
|
a6224b484e | ||
|
|
e95c1ad3aa | ||
|
|
9cfed56847 | ||
|
|
6c1abddb1e | ||
|
|
7dc239f506 | ||
|
|
3e8413c6b7 | ||
|
|
ff86bfb91d | ||
|
|
66494b7b5a | ||
|
|
973127aa8c | ||
|
|
83866c3dd3 | ||
|
|
4797abc287 | ||
|
|
50f73f1be2 | ||
|
|
2ebbc8f466 | ||
|
|
1a96b30a45 | ||
|
|
3b1b57a33e | ||
|
|
f85c0320ec | ||
|
|
bae7edb914 | ||
|
|
35322399e9 | ||
|
|
5d4c0f3c87 | ||
|
|
92c5c32e8c | ||
|
|
c8780b2d5f | ||
|
|
f11dbfa367 | ||
|
|
54fb9deb3e | ||
|
|
d3c34ef04d | ||
|
|
3d5470eb22 | ||
|
|
2a3d69d0d5 | ||
|
|
9862c32378 | ||
|
|
9f02232589 | ||
|
|
ffc86bf046 | ||
|
|
e4c8ca34ed | ||
|
|
f02b7036a9 | ||
|
|
6adef47340 | ||
|
|
5b044cc036 | ||
|
|
03ba7524b2 | ||
|
|
772921c6b8 | ||
|
|
d4d305d53b | ||
|
|
c58918e002 | ||
|
|
dec7854476 | ||
|
|
8ca102f584 | ||
|
|
4748c66d9c | ||
|
|
bf486b9f44 | ||
|
|
6ff433ed7a | ||
|
|
f9aa392c6d | ||
|
|
819912cfd3 | ||
|
|
50ee1ae922 | ||
|
|
c9ccc28251 | ||
|
|
2157f8e8cd |
@@ -1687,6 +1687,20 @@ do -- AI_A2A_DISPATCHER
|
|||||||
|
|
||||||
return DefenderSquadron
|
return DefenderSquadron
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get a resource count from a specific squadron
|
||||||
|
-- @param #AI_A2A_DISPATCHER self
|
||||||
|
-- @param #string Squadron Name of the squadron.
|
||||||
|
-- @return #number Number of airframes available or nil if the squadron does not exist
|
||||||
|
function AI_A2A_DISPATCHER:QuerySquadron(Squadron)
|
||||||
|
local Squadron = self:GetSquadron(Squadron)
|
||||||
|
if Squadron.ResourceCount then
|
||||||
|
self:T2(string.format("%s = %s",Squadron.Name,Squadron.ResourceCount))
|
||||||
|
return Squadron.ResourceCount
|
||||||
|
end
|
||||||
|
self:F({Squadron = Squadron.Name,SquadronResourceCount = Squadron.ResourceCount})
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
--- [DEPRECATED - Might create problems launching planes] Set the Squadron visible before startup of the dispatcher.
|
--- [DEPRECATED - Might create problems launching planes] Set the Squadron visible before startup of the dispatcher.
|
||||||
-- All planes will be spawned as uncontrolled on the parking spot.
|
-- All planes will be spawned as uncontrolled on the parking spot.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
--- **AI** - Create an automated A2G defense system based on a detection network of reconnaissance vehicles and air units, coordinating SEAD, BAI and CAS operations.
|
--- **AI** - Create an automated A2G defense system with reconnaissance units, coordinating SEAD, BAI and CAS operations.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -951,7 +951,7 @@ do -- AI_A2G_DISPATCHER
|
|||||||
AI_A2G_DISPATCHER.DefenseQueue = {}
|
AI_A2G_DISPATCHER.DefenseQueue = {}
|
||||||
|
|
||||||
--- Defense approach types.
|
--- Defense approach types.
|
||||||
-- @type #AI_A2G_DISPATCHER.DefenseApproach
|
-- @type AI_A2G_DISPATCHER.DefenseApproach
|
||||||
AI_A2G_DISPATCHER.DefenseApproach = {
|
AI_A2G_DISPATCHER.DefenseApproach = {
|
||||||
Random = 1,
|
Random = 1,
|
||||||
Distance = 2,
|
Distance = 2,
|
||||||
@@ -1806,6 +1806,19 @@ do -- AI_A2G_DISPATCHER
|
|||||||
return DefenderSquadron
|
return DefenderSquadron
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get a resource count from a specific squadron
|
||||||
|
-- @param #AI_A2G_DISPATCHER self
|
||||||
|
-- @param #string Squadron Name of the squadron.
|
||||||
|
-- @return #number Number of airframes available or nil if the squadron does not exist
|
||||||
|
function AI_A2G_DISPATCHER:QuerySquadron(Squadron)
|
||||||
|
local Squadron = self:GetSquadron(Squadron)
|
||||||
|
if Squadron.ResourceCount then
|
||||||
|
self:T2(string.format("%s = %s",Squadron.Name,Squadron.ResourceCount))
|
||||||
|
return Squadron.ResourceCount
|
||||||
|
end
|
||||||
|
self:F({Squadron = Squadron.Name,SquadronResourceCount = Squadron.ResourceCount})
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
--- Set the Squadron visible before startup of the dispatcher.
|
--- Set the Squadron visible before startup of the dispatcher.
|
||||||
-- All planes will be spawned as uncontrolled on the parking spot.
|
-- All planes will be spawned as uncontrolled on the parking spot.
|
||||||
@@ -1839,7 +1852,7 @@ do -- AI_A2G_DISPATCHER
|
|||||||
--- Check if the Squadron is visible before startup of the dispatcher.
|
--- Check if the Squadron is visible before startup of the dispatcher.
|
||||||
-- @param #AI_A2G_DISPATCHER self
|
-- @param #AI_A2G_DISPATCHER self
|
||||||
-- @param #string SquadronName The squadron name.
|
-- @param #string SquadronName The squadron name.
|
||||||
-- @return #bool true if visible.
|
-- @return #boolean true if visible.
|
||||||
-- @usage
|
-- @usage
|
||||||
--
|
--
|
||||||
-- -- Set the Squadron visible before startup of dispatcher.
|
-- -- Set the Squadron visible before startup of dispatcher.
|
||||||
|
|||||||
@@ -947,7 +947,7 @@ do -- AI_AIR_DISPATCHER
|
|||||||
AI_AIR_DISPATCHER.DefenseQueue = {}
|
AI_AIR_DISPATCHER.DefenseQueue = {}
|
||||||
|
|
||||||
--- Defense approach types
|
--- Defense approach types
|
||||||
-- @type #AI_AIR_DISPATCHER.DefenseApproach
|
-- @type AI_AIR_DISPATCHER.DefenseApproach
|
||||||
AI_AIR_DISPATCHER.DefenseApproach = {
|
AI_AIR_DISPATCHER.DefenseApproach = {
|
||||||
Random = 1,
|
Random = 1,
|
||||||
Distance = 2,
|
Distance = 2,
|
||||||
@@ -1852,7 +1852,7 @@ do -- AI_AIR_DISPATCHER
|
|||||||
--- Check if the Squadron is visible before startup of the dispatcher.
|
--- Check if the Squadron is visible before startup of the dispatcher.
|
||||||
-- @param #AI_AIR_DISPATCHER self
|
-- @param #AI_AIR_DISPATCHER self
|
||||||
-- @param #string SquadronName The squadron name.
|
-- @param #string SquadronName The squadron name.
|
||||||
-- @return #bool true if visible.
|
-- @return #boolean true if visible.
|
||||||
-- @usage
|
-- @usage
|
||||||
--
|
--
|
||||||
-- -- Set the Squadron visible before startup of dispatcher.
|
-- -- Set the Squadron visible before startup of dispatcher.
|
||||||
|
|||||||
@@ -443,9 +443,9 @@ end
|
|||||||
-- @param #AI_ESCORT self
|
-- @param #AI_ESCORT self
|
||||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||||
-- @param #number ZLevels The amount of levels on the Z-axis.
|
-- @param #number ZLevels The amount of levels on the Z-axis.
|
||||||
-- @return #AI_ESCORT
|
-- @return #AI_ESCORT
|
||||||
@@ -493,9 +493,9 @@ end
|
|||||||
-- @param #AI_ESCORT self
|
-- @param #AI_ESCORT self
|
||||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||||
-- @param #number ZLevels The amount of levels on the Z-axis.
|
-- @param #number ZLevels The amount of levels on the Z-axis.
|
||||||
-- @return #AI_ESCORT
|
-- @return #AI_ESCORT
|
||||||
@@ -580,7 +580,7 @@ end
|
|||||||
-- @param #AI_ESCORT self
|
-- @param #AI_ESCORT self
|
||||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||||
-- @return #AI_ESCORT
|
-- @return #AI_ESCORT
|
||||||
function AI_ESCORT:MenuFormationTrail( XStart, XSpace, YStart )
|
function AI_ESCORT:MenuFormationTrail( XStart, XSpace, YStart )
|
||||||
|
|
||||||
@@ -594,7 +594,7 @@ end
|
|||||||
-- @param #AI_ESCORT self
|
-- @param #AI_ESCORT self
|
||||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||||
-- @return #AI_ESCORT
|
-- @return #AI_ESCORT
|
||||||
function AI_ESCORT:MenuFormationStack( XStart, XSpace, YStart, YSpace )
|
function AI_ESCORT:MenuFormationStack( XStart, XSpace, YStart, YSpace )
|
||||||
@@ -609,8 +609,8 @@ end
|
|||||||
-- This menu will appear under **Formation**.
|
-- This menu will appear under **Formation**.
|
||||||
-- @param #AI_ESCORT self
|
-- @param #AI_ESCORT self
|
||||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||||
-- @return #AI_ESCORT
|
-- @return #AI_ESCORT
|
||||||
function AI_ESCORT:MenuFormationLeftLine( XStart, YStart, ZStart, ZSpace )
|
function AI_ESCORT:MenuFormationLeftLine( XStart, YStart, ZStart, ZSpace )
|
||||||
@@ -625,8 +625,8 @@ end
|
|||||||
-- This menu will appear under **Formation**.
|
-- This menu will appear under **Formation**.
|
||||||
-- @param #AI_ESCORT self
|
-- @param #AI_ESCORT self
|
||||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||||
-- @return #AI_ESCORT
|
-- @return #AI_ESCORT
|
||||||
function AI_ESCORT:MenuFormationRightLine( XStart, YStart, ZStart, ZSpace )
|
function AI_ESCORT:MenuFormationRightLine( XStart, YStart, ZStart, ZSpace )
|
||||||
@@ -642,8 +642,8 @@ end
|
|||||||
-- @param #AI_ESCORT self
|
-- @param #AI_ESCORT self
|
||||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||||
-- @return #AI_ESCORT
|
-- @return #AI_ESCORT
|
||||||
function AI_ESCORT:MenuFormationLeftWing( XStart, XSpace, YStart, ZStart, ZSpace )
|
function AI_ESCORT:MenuFormationLeftWing( XStart, XSpace, YStart, ZStart, ZSpace )
|
||||||
@@ -659,8 +659,8 @@ end
|
|||||||
-- @param #AI_ESCORT self
|
-- @param #AI_ESCORT self
|
||||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||||
-- @return #AI_ESCORT
|
-- @return #AI_ESCORT
|
||||||
function AI_ESCORT:MenuFormationRightWing( XStart, XSpace, YStart, ZStart, ZSpace )
|
function AI_ESCORT:MenuFormationRightWing( XStart, XSpace, YStart, ZStart, ZSpace )
|
||||||
@@ -676,9 +676,9 @@ end
|
|||||||
-- @param #AI_ESCORT self
|
-- @param #AI_ESCORT self
|
||||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||||
-- @return #AI_ESCORT
|
-- @return #AI_ESCORT
|
||||||
function AI_ESCORT:MenuFormationCenterWing( XStart, XSpace, YStart, YSpace, ZStart, ZSpace )
|
function AI_ESCORT:MenuFormationCenterWing( XStart, XSpace, YStart, YSpace, ZStart, ZSpace )
|
||||||
@@ -694,9 +694,9 @@ end
|
|||||||
-- @param #AI_ESCORT self
|
-- @param #AI_ESCORT self
|
||||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||||
-- @return #AI_ESCORT
|
-- @return #AI_ESCORT
|
||||||
function AI_ESCORT:MenuFormationVic( XStart, XSpace, YStart, YSpace, ZStart, ZSpace )
|
function AI_ESCORT:MenuFormationVic( XStart, XSpace, YStart, YSpace, ZStart, ZSpace )
|
||||||
@@ -712,9 +712,9 @@ end
|
|||||||
-- @param #AI_ESCORT self
|
-- @param #AI_ESCORT self
|
||||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||||
-- @param #number ZLevels The amount of levels on the Z-axis.
|
-- @param #number ZLevels The amount of levels on the Z-axis.
|
||||||
-- @return #AI_ESCORT
|
-- @return #AI_ESCORT
|
||||||
@@ -1471,7 +1471,7 @@ end
|
|||||||
-- @param #AI_ESCORT self
|
-- @param #AI_ESCORT self
|
||||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||||
-- @return #AI_ESCORT
|
-- @return #AI_ESCORT
|
||||||
function AI_ESCORT:_EscortFormationTrail( EscortGroup, XStart, XSpace, YStart )
|
function AI_ESCORT:_EscortFormationTrail( EscortGroup, XStart, XSpace, YStart )
|
||||||
|
|
||||||
|
|||||||
@@ -88,6 +88,8 @@ DATABASE = {
|
|||||||
WAREHOUSES = {},
|
WAREHOUSES = {},
|
||||||
FLIGHTGROUPS = {},
|
FLIGHTGROUPS = {},
|
||||||
FLIGHTCONTROLS = {},
|
FLIGHTCONTROLS = {},
|
||||||
|
OPSZONES = {},
|
||||||
|
PATHLINES = {},
|
||||||
}
|
}
|
||||||
|
|
||||||
local _DATABASECoalition =
|
local _DATABASECoalition =
|
||||||
@@ -244,7 +246,7 @@ function DATABASE:FindAirbase( AirbaseName )
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
do -- Zones
|
do -- Zones and Pathlines
|
||||||
|
|
||||||
--- Finds a @{Core.Zone} based on the zone name.
|
--- Finds a @{Core.Zone} based on the zone name.
|
||||||
-- @param #DATABASE self
|
-- @param #DATABASE self
|
||||||
@@ -267,7 +269,6 @@ do -- Zones
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Deletes a @{Core.Zone} from the DATABASE based on the zone name.
|
--- Deletes a @{Core.Zone} from the DATABASE based on the zone name.
|
||||||
-- @param #DATABASE self
|
-- @param #DATABASE self
|
||||||
-- @param #string ZoneName The name of the zone.
|
-- @param #string ZoneName The name of the zone.
|
||||||
@@ -277,6 +278,39 @@ do -- Zones
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Adds a @{Core.Pathline} based on its name in the DATABASE.
|
||||||
|
-- @param #DATABASE self
|
||||||
|
-- @param #string PathlineName The name of the pathline
|
||||||
|
-- @param Core.Pathline#PATHLINE Pathline The pathline.
|
||||||
|
function DATABASE:AddPathline( PathlineName, Pathline )
|
||||||
|
|
||||||
|
if not self.PATHLINES[PathlineName] then
|
||||||
|
self.PATHLINES[PathlineName]=Pathline
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Finds a @{Core.Pathline} by its name.
|
||||||
|
-- @param #DATABASE self
|
||||||
|
-- @param #string PathlineName The name of the Pathline.
|
||||||
|
-- @return Core.Pathline#PATHLINE The found PATHLINE.
|
||||||
|
function DATABASE:FindPathline( PathlineName )
|
||||||
|
|
||||||
|
local pathline = self.PATHLINES[PathlineName]
|
||||||
|
|
||||||
|
return pathline
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Deletes a @{Core.Pathline} from the DATABASE based on its name.
|
||||||
|
-- @param #DATABASE self
|
||||||
|
-- @param #string PathlineName The name of the PATHLINE.
|
||||||
|
function DATABASE:DeletePathline( PathlineName )
|
||||||
|
|
||||||
|
self.PATHLINES[PathlineName]=nil
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Private method that registers new ZONE_BASE derived objects within the DATABASE Object.
|
--- Private method that registers new ZONE_BASE derived objects within the DATABASE Object.
|
||||||
-- @param #DATABASE self
|
-- @param #DATABASE self
|
||||||
-- @return #DATABASE self
|
-- @return #DATABASE self
|
||||||
@@ -370,60 +404,148 @@ do -- Zones
|
|||||||
-- Add zone to DB.
|
-- Add zone to DB.
|
||||||
self:AddZone( ZoneName, Zone_Polygon )
|
self:AddZone( ZoneName, Zone_Polygon )
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Drawings as zones
|
-- Drawings as zones
|
||||||
if env.mission.drawings and env.mission.drawings.layers then
|
if env.mission.drawings and env.mission.drawings.layers then
|
||||||
|
|
||||||
-- Loop over layers.
|
-- Loop over layers.
|
||||||
for layerID, layerData in pairs(env.mission.drawings.layers or {}) do
|
for layerID, layerData in pairs(env.mission.drawings.layers or {}) do
|
||||||
|
|
||||||
-- Loop over objects in layers.
|
-- Loop over objects in layers.
|
||||||
for objectID, objectData in pairs(layerData.objects or {}) do
|
for objectID, objectData in pairs(layerData.objects or {}) do
|
||||||
|
|
||||||
-- Check for polygon which has at least 4 points (we would need 3 but the origin seems to be there twice)
|
-- Check for polygon which has at least 4 points (we would need 3 but the origin seems to be there twice)
|
||||||
if objectData.polygonMode=="free" and objectData.points and #objectData.points>=4 then
|
if objectData.polygonMode and (objectData.polygonMode=="free") and objectData.points and #objectData.points>=4 then
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Drawing: Polygon free
|
||||||
|
---
|
||||||
|
|
||||||
-- Name of the zone.
|
-- Name of the zone.
|
||||||
local ZoneName=objectData.name or "Unknown Drawing Zone"
|
local ZoneName=objectData.name or "Unknown free Polygon Drawing"
|
||||||
|
|
||||||
-- Reference point. All other points need to be translated by this.
|
-- Reference point. All other points need to be translated by this.
|
||||||
local vec2={x=objectData.mapX, y=objectData.mapY}
|
local vec2={x=objectData.mapX, y=objectData.mapY}
|
||||||
|
|
||||||
-- Copy points array.
|
-- Debug stuff.
|
||||||
|
--local vec3={x=objectData.mapX, y=0, z=objectData.mapY}
|
||||||
|
--local coord=COORDINATE:NewFromVec2(vec2):MarkToAll("MapX, MapY")
|
||||||
|
--trigger.action.markToAll(id, "mapXY", vec3)
|
||||||
|
|
||||||
|
-- Copy points array.
|
||||||
local points=UTILS.DeepCopy(objectData.points)
|
local points=UTILS.DeepCopy(objectData.points)
|
||||||
|
|
||||||
-- Translate points.
|
-- Translate points.
|
||||||
for i,_point in pairs(points) do
|
for i,_point in pairs(points) do
|
||||||
local point=_point --DCS#Vec2
|
local point=_point --DCS#Vec2
|
||||||
points[i]=UTILS.Vec2Add(point, vec2)
|
points[i]=UTILS.Vec2Add(point, vec2)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Remove last point.
|
-- Remove last point.
|
||||||
table.remove(points, #points)
|
table.remove(points, #points)
|
||||||
|
|
||||||
-- Debug output
|
-- Debug output
|
||||||
self:I(string.format("Register ZONE: %s (Polygon drawing with %d verticies)", ZoneName, #points))
|
self:I(string.format("Register ZONE: %s (Polygon (free) drawing with %d vertices)", ZoneName, #points))
|
||||||
|
|
||||||
-- Create new polygon zone.
|
-- Create new polygon zone.
|
||||||
local Zone=ZONE_POLYGON:NewFromPointsArray(ZoneName, points)
|
local Zone=ZONE_POLYGON:NewFromPointsArray(ZoneName, points)
|
||||||
|
|
||||||
-- Set color.
|
-- Set color.
|
||||||
Zone:SetColor({1, 0, 0}, 0.15)
|
Zone:SetColor({1, 0, 0}, 0.15)
|
||||||
|
|
||||||
-- Store in DB.
|
-- Store in DB.
|
||||||
self.ZONENAMES[ZoneName] = ZoneName
|
self.ZONENAMES[ZoneName] = ZoneName
|
||||||
|
|
||||||
-- Add zone.
|
-- Add zone.
|
||||||
self:AddZone(ZoneName, Zone)
|
self:AddZone(ZoneName, Zone)
|
||||||
|
|
||||||
end
|
-- Check for polygon which has at least 4 points (we would need 3 but the origin seems to be there twice)
|
||||||
end
|
elseif objectData.polygonMode and objectData.polygonMode=="rect" then
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Drawing: Polygon rect
|
||||||
|
---
|
||||||
|
|
||||||
|
-- Name of the zone.
|
||||||
|
local ZoneName=objectData.name or "Unknown rect Polygon Drawing"
|
||||||
|
|
||||||
|
-- Reference point (center of the rectangle).
|
||||||
|
local vec2={x=objectData.mapX, y=objectData.mapY}
|
||||||
|
|
||||||
|
-- For a rectangular polygon drawing, we have the width (y) and height (x).
|
||||||
|
local w=objectData.width
|
||||||
|
local h=objectData.height
|
||||||
|
|
||||||
|
-- Create points from center using with and height (width for y and height for x is a bit confusing, but this is how ED implemented it).
|
||||||
|
local points={}
|
||||||
|
points[1]={x=vec2.x-h/2, y=vec2.y+w/2} --Upper left
|
||||||
|
points[2]={x=vec2.x+h/2, y=vec2.y+w/2} --Upper right
|
||||||
|
points[3]={x=vec2.x+h/2, y=vec2.y-w/2} --Lower right
|
||||||
|
points[4]={x=vec2.x-h/2, y=vec2.y-w/2} --Lower left
|
||||||
|
|
||||||
|
--local coord=COORDINATE:NewFromVec2(vec2):MarkToAll("MapX, MapY")
|
||||||
|
|
||||||
|
-- Debug output
|
||||||
|
self:I(string.format("Register ZONE: %s (Polygon (rect) drawing with %d vertices)", ZoneName, #points))
|
||||||
|
|
||||||
|
-- Create new polygon zone.
|
||||||
|
local Zone=ZONE_POLYGON:NewFromPointsArray(ZoneName, points)
|
||||||
|
|
||||||
|
-- Set color.
|
||||||
|
Zone:SetColor({1, 0, 0}, 0.15)
|
||||||
|
|
||||||
|
-- Store in DB.
|
||||||
|
self.ZONENAMES[ZoneName] = ZoneName
|
||||||
|
|
||||||
|
-- Add zone.
|
||||||
|
self:AddZone(ZoneName, Zone)
|
||||||
|
|
||||||
|
elseif objectData.lineMode and (objectData.lineMode=="segments" or objectData.lineMode=="segment" or objectData.lineMode=="free") and objectData.points and #objectData.points>=2 then
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Drawing: Line (segments, segment or free)
|
||||||
|
---
|
||||||
|
|
||||||
|
-- Name of the zone.
|
||||||
|
local Name=objectData.name or "Unknown Line Drawing"
|
||||||
|
|
||||||
|
-- Reference point. All other points need to be translated by this.
|
||||||
|
local vec2={x=objectData.mapX, y=objectData.mapY}
|
||||||
|
|
||||||
|
-- Copy points array.
|
||||||
|
local points=UTILS.DeepCopy(objectData.points)
|
||||||
|
|
||||||
|
-- Translate points.
|
||||||
|
for i,_point in pairs(points) do
|
||||||
|
local point=_point --DCS#Vec2
|
||||||
|
points[i]=UTILS.Vec2Add(point, vec2)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Debug output
|
||||||
|
self:I(string.format("Register PATHLINE: %s (Line drawing with %d points)", Name, #points))
|
||||||
|
|
||||||
|
-- Create new polygon zone.
|
||||||
|
local Pathline=PATHLINE:NewFromVec2Array(Name, points)
|
||||||
|
|
||||||
|
-- Set color.
|
||||||
|
--Zone:SetColor({1, 0, 0}, 0.15)
|
||||||
|
|
||||||
|
-- Add zone.
|
||||||
|
self:AddPathline(Name,Pathline)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end -- zone
|
end -- zone
|
||||||
|
|
||||||
do -- Zone_Goal
|
do -- Zone_Goal
|
||||||
@@ -459,6 +581,46 @@ do -- Zone_Goal
|
|||||||
end
|
end
|
||||||
|
|
||||||
end -- Zone_Goal
|
end -- Zone_Goal
|
||||||
|
|
||||||
|
do -- OpsZone
|
||||||
|
|
||||||
|
--- Finds a @{Ops.OpsZone#OPSZONE} based on the zone name.
|
||||||
|
-- @param #DATABASE self
|
||||||
|
-- @param #string ZoneName The name of the zone.
|
||||||
|
-- @return Ops.OpsZone#OPSZONE The found OPSZONE.
|
||||||
|
function DATABASE:FindOpsZone( ZoneName )
|
||||||
|
|
||||||
|
local ZoneFound = self.OPSZONES[ZoneName]
|
||||||
|
|
||||||
|
return ZoneFound
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Adds a @{Ops.OpsZone#OPSZONE} based on the zone name in the DATABASE.
|
||||||
|
-- @param #DATABASE self
|
||||||
|
-- @param Ops.OpsZone#OPSZONE OpsZone The zone.
|
||||||
|
function DATABASE:AddOpsZone( OpsZone )
|
||||||
|
|
||||||
|
if OpsZone then
|
||||||
|
|
||||||
|
local ZoneName=OpsZone:GetName()
|
||||||
|
|
||||||
|
if not self.OPSZONES[ZoneName] then
|
||||||
|
self.OPSZONES[ZoneName] = OpsZone
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Deletes a @{Ops.OpsZone#OPSZONE} from the DATABASE based on the zone name.
|
||||||
|
-- @param #DATABASE self
|
||||||
|
-- @param #string ZoneName The name of the zone.
|
||||||
|
function DATABASE:DeleteOpsZone( ZoneName )
|
||||||
|
self.OPSZONES[ZoneName] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
end -- OpsZone
|
||||||
|
|
||||||
do -- cargo
|
do -- cargo
|
||||||
|
|
||||||
--- Adds a Cargo based on the Cargo Name in the DATABASE.
|
--- Adds a Cargo based on the Cargo Name in the DATABASE.
|
||||||
@@ -1040,7 +1202,7 @@ function DATABASE:_RegisterClients()
|
|||||||
for ClientName, ClientTemplate in pairs( self.Templates.ClientsByName ) do
|
for ClientName, ClientTemplate in pairs( self.Templates.ClientsByName ) do
|
||||||
self:I(string.format("Register Client: %s", tostring(ClientName)))
|
self:I(string.format("Register Client: %s", tostring(ClientName)))
|
||||||
local client=self:AddClient( ClientName )
|
local client=self:AddClient( ClientName )
|
||||||
client.SpawnCoord=COORDINATE:New(ClientTemplate.x, ClientTemplate.alt, ClientTemplate.y)
|
client.SpawnCoord=COORDINATE:New(ClientTemplate.x, ClientTemplate.alt, ClientTemplate.y)
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -1090,7 +1252,7 @@ end
|
|||||||
function DATABASE:_RegisterAirbase(airbase)
|
function DATABASE:_RegisterAirbase(airbase)
|
||||||
|
|
||||||
if airbase then
|
if airbase then
|
||||||
|
|
||||||
-- Get the airbase name.
|
-- Get the airbase name.
|
||||||
local DCSAirbaseName = airbase:getName()
|
local DCSAirbaseName = airbase:getName()
|
||||||
|
|
||||||
@@ -1478,10 +1640,10 @@ end
|
|||||||
-- @param #DATABASE self
|
-- @param #DATABASE self
|
||||||
-- @param #function IteratorFunction The function that will be called object in the database. The function needs to accept a CLIENT parameter.
|
-- @param #function IteratorFunction The function that will be called object in the database. The function needs to accept a CLIENT parameter.
|
||||||
-- @return #DATABASE self
|
-- @return #DATABASE self
|
||||||
function DATABASE:ForEachClient( IteratorFunction, ... )
|
function DATABASE:ForEachClient( IteratorFunction, FinalizeFunction, ... )
|
||||||
self:F2( arg )
|
self:F2( arg )
|
||||||
|
|
||||||
self:ForEach( IteratorFunction, arg, self.CLIENTS )
|
self:ForEach( IteratorFunction, FinalizeFunction, arg, self.CLIENTS )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -1490,10 +1652,10 @@ end
|
|||||||
-- @param #DATABASE self
|
-- @param #DATABASE self
|
||||||
-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a CLIENT parameter.
|
-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a CLIENT parameter.
|
||||||
-- @return #DATABASE self
|
-- @return #DATABASE self
|
||||||
function DATABASE:ForEachCargo( IteratorFunction, ... )
|
function DATABASE:ForEachCargo( IteratorFunction, FinalizeFunction, ... )
|
||||||
self:F2( arg )
|
self:F2( arg )
|
||||||
|
|
||||||
self:ForEach( IteratorFunction, arg, self.CARGOS )
|
self:ForEach( IteratorFunction, FinalizeFunction, arg, self.CARGOS )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -1719,7 +1881,7 @@ function DATABASE:_RegisterTemplates()
|
|||||||
|
|
||||||
if obj_type_name ~= "static" and Template and Template.units and type(Template.units) == 'table' then --making sure again- this is a valid group
|
if obj_type_name ~= "static" and Template and Template.units and type(Template.units) == 'table' then --making sure again- this is a valid group
|
||||||
|
|
||||||
self:_RegisterGroupTemplate(Template, CoalitionSide, _DATABASECategory[string.lower(CategoryName)], CountryID)
|
self:_RegisterGroupTemplate(Template, CoalitionSide, _DATABASECategory[string.lower(CategoryName)], CountryID)
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
|
|||||||
@@ -305,8 +305,8 @@ EVENTS = {
|
|||||||
-- @field Wrapper.Airbase#AIRBASE Place The MOOSE airbase object.
|
-- @field Wrapper.Airbase#AIRBASE Place The MOOSE airbase object.
|
||||||
-- @field #string PlaceName The name of the airbase.
|
-- @field #string PlaceName The name of the airbase.
|
||||||
--
|
--
|
||||||
-- @field #table weapon The weapon used during the event.
|
-- @field DCS#Weapon weapon The weapon used during the event.
|
||||||
-- @field #table Weapon
|
-- @field DCS#Weapon Weapon The weapon used during the event.
|
||||||
-- @field #string WeaponName Name of the weapon.
|
-- @field #string WeaponName Name of the weapon.
|
||||||
-- @field DCS#Unit WeaponTgtDCSUnit Target DCS unit of the weapon.
|
-- @field DCS#Unit WeaponTgtDCSUnit Target DCS unit of the weapon.
|
||||||
--
|
--
|
||||||
@@ -1224,7 +1224,7 @@ function EVENT:onEvent( Event )
|
|||||||
if Event.TgtObjectCategory == Object.Category.STATIC then
|
if Event.TgtObjectCategory == Object.Category.STATIC then
|
||||||
-- get base data
|
-- get base data
|
||||||
Event.TgtDCSUnit = Event.target
|
Event.TgtDCSUnit = Event.target
|
||||||
if Event.target:isExist() and Event.id ~= 33 and not Event.TgtObjectCategory == Object.Category.COORDINATE then -- leave out ejected seat object
|
if Event.target:isExist() and Event.id ~= 33 then -- leave out ejected seat object
|
||||||
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
||||||
Event.TgtUnitName = Event.TgtDCSUnitName
|
Event.TgtUnitName = Event.TgtDCSUnitName
|
||||||
Event.TgtUnit = STATIC:FindByName( Event.TgtDCSUnitName, false )
|
Event.TgtUnit = STATIC:FindByName( Event.TgtDCSUnitName, false )
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
-- ### Author: **Applevangelist**
|
-- ### Author: **Applevangelist**
|
||||||
--
|
--
|
||||||
-- Date: 5 May 2021
|
-- Date: 5 May 2021
|
||||||
-- Last Update: Sep 2022
|
-- Last Update: Feb 2023
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
---
|
---
|
||||||
@@ -50,7 +50,7 @@ MARKEROPS_BASE = {
|
|||||||
ClassName = "MARKEROPS",
|
ClassName = "MARKEROPS",
|
||||||
Tag = "mytag",
|
Tag = "mytag",
|
||||||
Keywords = {},
|
Keywords = {},
|
||||||
version = "0.1.0",
|
version = "0.1.1",
|
||||||
debug = false,
|
debug = false,
|
||||||
Casesensitive = true,
|
Casesensitive = true,
|
||||||
}
|
}
|
||||||
@@ -124,7 +124,8 @@ function MARKEROPS_BASE:New(Tagname,Keywords,Casesensitive)
|
|||||||
-- @param #string Text The text on the marker
|
-- @param #string Text The text on the marker
|
||||||
-- @param #table Keywords Table of matching keywords found in the Event text
|
-- @param #table Keywords Table of matching keywords found in the Event text
|
||||||
-- @param Core.Point#COORDINATE Coord Coordinate of the marker.
|
-- @param Core.Point#COORDINATE Coord Coordinate of the marker.
|
||||||
|
-- @param #number idx DCS Marker ID
|
||||||
|
|
||||||
--- On after "MarkDeleted" event. Triggered when a Marker is deleted from the F10 map.
|
--- On after "MarkDeleted" event. Triggered when a Marker is deleted from the F10 map.
|
||||||
-- @function [parent=#MARKEROPS_BASE] OnAfterMarkDeleted
|
-- @function [parent=#MARKEROPS_BASE] OnAfterMarkDeleted
|
||||||
-- @param #MARKEROPS_BASE self
|
-- @param #MARKEROPS_BASE self
|
||||||
@@ -172,7 +173,7 @@ function MARKEROPS_BASE:OnEventMark(Event)
|
|||||||
if Eventtext~=nil then
|
if Eventtext~=nil then
|
||||||
if self:_MatchTag(Eventtext) then
|
if self:_MatchTag(Eventtext) then
|
||||||
local matchtable = self:_MatchKeywords(Eventtext)
|
local matchtable = self:_MatchKeywords(Eventtext)
|
||||||
self:MarkChanged(Eventtext,matchtable,coord)
|
self:MarkChanged(Eventtext,matchtable,coord,Event.idx)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif Event.id==world.event.S_EVENT_MARK_REMOVED then
|
elseif Event.id==world.event.S_EVENT_MARK_REMOVED then
|
||||||
|
|||||||
370
Moose Development/Moose/Core/Pathline.lua
Normal file
370
Moose Development/Moose/Core/Pathline.lua
Normal file
@@ -0,0 +1,370 @@
|
|||||||
|
--- **Core** - Path from A to B.
|
||||||
|
--
|
||||||
|
-- **Main Features:**
|
||||||
|
--
|
||||||
|
-- * Path from A to B
|
||||||
|
-- * Arbitrary number of points
|
||||||
|
-- * Automatically from lines drawtool
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Author: **funkyfranky**
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
-- @module Core.Pathline
|
||||||
|
-- @image CORE_Pathline.png
|
||||||
|
|
||||||
|
|
||||||
|
--- PATHLINE class.
|
||||||
|
-- @type PATHLINE
|
||||||
|
-- @field #string ClassName Name of the class.
|
||||||
|
-- @field #string lid Class id string for output to DCS log file.
|
||||||
|
-- @field #string name Name of the path line.
|
||||||
|
-- @field #table points List of 3D points defining the path.
|
||||||
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
--- *The shortest distance between two points is a straight line.* -- Archimedes
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- # 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
|
||||||
|
-- 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.
|
||||||
|
-- They can be accessed with the @{#PATHLINE.FindByName) function.
|
||||||
|
--
|
||||||
|
-- # 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}
|
||||||
|
--
|
||||||
|
-- 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
|
||||||
|
--
|
||||||
|
-- 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
|
||||||
|
-- you specify in the draw panel.
|
||||||
|
--
|
||||||
|
-- # 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
|
||||||
|
-- water depth.
|
||||||
|
--
|
||||||
|
-- To remove the marks, use @{#PATHLINE.MarkPoints}(`false`).
|
||||||
|
--
|
||||||
|
-- @field #PATHLINE
|
||||||
|
PATHLINE = {
|
||||||
|
ClassName = "PATHLINE",
|
||||||
|
lid = nil,
|
||||||
|
points = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Point of line.
|
||||||
|
-- @type PATHLINE.Point
|
||||||
|
-- @field DCS#Vec3 vec3 3D position.
|
||||||
|
-- @field DCS#Vec2 vec2 2D position.
|
||||||
|
-- @field #number surfaceType Surface type.
|
||||||
|
-- @field #number landHeight Land height in meters.
|
||||||
|
-- @field #number depth Water depth in meters.
|
||||||
|
-- @field #number markerID Marker ID.
|
||||||
|
|
||||||
|
|
||||||
|
--- PATHLINE class version.
|
||||||
|
-- @field #string version
|
||||||
|
PATHLINE.version="0.1.0"
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- TODO list
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- TODO: A lot...
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Constructor
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Create a new PATHLINE object. Points need to be added later.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param #string Name Name of the path.
|
||||||
|
-- @return #PATHLINE self
|
||||||
|
function PATHLINE:New(Name)
|
||||||
|
|
||||||
|
-- Inherit everything from INTEL class.
|
||||||
|
local self=BASE:Inherit(self, BASE:New()) --#PATHLINE
|
||||||
|
|
||||||
|
self.name=Name or "Unknown Path"
|
||||||
|
|
||||||
|
self.lid=string.format("PATHLINE %s | ", Name)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create a new PATHLINE object from a given list of 2D points.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param #string Name Name of the pathline.
|
||||||
|
-- @param #table Vec2Array List of DCS#Vec2 points.
|
||||||
|
-- @return #PATHLINE self
|
||||||
|
function PATHLINE:NewFromVec2Array(Name, Vec2Array)
|
||||||
|
|
||||||
|
local self=PATHLINE:New(Name)
|
||||||
|
|
||||||
|
for i=1,#Vec2Array do
|
||||||
|
self:AddPointFromVec2(Vec2Array[i])
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create a new PATHLINE object from a given list of 3D points.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param #string Name Name of the pathline.
|
||||||
|
-- @param #table Vec3Array List of DCS#Vec3 points.
|
||||||
|
-- @return #PATHLINE self
|
||||||
|
function PATHLINE:NewFromVec3Array(Name, Vec3Array)
|
||||||
|
|
||||||
|
local self=PATHLINE:New(Name)
|
||||||
|
|
||||||
|
for i=1,#Vec3Array do
|
||||||
|
self:AddPointFromVec3(Vec3Array[i])
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- User functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Find a pathline in the database.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param #string Name The name of the pathline.
|
||||||
|
-- @return #PATHLINE self
|
||||||
|
function PATHLINE:FindByName(Name)
|
||||||
|
local pathline = _DATABASE:FindPathline(Name)
|
||||||
|
return pathline
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- Add a point to the path from a given 2D position. The third dimension is determined from the land height.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param DCS#Vec2 Vec2 The 2D vector (x,y) to add.
|
||||||
|
-- @return #PATHLINE self
|
||||||
|
function PATHLINE:AddPointFromVec2(Vec2)
|
||||||
|
|
||||||
|
if Vec2 then
|
||||||
|
|
||||||
|
local point=self:_CreatePoint(Vec2)
|
||||||
|
|
||||||
|
table.insert(self.points, point)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Add a point to the path from a given 3D position.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param DCS#Vec3 Vec3 The 3D vector (x,y) to add.
|
||||||
|
-- @return #PATHLINE self
|
||||||
|
function PATHLINE:AddPointFromVec3(Vec3)
|
||||||
|
|
||||||
|
if Vec3 then
|
||||||
|
|
||||||
|
local point=self:_CreatePoint(Vec3)
|
||||||
|
|
||||||
|
table.insert(self.points, point)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get name of pathline.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @return #string Name of the pathline.
|
||||||
|
function PATHLINE:GetName()
|
||||||
|
return self.name
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get number of points.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @return #number Number of points.
|
||||||
|
function PATHLINE:GetNumberOfPoints()
|
||||||
|
local N=#self.points
|
||||||
|
return N
|
||||||
|
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.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @return <#PATHLINE.Point> List of points.
|
||||||
|
function PATHLINE:GetPoints()
|
||||||
|
return self.points
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get 3D points of pathline.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @return <DCS#Vec3> List of DCS#Vec3 points.
|
||||||
|
function PATHLINE:GetPoints3D()
|
||||||
|
|
||||||
|
local vecs={}
|
||||||
|
|
||||||
|
for _,_point in pairs(self.points) do
|
||||||
|
local point=_point --#PATHLINE.Point
|
||||||
|
table.insert(vecs, point.vec3)
|
||||||
|
end
|
||||||
|
|
||||||
|
return vecs
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get 2D points of pathline.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @return <DCS#Vec2> List of DCS#Vec2 points.
|
||||||
|
function PATHLINE:GetPoints2D()
|
||||||
|
|
||||||
|
local vecs={}
|
||||||
|
|
||||||
|
for _,_point in pairs(self.points) do
|
||||||
|
local point=_point --#PATHLINE.Point
|
||||||
|
table.insert(vecs, point.vec2)
|
||||||
|
end
|
||||||
|
|
||||||
|
return vecs
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get COORDINATES of pathline. Note that COORDINATE objects are created when calling this function. That does involve deep copy calls and can have an impact on performance if done too often.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @return <Core.Point#COORDINATE> List of COORDINATES points.
|
||||||
|
function PATHLINE:GetCoordinats()
|
||||||
|
|
||||||
|
local vecs={}
|
||||||
|
|
||||||
|
for _,_point in pairs(self.points) do
|
||||||
|
local point=_point --#PATHLINE.Point
|
||||||
|
local coord=COORDINATE:NewFromVec3(point.vec3)
|
||||||
|
end
|
||||||
|
|
||||||
|
return vecs
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the n-th point of the pathline.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param #number n The index of the point. Default is the first point.
|
||||||
|
-- @return #PATHLINE.Point Point.
|
||||||
|
function PATHLINE:GetPointFromIndex(n)
|
||||||
|
|
||||||
|
local N=self:GetNumberOfPoints()
|
||||||
|
|
||||||
|
n=n or 1
|
||||||
|
|
||||||
|
local point=nil --#PATHLINE.Point
|
||||||
|
|
||||||
|
if n>=1 and n<=N then
|
||||||
|
point=self.point[n]
|
||||||
|
else
|
||||||
|
self:E(self.lid..string.format("ERROR: No point in pathline for N=%s", tostring(n)))
|
||||||
|
end
|
||||||
|
|
||||||
|
return point
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the 3D position of the n-th point.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param #number n The n-th point.
|
||||||
|
-- @return DCS#VEC3 Position in 3D.
|
||||||
|
function PATHLINE:GetPoint3DFromIndex(n)
|
||||||
|
|
||||||
|
local point=self:GetPointFromIndex(n)
|
||||||
|
|
||||||
|
if point then
|
||||||
|
return point.vec3
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the 2D position of the n-th point.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param #number n The n-th point.
|
||||||
|
-- @return DCS#VEC2 Position in 3D.
|
||||||
|
function PATHLINE:GetPoint2DFromIndex(n)
|
||||||
|
|
||||||
|
local point=self:GetPointFromIndex(n)
|
||||||
|
|
||||||
|
if point then
|
||||||
|
return point.vec2
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Mark points on F10 map.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param #boolean Switch If `true` or nil, set marks. If `false`, remove marks.
|
||||||
|
-- @return <DCS#Vec3> List of DCS#Vec3 points.
|
||||||
|
function PATHLINE:MarkPoints(Switch)
|
||||||
|
for i,_point in pairs(self.points) do
|
||||||
|
local point=_point --#PATHLINE.Point
|
||||||
|
if Switch==false then
|
||||||
|
|
||||||
|
if point.markerID then
|
||||||
|
UTILS.RemoveMark(point.markerID, Delay)
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
if point.markerID then
|
||||||
|
UTILS.RemoveMark(point.markerID)
|
||||||
|
end
|
||||||
|
|
||||||
|
point.markerID=UTILS.GetMarkID()
|
||||||
|
|
||||||
|
local text=string.format("Pathline %s: Point #%d\nSurface Type=%d\nHeight=%.1f m\nDepth=%.1f m", self.name, i, point.surfaceType, point.landHeight, point.depth)
|
||||||
|
|
||||||
|
trigger.action.markToAll(point.markerID, text, point.vec3, "")
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Private functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Get 3D points of pathline.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param DCS#Vec3 Vec Position vector. Can also be a DCS#Vec2 in which case the altitude at landheight is taken.
|
||||||
|
-- @return #PATHLINE.Point
|
||||||
|
function PATHLINE:_CreatePoint(Vec)
|
||||||
|
|
||||||
|
local point={} --#PATHLINE.Point
|
||||||
|
|
||||||
|
if Vec.z then
|
||||||
|
-- Given vec is 3D
|
||||||
|
point.vec3=UTILS.DeepCopy(Vec)
|
||||||
|
point.vec2={x=Vec.x, y=Vec.z}
|
||||||
|
else
|
||||||
|
-- Given vec is 2D
|
||||||
|
point.vec2=UTILS.DeepCopy(Vec)
|
||||||
|
point.vec3={x=Vec.x, y=land.getHeight(Vec), z=Vec.y}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get surface type.
|
||||||
|
point.surfaceType=land.getSurfaceType(point.vec2)
|
||||||
|
|
||||||
|
-- Get land height and depth.
|
||||||
|
point.landHeight, point.depth=land.getSurfaceHeightWithSeabed(point.vec2)
|
||||||
|
|
||||||
|
point.markerID=nil
|
||||||
|
|
||||||
|
return point
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -540,7 +540,7 @@ do -- COORDINATE
|
|||||||
local gotscenery=false
|
local gotscenery=false
|
||||||
|
|
||||||
local function EvaluateZone(ZoneObject)
|
local function EvaluateZone(ZoneObject)
|
||||||
BASE:T({ZoneObject})
|
|
||||||
if ZoneObject then
|
if ZoneObject then
|
||||||
|
|
||||||
-- Get category of scanned object.
|
-- Get category of scanned object.
|
||||||
@@ -603,6 +603,46 @@ do -- COORDINATE
|
|||||||
|
|
||||||
return set
|
return set
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Scan/find STATICS within a certain radius around the coordinate using the world.searchObjects() DCS API function.
|
||||||
|
-- @param #COORDINATE self
|
||||||
|
-- @param #number radius (Optional) Scan radius in meters. Default 100 m.
|
||||||
|
-- @return Core.Set#SET_UNIT Set of units.
|
||||||
|
function COORDINATE:ScanStatics(radius)
|
||||||
|
|
||||||
|
local _,_,_,_,statics=self:ScanObjects(radius, false, true, false)
|
||||||
|
|
||||||
|
local set=SET_STATIC:New()
|
||||||
|
|
||||||
|
for _,stat in pairs(statics) do
|
||||||
|
set:AddStatic(STATIC:Find(stat))
|
||||||
|
end
|
||||||
|
|
||||||
|
return set
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Find the closest static to the COORDINATE within a certain radius.
|
||||||
|
-- @param #COORDINATE self
|
||||||
|
-- @param #number radius Scan radius in meters. Default 100 m.
|
||||||
|
-- @return Wrapper.Static#STATIC The closest static or #nil if no unit is inside the given radius.
|
||||||
|
function COORDINATE:FindClosestStatic(radius)
|
||||||
|
|
||||||
|
local units=self:ScanStatics(radius)
|
||||||
|
|
||||||
|
local umin=nil --Wrapper.Unit#UNIT
|
||||||
|
local dmin=math.huge
|
||||||
|
for _,_unit in pairs(units.Set) do
|
||||||
|
local unit=_unit --Wrapper.Static#STATIC
|
||||||
|
local coordinate=unit:GetCoordinate()
|
||||||
|
local d=self:Get2DDistance(coordinate)
|
||||||
|
if d<dmin then
|
||||||
|
dmin=d
|
||||||
|
umin=unit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return umin
|
||||||
|
end
|
||||||
|
|
||||||
--- Find the closest unit to the COORDINATE within a certain radius.
|
--- Find the closest unit to the COORDINATE within a certain radius.
|
||||||
-- @param #COORDINATE self
|
-- @param #COORDINATE self
|
||||||
@@ -1054,28 +1094,55 @@ do -- COORDINATE
|
|||||||
return heading
|
return heading
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Returns the 3D wind direction vector. Note that vector points into the direction the wind in blowing to.
|
||||||
|
-- @param #COORDINATE self
|
||||||
|
-- @param #number height (Optional) parameter specifying the height ASL in meters. The minimum height will be always be the land height since the wind is zero below the ground.
|
||||||
|
-- @param #boolean turbulence (Optional) If `true`, include turbulence.
|
||||||
|
-- @return DCS#Vec3 Wind 3D vector. Components in m/s.
|
||||||
|
function COORDINATE:GetWindVec3(height, turbulence)
|
||||||
|
|
||||||
|
-- We at 0.1 meters to be sure to be above ground since wind is zero below ground level.
|
||||||
|
local landheight=self:GetLandHeight()+0.1
|
||||||
|
|
||||||
|
local point={x=self.x, y=math.max(height or self.y, landheight), z=self.z}
|
||||||
|
|
||||||
|
-- Get wind velocity vector.
|
||||||
|
local wind = nil --DCS#Vec3
|
||||||
|
|
||||||
|
if turbulence then
|
||||||
|
wind = atmosphere.getWindWithTurbulence(point)
|
||||||
|
else
|
||||||
|
wind = atmosphere.getWind(point)
|
||||||
|
end
|
||||||
|
|
||||||
|
return wind
|
||||||
|
end
|
||||||
|
|
||||||
--- Returns the wind direction (from) and strength.
|
--- Returns the wind direction (from) and strength.
|
||||||
-- @param #COORDINATE self
|
-- @param #COORDINATE self
|
||||||
-- @param height (Optional) parameter specifying the height ASL. The minimum height will be always be the land height since the wind is zero below the ground.
|
-- @param #number height (Optional) parameter specifying the height ASL. The minimum height will be always be the land height since the wind is zero below the ground.
|
||||||
-- @return Direction the wind is blowing from in degrees.
|
-- @param #boolean turbulence If `true`, include turbulence. If `false` or `nil`, wind without turbulence.
|
||||||
-- @return Wind strength in m/s.
|
-- @return #number Direction the wind is blowing from in degrees.
|
||||||
function COORDINATE:GetWind(height)
|
-- @return #number Wind strength in m/s.
|
||||||
local landheight=self:GetLandHeight()+0.1 -- we at 0.1 meters to be sure to be above ground since wind is zero below ground level.
|
function COORDINATE:GetWind(height, turbulence)
|
||||||
local point={x=self.x, y=math.max(height or self.y, landheight), z=self.z}
|
|
||||||
-- get wind velocity vector
|
-- Get wind velocity vector
|
||||||
local wind = atmosphere.getWind(point)
|
local wind = self:GetWindVec3(height, turbulence)
|
||||||
local direction = math.deg(math.atan2(wind.z, wind.x))
|
|
||||||
if direction < 0 then
|
-- Calculate the direction of the vector.
|
||||||
direction = 360 + direction
|
local direction=UTILS.VecHdg(wind)
|
||||||
end
|
|
||||||
-- Convert to direction to from direction
|
-- Invert "to" direction to "from" direction.
|
||||||
if direction > 180 then
|
if direction > 180 then
|
||||||
direction = direction-180
|
direction = direction-180
|
||||||
else
|
else
|
||||||
direction = direction+180
|
direction = direction+180
|
||||||
end
|
end
|
||||||
local strength=math.sqrt((wind.x)^2+(wind.z)^2)
|
|
||||||
-- Return wind direction and strength km/h.
|
-- Wind strength in m/s.
|
||||||
|
local strength=UTILS.VecNorm(wind) -- math.sqrt((wind.x)^2+(wind.z)^2)
|
||||||
|
|
||||||
|
-- Return wind direction and strength.
|
||||||
return direction, strength
|
return direction, strength
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1133,12 +1200,15 @@ do -- COORDINATE
|
|||||||
|
|
||||||
--- Return the 3D distance in meters between the target COORDINATE and the COORDINATE.
|
--- Return the 3D distance in meters between the target COORDINATE and the COORDINATE.
|
||||||
-- @param #COORDINATE self
|
-- @param #COORDINATE self
|
||||||
-- @param #COORDINATE TargetCoordinate The target COORDINATE.
|
-- @param #COORDINATE TargetCoordinate The target COORDINATE. Can also be a DCS#Vec3.
|
||||||
-- @return DCS#Distance Distance The distance in meters.
|
-- @return DCS#Distance Distance The distance in meters.
|
||||||
function COORDINATE:Get3DDistance( TargetCoordinate )
|
function COORDINATE:Get3DDistance( TargetCoordinate )
|
||||||
local TargetVec3 = TargetCoordinate:GetVec3()
|
--local TargetVec3 = TargetCoordinate:GetVec3()
|
||||||
|
local TargetVec3 = {x=TargetCoordinate.x, y=TargetCoordinate.y, z=TargetCoordinate.z}
|
||||||
local SourceVec3 = self:GetVec3()
|
local SourceVec3 = self:GetVec3()
|
||||||
return ( ( TargetVec3.x - SourceVec3.x ) ^ 2 + ( TargetVec3.y - SourceVec3.y ) ^ 2 + ( TargetVec3.z - SourceVec3.z ) ^ 2 ) ^ 0.5
|
--local dist=( ( TargetVec3.x - SourceVec3.x ) ^ 2 + ( TargetVec3.y - SourceVec3.y ) ^ 2 + ( TargetVec3.z - SourceVec3.z ) ^ 2 ) ^ 0.5
|
||||||
|
local dist=UTILS.VecDist3D(TargetVec3, SourceVec3)
|
||||||
|
return dist
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -3253,7 +3323,52 @@ do -- COORDINATE
|
|||||||
|
|
||||||
return self:GetTemperatureText( nil, Settings )
|
return self:GetTemperatureText( nil, Settings )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Function to check if a coordinate is in a steep (>8% elevation) area of the map
|
||||||
|
-- @param #COORDINATE self
|
||||||
|
-- @param #number Radius (Optional) Radius to check around the coordinate, defaults to 50m (100m diameter)
|
||||||
|
-- @param #number Minelevation (Optional) Elevation from which on a area is defined as steep, defaults to 8% (8m height gain across 100 meters)
|
||||||
|
-- @return #boolen IsSteep If true, area is steep
|
||||||
|
-- @return #number MaxElevation Elevation in meters measured over 100m
|
||||||
|
function COORDINATE:IsInSteepArea(Radius,Minelevation)
|
||||||
|
local steep = false
|
||||||
|
local elev = Minelevation or 8
|
||||||
|
local bdelta = 0
|
||||||
|
local h0 = self:GetLandHeight()
|
||||||
|
local radius = Radius or 50
|
||||||
|
local diam = radius * 2
|
||||||
|
for i=0,150,30 do
|
||||||
|
local polar = math.fmod(i+180,360)
|
||||||
|
local c1 = self:Translate(radius,i,false,false)
|
||||||
|
local c2 = self:Translate(radius,polar,false,false)
|
||||||
|
local h1 = c1:GetLandHeight()
|
||||||
|
local h2 = c2:GetLandHeight()
|
||||||
|
local d1 = math.abs(h1-h2)
|
||||||
|
local d2 = math.abs(h0-h1)
|
||||||
|
local d3 = math.abs(h0-h2)
|
||||||
|
local dm = d1 > d2 and d1 or d2
|
||||||
|
local dm1 = dm > d3 and dm or d3
|
||||||
|
bdelta = dm1 > bdelta and dm1 or bdelta
|
||||||
|
self:T(string.format("d1=%d, d2=%d, d3=%d, max delta=%d",d1,d2,d3,bdelta))
|
||||||
|
end
|
||||||
|
local steepness = bdelta / (radius / 100)
|
||||||
|
if steepness >= elev then steep = true end
|
||||||
|
return steep, math.floor(steepness)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to check if a coordinate is in a flat (<8% elevation) area of the map
|
||||||
|
-- @param #COORDINATE self
|
||||||
|
-- @param #number Radius (Optional) Radius to check around the coordinate, defaults to 50m (100m diameter)
|
||||||
|
-- @param #number Minelevation (Optional) Elevation from which on a area is defined as steep, defaults to 8% (8m height gain across 100 meters)
|
||||||
|
-- @return #boolen IsFlat If true, area is flat
|
||||||
|
-- @return #number MaxElevation Elevation in meters measured over 100m
|
||||||
|
function COORDINATE:IsInFlatArea(Radius,Minelevation)
|
||||||
|
local steep, elev = self:IsInSteepArea(Radius,Minelevation)
|
||||||
|
local flat = not steep
|
||||||
|
return flat, elev
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
do -- POINT_VEC3
|
do -- POINT_VEC3
|
||||||
|
|||||||
@@ -903,7 +903,9 @@ end
|
|||||||
|
|
||||||
do -- SET_GROUP
|
do -- SET_GROUP
|
||||||
|
|
||||||
--- @type SET_GROUP
|
--- @type SET_GROUP #SET_GROUP
|
||||||
|
-- @field Core.Timer#TIMER ZoneTimer
|
||||||
|
-- @field #number ZoneTimerInterval
|
||||||
-- @extends Core.Set#SET_BASE
|
-- @extends Core.Set#SET_BASE
|
||||||
|
|
||||||
--- Mission designers can use the @{Core.Set#SET_GROUP} class to build sets of groups belonging to certain:
|
--- Mission designers can use the @{Core.Set#SET_GROUP} class to build sets of groups belonging to certain:
|
||||||
@@ -1343,6 +1345,25 @@ do -- SET_GROUP
|
|||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- [Internal] Private function for use of continous zone filter
|
||||||
|
-- @param #SET_GROUP self
|
||||||
|
-- @return #SET_GROUP self
|
||||||
|
function SET_GROUP:_ContinousZoneFilter()
|
||||||
|
|
||||||
|
local Database = _DATABASE.GROUPS
|
||||||
|
|
||||||
|
for ObjectName, Object in pairs( Database ) do
|
||||||
|
if self:IsIncludeObject( Object ) and self:IsNotInSet(Object) then
|
||||||
|
self:Add( ObjectName, Object )
|
||||||
|
elseif (not self:IsIncludeObject( Object )) and self:IsInSet(Object) then
|
||||||
|
self:Remove(ObjectName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
--- Builds a set of groups that are only active.
|
--- Builds a set of groups that are only active.
|
||||||
-- Only the groups that are active will be included within the set.
|
-- Only the groups that are active will be included within the set.
|
||||||
@@ -1381,10 +1402,46 @@ do -- SET_GROUP
|
|||||||
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
|
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
|
||||||
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
|
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
|
||||||
self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash )
|
self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash )
|
||||||
|
if self.Filter.Zones then
|
||||||
|
self.ZoneTimer = TIMER:New(self._ContinousZoneFilter,self)
|
||||||
|
local timing = self.ZoneTimerInterval or 30
|
||||||
|
self.ZoneTimer:Start(timing,timing)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set filter timer interval for FilterZones if using active filtering with FilterStart().
|
||||||
|
-- @param #SET_GROUP self
|
||||||
|
-- @param #number Seconds Seconds between check intervals, defaults to 30. **Caution** - do not be too agressive with timing! Groups are usually not moving fast enough
|
||||||
|
-- to warrant a check of below 10 seconds.
|
||||||
|
-- @return #SET_GROUP self
|
||||||
|
function SET_GROUP:FilterZoneTimer(Seconds)
|
||||||
|
self.ZoneTimerInterval = Seconds or 30
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Stops the filtering.
|
||||||
|
-- @param #SET_GROUP self
|
||||||
|
-- @return #SET_GROUP self
|
||||||
|
function SET_GROUP:FilterStop()
|
||||||
|
|
||||||
|
if _DATABASE then
|
||||||
|
|
||||||
|
self:UnHandleEvent(EVENTS.Birth)
|
||||||
|
self:UnHandleEvent(EVENTS.Dead)
|
||||||
|
self:UnHandleEvent(EVENTS.Crash)
|
||||||
|
self:UnHandleEvent(EVENTS.RemoveUnit)
|
||||||
|
|
||||||
|
if self.Filter.Zones and self.ZoneTimer and self.ZoneTimer:IsRunning() then
|
||||||
|
self.ZoneTimer:Stop()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Handles the OnDead or OnCrash event for alive groups set.
|
--- Handles the OnDead or OnCrash event for alive groups set.
|
||||||
-- Note: The GROUP object in the SET_GROUP collection will only be removed if the last unit is destroyed of the GROUP.
|
-- Note: The GROUP object in the SET_GROUP collection will only be removed if the last unit is destroyed of the GROUP.
|
||||||
@@ -1888,7 +1945,7 @@ do -- SET_GROUP
|
|||||||
if self.Filter.Zones then
|
if self.Filter.Zones then
|
||||||
local MGroupZone = false
|
local MGroupZone = false
|
||||||
for ZoneName, Zone in pairs( self.Filter.Zones ) do
|
for ZoneName, Zone in pairs( self.Filter.Zones ) do
|
||||||
self:T3( "Zone:", ZoneName )
|
--self:I( "Zone:", ZoneName )
|
||||||
if MGroup:IsInZone(Zone) then
|
if MGroup:IsInZone(Zone) then
|
||||||
MGroupZone = true
|
MGroupZone = true
|
||||||
end
|
end
|
||||||
@@ -1900,24 +1957,6 @@ do -- SET_GROUP
|
|||||||
return MGroupInclude
|
return MGroupInclude
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Iterate the SET_GROUP and set for each unit the default cargo bay weight limit.
|
|
||||||
-- Because within a group, the type of carriers can differ, each cargo bay weight limit is set on @{Wrapper.Unit} level.
|
|
||||||
-- @param #SET_GROUP self
|
|
||||||
-- @usage
|
|
||||||
-- -- Set the default cargo bay weight limits of the carrier units.
|
|
||||||
-- local MySetGroup = SET_GROUP:New()
|
|
||||||
-- MySetGroup:SetCargoBayWeightLimit()
|
|
||||||
function SET_GROUP:SetCargoBayWeightLimit()
|
|
||||||
local Set = self:GetSet()
|
|
||||||
for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP
|
|
||||||
for UnitName, UnitData in pairs( GroupData:GetUnits() ) do
|
|
||||||
-- local UnitData = UnitData -- Wrapper.Unit#UNIT
|
|
||||||
UnitData:SetCargoBayWeightLimit()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- Get the closest group of the set with respect to a given reference coordinate. Optionally, only groups of given coalitions are considered in the search.
|
--- Get the closest group of the set with respect to a given reference coordinate. Optionally, only groups of given coalitions are considered in the search.
|
||||||
-- @param #SET_GROUP self
|
-- @param #SET_GROUP self
|
||||||
-- @param Core.Point#COORDINATE Coordinate Reference Coordinate from which the closest group is determined.
|
-- @param Core.Point#COORDINATE Coordinate Reference Coordinate from which the closest group is determined.
|
||||||
@@ -1952,11 +1991,30 @@ do -- SET_GROUP
|
|||||||
return gmin, dmin
|
return gmin, dmin
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Iterate the SET_GROUP and set for each unit the default cargo bay weight limit.
|
||||||
|
-- Because within a group, the type of carriers can differ, each cargo bay weight limit is set on @{Wrapper.Unit} level.
|
||||||
|
-- @param #SET_GROUP self
|
||||||
|
-- @usage
|
||||||
|
-- -- Set the default cargo bay weight limits of the carrier units.
|
||||||
|
-- local MySetGroup = SET_GROUP:New()
|
||||||
|
-- MySetGroup:SetCargoBayWeightLimit()
|
||||||
|
function SET_GROUP:SetCargoBayWeightLimit()
|
||||||
|
local Set = self:GetSet()
|
||||||
|
for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP
|
||||||
|
for UnitName, UnitData in pairs( GroupData:GetUnits() ) do
|
||||||
|
-- local UnitData = UnitData -- Wrapper.Unit#UNIT
|
||||||
|
UnitData:SetCargoBayWeightLimit()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
do -- SET_UNIT
|
do -- SET_UNIT
|
||||||
|
|
||||||
--- @type SET_UNIT
|
--- @type SET_UNIT
|
||||||
|
-- @field Core.Timer#TIMER ZoneTimer
|
||||||
|
-- @field #number ZoneTimerInterval
|
||||||
-- @extends Core.Set#SET_BASE
|
-- @extends Core.Set#SET_BASE
|
||||||
|
|
||||||
--- Mission designers can use the SET_UNIT class to build sets of units belonging to certain:
|
--- Mission designers can use the SET_UNIT class to build sets of units belonging to certain:
|
||||||
@@ -2114,10 +2172,12 @@ do -- SET_UNIT
|
|||||||
self:F2( Unit:GetName() )
|
self:F2( Unit:GetName() )
|
||||||
|
|
||||||
self:Add( Unit:GetName(), Unit )
|
self:Add( Unit:GetName(), Unit )
|
||||||
|
|
||||||
-- Set the default cargo bay limit each time a new unit is added to the set.
|
if Unit:IsInstanceOf("UNIT") then
|
||||||
Unit:SetCargoBayWeightLimit()
|
-- Set the default cargo bay limit each time a new unit is added to the set.
|
||||||
|
Unit:SetCargoBayWeightLimit()
|
||||||
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2346,7 +2406,56 @@ do -- SET_UNIT
|
|||||||
|
|
||||||
return CountU
|
return CountU
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- [Internal] Private function for use of continous zone filter
|
||||||
|
-- @param #SET_UNIT self
|
||||||
|
-- @return #SET_UNIT self
|
||||||
|
function SET_UNIT:_ContinousZoneFilter()
|
||||||
|
|
||||||
|
local Database = _DATABASE.UNITS
|
||||||
|
|
||||||
|
for ObjectName, Object in pairs( Database ) do
|
||||||
|
if self:IsIncludeObject( Object ) and self:IsNotInSet(Object) then
|
||||||
|
self:Add( ObjectName, Object )
|
||||||
|
elseif (not self:IsIncludeObject( Object )) and self:IsInSet(Object) then
|
||||||
|
self:Remove(ObjectName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set filter timer interval for FilterZones if using active filtering with FilterStart().
|
||||||
|
-- @param #SET_UNIT self
|
||||||
|
-- @param #number Seconds Seconds between check intervals, defaults to 30. **Caution** - do not be too agressive with timing! Groups are usually not moving fast enough
|
||||||
|
-- to warrant a check of below 10 seconds.
|
||||||
|
-- @return #SET_UNIT self
|
||||||
|
function SET_UNIT:FilterZoneTimer(Seconds)
|
||||||
|
self.ZoneTimerInterval = Seconds or 30
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Stops the filtering.
|
||||||
|
-- @param #SET_UNIT self
|
||||||
|
-- @return #SET_UNIT self
|
||||||
|
function SET_UNIT:FilterStop()
|
||||||
|
|
||||||
|
if _DATABASE then
|
||||||
|
|
||||||
|
self:UnHandleEvent(EVENTS.Birth)
|
||||||
|
self:UnHandleEvent(EVENTS.Dead)
|
||||||
|
self:UnHandleEvent(EVENTS.Crash)
|
||||||
|
self:UnHandleEvent(EVENTS.RemoveUnit)
|
||||||
|
|
||||||
|
if self.Filter.Zones and self.ZoneTimer and self.ZoneTimer:IsRunning() then
|
||||||
|
self.ZoneTimer:Stop()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Starts the filtering.
|
--- Starts the filtering.
|
||||||
-- @param #SET_UNIT self
|
-- @param #SET_UNIT self
|
||||||
-- @return #SET_UNIT self
|
-- @return #SET_UNIT self
|
||||||
@@ -2358,6 +2467,11 @@ do -- SET_UNIT
|
|||||||
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
|
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
|
||||||
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
|
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
|
||||||
self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash )
|
self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash )
|
||||||
|
if self.Filter.Zones then
|
||||||
|
self.ZoneTimer = TIMER:New(self._ContinousZoneFilter,self)
|
||||||
|
local timing = self.ZoneTimerInterval or 30
|
||||||
|
self.ZoneTimer:Start(timing,timing)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -3794,11 +3908,47 @@ do -- SET_STATIC
|
|||||||
return TypeReport:Text( Delimiter )
|
return TypeReport:Text( Delimiter )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get the closest static of the set with respect to a given reference coordinate. Optionally, only statics of given coalitions are considered in the search.
|
||||||
|
-- @param #SET_STATIC self
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate Reference Coordinate from which the closest static is determined.
|
||||||
|
-- @return Wrapper.Static#STATIC The closest static (if any).
|
||||||
|
-- @return #number Distance in meters to the closest static.
|
||||||
|
function SET_STATIC:GetClosestStatic(Coordinate, Coalitions)
|
||||||
|
|
||||||
|
local Set = self:GetSet()
|
||||||
|
|
||||||
|
local dmin=math.huge
|
||||||
|
local gmin=nil
|
||||||
|
|
||||||
|
for GroupID, GroupData in pairs( Set ) do -- For each STATIC in SET_STATIC
|
||||||
|
local group=GroupData --Wrapper.Static#STATIC
|
||||||
|
|
||||||
|
if group and group:IsAlive() and (Coalitions==nil or UTILS.IsAnyInTable(Coalitions, group:GetCoalition())) then
|
||||||
|
|
||||||
|
local coord=group:GetCoord()
|
||||||
|
|
||||||
|
-- Distance between ref. coordinate and group coordinate.
|
||||||
|
local d=UTILS.VecDist3D(Coordinate, coord)
|
||||||
|
|
||||||
|
if d<dmin then
|
||||||
|
dmin=d
|
||||||
|
gmin=group
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return gmin, dmin
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
do -- SET_CLIENT
|
do -- SET_CLIENT
|
||||||
|
|
||||||
--- @type SET_CLIENT
|
--- @type SET_CLIENT
|
||||||
|
-- @field Core.Timer#TIMER ZoneTimer
|
||||||
|
-- @field #number ZoneTimerInterval
|
||||||
-- @extends Core.Set#SET_BASE
|
-- @extends Core.Set#SET_BASE
|
||||||
|
|
||||||
--- Mission designers can use the @{Core.Set#SET_CLIENT} class to build sets of units belonging to certain:
|
--- Mission designers can use the @{Core.Set#SET_CLIENT} class to build sets of units belonging to certain:
|
||||||
@@ -4106,6 +4256,54 @@ do -- SET_CLIENT
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- [Internal] Private function for use of continous zone filter
|
||||||
|
-- @param #SET_CLIENT self
|
||||||
|
-- @return #SET_CLIENT self
|
||||||
|
function SET_CLIENT:_ContinousZoneFilter()
|
||||||
|
|
||||||
|
local Database = _DATABASE.CLIENTS
|
||||||
|
|
||||||
|
for ObjectName, Object in pairs( Database ) do
|
||||||
|
if self:IsIncludeObject( Object ) and self:IsNotInSet(Object) then
|
||||||
|
self:Add( ObjectName, Object )
|
||||||
|
elseif (not self:IsIncludeObject( Object )) and self:IsInSet(Object) then
|
||||||
|
self:Remove(ObjectName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set filter timer interval for FilterZones if using active filtering with FilterStart().
|
||||||
|
-- @param #SET_CLIENT self
|
||||||
|
-- @param #number Seconds Seconds between check intervals, defaults to 30. **Caution** - do not be too agressive with timing! Groups are usually not moving fast enough
|
||||||
|
-- to warrant a check of below 10 seconds.
|
||||||
|
-- @return #SET_CLIENT self
|
||||||
|
function SET_CLIENT:FilterZoneTimer(Seconds)
|
||||||
|
self.ZoneTimerInterval = Seconds or 30
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Stops the filtering.
|
||||||
|
-- @param #SET_CLIENT self
|
||||||
|
-- @return #SET_CLIENT self
|
||||||
|
function SET_CLIENT:FilterStop()
|
||||||
|
|
||||||
|
if _DATABASE then
|
||||||
|
|
||||||
|
self:UnHandleEvent(EVENTS.Birth)
|
||||||
|
self:UnHandleEvent(EVENTS.Dead)
|
||||||
|
self:UnHandleEvent(EVENTS.Crash)
|
||||||
|
|
||||||
|
if self.Filter.Zones and self.ZoneTimer and self.ZoneTimer:IsRunning() then
|
||||||
|
self.ZoneTimer:Stop()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Starts the filtering.
|
--- Starts the filtering.
|
||||||
-- @param #SET_CLIENT self
|
-- @param #SET_CLIENT self
|
||||||
-- @return #SET_CLIENT self
|
-- @return #SET_CLIENT self
|
||||||
@@ -4116,6 +4314,11 @@ do -- SET_CLIENT
|
|||||||
self:HandleEvent( EVENTS.Birth, self._EventOnBirth )
|
self:HandleEvent( EVENTS.Birth, self._EventOnBirth )
|
||||||
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
|
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
|
||||||
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
|
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
|
||||||
|
if self.Filter.Zones then
|
||||||
|
self.ZoneTimer = TIMER:New(self._ContinousZoneFilter,self)
|
||||||
|
local timing = self.ZoneTimerInterval or 30
|
||||||
|
self.ZoneTimer:Start(timing,timing)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -7594,7 +7797,7 @@ do -- SET_SCENERY
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---
|
--- [Internal] Determine if an object is to be included in the SET
|
||||||
-- @param #SET_SCENERY self
|
-- @param #SET_SCENERY self
|
||||||
-- @param Wrapper.Scenery#SCENERY MScenery
|
-- @param Wrapper.Scenery#SCENERY MScenery
|
||||||
-- @return #SET_SCENERY self
|
-- @return #SET_SCENERY self
|
||||||
@@ -7602,4 +7805,47 @@ do -- SET_SCENERY
|
|||||||
self:F2( MScenery )
|
self:F2( MScenery )
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Count overall initial (Life0) lifepoints of the SET objects.
|
||||||
|
-- @param #SET_SCENERY self
|
||||||
|
-- @return #number LIfe0Points
|
||||||
|
function SET_SCENERY:GetLife0()
|
||||||
|
local life0 = 0
|
||||||
|
self:ForEachScenery(
|
||||||
|
function(obj)
|
||||||
|
local Obj = obj -- Wrapper.Scenery#SCENERY
|
||||||
|
life0 = life0 + Obj:GetLife0()
|
||||||
|
end
|
||||||
|
)
|
||||||
|
return life0
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Count overall current lifepoints of the SET objects.
|
||||||
|
-- @param #SET_SCENERY self
|
||||||
|
-- @return #number LifePoints
|
||||||
|
function SET_SCENERY:GetLife()
|
||||||
|
local life = 0
|
||||||
|
self:ForEachScenery(
|
||||||
|
function(obj)
|
||||||
|
local Obj = obj -- Wrapper.Scenery#SCENERY
|
||||||
|
life = life + Obj:GetLife()
|
||||||
|
end
|
||||||
|
)
|
||||||
|
return life
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Calculate current relative lifepoints of the SET objects, i.e. Life divided by Life0 as percentage value, eg 75 meaning 75% alive.
|
||||||
|
-- **CAVEAT**: Some objects change their life value or "hitpoints" **after** the first hit. Hence we will adjust the Life0 value to 120%
|
||||||
|
-- of the last life value if life exceeds life0 ata any point.
|
||||||
|
-- Thus will will get a smooth percentage decrease, if you use this e.g. as success criteria for a bombing task.
|
||||||
|
-- @param #SET_SCENERY self
|
||||||
|
-- @return #number LifePoints
|
||||||
|
function SET_SCENERY:GetRelativeLife()
|
||||||
|
local life = self:GetLife()
|
||||||
|
local life0 = self:GetLife0()
|
||||||
|
self:T3(string.format("Set Lifepoints: %d life0 | %d life",life0,life))
|
||||||
|
local rlife = math.floor((life / life0) * 100)
|
||||||
|
return rlife
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -335,7 +335,7 @@ end
|
|||||||
-- @param #SPAWN self
|
-- @param #SPAWN self
|
||||||
-- @param #string SpawnTemplatePrefix is the name of the Group in the ME that defines the Template.
|
-- @param #string SpawnTemplatePrefix is the name of the Group in the ME that defines the Template.
|
||||||
-- @param #string SpawnAliasPrefix is the name that will be given to the Group at runtime.
|
-- @param #string SpawnAliasPrefix is the name that will be given to the Group at runtime.
|
||||||
-- @return #SPAWN
|
-- @return #SPAWN self
|
||||||
-- @usage
|
-- @usage
|
||||||
-- -- NATO helicopters engaging in the battle field.
|
-- -- NATO helicopters engaging in the battle field.
|
||||||
-- Spawn_BE_KA50 = SPAWN:NewWithAlias( 'BE KA-50@RAMP-Ground Defense', 'Helicopter Attacking a City' )
|
-- Spawn_BE_KA50 = SPAWN:NewWithAlias( 'BE KA-50@RAMP-Ground Defense', 'Helicopter Attacking a City' )
|
||||||
@@ -385,32 +385,129 @@ function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix )
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Creates a new SPAWN instance to create new groups based on the provided template.
|
--- Creates a new SPAWN instance to create new groups based on the provided template. This will also register the template for future use.
|
||||||
-- @param #SPAWN self
|
-- @param #SPAWN self
|
||||||
-- @param #table SpawnTemplate is the Template of the Group. This must be a valid Group Template structure!
|
-- @param #table SpawnTemplate is the Template of the Group. This must be a valid Group Template structure - see [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_func_addGroup)!
|
||||||
-- @param #string SpawnTemplatePrefix is the name of the Group that will be given at each spawn.
|
-- @param #string SpawnTemplatePrefix [Mandatory] is the name of the template and the prefix of the GROUP on spawn.
|
||||||
-- @param #string SpawnAliasPrefix is the name that will be given to the Group at runtime.
|
-- @param #string SpawnAliasPrefix [Optional] is the prefix that will be given to the GROUP on spawn.
|
||||||
-- @return #SPAWN
|
-- @param #boolean MooseNaming [Optional] If false, skip the Moose naming additions (like groupname#001-01) - you need to ensure yourself no duplicate group names exist!
|
||||||
|
-- @return #SPAWN self
|
||||||
-- @usage
|
-- @usage
|
||||||
-- -- Create a new SPAWN object based on a Group Template defined from scratch.
|
-- -- Spawn a P51 Mustang from scratch
|
||||||
-- Spawn_BE_KA50 = SPAWN:NewWithAlias( 'BE KA-50@RAMP-Ground Defense', 'Helicopter Attacking a City' )
|
-- local ttemp =
|
||||||
-- @usage
|
-- {
|
||||||
--
|
-- ["modulation"] = 0,
|
||||||
-- -- Create a new CSAR_Spawn object based on a normal Group Template to spawn a soldier.
|
-- ["tasks"] =
|
||||||
-- local CSAR_Spawn = SPAWN:NewWithFromTemplate( Template, "CSAR", "Pilot" )
|
-- {
|
||||||
--
|
-- }, -- end of ["tasks"]
|
||||||
function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix )
|
-- ["task"] = "Reconnaissance",
|
||||||
local self = BASE:Inherit( self, BASE:New() )
|
-- ["uncontrolled"] = false,
|
||||||
self:F( { SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix } )
|
-- ["route"] =
|
||||||
if SpawnAliasPrefix == nil or SpawnAliasPrefix == "" then
|
-- {
|
||||||
BASE:I( "ERROR: in function NewFromTemplate, required parameter SpawnAliasPrefix is not set" )
|
-- ["points"] =
|
||||||
|
-- {
|
||||||
|
-- [1] =
|
||||||
|
-- {
|
||||||
|
-- ["alt"] = 2000,
|
||||||
|
-- ["action"] = "Turning Point",
|
||||||
|
-- ["alt_type"] = "BARO",
|
||||||
|
-- ["speed"] = 125,
|
||||||
|
-- ["task"] =
|
||||||
|
-- {
|
||||||
|
-- ["id"] = "ComboTask",
|
||||||
|
-- ["params"] =
|
||||||
|
-- {
|
||||||
|
-- ["tasks"] =
|
||||||
|
-- {
|
||||||
|
-- }, -- end of ["tasks"]
|
||||||
|
-- }, -- end of ["params"]
|
||||||
|
-- }, -- end of ["task"]
|
||||||
|
-- ["type"] = "Turning Point",
|
||||||
|
-- ["ETA"] = 0,
|
||||||
|
-- ["ETA_locked"] = true,
|
||||||
|
-- ["y"] = 666285.71428571,
|
||||||
|
-- ["x"] = -312000,
|
||||||
|
-- ["formation_template"] = "",
|
||||||
|
-- ["speed_locked"] = true,
|
||||||
|
-- }, -- end of [1]
|
||||||
|
-- }, -- end of ["points"]
|
||||||
|
-- }, -- end of ["route"]
|
||||||
|
-- ["groupId"] = 1,
|
||||||
|
-- ["hidden"] = false,
|
||||||
|
-- ["units"] =
|
||||||
|
-- {
|
||||||
|
-- [1] =
|
||||||
|
-- {
|
||||||
|
-- ["alt"] = 2000,
|
||||||
|
-- ["alt_type"] = "BARO",
|
||||||
|
-- ["livery_id"] = "USAF 364th FS",
|
||||||
|
-- ["skill"] = "High",
|
||||||
|
-- ["speed"] = 125,
|
||||||
|
-- ["type"] = "TF-51D",
|
||||||
|
-- ["unitId"] = 1,
|
||||||
|
-- ["psi"] = 0,
|
||||||
|
-- ["y"] = 666285.71428571,
|
||||||
|
-- ["x"] = -312000,
|
||||||
|
-- ["name"] = "P51-1-1",
|
||||||
|
-- ["payload"] =
|
||||||
|
-- {
|
||||||
|
-- ["pylons"] =
|
||||||
|
-- {
|
||||||
|
-- }, -- end of ["pylons"]
|
||||||
|
-- ["fuel"] = 340.68,
|
||||||
|
-- ["flare"] = 0,
|
||||||
|
-- ["chaff"] = 0,
|
||||||
|
-- ["gun"] = 100,
|
||||||
|
-- }, -- end of ["payload"]
|
||||||
|
-- ["heading"] = 0,
|
||||||
|
-- ["callsign"] =
|
||||||
|
-- {
|
||||||
|
-- [1] = 1,
|
||||||
|
-- [2] = 1,
|
||||||
|
-- ["name"] = "Enfield11",
|
||||||
|
-- [3] = 1,
|
||||||
|
-- }, -- end of ["callsign"]
|
||||||
|
-- ["onboard_num"] = "010",
|
||||||
|
-- }, -- end of [1]
|
||||||
|
-- }, -- end of ["units"]
|
||||||
|
-- ["y"] = 666285.71428571,
|
||||||
|
-- ["x"] = -312000,
|
||||||
|
-- ["name"] = "P51",
|
||||||
|
-- ["communication"] = true,
|
||||||
|
-- ["start_time"] = 0,
|
||||||
|
-- ["frequency"] = 124,
|
||||||
|
-- }
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- local mustang = SPAWN:NewFromTemplate(ttemp,"P51D")
|
||||||
|
-- -- you MUST set the next three:
|
||||||
|
-- mustang:InitCountry(country.id.FRANCE)
|
||||||
|
-- mustang:InitCategory(Group.Category.AIRPLANE)
|
||||||
|
-- mustang:InitCoalition(coalition.side.BLUE)
|
||||||
|
-- mustang:OnSpawnGroup(
|
||||||
|
-- function(grp)
|
||||||
|
-- MESSAGE:New("Group Spawned: "..grp:GetName(),15,"SPAWN"):ToAll()
|
||||||
|
-- end
|
||||||
|
-- )
|
||||||
|
-- mustang:Spawn()
|
||||||
|
--
|
||||||
|
function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix, MooseNaming )
|
||||||
|
local self = BASE:Inherit( self, BASE:New() )
|
||||||
|
self:F( { SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix } )
|
||||||
|
--if SpawnAliasPrefix == nil or SpawnAliasPrefix == "" then
|
||||||
|
--BASE:I( "ERROR: in function NewFromTemplate, required parameter SpawnAliasPrefix is not set" )
|
||||||
|
--return nil
|
||||||
|
--end
|
||||||
|
if SpawnTemplatePrefix == nil or SpawnTemplatePrefix == "" then
|
||||||
|
BASE:I( "ERROR: in function NewFromTemplate, required parameter SpawnTemplatePrefix is not set" )
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
if SpawnTemplate then
|
if SpawnTemplate then
|
||||||
self.SpawnTemplate = SpawnTemplate -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!!
|
self.SpawnTemplate = SpawnTemplate -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!!
|
||||||
self.SpawnTemplatePrefix = SpawnTemplatePrefix
|
self.SpawnTemplatePrefix = SpawnTemplatePrefix
|
||||||
self.SpawnAliasPrefix = SpawnAliasPrefix
|
self.SpawnAliasPrefix = SpawnAliasPrefix or SpawnTemplatePrefix
|
||||||
|
self.SpawnTemplate.name = SpawnTemplatePrefix
|
||||||
self.SpawnIndex = 0
|
self.SpawnIndex = 0
|
||||||
self.SpawnCount = 0 -- The internal counter of the amount of spawning the has happened since SpawnStart.
|
self.SpawnCount = 0 -- The internal counter of the amount of spawning the has happened since SpawnStart.
|
||||||
self.AliveUnits = 0 -- Contains the counter how many units are currently alive
|
self.AliveUnits = 0 -- Contains the counter how many units are currently alive
|
||||||
@@ -435,7 +532,8 @@ function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPr
|
|||||||
self.SpawnInitModex = nil
|
self.SpawnInitModex = nil
|
||||||
self.SpawnInitAirbase = nil
|
self.SpawnInitAirbase = nil
|
||||||
self.TweakedTemplate = true -- Check if the user is using self made template.
|
self.TweakedTemplate = true -- Check if the user is using self made template.
|
||||||
|
self.MooseNameing = MooseNaming or true
|
||||||
|
|
||||||
self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned.
|
self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned.
|
||||||
else
|
else
|
||||||
error( "There is no template provided for SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" )
|
error( "There is no template provided for SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" )
|
||||||
@@ -772,10 +870,8 @@ end
|
|||||||
-- @usage
|
-- @usage
|
||||||
--
|
--
|
||||||
-- -- NATO helicopters engaging in the battle field.
|
-- -- NATO helicopters engaging in the battle field.
|
||||||
-- -- The KA-50 has waypoints Start point ( =0 or SP ), 1, 2, 3, 4, End point (= 5 or DP).
|
-- -- UNIT positions of this group will be randomized around the base unit #1 in a circle of 50 to 500 meters.
|
||||||
-- -- Waypoints 2 and 3 will only be randomized. The others will remain on their original position with each new spawn of the helicopter.
|
-- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):InitRandomizeUnits( true, 500, 50 )
|
||||||
-- -- The randomization of waypoint 2 and 3 will take place within a radius of 2000 meters.
|
|
||||||
-- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):InitRandomizeRoute( 2, 2, 2000 )
|
|
||||||
--
|
--
|
||||||
function SPAWN:InitRandomizeUnits( RandomizeUnits, OuterRadius, InnerRadius )
|
function SPAWN:InitRandomizeUnits( RandomizeUnits, OuterRadius, InnerRadius )
|
||||||
self:F( { self.SpawnTemplatePrefix, RandomizeUnits, OuterRadius, InnerRadius } )
|
self:F( { self.SpawnTemplatePrefix, RandomizeUnits, OuterRadius, InnerRadius } )
|
||||||
@@ -791,6 +887,46 @@ function SPAWN:InitRandomizeUnits( RandomizeUnits, OuterRadius, InnerRadius )
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Spawn the UNITs of this group with individual relative positions to unit #1 and individual headings.
|
||||||
|
-- @param #SPAWN self
|
||||||
|
-- @param #table Positions Table of positions, needs to one entry per unit in the group(!). The table contains one table each for each unit, with x,y, and optionally z
|
||||||
|
-- relative positions, and optionally an individual heading.
|
||||||
|
-- @return #SPAWN
|
||||||
|
-- @usage
|
||||||
|
--
|
||||||
|
-- -- NATO helicopter group of three units engaging in the battle field.
|
||||||
|
-- local Positions = { [1] = {x = 0, y = 0, heading = 0}, [2] = {x = 50, y = 50, heading = 90}, [3] = {x = -50, y = 50, heading = 180} }
|
||||||
|
-- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):InitSetUnitRelativePositions(Positions)
|
||||||
|
--
|
||||||
|
function SPAWN:InitSetUnitRelativePositions(Positions)
|
||||||
|
self:F({self.SpawnTemplatePrefix, Positions})
|
||||||
|
|
||||||
|
self.SpawnUnitsWithRelativePositions = true
|
||||||
|
self.UnitsRelativePositions = Positions
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Spawn the UNITs of this group with individual absolute positions and individual headings.
|
||||||
|
-- @param #SPAWN self
|
||||||
|
-- @param #table Positions Table of positions, needs to one entry per unit in the group(!). The table contains one table each for each unit, with x,y, and optionally z
|
||||||
|
-- absolute positions, and optionally an individual heading.
|
||||||
|
-- @return #SPAWN
|
||||||
|
-- @usage
|
||||||
|
--
|
||||||
|
-- -- NATO helicopter group of three units engaging in the battle field.
|
||||||
|
-- local Positions = { [1] = {x = 0, y = 0, heading = 0}, [2] = {x = 50, y = 50, heading = 90}, [3] = {x = -50, y = 50, heading = 180} }
|
||||||
|
-- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):InitSetUnitAbsolutePositions(Positions)
|
||||||
|
--
|
||||||
|
function SPAWN:InitSetUnitAbsolutePositions(Positions)
|
||||||
|
self:F({self.SpawnTemplatePrefix, Positions})
|
||||||
|
|
||||||
|
self.SpawnUnitsWithAbsolutePositions = true
|
||||||
|
self.UnitsAbsolutePositions = Positions
|
||||||
|
|
||||||
|
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.
|
||||||
@@ -1146,7 +1282,8 @@ do -- Delay methods
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Turns the Delay On for the @{Wrapper.Group} when spawning.
|
--- Turns the Delay On for the @{Wrapper.Group} when spawning with @{SpawnScheduled}(). In effect then the 1st group will only be spawned
|
||||||
|
-- after the number of seconds given in SpawnScheduled as arguments, and not immediately.
|
||||||
-- @param #SPAWN self
|
-- @param #SPAWN self
|
||||||
-- @return #SPAWN The SPAWN object
|
-- @return #SPAWN The SPAWN object
|
||||||
function SPAWN:InitDelayOn()
|
function SPAWN:InitDelayOn()
|
||||||
@@ -1342,7 +1479,38 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
|
|||||||
SpawnTemplate.units[UnitID].psi = -SpawnTemplate.units[UnitID].heading
|
SpawnTemplate.units[UnitID].psi = -SpawnTemplate.units[UnitID].heading
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Individual relative unit positions + heading
|
||||||
|
if self.SpawnUnitsWithRelativePositions and self.UnitsRelativePositions then
|
||||||
|
local BaseX = SpawnTemplate.units[1].x or 0
|
||||||
|
local BaseY = SpawnTemplate.units[1].y or 0
|
||||||
|
local BaseZ = SpawnTemplate.units[1].z or 0
|
||||||
|
for UnitID = 1, #SpawnTemplate.units do
|
||||||
|
if self.UnitsRelativePositions[UnitID].heading then
|
||||||
|
SpawnTemplate.units[UnitID].heading = math.rad(self.UnitsRelativePositions[UnitID].heading or 0)
|
||||||
|
end
|
||||||
|
SpawnTemplate.units[UnitID].x = BaseX + (self.UnitsRelativePositions[UnitID].x or 0)
|
||||||
|
SpawnTemplate.units[UnitID].y = BaseY + (self.UnitsRelativePositions[UnitID].y or 0)
|
||||||
|
if self.UnitsRelativePositions[UnitID].z then
|
||||||
|
SpawnTemplate.units[UnitID].z = BaseZ + (self.UnitsRelativePositions[UnitID].z or 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Individual asbolute unit positions + heading
|
||||||
|
if self.SpawnUnitsWithAbsolutePositions and self.UnitsAbsolutePositions then
|
||||||
|
for UnitID = 1, #SpawnTemplate.units do
|
||||||
|
if self.UnitsAbsolutePositions[UnitID].heading then
|
||||||
|
SpawnTemplate.units[UnitID].heading = math.rad(self.UnitsAbsolutePositions[UnitID].heading or 0)
|
||||||
|
end
|
||||||
|
SpawnTemplate.units[UnitID].x = self.UnitsAbsolutePositions[UnitID].x or 0
|
||||||
|
SpawnTemplate.units[UnitID].y = self.UnitsAbsolutePositions[UnitID].y or 0
|
||||||
|
if self.UnitsAbsolutePositions[UnitID].z then
|
||||||
|
SpawnTemplate.units[UnitID].z = self.UnitsAbsolutePositions[UnitID].z or 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Set livery.
|
-- Set livery.
|
||||||
if self.SpawnInitLivery then
|
if self.SpawnInitLivery then
|
||||||
for UnitID = 1, #SpawnTemplate.units do
|
for UnitID = 1, #SpawnTemplate.units do
|
||||||
@@ -1443,6 +1611,8 @@ end
|
|||||||
-- @param #number SpawnTime The time interval defined in seconds between each new spawn of new groups.
|
-- @param #number SpawnTime The time interval defined in seconds between each new spawn of new groups.
|
||||||
-- @param #number SpawnTimeVariation The variation to be applied on the defined time interval between each new spawn.
|
-- @param #number SpawnTimeVariation The variation to be applied on the defined time interval between each new spawn.
|
||||||
-- The variation is a number between 0 and 1, representing the % of variation to be applied on the time interval.
|
-- The variation is a number between 0 and 1, representing the % of variation to be applied on the time interval.
|
||||||
|
-- @param #boolen WithDelay Do not spawn the **first** group immediately, but delay the spawn as per the calculation below.
|
||||||
|
-- Effectively the same as @{InitDelayOn}().
|
||||||
-- @return #SPAWN self
|
-- @return #SPAWN self
|
||||||
-- @usage
|
-- @usage
|
||||||
-- -- NATO helicopters engaging in the battle field.
|
-- -- NATO helicopters engaging in the battle field.
|
||||||
@@ -1453,17 +1623,20 @@ end
|
|||||||
-- -- High limit: 600 * ( 1 + 0.5 / 2 ) = 750
|
-- -- High limit: 600 * ( 1 + 0.5 / 2 ) = 750
|
||||||
-- -- Between these two values, a random amount of seconds will be chosen for each new spawn of the helicopters.
|
-- -- Between these two values, a random amount of seconds will be chosen for each new spawn of the helicopters.
|
||||||
-- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):SpawnScheduled( 600, 0.5 )
|
-- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):SpawnScheduled( 600, 0.5 )
|
||||||
function SPAWN:SpawnScheduled( SpawnTime, SpawnTimeVariation )
|
function SPAWN:SpawnScheduled( SpawnTime, SpawnTimeVariation, WithDelay )
|
||||||
self:F( { SpawnTime, SpawnTimeVariation } )
|
self:F( { SpawnTime, SpawnTimeVariation } )
|
||||||
|
|
||||||
|
local SpawnTime = SpawnTime or 60
|
||||||
|
local SpawnTimeVariation = SpawnTimeVariation or 0.5
|
||||||
|
|
||||||
if SpawnTime ~= nil and SpawnTimeVariation ~= nil then
|
if SpawnTime ~= nil and SpawnTimeVariation ~= nil then
|
||||||
local InitialDelay = 0
|
local InitialDelay = 0
|
||||||
if self.DelayOnOff == true then
|
if WithDelay or self.DelayOnOff == true then
|
||||||
InitialDelay = math.random( SpawnTime - SpawnTime * SpawnTimeVariation, SpawnTime + SpawnTime * SpawnTimeVariation )
|
InitialDelay = math.random( SpawnTime - SpawnTime * SpawnTimeVariation, SpawnTime + SpawnTime * SpawnTimeVariation )
|
||||||
end
|
end
|
||||||
self.SpawnScheduler = SCHEDULER:New( self, self._Scheduler, {}, InitialDelay, SpawnTime, SpawnTimeVariation )
|
self.SpawnScheduler = SCHEDULER:New( self, self._Scheduler, {}, InitialDelay, SpawnTime, SpawnTimeVariation )
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2985,6 +3158,9 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2
|
|||||||
if self.TweakedTemplate ~= nil and self.TweakedTemplate == true then
|
if self.TweakedTemplate ~= nil and self.TweakedTemplate == true then
|
||||||
BASE:I( "WARNING: You are using a tweaked template." )
|
BASE:I( "WARNING: You are using a tweaked template." )
|
||||||
SpawnTemplate = self.SpawnTemplate
|
SpawnTemplate = self.SpawnTemplate
|
||||||
|
if self.MooseNameing then
|
||||||
|
SpawnTemplate.name = self:SpawnGroupName( SpawnIndex )
|
||||||
|
end
|
||||||
else
|
else
|
||||||
SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix )
|
SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix )
|
||||||
SpawnTemplate.name = self:SpawnGroupName( SpawnIndex )
|
SpawnTemplate.name = self:SpawnGroupName( SpawnIndex )
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
-- SPOT implements the DCS Spot class functionality, but adds additional luxury to be able to:
|
-- SPOT implements the DCS Spot class functionality, but adds additional luxury to be able to:
|
||||||
--
|
--
|
||||||
-- * Spot for a defined duration.
|
-- * Spot for a defined duration.
|
||||||
-- * Updates of laer spot position every 0.2 seconds for moving targets.
|
-- * Updates of laser spot position every 0.2 seconds for moving targets.
|
||||||
-- * Wiggle the spot at the target.
|
-- * Wiggle the spot at the target.
|
||||||
-- * Provide a @{Wrapper.Unit} as a target, instead of a point.
|
-- * Provide a @{Wrapper.Unit} as a target, instead of a point.
|
||||||
-- * Implement a status machine, LaseOn, LaseOff.
|
-- * Implement a status machine, LaseOn, LaseOff.
|
||||||
@@ -13,18 +13,8 @@
|
|||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- # Demo Missions
|
-- # Demo Missions
|
||||||
--
|
|
||||||
-- ### [SPOT Demo Missions source code]()
|
|
||||||
--
|
|
||||||
-- ### [SPOT Demo Missions, only for beta testers]()
|
|
||||||
--
|
--
|
||||||
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
|
-- ### [Demo Missions on GitHub](https://github.com/FlightControl-Master/MOOSE_MISSIONS)
|
||||||
--
|
|
||||||
-- ===
|
|
||||||
--
|
|
||||||
-- # YouTube Channel
|
|
||||||
--
|
|
||||||
-- ### [SPOT YouTube Channel]()
|
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -50,14 +40,14 @@ do
|
|||||||
--- Implements the target spotting or marking functionality, but adds additional luxury to be able to:
|
--- Implements the target spotting or marking functionality, but adds additional luxury to be able to:
|
||||||
--
|
--
|
||||||
-- * Mark targets for a defined duration.
|
-- * Mark targets for a defined duration.
|
||||||
-- * Updates of laer spot position every 0.2 seconds for moving targets.
|
-- * Updates of laser spot position every 0.25 seconds for moving targets.
|
||||||
-- * Wiggle the spot at the target.
|
-- * Wiggle the spot at the target.
|
||||||
-- * Provide a @{Wrapper.Unit} as a target, instead of a point.
|
-- * Provide a @{Wrapper.Unit} as a target, instead of a point.
|
||||||
-- * Implement a status machine, LaseOn, LaseOff.
|
-- * Implement a status machine, LaseOn, LaseOff.
|
||||||
--
|
--
|
||||||
-- ## 1. SPOT constructor
|
-- ## 1. SPOT constructor
|
||||||
--
|
--
|
||||||
-- * @{#SPOT.New}(..\Presentations\SPOT\Dia2.JPG): Creates a new SPOT object.
|
-- * @{#SPOT.New}(): Creates a new SPOT object.
|
||||||
--
|
--
|
||||||
-- ## 2. SPOT is a FSM
|
-- ## 2. SPOT is a FSM
|
||||||
--
|
--
|
||||||
@@ -217,6 +207,8 @@ do
|
|||||||
|
|
||||||
|
|
||||||
self.Recce = Recce
|
self.Recce = Recce
|
||||||
|
|
||||||
|
self.RecceName = self.Recce:GetName()
|
||||||
|
|
||||||
self.LaseScheduler = SCHEDULER:New( self )
|
self.LaseScheduler = SCHEDULER:New( self )
|
||||||
|
|
||||||
@@ -243,6 +235,9 @@ do
|
|||||||
end
|
end
|
||||||
|
|
||||||
self.Target = Target
|
self.Target = Target
|
||||||
|
|
||||||
|
self.TargetName = Target:GetName()
|
||||||
|
|
||||||
self.LaserCode = LaserCode
|
self.LaserCode = LaserCode
|
||||||
|
|
||||||
self.Lasing = true
|
self.Lasing = true
|
||||||
@@ -302,12 +297,18 @@ do
|
|||||||
function SPOT:OnEventDead(EventData)
|
function SPOT:OnEventDead(EventData)
|
||||||
self:F( { Dead = EventData.IniDCSUnitName, Target = self.Target } )
|
self:F( { Dead = EventData.IniDCSUnitName, Target = self.Target } )
|
||||||
if self.Target then
|
if self.Target then
|
||||||
if EventData.IniDCSUnitName == self.Target:GetName() then
|
if EventData.IniDCSUnitName == self.TargetName then
|
||||||
self:F( {"Target dead ", self.Target:GetName() } )
|
self:F( {"Target dead ", self.TargetName } )
|
||||||
self:Destroyed()
|
self:Destroyed()
|
||||||
self:LaseOff()
|
self:LaseOff()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if self.Recce then
|
||||||
|
if EventData.IniDCSUnitName == self.RecceName then
|
||||||
|
self:F( {"Recce dead ", self.RecceName } )
|
||||||
|
self:LaseOff()
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #SPOT self
|
--- @param #SPOT self
|
||||||
@@ -382,4 +383,4 @@ do
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -555,7 +555,7 @@ function ZONE_BASE:GetZoneMaybe()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Returns the Value of the zone with the given PropertyName, or nil if no matching property exists.
|
--- Returns the Value of the zone with the given PropertyName, or nil if no matching property exists.
|
||||||
-- @param #ZONE_BASE self
|
-- @param #ZONE_BASE self
|
||||||
-- @param #string PropertyName The name of a the TriggerZone Property to be retrieved.
|
-- @param #string PropertyName The name of a the TriggerZone Property to be retrieved.
|
||||||
-- @return #string The Value of the TriggerZone Property with the given PropertyName, or nil if absent.
|
-- @return #string The Value of the TriggerZone Property with the given PropertyName, or nil if absent.
|
||||||
@@ -569,7 +569,7 @@ function ZONE_BASE:GetProperty(PropertyName)
|
|||||||
return self.Properties[PropertyName]
|
return self.Properties[PropertyName]
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Returns the zone Properties table.
|
--- Returns the zone Properties table.
|
||||||
-- @param #ZONE_BASE self
|
-- @param #ZONE_BASE self
|
||||||
-- @return #table The Key:Value table of TriggerZone properties of the zone.
|
-- @return #table The Key:Value table of TriggerZone properties of the zone.
|
||||||
function ZONE_BASE:GetAllProperties()
|
function ZONE_BASE:GetAllProperties()
|
||||||
@@ -927,7 +927,7 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories )
|
|||||||
local ZoneCoord = self:GetCoordinate()
|
local ZoneCoord = self:GetCoordinate()
|
||||||
local ZoneRadius = self:GetRadius()
|
local ZoneRadius = self:GetRadius()
|
||||||
|
|
||||||
self:F({ZoneCoord = ZoneCoord, ZoneRadius = ZoneRadius, ZoneCoordLL = ZoneCoord:ToStringLLDMS()})
|
--self:F({ZoneCoord = ZoneCoord, ZoneRadius = ZoneRadius, ZoneCoordLL = ZoneCoord:ToStringLLDMS()})
|
||||||
|
|
||||||
local SphereSearch = {
|
local SphereSearch = {
|
||||||
id = world.VolumeType.SPHERE,
|
id = world.VolumeType.SPHERE,
|
||||||
@@ -1001,6 +1001,24 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories )
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Remove junk inside the zone using the `world.removeJunk` function.
|
||||||
|
-- @param #ZONE_RADIUS self
|
||||||
|
-- @return #number Number of deleted objects.
|
||||||
|
function ZONE_RADIUS:RemoveJunk()
|
||||||
|
|
||||||
|
local radius=self.Radius
|
||||||
|
local vec3=self:GetVec3()
|
||||||
|
|
||||||
|
local volS = {
|
||||||
|
id = world.VolumeType.SPHERE,
|
||||||
|
params = {point = vec3, radius = radius}
|
||||||
|
}
|
||||||
|
|
||||||
|
local n=world.removeJunk(volS)
|
||||||
|
|
||||||
|
return n
|
||||||
|
end
|
||||||
|
|
||||||
--- Count the number of different coalitions inside the zone.
|
--- Count the number of different coalitions inside the zone.
|
||||||
-- @param #ZONE_RADIUS self
|
-- @param #ZONE_RADIUS self
|
||||||
-- @return #table Table of DCS units and DCS statics inside the zone.
|
-- @return #table Table of DCS units and DCS statics inside the zone.
|
||||||
@@ -2084,13 +2102,10 @@ function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlph
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get the smallest radius encompassing all points of the polygon zone.
|
||||||
--- Get the smallest circular zone encompassing all points points of the polygon zone.
|
|
||||||
-- @param #ZONE_POLYGON_BASE self
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
-- @param #string ZoneName (Optional) Name of the zone. Default is the name of the polygon zone.
|
-- @return #number Radius of the zone in meters.
|
||||||
-- @param #boolean DoNotRegisterZone (Optional) If `true`, zone is not registered.
|
function ZONE_POLYGON_BASE:GetRadius()
|
||||||
-- @return #ZONE_RADIUS The circular zone.
|
|
||||||
function ZONE_POLYGON_BASE:GetZoneRadius(ZoneName, DoNotRegisterZone)
|
|
||||||
|
|
||||||
local center=self:GetVec2()
|
local center=self:GetVec2()
|
||||||
|
|
||||||
@@ -2106,6 +2121,20 @@ function ZONE_POLYGON_BASE:GetZoneRadius(ZoneName, DoNotRegisterZone)
|
|||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return radius
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the smallest circular zone encompassing all points of the polygon zone.
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @param #string ZoneName (Optional) Name of the zone. Default is the name of the polygon zone.
|
||||||
|
-- @param #boolean DoNotRegisterZone (Optional) If `true`, zone is not registered.
|
||||||
|
-- @return #ZONE_RADIUS The circular zone.
|
||||||
|
function ZONE_POLYGON_BASE:GetZoneRadius(ZoneName, DoNotRegisterZone)
|
||||||
|
|
||||||
|
local center=self:GetVec2()
|
||||||
|
|
||||||
|
local radius=self:GetRadius()
|
||||||
|
|
||||||
local zone=ZONE_RADIUS:New(ZoneName or self.ZoneName, center, radius, DoNotRegisterZone)
|
local zone=ZONE_RADIUS:New(ZoneName or self.ZoneName, center, radius, DoNotRegisterZone)
|
||||||
|
|
||||||
@@ -2130,6 +2159,35 @@ function ZONE_POLYGON_BASE:GetZoneQuad(ZoneName, DoNotRegisterZone)
|
|||||||
return zone
|
return zone
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Remove junk inside the zone. Due to DCS limitations, this works only for rectangular zones. So we get the smallest rectangular zone encompassing all points points of the polygon zone.
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @param #number Height Height of the box in meters. Default 1000.
|
||||||
|
-- @return #number Number of removed objects.
|
||||||
|
function ZONE_POLYGON_BASE:RemoveJunk(Height)
|
||||||
|
|
||||||
|
Height=Height or 1000
|
||||||
|
|
||||||
|
local vec2SW, vec2NE=self:GetBoundingVec2()
|
||||||
|
|
||||||
|
local vec3SW={x=vec2SW.x, y=-Height, z=vec2SW.y} --DCS#Vec3
|
||||||
|
local vec3NE={x=vec2NE.x, y= Height, z=vec2NE.y} --DCS#Vec3
|
||||||
|
|
||||||
|
--local coord1=COORDINATE:NewFromVec3(vec3SW):MarkToAll("SW")
|
||||||
|
--local coord1=COORDINATE:NewFromVec3(vec3NE):MarkToAll("NE")
|
||||||
|
|
||||||
|
local volume = {
|
||||||
|
id = world.VolumeType.BOX,
|
||||||
|
params = {
|
||||||
|
min=vec3SW,
|
||||||
|
max=vec3SW
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local n=world.removeJunk(volume)
|
||||||
|
|
||||||
|
return n
|
||||||
|
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 Utilities.Utils#SMOKECOLOR SmokeColor The smoke color.
|
-- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color.
|
||||||
@@ -2913,7 +2971,7 @@ do -- ZONE_ELASTIC
|
|||||||
|
|
||||||
--- Add a set of groups. Positions of the group will be considered as polygon vertices when contructing the convex hull.
|
--- Add a set of groups. Positions of the group will be considered as polygon vertices when contructing the convex hull.
|
||||||
-- @param #ZONE_ELASTIC self
|
-- @param #ZONE_ELASTIC self
|
||||||
-- @param Core.Set#SET_GROUP SetGroup Set of groups.
|
-- @param Core.Set#SET_GROUP GroupSet Set of groups.
|
||||||
-- @return #ZONE_ELASTIC self
|
-- @return #ZONE_ELASTIC self
|
||||||
function ZONE_ELASTIC:AddSetGroup(GroupSet)
|
function ZONE_ELASTIC:AddSetGroup(GroupSet)
|
||||||
|
|
||||||
|
|||||||
@@ -193,21 +193,29 @@ do -- land
|
|||||||
|
|
||||||
--- [Type of surface enumerator](https://wiki.hoggitworld.com/view/DCS_singleton_land)
|
--- [Type of surface enumerator](https://wiki.hoggitworld.com/view/DCS_singleton_land)
|
||||||
-- @type land.SurfaceType
|
-- @type land.SurfaceType
|
||||||
-- @field LAND
|
-- @field LAND Land=1
|
||||||
-- @field SHALLOW_WATER
|
-- @field SHALLOW_WATER Shallow water=2
|
||||||
-- @field WATER
|
-- @field WATER Water=3
|
||||||
-- @field ROAD
|
-- @field ROAD Road=4
|
||||||
-- @field RUNWAY
|
-- @field RUNWAY Runway=5
|
||||||
|
|
||||||
--- Returns altitude MSL of the point.
|
--- Returns the distance from sea level (y-axis) of a given vec2 point.
|
||||||
-- @function [parent=#land] getHeight
|
-- @function [parent=#land] getHeight
|
||||||
-- @param #Vec2 point point on the ground.
|
-- @param #Vec2 point Point on the ground.
|
||||||
-- @return #Distance
|
-- @return #number Height in meters.
|
||||||
|
|
||||||
|
--- Returns the surface height and depth of a point. Useful for checking if the path is deep enough to support a given ship.
|
||||||
|
-- Both values are positive. When checked over water at sea level the first value is always zero.
|
||||||
|
-- When checked over water at altitude, for example the reservoir of the Inguri Dam, the first value is the corresponding altitude the water level is at.
|
||||||
|
-- @function [parent=#land] getSurfaceHeightWithSeabed
|
||||||
|
-- @param #Vec2 point Position where to check.
|
||||||
|
-- @return #number Height in meters.
|
||||||
|
-- @return #number Depth in meters.
|
||||||
|
|
||||||
--- returns surface type at the given point.
|
--- Returns surface type at the given point.
|
||||||
-- @function [parent=#land] getSurfaceType
|
-- @function [parent=#land] getSurfaceType
|
||||||
-- @param #Vec2 point Point on the land.
|
-- @param #Vec2 point Point on the land.
|
||||||
-- @return #land.SurfaceType
|
-- @return #number Enumerator value from `land.SurfaceType` (LAND=1, SHALLOW_WATER=2, WATER=3, ROAD=4, RUNWAY=5)
|
||||||
|
|
||||||
--- [DCS Singleton land](https://wiki.hoggitworld.com/view/DCS_singleton_land)
|
--- [DCS Singleton land](https://wiki.hoggitworld.com/view/DCS_singleton_land)
|
||||||
land = {} --#land
|
land = {} --#land
|
||||||
@@ -427,8 +435,8 @@ do -- Types
|
|||||||
--- Vec3 type is a 3D-vector.
|
--- Vec3 type is a 3D-vector.
|
||||||
-- DCS world has 3-dimensional coordinate system. DCS ground is an infinite plain.
|
-- DCS world has 3-dimensional coordinate system. DCS ground is an infinite plain.
|
||||||
-- @type Vec3
|
-- @type Vec3
|
||||||
-- @field #Distance x is directed to the north
|
-- @field #Distance x is directed to the North
|
||||||
-- @field #Distance z is directed to the east
|
-- @field #Distance z is directed to the East
|
||||||
-- @field #Distance y is directed up
|
-- @field #Distance y is directed up
|
||||||
|
|
||||||
--- Vec2 is a 2D-vector for the ground plane as a reference plane.
|
--- Vec2 is a 2D-vector for the ground plane as a reference plane.
|
||||||
@@ -679,10 +687,11 @@ do -- Weapon
|
|||||||
|
|
||||||
--- Weapon.Category enum that stores weapon categories.
|
--- Weapon.Category enum that stores weapon categories.
|
||||||
-- @type Weapon.Category
|
-- @type Weapon.Category
|
||||||
-- @field SHELL
|
-- @field #number SHELL Shell.
|
||||||
-- @field MISSILE
|
-- @field #number MISSILE Missile
|
||||||
-- @field ROCKET
|
-- @field #number ROCKET Rocket.
|
||||||
-- @field BOMB
|
-- @field #number BOMB Bomb.
|
||||||
|
-- @field #number TORPEDO Torpedo.
|
||||||
|
|
||||||
|
|
||||||
--- Weapon.GuidanceType enum that stores guidance methods. Available only for guided weapon (Weapon.Category.MISSILE and some Weapon.Category.BOMB).
|
--- Weapon.GuidanceType enum that stores guidance methods. Available only for guided weapon (Weapon.Category.MISSILE and some Weapon.Category.BOMB).
|
||||||
@@ -1001,8 +1010,8 @@ do -- Unit
|
|||||||
|
|
||||||
--- Enum that stores aircraft refueling system types.
|
--- Enum that stores aircraft refueling system types.
|
||||||
-- @type Unit.RefuelingSystem
|
-- @type Unit.RefuelingSystem
|
||||||
-- @field BOOM_AND_RECEPTACLE
|
-- @field BOOM_AND_RECEPTACLE Tanker with a boom.
|
||||||
-- @field PROBE_AND_DROGUE
|
-- @field PROBE_AND_DROGUE Tanker with a probe.
|
||||||
|
|
||||||
--- Enum that stores sensor types.
|
--- Enum that stores sensor types.
|
||||||
-- @type Unit.SensorType
|
-- @type Unit.SensorType
|
||||||
|
|||||||
@@ -103,6 +103,7 @@
|
|||||||
-- @field #number coalition The coalition of the arty group.
|
-- @field #number coalition The coalition of the arty group.
|
||||||
-- @field #boolean respawnafterdeath Respawn arty group after all units are dead.
|
-- @field #boolean respawnafterdeath Respawn arty group after all units are dead.
|
||||||
-- @field #number respawndelay Respawn delay in seconds.
|
-- @field #number respawndelay Respawn delay in seconds.
|
||||||
|
-- @field #number dtTrack Time interval in seconds for weapon tracking.
|
||||||
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
||||||
|
|
||||||
--- Enables mission designers easily to assign targets for artillery units. Since the implementation is based on a Finite State Model (FSM), the mission designer can
|
--- Enables mission designers easily to assign targets for artillery units. Since the implementation is based on a Finite State Model (FSM), the mission designer can
|
||||||
@@ -693,7 +694,7 @@ ARTY.db={
|
|||||||
|
|
||||||
--- Arty script version.
|
--- Arty script version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
ARTY.version="1.2.0"
|
ARTY.version="1.3.0"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -801,6 +802,9 @@ function ARTY:New(group, alias)
|
|||||||
else
|
else
|
||||||
self.ismobile=false
|
self.ismobile=false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Set track time interval.
|
||||||
|
self.dtTrack=0.2
|
||||||
|
|
||||||
-- Set speed to 0.7 of maximum.
|
-- Set speed to 0.7 of maximum.
|
||||||
self.Speed=self.SpeedMax * 0.7
|
self.Speed=self.SpeedMax * 0.7
|
||||||
@@ -1497,6 +1501,15 @@ function ARTY:SetStatusInterval(interval)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set time interval for weapon tracking.
|
||||||
|
-- @param #ARTY self
|
||||||
|
-- @param #number interval Time interval in seconds. Default 0.2 seconds.
|
||||||
|
-- @return self
|
||||||
|
function ARTY:SetTrackInterval(interval)
|
||||||
|
self.dtTrack=interval or 0.2
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Set time how it is waited a unit the first shot event happens. If no shot is fired after this time, the task to fire is aborted and the target removed.
|
--- Set time how it is waited a unit the first shot event happens. If no shot is fired after this time, the task to fire is aborted and the target removed.
|
||||||
-- @param #ARTY self
|
-- @param #ARTY self
|
||||||
-- @param #number waittime Time in seconds. Default 300 seconds.
|
-- @param #number waittime Time in seconds. Default 300 seconds.
|
||||||
@@ -2129,6 +2142,95 @@ end
|
|||||||
-- Event Handling
|
-- Event Handling
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Function called during tracking of weapon.
|
||||||
|
-- @param Wrapper.Weapon#WEAPON weapon Weapon object.
|
||||||
|
-- @param #ARTY self ARTY object.
|
||||||
|
-- @param #ARTY.Target target Target of the weapon.
|
||||||
|
function ARTY._FuncTrack(weapon, self, target)
|
||||||
|
|
||||||
|
-- Coordinate and distance to target.
|
||||||
|
local _coord=weapon.coordinate
|
||||||
|
local _dist=_coord:Get2DDistance(target.coord)
|
||||||
|
local _destroyweapon=false
|
||||||
|
|
||||||
|
-- Debug
|
||||||
|
self:T3(self.lid..string.format("ARTY %s weapon to target dist = %d m", self.groupname,_dist))
|
||||||
|
|
||||||
|
if target.weapontype==ARTY.WeaponType.IlluminationShells then
|
||||||
|
|
||||||
|
-- Check if within distace.
|
||||||
|
if _dist<target.radius then
|
||||||
|
|
||||||
|
-- Get random coordinate within certain radius of the target.
|
||||||
|
local _cr=target.coord:GetRandomCoordinateInRadius(target.radius)
|
||||||
|
|
||||||
|
-- Get random altitude over target.
|
||||||
|
local _alt=_cr:GetLandHeight()+math.random(self.illuMinalt, self.illuMaxalt)
|
||||||
|
|
||||||
|
-- Adjust explosion height of coordinate.
|
||||||
|
local _ci=COORDINATE:New(_cr.x,_alt,_cr.z)
|
||||||
|
|
||||||
|
-- Create illumination flare.
|
||||||
|
_ci:IlluminationBomb(self.illuPower)
|
||||||
|
|
||||||
|
-- Destroy actual shell.
|
||||||
|
_destroyweapon=true
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif target.weapontype==ARTY.WeaponType.SmokeShells then
|
||||||
|
|
||||||
|
if _dist<target.radius then
|
||||||
|
|
||||||
|
-- Get random coordinate within a certain radius.
|
||||||
|
local _cr=_coord:GetRandomCoordinateInRadius(_data.target.radius)
|
||||||
|
|
||||||
|
-- Fire smoke at this coordinate.
|
||||||
|
_cr:Smoke(self.smokeColor)
|
||||||
|
|
||||||
|
-- Destroy actual shell.
|
||||||
|
_destroyweapon=true
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
if _destroyweapon then
|
||||||
|
|
||||||
|
self:T2(self.lid..string.format("ARTY %s destroying shell, stopping timer.", self.groupname))
|
||||||
|
|
||||||
|
-- Destroy weapon and stop timer.
|
||||||
|
weapon:Destroy()
|
||||||
|
|
||||||
|
-- No more tracking.
|
||||||
|
weapon.tracking=false
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Function called after impact of weapon.
|
||||||
|
-- @param Wrapper.Weapon#WEAPON weapon Weapon object.
|
||||||
|
-- @param #ARTY self ARTY object.
|
||||||
|
-- @param #ARTY.Target target Target of the weapon.
|
||||||
|
function ARTY._FuncImpact(weapon, self, target)
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:I(self.lid..string.format("ARTY %s weapon NOT ALIVE any more.", self.groupname))
|
||||||
|
|
||||||
|
-- Get impact coordinate.
|
||||||
|
local _impactcoord=weapon:GetImpactCoordinate()
|
||||||
|
|
||||||
|
-- Create a "nuclear" explosion and blast at the impact point.
|
||||||
|
if target.weapontype==ARTY.WeaponType.TacticalNukes then
|
||||||
|
self:T(self.lid..string.format("ARTY %s triggering nuclear explosion in one second.", self.groupname))
|
||||||
|
--SCHEDULER:New(nil, ARTY._NuclearBlast, {self,_impactcoord}, 1.0)
|
||||||
|
self:ScheduleOnce(1.0, ARTY._NuclearBlast, self, _impactcoord)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Eventhandler for shot event.
|
--- Eventhandler for shot event.
|
||||||
-- @param #ARTY self
|
-- @param #ARTY self
|
||||||
-- @param Core.Event#EVENTDATA EventData
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
@@ -2162,128 +2264,32 @@ function ARTY:OnEventShot(EventData)
|
|||||||
self:T(self.lid..text)
|
self:T(self.lid..text)
|
||||||
MESSAGE:New(text, 5):Clear():ToAllIf(self.report or self.Debug)
|
MESSAGE:New(text, 5):Clear():ToAllIf(self.report or self.Debug)
|
||||||
|
|
||||||
-- Last known position of the weapon fired.
|
|
||||||
local _lastpos={x=0, y=0, z=0}
|
|
||||||
|
|
||||||
--- Track the position of the weapon if it is supposed to model a tac nuke, illumination or smoke shell.
|
|
||||||
-- @param #table _weapon
|
|
||||||
local function _TrackWeapon(_data)
|
|
||||||
|
|
||||||
-- When the pcall status returns false the weapon has hit.
|
|
||||||
local _weaponalive,_currpos = pcall(
|
|
||||||
function()
|
|
||||||
return _data.weapon:getPoint()
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Debug
|
|
||||||
self:T3(self.lid..string.format("ARTY %s: Weapon still in air: %s", self.groupname, tostring(_weaponalive)))
|
|
||||||
|
|
||||||
-- Destroy weapon before impact.
|
|
||||||
local _destroyweapon=false
|
|
||||||
|
|
||||||
if _weaponalive then
|
|
||||||
|
|
||||||
-- Update last position.
|
|
||||||
_lastpos={x=_currpos.x, y=_currpos.y, z=_currpos.z}
|
|
||||||
|
|
||||||
-- Coordinate and distance to target.
|
|
||||||
local _coord=COORDINATE:NewFromVec3(_lastpos)
|
|
||||||
local _dist=_coord:Get2DDistance(_data.target.coord)
|
|
||||||
|
|
||||||
-- Debug
|
|
||||||
self:T3(self.lid..string.format("ARTY %s weapon to target dist = %d m", self.groupname,_dist))
|
|
||||||
|
|
||||||
if _data.target.weapontype==ARTY.WeaponType.IlluminationShells then
|
|
||||||
|
|
||||||
-- Check if within distace.
|
|
||||||
if _dist<_data.target.radius then
|
|
||||||
|
|
||||||
-- Get random coordinate within certain radius of the target.
|
|
||||||
local _cr=_data.target.coord:GetRandomCoordinateInRadius(_data.target.radius)
|
|
||||||
|
|
||||||
-- Get random altitude over target.
|
|
||||||
local _alt=_cr:GetLandHeight()+math.random(self.illuMinalt, self.illuMaxalt)
|
|
||||||
|
|
||||||
-- Adjust explosion height of coordinate.
|
|
||||||
local _ci=COORDINATE:New(_cr.x,_alt,_cr.z)
|
|
||||||
|
|
||||||
-- Create illumination flare.
|
|
||||||
_ci:IlluminationBomb(self.illuPower)
|
|
||||||
|
|
||||||
-- Destroy actual shell.
|
|
||||||
_destroyweapon=true
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif _data.target.weapontype==ARTY.WeaponType.SmokeShells then
|
|
||||||
|
|
||||||
if _dist<_data.target.radius then
|
|
||||||
|
|
||||||
-- Get random coordinate within a certain radius.
|
|
||||||
local _cr=_coord:GetRandomCoordinateInRadius(_data.target.radius)
|
|
||||||
|
|
||||||
-- Fire smoke at this coordinate.
|
|
||||||
_cr:Smoke(self.smokeColor)
|
|
||||||
|
|
||||||
-- Destroy actual shell.
|
|
||||||
_destroyweapon=true
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
if _destroyweapon then
|
|
||||||
|
|
||||||
self:T2(self.lid..string.format("ARTY %s destroying shell, stopping timer.", self.groupname))
|
|
||||||
|
|
||||||
-- Destroy weapon and stop timer.
|
|
||||||
_data.weapon:destroy()
|
|
||||||
return nil
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
-- TODO: Make dt input parameter.
|
|
||||||
local dt=0.02
|
|
||||||
|
|
||||||
self:T3(self.lid..string.format("ARTY %s tracking weapon again in %.3f seconds", self.groupname, dt))
|
|
||||||
|
|
||||||
-- Check again in 0.05 seconds.
|
|
||||||
return timer.getTime() + dt
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
-- Get impact coordinate.
|
|
||||||
local _impactcoord=COORDINATE:NewFromVec3(_lastpos)
|
|
||||||
|
|
||||||
self:I(self.lid..string.format("ARTY %s weapon NOT ALIVE any more.", self.groupname))
|
|
||||||
|
|
||||||
-- Create a "nuclear" explosion and blast at the impact point.
|
|
||||||
if _data.target.weapontype==ARTY.WeaponType.TacticalNukes then
|
|
||||||
self:T(self.lid..string.format("ARTY %s triggering nuclear explosion in one second.", self.groupname))
|
|
||||||
SCHEDULER:New(nil, ARTY._NuclearBlast, {self,_impactcoord}, 1.0)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Stop timer.
|
|
||||||
return nil
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Start track the shell if we want to model a tactical nuke.
|
-- Start track the shell if we want to model a tactical nuke.
|
||||||
local _tracknuke = self.currentTarget.weapontype==ARTY.WeaponType.TacticalNukes and self.Nukes>0
|
local _tracknuke = self.currentTarget.weapontype==ARTY.WeaponType.TacticalNukes and self.Nukes>0
|
||||||
local _trackillu = self.currentTarget.weapontype==ARTY.WeaponType.IlluminationShells and self.Nillu>0
|
local _trackillu = self.currentTarget.weapontype==ARTY.WeaponType.IlluminationShells and self.Nillu>0
|
||||||
local _tracksmoke = self.currentTarget.weapontype==ARTY.WeaponType.SmokeShells and self.Nsmoke>0
|
local _tracksmoke = self.currentTarget.weapontype==ARTY.WeaponType.SmokeShells and self.Nsmoke>0
|
||||||
|
|
||||||
|
|
||||||
if _tracknuke or _trackillu or _tracksmoke then
|
if _tracknuke or _trackillu or _tracksmoke then
|
||||||
|
|
||||||
self:T(self.lid..string.format("ARTY %s: Tracking of weapon starts in two seconds.", self.groupname))
|
-- Debug info.
|
||||||
|
self:T(self.lid..string.format("ARTY %s: Tracking of weapon starts in two seconds.", self.groupname))
|
||||||
local _peter={}
|
|
||||||
_peter.weapon=EventData.weapon
|
-- Create a weapon object.
|
||||||
_peter.target=UTILS.DeepCopy(self.currentTarget)
|
local weapon=WEAPON:New(EventData.weapon)
|
||||||
|
|
||||||
timer.scheduleFunction(_TrackWeapon, _peter, timer.getTime() + 2.0)
|
-- Set time step for tracking.
|
||||||
|
weapon:SetTimeStepTrack(self.dtTrack)
|
||||||
|
|
||||||
|
-- Copy target. We need a copy because it might already be overwritten with the next target during flight of weapon.
|
||||||
|
local target=UTILS.DeepCopy(self.currentTarget)
|
||||||
|
|
||||||
|
-- Set callback functions.
|
||||||
|
weapon:SetFuncTrack(ARTY._FuncTrack, self, target)
|
||||||
|
weapon:SetFuncImpact(ARTY._FuncImpact, self, target)
|
||||||
|
|
||||||
|
-- Start tracking in 2 sec (arty ammo should fly a bit).
|
||||||
|
weapon:StartTrack(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get current ammo.
|
-- Get current ammo.
|
||||||
@@ -3931,9 +3937,10 @@ function ARTY:GetAmmo(display)
|
|||||||
return nammo, nshells, nrockets, nmissiles
|
return nammo, nshells, nrockets, nmissiles
|
||||||
end
|
end
|
||||||
|
|
||||||
for _,unit in pairs(units) do
|
for _,_unit in pairs(units) do
|
||||||
|
local unit=_unit --Wrapper.Unit#UNIT
|
||||||
|
|
||||||
if unit and unit:IsAlive() then
|
if unit then
|
||||||
|
|
||||||
-- Output.
|
-- Output.
|
||||||
local text=string.format("ARTY group %s - unit %s:\n", self.groupname, unit:GetName())
|
local text=string.format("ARTY group %s - unit %s:\n", self.groupname, unit:GetName())
|
||||||
|
|||||||
@@ -474,7 +474,7 @@ do -- DETECTION_BASE
|
|||||||
-- @param #string From The From State string.
|
-- @param #string From The From State string.
|
||||||
-- @param #string Event The Event string.
|
-- @param #string Event The Event string.
|
||||||
-- @param #string To The To State string.
|
-- @param #string To The To State string.
|
||||||
-- @param #table DetectedItem The DetectedItem.
|
-- @param #DetectedItem DetectedItem The DetectedItem data structure.
|
||||||
|
|
||||||
self:AddTransition( "*", "Stop", "Stopped" )
|
self:AddTransition( "*", "Stop", "Stopped" )
|
||||||
|
|
||||||
@@ -2478,14 +2478,14 @@ do -- DETECTION_AREAS
|
|||||||
--- DETECTION_AREAS constructor.
|
--- DETECTION_AREAS constructor.
|
||||||
-- @param #DETECTION_AREAS self
|
-- @param #DETECTION_AREAS self
|
||||||
-- @param Core.Set#SET_GROUP DetectionSetGroup The @{Core.Set} of GROUPs in the Forward Air Controller role.
|
-- @param Core.Set#SET_GROUP DetectionSetGroup The @{Core.Set} of GROUPs in the Forward Air Controller role.
|
||||||
-- @param DCS#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target.
|
-- @param #number DetectionZoneRange The range in meters within which targets are grouped upon the first detected target. Default 5000m.
|
||||||
-- @return #DETECTION_AREAS
|
-- @return #DETECTION_AREAS
|
||||||
function DETECTION_AREAS:New( DetectionSetGroup, DetectionZoneRange )
|
function DETECTION_AREAS:New( DetectionSetGroup, DetectionZoneRange )
|
||||||
|
|
||||||
-- Inherits from DETECTION_BASE
|
-- Inherits from DETECTION_BASE
|
||||||
local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) )
|
local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) )
|
||||||
|
|
||||||
self.DetectionZoneRange = DetectionZoneRange
|
self.DetectionZoneRange = DetectionZoneRange or 5000
|
||||||
|
|
||||||
self._SmokeDetectedUnits = false
|
self._SmokeDetectedUnits = false
|
||||||
self._FlareDetectedUnits = false
|
self._FlareDetectedUnits = false
|
||||||
@@ -2988,4 +2988,3 @@ do -- DETECTION_AREAS
|
|||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
--- FOX class.
|
--- FOX class.
|
||||||
-- @type FOX
|
-- @type FOX
|
||||||
-- @field #string ClassName Name of the class.
|
-- @field #string ClassName Name of the class.
|
||||||
|
-- @field #number verbose Verbosity level.
|
||||||
-- @field #boolean Debug Debug mode. Messages to all about status.
|
-- @field #boolean Debug Debug mode. Messages to all about status.
|
||||||
-- @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 #table menuadded Table of groups the menu was added for.
|
-- @field #table menuadded Table of groups the menu was added for.
|
||||||
@@ -124,6 +125,7 @@
|
|||||||
-- @field #FOX
|
-- @field #FOX
|
||||||
FOX = {
|
FOX = {
|
||||||
ClassName = "FOX",
|
ClassName = "FOX",
|
||||||
|
verbose = 0,
|
||||||
Debug = false,
|
Debug = false,
|
||||||
lid = nil,
|
lid = nil,
|
||||||
menuadded = {},
|
menuadded = {},
|
||||||
@@ -168,7 +170,7 @@ FOX = {
|
|||||||
|
|
||||||
--- Missile data table.
|
--- Missile data table.
|
||||||
-- @type FOX.MissileData
|
-- @type FOX.MissileData
|
||||||
-- @field Wrapper.Unit#UNIT weapon Missile weapon unit.
|
-- @field DCS#Weapon weapon Missile weapon object.
|
||||||
-- @field #boolean active If true the missile is active.
|
-- @field #boolean active If true the missile is active.
|
||||||
-- @field #string missileType Type of missile.
|
-- @field #string missileType Type of missile.
|
||||||
-- @field #string missileName Name of missile.
|
-- @field #string missileName Name of missile.
|
||||||
@@ -185,6 +187,8 @@ FOX = {
|
|||||||
-- @field #string targetName Name of the target unit or "unknown".
|
-- @field #string targetName Name of the target unit or "unknown".
|
||||||
-- @field #string targetOrig Name of the "original" target, i.e. the one right after launched.
|
-- @field #string targetOrig Name of the "original" target, i.e. the one right after launched.
|
||||||
-- @field #FOX.PlayerData targetPlayer Player that was targeted or nil.
|
-- @field #FOX.PlayerData targetPlayer Player that was targeted or nil.
|
||||||
|
-- @field Core.Point#COORDINATE missileCoord Missile coordinate during tracking.
|
||||||
|
-- @field Wrapper.Weapon#WEAPON Weapon Weapon object.
|
||||||
|
|
||||||
--- Main radio menu on group level.
|
--- Main radio menu on group level.
|
||||||
-- @field #table MenuF10 Root menu table on group level.
|
-- @field #table MenuF10 Root menu table on group level.
|
||||||
@@ -196,7 +200,7 @@ FOX.MenuF10Root=nil
|
|||||||
|
|
||||||
--- FOX class version.
|
--- FOX class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
FOX.version="0.6.1"
|
FOX.version="0.8.0"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- ToDo list
|
-- ToDo list
|
||||||
@@ -500,6 +504,7 @@ function FOX:SetDisableF10Menu()
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Enable F10 menu for all players.
|
--- Enable F10 menu for all players.
|
||||||
-- @param #FOX self
|
-- @param #FOX self
|
||||||
-- @return #FOX self
|
-- @return #FOX self
|
||||||
@@ -510,6 +515,15 @@ function FOX:SetEnableF10Menu()
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set verbosity level.
|
||||||
|
-- @param #FOX self
|
||||||
|
-- @param #number VerbosityLevel Level of output (higher=more). Default 0.
|
||||||
|
-- @return #FOX self
|
||||||
|
function FOX:SetVerbosity(VerbosityLevel)
|
||||||
|
self.verbose=VerbosityLevel or 0
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Set default player setting for missile destruction.
|
--- Set default player setting for missile destruction.
|
||||||
-- @param #FOX self
|
-- @param #FOX self
|
||||||
-- @param #boolean switch If true missiles are destroyed. If false/nil missiles are not destroyed.
|
-- @param #boolean switch If true missiles are destroyed. If false/nil missiles are not destroyed.
|
||||||
@@ -605,7 +619,9 @@ function FOX:onafterStatus(From, Event, To)
|
|||||||
local clock=UTILS.SecondsToClock(time)
|
local clock=UTILS.SecondsToClock(time)
|
||||||
|
|
||||||
-- Status.
|
-- Status.
|
||||||
self:I(self.lid..string.format("Missile trainer status %s: %s", clock, fsmstate))
|
if self.verbose>=1 then
|
||||||
|
self:I(self.lid..string.format("Missile trainer status %s: %s", clock, fsmstate))
|
||||||
|
end
|
||||||
|
|
||||||
-- Check missile status.
|
-- Check missile status.
|
||||||
self:_CheckMissileStatus()
|
self:_CheckMissileStatus()
|
||||||
@@ -713,7 +729,9 @@ function FOX:_CheckMissileStatus()
|
|||||||
if #self.missiles==0 then
|
if #self.missiles==0 then
|
||||||
text=text.." none"
|
text=text.." none"
|
||||||
end
|
end
|
||||||
self:I(self.lid..text)
|
if self.verbose>=2 then
|
||||||
|
self:I(self.lid..text)
|
||||||
|
end
|
||||||
|
|
||||||
-- Remove inactive missiles.
|
-- Remove inactive missiles.
|
||||||
for i=#self.missiles,1,-1 do
|
for i=#self.missiles,1,-1 do
|
||||||
@@ -743,7 +761,7 @@ function FOX:_IsProtected(targetunit)
|
|||||||
if targetgroup then
|
if targetgroup then
|
||||||
local targetname=targetgroup:GetName()
|
local targetname=targetgroup:GetName()
|
||||||
|
|
||||||
for _,_group in pairs(self.protectedset:GetSetObjects()) do
|
for _,_group in pairs(self.protectedset:GetSet()) do
|
||||||
local group=_group --Wrapper.Group#GROUP
|
local group=_group --Wrapper.Group#GROUP
|
||||||
|
|
||||||
if group then
|
if group then
|
||||||
@@ -762,6 +780,277 @@ function FOX:_IsProtected(targetunit)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Function called from weapon tracking.
|
||||||
|
-- @param Wrapper.Weapon#WEAPON weapon Weapon object.
|
||||||
|
-- @param #FOX self FOX object.
|
||||||
|
-- @param #FOX.MissileData missile Fired missile
|
||||||
|
function FOX._FuncTrack(weapon, self, missile)
|
||||||
|
|
||||||
|
-- Missile coordinate.
|
||||||
|
local missileCoord= missile.missileCoord:UpdateFromVec3(weapon.vec3) --COORDINATE:NewFromVec3(_lastBombPos)
|
||||||
|
|
||||||
|
-- Missile velocity in m/s.
|
||||||
|
local missileVelocity=weapon:GetSpeed() --UTILS.VecNorm(_ordnance:getVelocity())
|
||||||
|
|
||||||
|
-- Update missile target if necessary.
|
||||||
|
self:GetMissileTarget(missile)
|
||||||
|
|
||||||
|
-- Target unit of the missile.
|
||||||
|
local target=nil --Wrapper.Unit#UNIT
|
||||||
|
|
||||||
|
if missile.targetUnit then
|
||||||
|
|
||||||
|
-----------------------------------
|
||||||
|
-- Missile has a specific target --
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
if missile.targetPlayer then
|
||||||
|
-- Target is a player.
|
||||||
|
if missile.targetPlayer.destroy==true then
|
||||||
|
target=missile.targetUnit
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Check if unit is protected.
|
||||||
|
if self:_IsProtected(missile.targetUnit) then
|
||||||
|
target=missile.targetUnit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
------------------------------------
|
||||||
|
-- Missile has NO specific target --
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
-- TODO: This might cause a problem with wingman. Even if the shooter itself is excluded from the check, it's wingmen are not.
|
||||||
|
-- That would trigger the distance check right after missile launch if things to wrong.
|
||||||
|
--
|
||||||
|
-- Possible solutions:
|
||||||
|
-- * Time check: enable this check after X seconds after missile was fired. What is X?
|
||||||
|
-- * Coalition check. But would not work in training situations where blue on blue is valid!
|
||||||
|
-- * At least enable it for surface-to-air missiles.
|
||||||
|
|
||||||
|
local function _GetTarget(_unit)
|
||||||
|
local unit=_unit --Wrapper.Unit#UNIT
|
||||||
|
|
||||||
|
-- Player position.
|
||||||
|
local playerCoord=unit:GetCoordinate()
|
||||||
|
|
||||||
|
-- Distance.
|
||||||
|
local dist=missileCoord:Get3DDistance(playerCoord)
|
||||||
|
|
||||||
|
-- Update mindist if necessary. Only include players in range of missile + 50% safety margin.
|
||||||
|
if dist<=self.explosiondist then
|
||||||
|
return unit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Distance to closest player.
|
||||||
|
local mindist=nil
|
||||||
|
|
||||||
|
-- Loop over players.
|
||||||
|
for _,_player in pairs(self.players) do
|
||||||
|
local player=_player --#FOX.PlayerData
|
||||||
|
|
||||||
|
-- Check that player was not the one who launched the missile.
|
||||||
|
if player.unitname~=missile.shooterName then
|
||||||
|
|
||||||
|
-- Player position.
|
||||||
|
local playerCoord=player.unit:GetCoordinate()
|
||||||
|
|
||||||
|
-- Distance.
|
||||||
|
local dist=missileCoord:Get3DDistance(playerCoord)
|
||||||
|
|
||||||
|
-- Distance from shooter to player.
|
||||||
|
local Dshooter2player=playerCoord:Get3DDistance(missile.shotCoord)
|
||||||
|
|
||||||
|
-- Update mindist if necessary. Only include players in range of missile + 50% safety margin.
|
||||||
|
if (mindist==nil or dist<mindist) and (Dshooter2player<=missile.missileRange*1.5 or dist<=self.explosiondist) then
|
||||||
|
mindist=dist
|
||||||
|
target=player.unit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.protectedset then
|
||||||
|
|
||||||
|
-- Distance to closest protected unit.
|
||||||
|
mindist=nil
|
||||||
|
|
||||||
|
for _,_group in pairs(self.protectedset:GetSet()) do
|
||||||
|
local group=_group --Wrapper.Group#GROUP
|
||||||
|
for _,_unit in pairs(group:GetUnits()) do
|
||||||
|
local unit=_unit --Wrapper.Unit#UNIT
|
||||||
|
|
||||||
|
if unit and unit:IsAlive() then
|
||||||
|
|
||||||
|
-- Check that player was not the one who launched the missile.
|
||||||
|
if unit:GetName()~=missile.shooterName then
|
||||||
|
|
||||||
|
-- Player position.
|
||||||
|
local playerVec3=unit:GetVec3()
|
||||||
|
|
||||||
|
-- Distance.
|
||||||
|
local dist=missileCoord:Get3DDistance(playerVec3)
|
||||||
|
|
||||||
|
-- Distance from shooter to player.
|
||||||
|
local Dshooter2player=missile.shotCoord:Get3DDistance(playerVec3)
|
||||||
|
|
||||||
|
-- Update mindist if necessary. Only include players in range of missile + 50% safety margin.
|
||||||
|
if (mindist==nil or dist<mindist) and (Dshooter2player<=missile.missileRange*1.5 or dist<=self.explosiondist) then
|
||||||
|
mindist=dist
|
||||||
|
target=unit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if target then
|
||||||
|
self:T(self.lid..string.format("Missile %s with NO explicit target got closest unit to missile as target %s. Dist=%s m", missile.missileType, target:GetName(), tostring(mindist)))
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if missile has a valid target.
|
||||||
|
if target then
|
||||||
|
|
||||||
|
-- Target coordinate.
|
||||||
|
local targetVec3=target:GetVec3() --target:GetCoordinate()
|
||||||
|
|
||||||
|
-- Distance from missile to target.
|
||||||
|
local distance=missileCoord:Get3DDistance(targetVec3)
|
||||||
|
|
||||||
|
-- Distance missile to shooter.
|
||||||
|
local distShooter=nil
|
||||||
|
if missile.shooterUnit and missile.shooterUnit:IsAlive() then
|
||||||
|
distShooter=missileCoord:Get3DDistance(missile.shooterUnit:GetVec3())
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Debug output.
|
||||||
|
if self.Debug then
|
||||||
|
local bearing=missileCoord:HeadingTo(targetVec3)
|
||||||
|
local eta=distance/missileVelocity
|
||||||
|
-- Debug distance check.
|
||||||
|
self:I(self.lid..string.format("Missile %s Target %s: Distance = %.1f m, v=%.1f m/s, bearing=%03d°, ETA=%.1f sec", missile.missileType, target:GetName(), distance, missileVelocity, bearing, eta))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Distroy missile if it's getting too close.
|
||||||
|
local destroymissile=distance<=self.explosiondist
|
||||||
|
|
||||||
|
-- Check BIG missiles.
|
||||||
|
if self.explosiondist2 and distance<=self.explosiondist2 and not destroymissile then
|
||||||
|
destroymissile=missile.explosive>=self.bigmissilemass
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If missile is 150 m from target ==> destroy missile if in safe zone.
|
||||||
|
if destroymissile and self:_CheckCoordSafe(targetVec3) then
|
||||||
|
|
||||||
|
-- Destroy missile.
|
||||||
|
self:I(self.lid..string.format("Destroying missile %s(%s) fired by %s aimed at %s [player=%s] at distance %.1f m",
|
||||||
|
missile.missileType, missile.missileName, missile.shooterName, target:GetName(), tostring(missile.targetPlayer~=nil), distance))
|
||||||
|
weapon:Destroy()
|
||||||
|
|
||||||
|
-- Missile is not active any more.
|
||||||
|
missile.active=false
|
||||||
|
|
||||||
|
-- Debug smoke.
|
||||||
|
if self.Debug then
|
||||||
|
missileCoord:SmokeRed()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Create event.
|
||||||
|
self:MissileDestroyed(missile)
|
||||||
|
|
||||||
|
-- Little explosion for the visual effect.
|
||||||
|
if self.explosionpower>0 and distance>50 and (distShooter==nil or (distShooter and distShooter>50)) then
|
||||||
|
missileCoord:Explosion(self.explosionpower)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Target was a player.
|
||||||
|
if missile.targetPlayer then
|
||||||
|
|
||||||
|
-- Message to target.
|
||||||
|
local text=string.format("Destroying missile. %s", self:_DeadText())
|
||||||
|
MESSAGE:New(text, 10):ToGroup(target:GetGroup())
|
||||||
|
|
||||||
|
-- Increase dead counter.
|
||||||
|
missile.targetPlayer.dead=missile.targetPlayer.dead+1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- We could disable the tracking here but then the impact function would not be called.
|
||||||
|
--weapon.tracking=false
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
-- Time step.
|
||||||
|
local dt=1.0
|
||||||
|
if distance>50000 then
|
||||||
|
-- > 50 km
|
||||||
|
dt=self.dt50 --=5.0
|
||||||
|
elseif distance>10000 then
|
||||||
|
-- 10-50 km
|
||||||
|
dt=self.dt10 --=1.0
|
||||||
|
elseif distance>5000 then
|
||||||
|
-- 5-10 km
|
||||||
|
dt=self.dt05 --0.5
|
||||||
|
elseif distance>1000 then
|
||||||
|
-- 1-5 km
|
||||||
|
dt=self.dt01 --0.1
|
||||||
|
else
|
||||||
|
-- < 1 km
|
||||||
|
dt=self.dt00 --0.01
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set time step.
|
||||||
|
weapon:SetTimeStepTrack(dt)
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
-- No current target.
|
||||||
|
self:T(self.lid..string.format("Missile %s(%s) fired by %s has no current target. Checking back in 0.1 sec.", missile.missileType, missile.missileName, missile.shooterName))
|
||||||
|
weapon:SetTimeStepTrack(0.1)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Callback function on impact or destroy otherwise.
|
||||||
|
-- @param Wrapper.Weapon#WEAPON weapon Weapon object.
|
||||||
|
-- @param #FOX self FOX object.
|
||||||
|
-- @param #FOX.MissileData missile Fired missile.
|
||||||
|
function FOX._FuncImpact(weapon, self, missile)
|
||||||
|
|
||||||
|
if missile.targetPlayer then
|
||||||
|
|
||||||
|
-- Get human player.
|
||||||
|
local player=missile.targetPlayer
|
||||||
|
|
||||||
|
-- Check for player and distance < 10 km.
|
||||||
|
if player and player.unit:IsAlive() then -- and missileCoord and player.unit:GetCoordinate():Get3DDistance(missileCoord)<10*1000 then
|
||||||
|
local text=string.format("Missile defeated. Well done, %s!", player.name)
|
||||||
|
MESSAGE:New(text, 10):ToClient(player.client)
|
||||||
|
|
||||||
|
-- Increase defeated counter.
|
||||||
|
player.defeated=player.defeated+1
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Missile is not active any more.
|
||||||
|
missile.active=false
|
||||||
|
|
||||||
|
--Terminate the timer.
|
||||||
|
self:T(FOX.lid..string.format("Terminating missile track timer."))
|
||||||
|
weapon.tracking=false
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
--- Missle launch event.
|
--- Missle launch event.
|
||||||
-- @param #FOX self
|
-- @param #FOX self
|
||||||
-- @param #string From From state.
|
-- @param #string From From state.
|
||||||
@@ -818,304 +1107,19 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
|
|||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Init missile position.
|
-- Set callback function for tracking.
|
||||||
local _lastBombPos = {x=0,y=0,z=0}
|
missile.Weapon:SetFuncTrack(FOX._FuncTrack, self, missile)
|
||||||
|
|
||||||
-- Missile coordinate.
|
-- Set callback function for impact.
|
||||||
local missileCoord = nil --Core.Point#COORDINATE
|
missile.Weapon:SetFuncImpact(FOX._FuncImpact, self, missile)
|
||||||
|
|
||||||
-- Target unit of the missile.
|
|
||||||
local target=nil --Wrapper.Unit#UNIT
|
|
||||||
|
|
||||||
--- Function monitoring the position of a bomb until impact.
|
|
||||||
local function trackMissile(_ordnance)
|
|
||||||
|
|
||||||
-- When the pcall returns a failure the weapon has hit.
|
|
||||||
local _status,_bombPos = pcall(
|
|
||||||
function()
|
|
||||||
return _ordnance:getPoint()
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Check if status is not nil. If so, we have a valid point.
|
|
||||||
if _status then
|
|
||||||
|
|
||||||
----------------------------------------------
|
|
||||||
-- Still in the air. Remember this position --
|
|
||||||
----------------------------------------------
|
|
||||||
|
|
||||||
-- Missile position.
|
|
||||||
_lastBombPos = {x=_bombPos.x, y=_bombPos.y, z=_bombPos.z}
|
|
||||||
|
|
||||||
-- Missile coordinate.
|
|
||||||
missileCoord=COORDINATE:NewFromVec3(_lastBombPos)
|
|
||||||
|
|
||||||
-- Missile velocity in m/s.
|
|
||||||
local missileVelocity=UTILS.VecNorm(_ordnance:getVelocity())
|
|
||||||
|
|
||||||
-- Update missile target if necessary.
|
|
||||||
self:GetMissileTarget(missile)
|
|
||||||
|
|
||||||
if missile.targetUnit then
|
|
||||||
|
|
||||||
-----------------------------------
|
|
||||||
-- Missile has a specific target --
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
if missile.targetPlayer then
|
|
||||||
-- Target is a player.
|
|
||||||
if missile.targetPlayer.destroy==true then
|
|
||||||
target=missile.targetUnit
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- Check if unit is protected.
|
|
||||||
if self:_IsProtected(missile.targetUnit) then
|
|
||||||
target=missile.targetUnit
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
------------------------------------
|
|
||||||
-- Missile has NO specific target --
|
|
||||||
------------------------------------
|
|
||||||
|
|
||||||
-- TODO: This might cause a problem with wingman. Even if the shooter itself is excluded from the check, it's wingmen are not.
|
|
||||||
-- That would trigger the distance check right after missile launch if things to wrong.
|
|
||||||
--
|
|
||||||
-- Possible solutions:
|
|
||||||
-- * Time check: enable this check after X seconds after missile was fired. What is X?
|
|
||||||
-- * Coalition check. But would not work in training situations where blue on blue is valid!
|
|
||||||
-- * At least enable it for surface-to-air missiles.
|
|
||||||
|
|
||||||
local function _GetTarget(_unit)
|
|
||||||
local unit=_unit --Wrapper.Unit#UNIT
|
|
||||||
|
|
||||||
-- Player position.
|
|
||||||
local playerCoord=unit:GetCoordinate()
|
|
||||||
|
|
||||||
-- Distance.
|
|
||||||
local dist=missileCoord:Get3DDistance(playerCoord)
|
|
||||||
|
|
||||||
-- Update mindist if necessary. Only include players in range of missile + 50% safety margin.
|
|
||||||
if dist<=self.explosiondist then
|
|
||||||
return unit
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Distance to closest player.
|
|
||||||
local mindist=nil
|
|
||||||
|
|
||||||
-- Loop over players.
|
|
||||||
for _,_player in pairs(self.players) do
|
|
||||||
local player=_player --#FOX.PlayerData
|
|
||||||
|
|
||||||
-- Check that player was not the one who launched the missile.
|
|
||||||
if player.unitname~=missile.shooterName then
|
|
||||||
|
|
||||||
-- Player position.
|
|
||||||
local playerCoord=player.unit:GetCoordinate()
|
|
||||||
|
|
||||||
-- Distance.
|
|
||||||
local dist=missileCoord:Get3DDistance(playerCoord)
|
|
||||||
|
|
||||||
-- Distance from shooter to player.
|
|
||||||
local Dshooter2player=playerCoord:Get3DDistance(missile.shotCoord)
|
|
||||||
|
|
||||||
-- Update mindist if necessary. Only include players in range of missile + 50% safety margin.
|
|
||||||
if (mindist==nil or dist<mindist) and (Dshooter2player<=missile.missileRange*1.5 or dist<=self.explosiondist) then
|
|
||||||
mindist=dist
|
|
||||||
target=player.unit
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.protectedset then
|
|
||||||
|
|
||||||
-- Distance to closest protected unit.
|
|
||||||
mindist=nil
|
|
||||||
|
|
||||||
for _,_group in pairs(self.protectedset:GetSet()) do
|
|
||||||
local group=_group --Wrapper.Group#GROUP
|
|
||||||
for _,_unit in pairs(group:GetUnits()) do
|
|
||||||
local unit=_unit --Wrapper.Unit#UNIT
|
|
||||||
|
|
||||||
if unit and unit:IsAlive() then
|
|
||||||
|
|
||||||
-- Check that player was not the one who launched the missile.
|
|
||||||
if unit:GetName()~=missile.shooterName then
|
|
||||||
|
|
||||||
-- Player position.
|
|
||||||
local playerCoord=unit:GetCoordinate()
|
|
||||||
|
|
||||||
-- Distance.
|
|
||||||
local dist=missileCoord:Get3DDistance(playerCoord)
|
|
||||||
|
|
||||||
-- Distance from shooter to player.
|
|
||||||
local Dshooter2player=playerCoord:Get3DDistance(missile.shotCoord)
|
|
||||||
|
|
||||||
-- Update mindist if necessary. Only include players in range of missile + 50% safety margin.
|
|
||||||
if (mindist==nil or dist<mindist) and (Dshooter2player<=missile.missileRange*1.5 or dist<=self.explosiondist) then
|
|
||||||
mindist=dist
|
|
||||||
target=unit
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if target then
|
|
||||||
self:T(self.lid..string.format("Missile %s with NO explicit target got closest unit to missile as target %s. Dist=%s m", missile.missileType, target:GetName(), tostring(mindist)))
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check if missile has a valid target.
|
|
||||||
if target then
|
|
||||||
|
|
||||||
-- Target coordinate.
|
|
||||||
local targetCoord=target:GetCoordinate()
|
|
||||||
|
|
||||||
-- Distance from missile to target.
|
|
||||||
local distance=missileCoord:Get3DDistance(targetCoord)
|
|
||||||
|
|
||||||
-- Distance missile to shooter.
|
|
||||||
local distShooter=nil
|
|
||||||
if missile.shooterUnit and missile.shooterUnit:IsAlive() then
|
|
||||||
distShooter=missileCoord:Get3DDistance(missile.shooterUnit:GetCoordinate())
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- Debug output.
|
|
||||||
if self.Debug then
|
|
||||||
local bearing=targetCoord:HeadingTo(missileCoord)
|
|
||||||
local eta=distance/missileVelocity
|
|
||||||
|
|
||||||
-- Debug distance check.
|
|
||||||
self:I(self.lid..string.format("Missile %s Target %s: Distance = %.1f m, v=%.1f m/s, bearing=%03d°, ETA=%.1f sec", missile.missileType, target:GetName(), distance, missileVelocity, bearing, eta))
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Distroy missile if it's getting too close.
|
|
||||||
local destroymissile=distance<=self.explosiondist
|
|
||||||
|
|
||||||
-- Check BIG missiles.
|
|
||||||
if self.explosiondist2 and distance<=self.explosiondist2 and not destroymissile then
|
|
||||||
destroymissile=missile.explosive>=self.bigmissilemass
|
|
||||||
end
|
|
||||||
|
|
||||||
-- If missile is 150 m from target ==> destroy missile if in safe zone.
|
|
||||||
if destroymissile and self:_CheckCoordSafe(targetCoord) then
|
|
||||||
|
|
||||||
-- Destroy missile.
|
|
||||||
self:I(self.lid..string.format("Destroying missile %s(%s) fired by %s aimed at %s [player=%s] at distance %.1f m",
|
|
||||||
missile.missileType, missile.missileName, missile.shooterName, target:GetName(), tostring(missile.targetPlayer~=nil), distance))
|
|
||||||
_ordnance:destroy()
|
|
||||||
|
|
||||||
-- Missile is not active any more.
|
|
||||||
missile.active=false
|
|
||||||
|
|
||||||
-- Debug smoke.
|
|
||||||
if self.Debug then
|
|
||||||
missileCoord:SmokeRed()
|
|
||||||
targetCoord:SmokeGreen()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Create event.
|
|
||||||
self:MissileDestroyed(missile)
|
|
||||||
|
|
||||||
-- Little explosion for the visual effect.
|
|
||||||
if self.explosionpower>0 and distance>50 and (distShooter==nil or (distShooter and distShooter>50)) then
|
|
||||||
missileCoord:Explosion(self.explosionpower)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Target was a player.
|
|
||||||
if missile.targetPlayer then
|
|
||||||
|
|
||||||
-- Message to target.
|
|
||||||
local text=string.format("Destroying missile. %s", self:_DeadText())
|
|
||||||
MESSAGE:New(text, 10):ToGroup(target:GetGroup())
|
|
||||||
|
|
||||||
-- Increase dead counter.
|
|
||||||
missile.targetPlayer.dead=missile.targetPlayer.dead+1
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Terminate timer.
|
|
||||||
return nil
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
-- Time step.
|
|
||||||
local dt=1.0
|
|
||||||
if distance>50000 then
|
|
||||||
-- > 50 km
|
|
||||||
dt=self.dt50 --=5.0
|
|
||||||
elseif distance>10000 then
|
|
||||||
-- 10-50 km
|
|
||||||
dt=self.dt10 --=1.0
|
|
||||||
elseif distance>5000 then
|
|
||||||
-- 5-10 km
|
|
||||||
dt=self.dt05 --0.5
|
|
||||||
elseif distance>1000 then
|
|
||||||
-- 1-5 km
|
|
||||||
dt=self.dt01 --0.1
|
|
||||||
else
|
|
||||||
-- < 1 km
|
|
||||||
dt=self.dt00 --0.01
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check again in dt seconds.
|
|
||||||
return timer.getTime()+dt
|
|
||||||
end
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
-- Destroy missile.
|
|
||||||
self:T(self.lid..string.format("Missile %s(%s) fired by %s has no current target. Checking back in 0.1 sec.", missile.missileType, missile.missileName, missile.shooterName))
|
|
||||||
return timer.getTime()+0.1
|
|
||||||
|
|
||||||
-- No target ==> terminate timer.
|
|
||||||
--return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
-------------------------------------
|
|
||||||
-- Missile does not exist any more --
|
|
||||||
-------------------------------------
|
|
||||||
|
|
||||||
if target then
|
|
||||||
|
|
||||||
-- Get human player.
|
|
||||||
local player=self:_GetPlayerFromUnit(target)
|
|
||||||
|
|
||||||
-- Check for player and distance < 10 km.
|
|
||||||
if player and player.unit:IsAlive() then -- and missileCoord and player.unit:GetCoordinate():Get3DDistance(missileCoord)<10*1000 then
|
|
||||||
local text=string.format("Missile defeated. Well done, %s!", player.name)
|
|
||||||
MESSAGE:New(text, 10):ToClient(player.client)
|
|
||||||
|
|
||||||
-- Increase defeated counter.
|
|
||||||
player.defeated=player.defeated+1
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Missile is not active any more.
|
|
||||||
missile.active=false
|
|
||||||
|
|
||||||
--Terminate the timer.
|
|
||||||
self:T(FOX.lid..string.format("Terminating missile track timer."))
|
|
||||||
return nil
|
|
||||||
|
|
||||||
end -- _status check
|
|
||||||
|
|
||||||
end -- end function trackBomb
|
|
||||||
|
|
||||||
-- Weapon is not yet "alife" just yet. Start timer with a little delay.
|
-- Weapon is not yet "alife" just yet. Start timer with a little delay.
|
||||||
self:T(FOX.lid..string.format("Tracking of missile starts in 0.0001 seconds."))
|
self:T(FOX.lid..string.format("Tracking of missile starts in 0.0001 seconds."))
|
||||||
timer.scheduleFunction(trackMissile, missile.weapon, timer.getTime()+0.0001)
|
--timer.scheduleFunction(trackMissile, missile.weapon, timer.getTime()+0.0001)
|
||||||
|
missile.Weapon:StartTrack(0.0001)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1246,30 +1250,29 @@ end
|
|||||||
-- @param Core.Event#EVENTDATA EventData
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
function FOX:OnEventShot(EventData)
|
function FOX:OnEventShot(EventData)
|
||||||
self:T2({eventshot=EventData})
|
self:T2({eventshot=EventData})
|
||||||
|
|
||||||
|
-- Nil checks.
|
||||||
|
if EventData.Weapon==nil or EventData.IniDCSUnit==nil or EventData.weapon==nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
if EventData.Weapon==nil then
|
-- Create a weapon object.
|
||||||
return
|
local weapon=WEAPON:New(EventData.weapon)
|
||||||
end
|
|
||||||
if EventData.IniDCSUnit==nil then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Weapon data.
|
-- Weapon data.
|
||||||
local _weapon = EventData.WeaponName
|
local _weapon = weapon:GetTypeName()
|
||||||
local _target = EventData.Weapon:getTarget()
|
local _target = EventData.Weapon:getTarget()
|
||||||
local _targetName = "unknown"
|
local _targetName = "unknown"
|
||||||
local _targetUnit = nil --Wrapper.Unit#UNIT
|
local _targetUnit = nil --Wrapper.Unit#UNIT
|
||||||
|
|
||||||
-- Weapon descriptor.
|
-- Weapon descriptor.
|
||||||
local desc=EventData.Weapon:getDesc()
|
local desc=weapon.desc
|
||||||
self:T2({desc=desc})
|
self:T2({desc=desc})
|
||||||
|
|
||||||
-- Weapon category: 0=Shell, 1=Missile, 2=Rocket, 3=BOMB
|
|
||||||
local weaponcategory=desc.category
|
|
||||||
|
|
||||||
-- Missile category: 1=AAM, 2=SAM, 6=OTHER
|
-- Missile category: 1=AAM, 2=SAM, 6=OTHER
|
||||||
local missilecategory=desc.missileCategory
|
local missilecategory=desc.missileCategory
|
||||||
|
|
||||||
|
-- Missile range.
|
||||||
local missilerange=nil
|
local missilerange=nil
|
||||||
if missilecategory then
|
if missilecategory then
|
||||||
missilerange=desc.rangeMaxAltMax
|
missilerange=desc.rangeMaxAltMax
|
||||||
@@ -1279,8 +1282,8 @@ function FOX:OnEventShot(EventData)
|
|||||||
self:T2(FOX.lid.."EVENT SHOT: FOX")
|
self:T2(FOX.lid.."EVENT SHOT: FOX")
|
||||||
self:T2(FOX.lid..string.format("EVENT SHOT: Ini unit = %s", tostring(EventData.IniUnitName)))
|
self:T2(FOX.lid..string.format("EVENT SHOT: Ini unit = %s", tostring(EventData.IniUnitName)))
|
||||||
self:T2(FOX.lid..string.format("EVENT SHOT: Ini group = %s", tostring(EventData.IniGroupName)))
|
self:T2(FOX.lid..string.format("EVENT SHOT: Ini group = %s", tostring(EventData.IniGroupName)))
|
||||||
self:T2(FOX.lid..string.format("EVENT SHOT: Weapon type = %s", tostring(_weapon)))
|
self:T2(FOX.lid..string.format("EVENT SHOT: Weapon type = %s", tostring(weapon:GetTypeName())))
|
||||||
self:T2(FOX.lid..string.format("EVENT SHOT: Weapon categ = %s", tostring(weaponcategory)))
|
self:T2(FOX.lid..string.format("EVENT SHOT: Weapon categ = %s", tostring(weapon:GetCategory())))
|
||||||
self:T2(FOX.lid..string.format("EVENT SHOT: Missil categ = %s", tostring(missilecategory)))
|
self:T2(FOX.lid..string.format("EVENT SHOT: Missil categ = %s", tostring(missilecategory)))
|
||||||
self:T2(FOX.lid..string.format("EVENT SHOT: Missil range = %s", tostring(missilerange)))
|
self:T2(FOX.lid..string.format("EVENT SHOT: Missil range = %s", tostring(missilerange)))
|
||||||
|
|
||||||
@@ -1292,7 +1295,7 @@ function FOX:OnEventShot(EventData)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Track missiles of type AAM=1, SAM=2 or OTHER=6
|
-- Track missiles of type AAM=1, SAM=2 or OTHER=6
|
||||||
local _track = weaponcategory==1 and missilecategory and (missilecategory==1 or missilecategory==2 or missilecategory==6)
|
local _track = weapon:IsMissile() and missilecategory and (missilecategory==1 or missilecategory==2 or missilecategory==6)
|
||||||
|
|
||||||
-- Only track missiles
|
-- Only track missiles
|
||||||
if _track then
|
if _track then
|
||||||
@@ -1301,6 +1304,7 @@ function FOX:OnEventShot(EventData)
|
|||||||
|
|
||||||
missile.active=true
|
missile.active=true
|
||||||
missile.weapon=EventData.weapon
|
missile.weapon=EventData.weapon
|
||||||
|
missile.Weapon=weapon
|
||||||
missile.missileType=_weapon
|
missile.missileType=_weapon
|
||||||
missile.missileRange=missilerange
|
missile.missileRange=missilerange
|
||||||
missile.missileName=EventData.weapon:getName()
|
missile.missileName=EventData.weapon:getName()
|
||||||
@@ -1313,6 +1317,7 @@ function FOX:OnEventShot(EventData)
|
|||||||
missile.fuseDist=desc.fuseDist
|
missile.fuseDist=desc.fuseDist
|
||||||
missile.explosive=desc.warhead.explosiveMass or desc.warhead.shapedExplosiveMass
|
missile.explosive=desc.warhead.explosiveMass or desc.warhead.shapedExplosiveMass
|
||||||
missile.targetOrig=missile.targetName
|
missile.targetOrig=missile.targetName
|
||||||
|
missile.missileCoord=COORDINATE:New(0,0,0)
|
||||||
|
|
||||||
-- Set missile target name, unit and player.
|
-- Set missile target name, unit and player.
|
||||||
self:GetMissileTarget(missile)
|
self:GetMissileTarget(missile)
|
||||||
@@ -1631,7 +1636,7 @@ end
|
|||||||
|
|
||||||
--- Check if a coordinate lies within a safe training zone.
|
--- Check if a coordinate lies within a safe training zone.
|
||||||
-- @param #FOX self
|
-- @param #FOX self
|
||||||
-- @param Core.Point#COORDINATE coord Coordinate to check.
|
-- @param Core.Point#COORDINATE coord Coordinate to check. Can also be a DCS#Vec3.
|
||||||
-- @return #boolean True if safe.
|
-- @return #boolean True if safe.
|
||||||
function FOX:_CheckCoordSafe(coord)
|
function FOX:_CheckCoordSafe(coord)
|
||||||
|
|
||||||
@@ -1643,7 +1648,9 @@ function FOX:_CheckCoordSafe(coord)
|
|||||||
-- Loop over all zones.
|
-- Loop over all zones.
|
||||||
for _,_zone in pairs(self.safezones) do
|
for _,_zone in pairs(self.safezones) do
|
||||||
local zone=_zone --Core.Zone#ZONE
|
local zone=_zone --Core.Zone#ZONE
|
||||||
local inzone=zone:IsCoordinateInZone(coord)
|
local Vec2={x=coord.x, y=coord.z}
|
||||||
|
local inzone=zone:IsVec2InZone(Vec2)
|
||||||
|
--local inzone=zone:IsCoordinateInZone(coord)
|
||||||
if inzone then
|
if inzone then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@@ -1654,7 +1661,7 @@ end
|
|||||||
|
|
||||||
--- Check if a coordinate lies within a launch zone.
|
--- Check if a coordinate lies within a launch zone.
|
||||||
-- @param #FOX self
|
-- @param #FOX self
|
||||||
-- @param Core.Point#COORDINATE coord Coordinate to check.
|
-- @param Core.Point#COORDINATE coord Coordinate to check. Can also be a DCS#Vec2.
|
||||||
-- @return #boolean True if in launch zone.
|
-- @return #boolean True if in launch zone.
|
||||||
function FOX:_CheckCoordLaunch(coord)
|
function FOX:_CheckCoordLaunch(coord)
|
||||||
|
|
||||||
@@ -1666,7 +1673,9 @@ function FOX:_CheckCoordLaunch(coord)
|
|||||||
-- Loop over all zones.
|
-- Loop over all zones.
|
||||||
for _,_zone in pairs(self.launchzones) do
|
for _,_zone in pairs(self.launchzones) do
|
||||||
local zone=_zone --Core.Zone#ZONE
|
local zone=_zone --Core.Zone#ZONE
|
||||||
local inzone=zone:IsCoordinateInZone(coord)
|
local Vec2={x=coord.x, y=coord.z}
|
||||||
|
local inzone=zone:IsVec2InZone(Vec2)
|
||||||
|
--local inzone=zone:IsCoordinateInZone(coord)
|
||||||
if inzone then
|
if inzone then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -26,9 +26,9 @@
|
|||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Author: **[funkyfranky](https://forums.eagle.ru/member.php?u=115026)**
|
-- ### Author: **funkyfranky**
|
||||||
--
|
--
|
||||||
-- ### Contributions: [FlightControl](https://forums.eagle.ru/member.php?u=89536)
|
-- ### Contributions: FlightControl, Applevangelist
|
||||||
--
|
--
|
||||||
-- ====
|
-- ====
|
||||||
-- @module Functional.PseudoATC
|
-- @module Functional.PseudoATC
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
-- @field #number mrefresh Interval in seconds after which the F10 menu is refreshed. E.g. by the closest airports. Default is 120 sec.
|
-- @field #number mrefresh Interval in seconds after which the F10 menu is refreshed. E.g. by the closest airports. Default is 120 sec.
|
||||||
-- @field #number talt Interval in seconds between reporting altitude until touchdown. Default 3 sec.
|
-- @field #number talt Interval in seconds between reporting altitude until touchdown. Default 3 sec.
|
||||||
-- @field #boolean chatty Display some messages on events like take-off and touchdown.
|
-- @field #boolean chatty Display some messages on events like take-off and touchdown.
|
||||||
-- @field #boolean eventsmoose If true, events are handled by MOOSE. If false, events are handled directly by DCS eventhandler.
|
-- @field #boolean eventsmoose [Deprecated] If true, events are handled by MOOSE. If false, events are handled directly by DCS eventhandler.
|
||||||
-- @field #boolean reportplayername If true, use playername not callsign on callouts
|
-- @field #boolean reportplayername If true, use playername not callsign on callouts
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
@@ -100,13 +100,14 @@ PSEUDOATC.id="PseudoATC | "
|
|||||||
|
|
||||||
--- PSEUDOATC version.
|
--- PSEUDOATC version.
|
||||||
-- @field #number version
|
-- @field #number version
|
||||||
PSEUDOATC.version="0.9.5"
|
PSEUDOATC.version="0.10.5"
|
||||||
|
|
||||||
-----------------------------------------------------------------------------------------------------------------------------------------
|
-----------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- TODO list
|
-- TODO list
|
||||||
-- DONE: Add takeoff event.
|
-- DONE: Add takeoff event.
|
||||||
-- DONE: Add user functions.
|
-- DONE: Add user functions.
|
||||||
|
-- DONE: Refactor to use Moose event handling only
|
||||||
|
|
||||||
-----------------------------------------------------------------------------------------------------------------------------------------
|
-----------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -131,23 +132,14 @@ function PSEUDOATC:Start()
|
|||||||
self:F()
|
self:F()
|
||||||
|
|
||||||
-- Debug info
|
-- Debug info
|
||||||
self:E(PSEUDOATC.id.."Starting PseudoATC")
|
self:I(PSEUDOATC.id.."Starting PseudoATC")
|
||||||
|
|
||||||
-- Handle events.
|
-- Handle events.
|
||||||
if self.eventsmoose then
|
self:HandleEvent(EVENTS.Birth, self._OnBirth)
|
||||||
self:T(PSEUDOATC.id.."Events are handled by MOOSE.")
|
self:HandleEvent(EVENTS.Land, self._PlayerLanded)
|
||||||
self:HandleEvent(EVENTS.Birth, self._OnBirth)
|
self:HandleEvent(EVENTS.Takeoff, self._PlayerTakeOff)
|
||||||
self:HandleEvent(EVENTS.Land, self._PlayerLanded)
|
self:HandleEvent(EVENTS.PlayerLeaveUnit, self._PlayerLeft)
|
||||||
self:HandleEvent(EVENTS.Takeoff, self._PlayerTakeOff)
|
self:HandleEvent(EVENTS.Crash, self._PlayerLeft)
|
||||||
self:HandleEvent(EVENTS.PlayerLeaveUnit, self._PlayerLeft)
|
|
||||||
self:HandleEvent(EVENTS.Crash, self._PlayerLeft)
|
|
||||||
--self:HandleEvent(EVENTS.Ejection, self._PlayerLeft)
|
|
||||||
--self:HandleEvent(EVENTS.PilotDead, self._PlayerLeft)
|
|
||||||
else
|
|
||||||
self:T(PSEUDOATC.id.."Events are handled by DCS.")
|
|
||||||
-- Events are handled directly by DCS.
|
|
||||||
world.addEventHandler(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -199,7 +191,7 @@ function PSEUDOATC:SetMenuRefresh(interval)
|
|||||||
self.mrefresh=interval or 120
|
self.mrefresh=interval or 120
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Enable/disable event handling by MOOSE or DCS.
|
--- [Deprecated] Enable/disable event handling by MOOSE or DCS.
|
||||||
-- @param #PSEUDOATC self
|
-- @param #PSEUDOATC self
|
||||||
-- @param #boolean switch If true, events are handled by MOOSE (default). If false, events are handled directly by DCS.
|
-- @param #boolean switch If true, events are handled by MOOSE (default). If false, events are handled directly by DCS.
|
||||||
function PSEUDOATC:SetEventsMoose(switch)
|
function PSEUDOATC:SetEventsMoose(switch)
|
||||||
@@ -216,84 +208,6 @@ end
|
|||||||
-----------------------------------------------------------------------------------------------------------------------------------------
|
-----------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- Event Handling
|
-- Event Handling
|
||||||
|
|
||||||
--- Event handler for suppressed groups.
|
|
||||||
--@param #PSEUDOATC self
|
|
||||||
--@param #table Event Event data table. Holds event.id, event.initiator and event.target etc.
|
|
||||||
function PSEUDOATC:onEvent(Event)
|
|
||||||
if Event == nil or Event.initiator == nil or Unit.getByName(Event.initiator:getName()) == nil then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
local DCSiniunit = Event.initiator
|
|
||||||
local DCSplace = Event.place
|
|
||||||
local DCSsubplace = Event.subplace
|
|
||||||
|
|
||||||
local EventData={}
|
|
||||||
local _playerunit=nil
|
|
||||||
local _playername=nil
|
|
||||||
|
|
||||||
if Event.initiator then
|
|
||||||
EventData.IniUnitName = Event.initiator:getName()
|
|
||||||
EventData.IniDCSGroup = Event.initiator:getGroup()
|
|
||||||
EventData.IniGroupName = Event.initiator:getGroup():getName()
|
|
||||||
-- Get player unit and name. This returns nil,nil if the event was not fired by a player unit. And these are the only events we are interested in.
|
|
||||||
_playerunit, _playername = self:_GetPlayerUnitAndName(EventData.IniUnitName)
|
|
||||||
end
|
|
||||||
|
|
||||||
if Event.place then
|
|
||||||
EventData.Place=Event.place
|
|
||||||
EventData.PlaceName=Event.place:getName()
|
|
||||||
end
|
|
||||||
if Event.subplace then
|
|
||||||
EventData.SubPlace=Event.subplace
|
|
||||||
EventData.SubPlaceName=Event.subplace:getName()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Event info.
|
|
||||||
self:T3(PSEUDOATC.id..string.format("EVENT: Event in onEvent with ID = %s", tostring(Event.id)))
|
|
||||||
self:T3(PSEUDOATC.id..string.format("EVENT: Ini unit = %s" , tostring(EventData.IniUnitName)))
|
|
||||||
self:T3(PSEUDOATC.id..string.format("EVENT: Ini group = %s" , tostring(EventData.IniGroupName)))
|
|
||||||
self:T3(PSEUDOATC.id..string.format("EVENT: Ini player = %s" , tostring(_playername)))
|
|
||||||
self:T3(PSEUDOATC.id..string.format("EVENT: Place = %s" , tostring(EventData.PlaceName)))
|
|
||||||
self:T3(PSEUDOATC.id..string.format("EVENT: SubPlace = %s" , tostring(EventData.SubPlaceName)))
|
|
||||||
|
|
||||||
-- Event birth.
|
|
||||||
if Event.id == world.event.S_EVENT_BIRTH and _playername then
|
|
||||||
self:_OnBirth(EventData)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Event takeoff.
|
|
||||||
if Event.id == world.event.S_EVENT_TAKEOFF and _playername and EventData.Place then
|
|
||||||
self:_PlayerTakeOff(EventData)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Event land.
|
|
||||||
if Event.id == world.event.S_EVENT_LAND and _playername and EventData.Place then
|
|
||||||
self:_PlayerLanded(EventData)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Event player left unit
|
|
||||||
if Event.id == world.event.S_EVENT_PLAYER_LEAVE_UNIT and _playername then
|
|
||||||
self:_PlayerLeft(EventData)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Event crash ==> player left unit
|
|
||||||
if Event.id == world.event.S_EVENT_CRASH and _playername then
|
|
||||||
self:_PlayerLeft(EventData)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[[
|
|
||||||
-- Event eject ==> player left unit
|
|
||||||
if Event.id == world.event.S_EVENT_EJECTION and _playername then
|
|
||||||
self:_PlayerLeft(EventData)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Event pilot dead ==> player left unit
|
|
||||||
if Event.id == world.event.S_EVENT_PILOT_DEAD and _playername then
|
|
||||||
self:_PlayerLeft(EventData)
|
|
||||||
end
|
|
||||||
]]
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Function called my MOOSE event handler when a player enters a unit.
|
--- Function called my MOOSE event handler when a player enters a unit.
|
||||||
-- @param #PSEUDOATC self
|
-- @param #PSEUDOATC self
|
||||||
@@ -303,7 +217,9 @@ function PSEUDOATC:_OnBirth(EventData)
|
|||||||
|
|
||||||
-- Get unit and player.
|
-- Get unit and player.
|
||||||
local _unitName=EventData.IniUnitName
|
local _unitName=EventData.IniUnitName
|
||||||
local _unit, _playername=self:_GetPlayerUnitAndName(_unitName)
|
--local _unit, _playername=self:_GetPlayerUnitAndName(_unitName)
|
||||||
|
local _unit = EventData.IniUnit
|
||||||
|
local _playername = EventData.IniPlayerName
|
||||||
|
|
||||||
-- Check if a player entered.
|
-- Check if a player entered.
|
||||||
if _unit and _playername then
|
if _unit and _playername then
|
||||||
@@ -320,7 +236,10 @@ function PSEUDOATC:_PlayerLeft(EventData)
|
|||||||
|
|
||||||
-- Get unit and player.
|
-- Get unit and player.
|
||||||
local _unitName=EventData.IniUnitName
|
local _unitName=EventData.IniUnitName
|
||||||
local _unit, _playername=self:_GetPlayerUnitAndName(_unitName)
|
--local _unit, _playername=self:_GetPlayerUnitAndName(_unitName)
|
||||||
|
|
||||||
|
local _unit = EventData.IniUnit
|
||||||
|
local _playername = EventData.IniPlayerName
|
||||||
|
|
||||||
-- Check if a player left.
|
-- Check if a player left.
|
||||||
if _unit and _playername then
|
if _unit and _playername then
|
||||||
@@ -335,18 +254,16 @@ function PSEUDOATC:_PlayerLanded(EventData)
|
|||||||
self:F({EventData=EventData})
|
self:F({EventData=EventData})
|
||||||
|
|
||||||
-- Get unit, player and place.
|
-- Get unit, player and place.
|
||||||
local _unitName=EventData.IniUnitName
|
local _unitName=EventData.IniUnitName
|
||||||
local _unit, _playername=self:_GetPlayerUnitAndName(_unitName)
|
local _unit = EventData.IniUnit
|
||||||
|
local _playername = EventData.IniPlayerName
|
||||||
|
--local _unit, _playername=self:_GetPlayerUnitAndName(_unitName)
|
||||||
local _base=nil
|
local _base=nil
|
||||||
local _baseName=nil
|
local _baseName=nil
|
||||||
if EventData.place then
|
if EventData.place then
|
||||||
_base=EventData.place
|
_base=EventData.place
|
||||||
_baseName=EventData.place:getName()
|
_baseName=EventData.place:getName()
|
||||||
end
|
end
|
||||||
-- if EventData.subplace then
|
|
||||||
-- local _subPlace=EventData.subplace
|
|
||||||
-- local _subPlaceName=EventData.subplace:getName()
|
|
||||||
-- end
|
|
||||||
|
|
||||||
-- Call landed function.
|
-- Call landed function.
|
||||||
if _unit and _playername and _base then
|
if _unit and _playername and _base then
|
||||||
@@ -361,8 +278,10 @@ function PSEUDOATC:_PlayerTakeOff(EventData)
|
|||||||
self:F({EventData=EventData})
|
self:F({EventData=EventData})
|
||||||
|
|
||||||
-- Get unit, player and place.
|
-- Get unit, player and place.
|
||||||
local _unitName=EventData.IniUnitName
|
local _unitName=EventData.IniUnitName
|
||||||
local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
|
local _unit = EventData.IniUnit
|
||||||
|
local _playername = EventData.IniPlayerName
|
||||||
|
--local _unit,_playername=self:_GetPlayerUnitAndName(_unitName)
|
||||||
local _base=nil
|
local _base=nil
|
||||||
local _baseName=nil
|
local _baseName=nil
|
||||||
if EventData.place then
|
if EventData.place then
|
||||||
@@ -450,9 +369,6 @@ function PSEUDOATC:PlayerLanded(unit, place)
|
|||||||
local group=unit:GetGroup()
|
local group=unit:GetGroup()
|
||||||
local GID=group:GetID()
|
local GID=group:GetID()
|
||||||
local UID=unit:GetDCSObject():getID()
|
local UID=unit:GetDCSObject():getID()
|
||||||
--local PlayerName=self.group[GID].player[UID].playername
|
|
||||||
--local UnitName=self.group[GID].player[UID].unitname
|
|
||||||
--local GroupName=self.group[GID].player[UID].groupname
|
|
||||||
local PlayerName = unit:GetPlayerName() or "Ghost"
|
local PlayerName = unit:GetPlayerName() or "Ghost"
|
||||||
local UnitName = unit:GetName() or "Ghostplane"
|
local UnitName = unit:GetName() or "Ghostplane"
|
||||||
local GroupName = group:GetName() or "Ghostgroup"
|
local GroupName = group:GetName() or "Ghostgroup"
|
||||||
@@ -483,12 +399,6 @@ function PSEUDOATC:PlayerTakeOff(unit, place)
|
|||||||
|
|
||||||
-- Gather some information.
|
-- Gather some information.
|
||||||
local group=unit:GetGroup()
|
local group=unit:GetGroup()
|
||||||
--local GID=group:GetID()
|
|
||||||
--local UID=unit:GetDCSObject():getID()
|
|
||||||
--local PlayerName=self.group[GID].player[UID].playername
|
|
||||||
--local CallSign=self.group[GID].player[UID].callsign
|
|
||||||
--local UnitName=self.group[GID].player[UID].unitname
|
|
||||||
--local GroupName=self.group[GID].player[UID].groupname
|
|
||||||
local PlayerName = unit:GetPlayerName() or "Ghost"
|
local PlayerName = unit:GetPlayerName() or "Ghost"
|
||||||
local UnitName = unit:GetName() or "Ghostplane"
|
local UnitName = unit:GetName() or "Ghostplane"
|
||||||
local GroupName = group:GetName() or "Ghostgroup"
|
local GroupName = group:GetName() or "Ghostgroup"
|
||||||
@@ -926,7 +836,7 @@ function PSEUDOATC:AltitudeTimeStart(GID, UID)
|
|||||||
self:T(PSEUDOATC.id..string.format("Starting altitude report timer for player ID %d.", UID))
|
self:T(PSEUDOATC.id..string.format("Starting altitude report timer for player ID %d.", UID))
|
||||||
|
|
||||||
-- Start timer. Altitude is reported every ~3 seconds.
|
-- Start timer. Altitude is reported every ~3 seconds.
|
||||||
self.group[GID].player[UID].altimer, self.group[GID].player[UID].altimerid=SCHEDULER:New(nil, self.ReportHeight, {self, GID, UID, 0.1, true}, 1, 3)
|
self.group[GID].player[UID].altimer, self.group[GID].player[UID].altimerid=SCHEDULER:New(nil, self.ReportHeight, {self, GID, UID, 1, true}, 1, 3)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Stop/destroy DCS scheduler function for reporting altitude.
|
--- Stop/destroy DCS scheduler function for reporting altitude.
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -873,8 +873,10 @@ end
|
|||||||
function SCORING:OnEventBirth( Event )
|
function SCORING:OnEventBirth( Event )
|
||||||
|
|
||||||
if Event.IniUnit then
|
if Event.IniUnit then
|
||||||
|
Event.IniUnit.ThreatLevel, Event.IniUnit.ThreatType = Event.IniUnit:GetThreatLevel()
|
||||||
if Event.IniObjectCategory == 1 then
|
if Event.IniObjectCategory == 1 then
|
||||||
local PlayerName = Event.IniUnit:GetPlayerName()
|
local PlayerName = Event.IniUnit:GetPlayerName()
|
||||||
|
Event.IniUnit.BirthTime = timer.getTime()
|
||||||
if PlayerName then
|
if PlayerName then
|
||||||
self:_AddPlayerFromUnit( Event.IniUnit )
|
self:_AddPlayerFromUnit( Event.IniUnit )
|
||||||
self:SetScoringMenu( Event.IniGroup )
|
self:SetScoringMenu( Event.IniGroup )
|
||||||
@@ -1005,7 +1007,18 @@ function SCORING:_EventOnHit( Event )
|
|||||||
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0
|
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0
|
||||||
PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0
|
PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0
|
||||||
PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT
|
PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT
|
||||||
|
-- After an instant kill we can't compute the thread level anymore. To fix this we compute at OnEventBirth
|
||||||
|
if PlayerHit.UNIT.ThreatType == nil then
|
||||||
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
|
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
|
||||||
|
-- if this fails for some reason, set a good default value
|
||||||
|
if PlayerHit.ThreatType == nil then
|
||||||
|
PlayerHit.ThreatLevel = 1
|
||||||
|
PlayerHit.ThreatType = "Unknown"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
PlayerHit.ThreatLevel = PlayerHit.UNIT.ThreatLevel
|
||||||
|
PlayerHit.ThreatType = PlayerHit.UNIT.ThreatType
|
||||||
|
end
|
||||||
|
|
||||||
-- Only grant hit scores if there was more than one second between the last hit.
|
-- Only grant hit scores if there was more than one second between the last hit.
|
||||||
if timer.getTime() - PlayerHit.TimeStamp > 1 then
|
if timer.getTime() - PlayerHit.TimeStamp > 1 then
|
||||||
@@ -1021,27 +1034,30 @@ function SCORING:_EventOnHit( Event )
|
|||||||
|
|
||||||
if InitCoalition then -- A coalition object was hit.
|
if InitCoalition then -- A coalition object was hit.
|
||||||
if InitCoalition == TargetCoalition then
|
if InitCoalition == TargetCoalition then
|
||||||
Player.Penalty = Player.Penalty + 10
|
local Penalty = 10
|
||||||
PlayerHit.Penalty = PlayerHit.Penalty + 10
|
Player.Penalty = Player.Penalty + Penalty
|
||||||
|
PlayerHit.Penalty = PlayerHit.Penalty + Penalty
|
||||||
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1
|
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1
|
||||||
|
|
||||||
if TargetPlayerName ~= nil then -- It is a player hitting another player ...
|
if TargetPlayerName ~= nil then -- It is a player hitting another player ...
|
||||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
|
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
|
||||||
"Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
|
"Penalty: -" .. Penalty .. ". 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() )
|
||||||
else
|
else
|
||||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
|
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
|
||||||
"Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
|
"Penalty: -" .. Penalty .. ". 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() )
|
||||||
end
|
end
|
||||||
self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
|
self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
|
||||||
else
|
else
|
||||||
Player.Score = Player.Score + 1
|
-- Hitting a target multiple times before destoying it should not result in a higger score
|
||||||
PlayerHit.Score = PlayerHit.Score + 1
|
-- Multiple hits is typically a results of bombs/missles missing their target but still inflict some spash damage
|
||||||
|
-- Player.Score = Player.Score + 1
|
||||||
|
-- PlayerHit.Score = PlayerHit.Score + 1
|
||||||
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
|
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
|
||||||
if TargetPlayerName ~= nil then -- It is a player hitting another player ...
|
if TargetPlayerName ~= nil then -- It is a player hitting another player ...
|
||||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " ..
|
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " ..
|
||||||
@@ -1104,7 +1120,18 @@ function SCORING:_EventOnHit( Event )
|
|||||||
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0
|
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0
|
||||||
PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0
|
PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0
|
||||||
PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT
|
PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT
|
||||||
|
-- After an instant kill we can't compute the thread level anymore. To fix this we compute at OnEventBirth
|
||||||
|
if PlayerHit.UNIT.ThreatType == nil then
|
||||||
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
|
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
|
||||||
|
-- if this fails for some reason, set a good default value
|
||||||
|
if PlayerHit.ThreatType == nil then
|
||||||
|
PlayerHit.ThreatLevel = 1
|
||||||
|
PlayerHit.ThreatType = "Unknown"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
PlayerHit.ThreatLevel = PlayerHit.UNIT.ThreatLevel
|
||||||
|
PlayerHit.ThreatType = PlayerHit.UNIT.ThreatType
|
||||||
|
end
|
||||||
|
|
||||||
-- Only grant hit scores if there was more than one second between the last hit.
|
-- Only grant hit scores if there was more than one second between the last hit.
|
||||||
if timer.getTime() - PlayerHit.TimeStamp > 1 then
|
if timer.getTime() - PlayerHit.TimeStamp > 1 then
|
||||||
@@ -1115,25 +1142,28 @@ function SCORING:_EventOnHit( Event )
|
|||||||
if InitCoalition then -- A coalition object was hit, probably a static.
|
if InitCoalition then -- A coalition object was hit, probably a static.
|
||||||
if InitCoalition == TargetCoalition then
|
if InitCoalition == TargetCoalition then
|
||||||
-- TODO: Penalty according scale
|
-- TODO: Penalty according scale
|
||||||
Player.Penalty = Player.Penalty + 10 --* self.ScaleDestroyPenalty
|
local Penalty = 10
|
||||||
PlayerHit.Penalty = PlayerHit.Penalty + 10 --* self.ScaleDestroyPenalty
|
Player.Penalty = Player.Penalty + Penalty --* self.ScaleDestroyPenalty
|
||||||
|
PlayerHit.Penalty = PlayerHit.Penalty + Penalty --* self.ScaleDestroyPenalty
|
||||||
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 * self.ScaleDestroyPenalty
|
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 * self.ScaleDestroyPenalty
|
||||||
|
|
||||||
MESSAGE
|
MESSAGE
|
||||||
:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit friendly target " ..
|
:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit friendly target " ..
|
||||||
TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
|
TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
|
||||||
"Penalty: -" .. PlayerHit.Penalty .. " = " .. Player.Score - Player.Penalty,
|
"Penalty: -" .. Penalty .. " = " .. Player.Score - Player.Penalty,
|
||||||
MESSAGE.Type.Update
|
MESSAGE.Type.Update
|
||||||
)
|
)
|
||||||
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
||||||
:ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
:ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
||||||
self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
|
self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
|
||||||
else
|
else
|
||||||
Player.Score = Player.Score + 1
|
-- Hitting a target multiple times before destoying it should not result in a higger score
|
||||||
PlayerHit.Score = PlayerHit.Score + 1
|
-- Multiple hits is typically a results of bombs/missles missing their target but still inflict some spash damage
|
||||||
|
-- Player.Score = Player.Score + 1
|
||||||
|
-- PlayerHit.Score = PlayerHit.Score + 1
|
||||||
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
|
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
|
||||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit enemy target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
|
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit enemy target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
|
||||||
"Score: +" .. PlayerHit.Score .. " = " .. 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( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
:ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
||||||
@@ -1211,7 +1241,7 @@ function SCORING:_EventOnDeadOrCrash( Event )
|
|||||||
local Destroyed = false
|
local Destroyed = false
|
||||||
|
|
||||||
-- What is the player destroying?
|
-- What is the player destroying?
|
||||||
if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] and Player.Hit[TargetCategory][TargetUnitName].TimeStamp ~= 0 then -- Was there a hit for this unit for this player before registered???
|
if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] and Player.Hit[TargetCategory][TargetUnitName].TimeStamp ~= 0 and (TargetUnit.BirthTime == nil or Player.Hit[TargetCategory][TargetUnitName].TimeStamp > TargetUnit.BirthTime) then -- Was there a hit for this unit for this player before registered???
|
||||||
|
|
||||||
local TargetThreatLevel = Player.Hit[TargetCategory][TargetUnitName].ThreatLevel
|
local TargetThreatLevel = Player.Hit[TargetCategory][TargetUnitName].ThreatLevel
|
||||||
local TargetThreatType = Player.Hit[TargetCategory][TargetUnitName].ThreatType
|
local TargetThreatType = Player.Hit[TargetCategory][TargetUnitName].ThreatType
|
||||||
@@ -1240,13 +1270,13 @@ function SCORING:_EventOnDeadOrCrash( Event )
|
|||||||
|
|
||||||
if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player
|
if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player
|
||||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
||||||
"Penalty: -" .. TargetDestroy.Penalty .. " = " .. Player.Score - Player.Penalty,
|
"Penalty: -" .. ThreatPenalty .. " = " .. Player.Score - Player.Penalty,
|
||||||
MESSAGE.Type.Information )
|
MESSAGE.Type.Information )
|
||||||
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
|
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
|
||||||
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
|
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
|
||||||
else
|
else
|
||||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly target " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly target " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
||||||
"Penalty: -" .. TargetDestroy.Penalty .. " = " .. Player.Score - Player.Penalty,
|
"Penalty: -" .. ThreatPenalty .. " = " .. Player.Score - Player.Penalty,
|
||||||
MESSAGE.Type.Information )
|
MESSAGE.Type.Information )
|
||||||
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
|
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
|
||||||
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
|
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
|
||||||
@@ -1268,13 +1298,13 @@ function SCORING:_EventOnDeadOrCrash( Event )
|
|||||||
TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy + 1
|
TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy + 1
|
||||||
if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player
|
if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player
|
||||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
||||||
"Score: +" .. TargetDestroy.Score .. " = " .. Player.Score - Player.Penalty,
|
"Score: +" .. ThreatScore .. " = " .. Player.Score - Player.Penalty,
|
||||||
MESSAGE.Type.Information )
|
MESSAGE.Type.Information )
|
||||||
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
|
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
|
||||||
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
|
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
|
||||||
else
|
else
|
||||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
||||||
"Score: +" .. TargetDestroy.Score .. " = " .. Player.Score - Player.Penalty,
|
"Score: +" .. ThreatScore .. " = " .. Player.Score - Player.Penalty,
|
||||||
MESSAGE.Type.Information )
|
MESSAGE.Type.Information )
|
||||||
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
|
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
|
||||||
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
|
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
|
||||||
|
|||||||
@@ -1798,7 +1798,7 @@ _WAREHOUSEDB = {
|
|||||||
|
|
||||||
--- Warehouse class version.
|
--- Warehouse class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
WAREHOUSE.version="1.0.2"
|
WAREHOUSE.version="1.0.2a"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- TODO: Warehouse todo list.
|
-- TODO: Warehouse todo list.
|
||||||
@@ -4561,7 +4561,8 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request)
|
|||||||
self:_ErrorMessage("ERROR: Cargo transport by train not supported yet!")
|
self:_ErrorMessage("ERROR: Cargo transport by train not supported yet!")
|
||||||
return
|
return
|
||||||
|
|
||||||
elseif Request.transporttype==WAREHOUSE.TransportType.SHIP or Request.transporttype==WAREHOUSE.TransportType.NAVALCARRIER then
|
elseif Request.transporttype==WAREHOUSE.TransportType.SHIP or Request.transporttype==WAREHOUSE.TransportType.NAVALCARRIER
|
||||||
|
or Request.transporttype==WAREHOUSE.TransportType.ARMEDSHIP or Request.transporttype==WAREHOUSE.TransportType.WARSHIP then
|
||||||
|
|
||||||
-- Spawn Ship in port zone
|
-- Spawn Ship in port zone
|
||||||
spawngroup=self:_SpawnAssetGroundNaval(_alias, _assetitem, Request, self.portzone)
|
spawngroup=self:_SpawnAssetGroundNaval(_alias, _assetitem, Request, self.portzone)
|
||||||
@@ -5829,62 +5830,65 @@ function WAREHOUSE:_SpawnAssetRequest(Request)
|
|||||||
|
|
||||||
-- Get stock item.
|
-- Get stock item.
|
||||||
local asset=cargoassets[i] --#WAREHOUSE.Assetitem
|
local asset=cargoassets[i] --#WAREHOUSE.Assetitem
|
||||||
|
|
||||||
|
if not asset.spawned then
|
||||||
|
|
||||||
-- Set asset status to not spawned until we capture its birth event.
|
-- Set asset status to not spawned until we capture its birth event.
|
||||||
asset.spawned=false
|
asset.iscargo=true
|
||||||
asset.iscargo=true
|
|
||||||
|
-- Set request ID.
|
||||||
-- Set request ID.
|
asset.rid=Request.uid
|
||||||
asset.rid=Request.uid
|
|
||||||
|
-- Spawn group name.
|
||||||
-- Spawn group name.
|
local _alias=asset.spawngroupname
|
||||||
local _alias=asset.spawngroupname
|
|
||||||
|
--Request add asset by id.
|
||||||
--Request add asset by id.
|
Request.assets[asset.uid]=asset
|
||||||
Request.assets[asset.uid]=asset
|
|
||||||
|
-- Spawn an asset group.
|
||||||
-- Spawn an asset group.
|
local _group=nil --Wrapper.Group#GROUP
|
||||||
local _group=nil --Wrapper.Group#GROUP
|
if asset.category==Group.Category.GROUND then
|
||||||
if asset.category==Group.Category.GROUND then
|
|
||||||
|
-- Spawn ground troops.
|
||||||
-- Spawn ground troops.
|
|
||||||
_group=self:_SpawnAssetGroundNaval(_alias, asset, Request, self.spawnzone, Request.lateActivation)
|
|
||||||
|
|
||||||
elseif asset.category==Group.Category.AIRPLANE or asset.category==Group.Category.HELICOPTER then
|
|
||||||
|
|
||||||
-- Spawn air units.
|
|
||||||
if Parking[asset.uid] then
|
|
||||||
_group=self:_SpawnAssetAircraft(_alias, asset, Request, Parking[asset.uid], UnControlled, Request.lateActivation)
|
|
||||||
else
|
|
||||||
_group=self:_SpawnAssetAircraft(_alias, asset, Request, nil, UnControlled, Request.lateActivation)
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif asset.category==Group.Category.TRAIN then
|
|
||||||
|
|
||||||
-- Spawn train.
|
|
||||||
if self.rail then
|
|
||||||
--TODO: Rail should only get one asset because they would spawn on top!
|
|
||||||
|
|
||||||
-- Spawn naval assets.
|
|
||||||
_group=self:_SpawnAssetGroundNaval(_alias, asset, Request, self.spawnzone, Request.lateActivation)
|
_group=self:_SpawnAssetGroundNaval(_alias, asset, Request, self.spawnzone, Request.lateActivation)
|
||||||
|
|
||||||
|
elseif asset.category==Group.Category.AIRPLANE or asset.category==Group.Category.HELICOPTER then
|
||||||
|
|
||||||
|
-- Spawn air units.
|
||||||
|
if Parking[asset.uid] then
|
||||||
|
_group=self:_SpawnAssetAircraft(_alias, asset, Request, Parking[asset.uid], UnControlled, Request.lateActivation)
|
||||||
|
else
|
||||||
|
_group=self:_SpawnAssetAircraft(_alias, asset, Request, nil, UnControlled, Request.lateActivation)
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif asset.category==Group.Category.TRAIN then
|
||||||
|
|
||||||
|
-- Spawn train.
|
||||||
|
if self.rail then
|
||||||
|
--TODO: Rail should only get one asset because they would spawn on top!
|
||||||
|
|
||||||
|
-- Spawn naval assets.
|
||||||
|
_group=self:_SpawnAssetGroundNaval(_alias, asset, Request, self.spawnzone, Request.lateActivation)
|
||||||
|
end
|
||||||
|
|
||||||
|
--self:E(self.lid.."ERROR: Spawning of TRAIN assets not possible yet!")
|
||||||
|
|
||||||
|
elseif asset.category==Group.Category.SHIP then
|
||||||
|
|
||||||
|
-- Spawn naval assets.
|
||||||
|
_group=self:_SpawnAssetGroundNaval(_alias, asset, Request, self.portzone, Request.lateActivation)
|
||||||
|
|
||||||
|
else
|
||||||
|
self:E(self.lid.."ERROR: Unknown asset category!")
|
||||||
end
|
end
|
||||||
|
|
||||||
--self:E(self.lid.."ERROR: Spawning of TRAIN assets not possible yet!")
|
-- Trigger event.
|
||||||
|
if _group then
|
||||||
elseif asset.category==Group.Category.SHIP then
|
self:__AssetSpawned(0.01, _group, asset, Request)
|
||||||
|
end
|
||||||
-- Spawn naval assets.
|
|
||||||
_group=self:_SpawnAssetGroundNaval(_alias, asset, Request, self.portzone, Request.lateActivation)
|
|
||||||
|
|
||||||
else
|
|
||||||
self:E(self.lid.."ERROR: Unknown asset category!")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Trigger event.
|
|
||||||
if _group then
|
|
||||||
self:__AssetSpawned(0.01, _group, asset, Request)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -715,7 +715,7 @@ do -- ZONE_CAPTURE_COALITION
|
|||||||
|
|
||||||
local UnitHit = EventData.TgtUnit
|
local UnitHit = EventData.TgtUnit
|
||||||
|
|
||||||
if UnitHit.ClassName ~= "SCENERY" then
|
if UnitHit and UnitHit.ClassName ~= "SCENERY" then
|
||||||
-- Check if unit is inside the capture zone and that it is of the defending coalition.
|
-- Check if unit is inside the capture zone and that it is of the defending coalition.
|
||||||
if UnitHit and UnitHit:IsInZone(self) and UnitHit:GetCoalition()==self.Coalition then
|
if UnitHit and UnitHit:IsInZone(self) and UnitHit:GetCoalition()==self.Coalition then
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ __Moose.Include( 'Scripts/Moose/Core/Goal.lua' )
|
|||||||
__Moose.Include( 'Scripts/Moose/Core/Spot.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Spot.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/MarkerOps_Base.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/MarkerOps_Base.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/TextAndSound.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/TextAndSound.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Core/Pathline.lua' )
|
||||||
|
|
||||||
__Moose.Include( 'Scripts/Moose/Wrapper/Object.lua' )
|
__Moose.Include( 'Scripts/Moose/Wrapper/Object.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Wrapper/Identifiable.lua' )
|
__Moose.Include( 'Scripts/Moose/Wrapper/Identifiable.lua' )
|
||||||
@@ -45,6 +46,8 @@ __Moose.Include( 'Scripts/Moose/Wrapper/Static.lua' )
|
|||||||
__Moose.Include( 'Scripts/Moose/Wrapper/Airbase.lua' )
|
__Moose.Include( 'Scripts/Moose/Wrapper/Airbase.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Wrapper/Scenery.lua' )
|
__Moose.Include( 'Scripts/Moose/Wrapper/Scenery.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Wrapper/Marker.lua' )
|
__Moose.Include( 'Scripts/Moose/Wrapper/Marker.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Wrapper/Weapon.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Wrapper/Net.lua' )
|
||||||
|
|
||||||
__Moose.Include( 'Scripts/Moose/Cargo/Cargo.lua' )
|
__Moose.Include( 'Scripts/Moose/Cargo/Cargo.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Cargo/CargoUnit.lua' )
|
__Moose.Include( 'Scripts/Moose/Cargo/CargoUnit.lua' )
|
||||||
|
|||||||
@@ -5826,7 +5826,7 @@ function AIRBOSS:_ScanCarrierZone()
|
|||||||
if knownflight then
|
if knownflight then
|
||||||
|
|
||||||
-- Check if flight is AI and if we want to handle it at all.
|
-- Check if flight is AI and if we want to handle it at all.
|
||||||
if knownflight.ai and knownflight.flag == -100 and self.handleai and false then --Disabled AI handling because of incorrect OPSGROUP reference!
|
if knownflight.ai and knownflight.flag == -100 and self.handleai and false then --Disabled AI handling because of incorrect OPSGROUP reference!
|
||||||
|
|
||||||
local putintomarshal = false
|
local putintomarshal = false
|
||||||
|
|
||||||
@@ -8102,7 +8102,7 @@ end
|
|||||||
-- @param Core.Event#EVENTDATA EventData
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
function AIRBOSS:OnEventBirth( EventData )
|
function AIRBOSS:OnEventBirth( EventData )
|
||||||
self:F3( { eventbirth = EventData } )
|
self:F3( { eventbirth = EventData } )
|
||||||
|
|
||||||
-- Nil checks.
|
-- Nil checks.
|
||||||
if EventData == nil then
|
if EventData == nil then
|
||||||
self:E( self.lid .. "ERROR: EventData=nil in event BIRTH!" )
|
self:E( self.lid .. "ERROR: EventData=nil in event BIRTH!" )
|
||||||
@@ -8114,7 +8114,9 @@ function AIRBOSS:OnEventBirth( EventData )
|
|||||||
self:E( EventData )
|
self:E( EventData )
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if EventData.IniObjectCategory ~= Object.Category.UNIT then return end
|
||||||
|
|
||||||
local _unitName = EventData.IniUnitName
|
local _unitName = EventData.IniUnitName
|
||||||
local _unit, _playername = self:_GetPlayerUnitAndName( _unitName )
|
local _unit, _playername = self:_GetPlayerUnitAndName( _unitName )
|
||||||
|
|
||||||
@@ -9792,23 +9794,23 @@ function AIRBOSS:_Groove( playerData )
|
|||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Long V/STOL groove time Wave Off over 75 seconds to IC - TOPGUN level Only. --pene testing (WIP)--- Need to think more about this.
|
-- Long V/STOL groove time Wave Off over 75 seconds to IC - TOPGUN level Only. --pene testing (WIP)--- Need to think more about this.
|
||||||
|
|
||||||
--if rho>=RAR and rho<=RIC and not playerData.waveoff and playerData.difficulty==AIRBOSS.Difficulty.HARD and playerData.actype== AIRBOSS.AircraftCarrier.AV8B then
|
--if rho>=RAR and rho<=RIC and not playerData.waveoff and playerData.difficulty==AIRBOSS.Difficulty.HARD and playerData.actype== AIRBOSS.AircraftCarrier.AV8B then
|
||||||
-- Get groove time
|
-- Get groove time
|
||||||
--local vSlow=groovedata.time
|
--local vSlow=groovedata.time
|
||||||
-- If too slow wave off.
|
-- If too slow wave off.
|
||||||
--if vSlow >75 then
|
--if vSlow >75 then
|
||||||
|
|
||||||
-- LSO Wave off!
|
-- LSO Wave off!
|
||||||
--self:RadioTransmission(self.LSORadio, self.LSOCall.WAVEOFF, nil, nil, nil, true)
|
--self:RadioTransmission(self.LSORadio, self.LSOCall.WAVEOFF, nil, nil, nil, true)
|
||||||
--playerData.Tlso=timer.getTime()
|
--playerData.Tlso=timer.getTime()
|
||||||
|
|
||||||
-- Player was waved Off
|
-- Player was waved Off
|
||||||
--playerData.waveoff=true
|
--playerData.waveoff=true
|
||||||
--return
|
--return
|
||||||
--end
|
--end
|
||||||
--end
|
--end
|
||||||
|
|
||||||
-- Groovedata step.
|
-- Groovedata step.
|
||||||
@@ -10162,7 +10164,7 @@ function AIRBOSS:_GetSternCoord()
|
|||||||
elseif case==2 or case==1 then
|
elseif case==2 or case==1 then
|
||||||
-- V/Stol: Translate 8 meters port.
|
-- V/Stol: Translate 8 meters port.
|
||||||
self.sterncoord:Translate(self.carrierparam.sterndist, hdg, true, true):Translate(8, FB-90, true, true)
|
self.sterncoord:Translate(self.carrierparam.sterndist, hdg, true, true):Translate(8, FB-90, true, true)
|
||||||
end
|
end
|
||||||
elseif self.carriertype==AIRBOSS.CarrierType.STENNIS then
|
elseif self.carriertype==AIRBOSS.CarrierType.STENNIS then
|
||||||
-- Stennis: translate 7 meters starboard wrt Final bearing.
|
-- Stennis: translate 7 meters starboard wrt Final bearing.
|
||||||
self.sterncoord:Translate( self.carrierparam.sterndist, hdg, true, true ):Translate( 7, FB + 90, true, true )
|
self.sterncoord:Translate( self.carrierparam.sterndist, hdg, true, true ):Translate( 7, FB + 90, true, true )
|
||||||
@@ -10180,6 +10182,28 @@ function AIRBOSS:_GetSternCoord()
|
|||||||
return self.sterncoord
|
return self.sterncoord
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get wire from draw argument.
|
||||||
|
-- @param #AIRBOSS self
|
||||||
|
-- @param Core.Point#COORDINATE Lcoord Landing position.
|
||||||
|
-- @return #number Trapped wire (1-4) or 99 if no wire was trapped.
|
||||||
|
function AIRBOSS:_GetWireFromDrawArg()
|
||||||
|
|
||||||
|
local wireArgs={}
|
||||||
|
wireArgs[1]=141
|
||||||
|
wireArgs[2]=142
|
||||||
|
wireArgs[3]=143
|
||||||
|
wireArgs[4]=144
|
||||||
|
|
||||||
|
for wire,drawArg in pairs(wireArgs) do
|
||||||
|
local value=self.carrier:GetDrawArgumentValue(drawArg)
|
||||||
|
if math.abs(value)>0.001 then
|
||||||
|
return wire
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return 99
|
||||||
|
end
|
||||||
|
|
||||||
--- Get wire from landing position.
|
--- Get wire from landing position.
|
||||||
-- @param #AIRBOSS self
|
-- @param #AIRBOSS self
|
||||||
-- @param Core.Point#COORDINATE Lcoord Landing position.
|
-- @param Core.Point#COORDINATE Lcoord Landing position.
|
||||||
@@ -11280,7 +11304,7 @@ function AIRBOSS:_GetOptLandingCoordinate()
|
|||||||
self.landingcoord:SetAltitude(UTILS.FeetToMeters(120))
|
self.landingcoord:SetAltitude(UTILS.FeetToMeters(120))
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
-- Ideally we want to land between 2nd and 3rd wire.
|
-- Ideally we want to land between 2nd and 3rd wire.
|
||||||
|
|||||||
@@ -2750,12 +2750,12 @@ function CSAR:onafterLoad(From, Event, To, path, filename)
|
|||||||
vec3.z = tonumber(dataset[4])
|
vec3.z = tonumber(dataset[4])
|
||||||
local point = COORDINATE:NewFromVec3(vec3)
|
local point = COORDINATE:NewFromVec3(vec3)
|
||||||
|
|
||||||
local coalition = dataset[5]
|
local coalition = tonumber(dataset[5])
|
||||||
local country = dataset[6]
|
local country = tonumber(dataset[6])
|
||||||
local description = dataset[7]
|
local description = dataset[7]
|
||||||
local typeName = dataset[8]
|
local typeName = dataset[8]
|
||||||
local unitName = dataset[9]
|
local unitName = dataset[9]
|
||||||
local freq = dataset[10]
|
local freq = tonumber(dataset[10])
|
||||||
|
|
||||||
self:_AddCsar(coalition, country, point, typeName, unitName, playerName, freq, nil, description, nil)
|
self:_AddCsar(coalition, country, point, typeName, unitName, playerName, freq, nil, description, nil)
|
||||||
end
|
end
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -198,7 +198,7 @@
|
|||||||
-- The first parameter *callsignname* defines the name (1=Texaco, 2=Arco, 3=Shell). The second (optional) parameter specifies the first number and has to be between 1-9.
|
-- The first parameter *callsignname* defines the name (1=Texaco, 2=Arco, 3=Shell). The second (optional) parameter specifies the first number and has to be between 1-9.
|
||||||
-- Also see [DCS_enum_callsigns](https://wiki.hoggitworld.com/view/DCS_enum_callsigns) and [DCS_command_setCallsign](https://wiki.hoggitworld.com/view/DCS_command_setCallsign).
|
-- Also see [DCS_enum_callsigns](https://wiki.hoggitworld.com/view/DCS_enum_callsigns) and [DCS_command_setCallsign](https://wiki.hoggitworld.com/view/DCS_command_setCallsign).
|
||||||
--
|
--
|
||||||
-- TexacoStennis:SetCAllsign(CALLSIGN.Tanker.Arco)
|
-- TexacoStennis:SetCallsign(CALLSIGN.Tanker.Arco)
|
||||||
--
|
--
|
||||||
-- For convenience, MOOSE has a CALLSIGN enumerator introduced.
|
-- For convenience, MOOSE has a CALLSIGN enumerator introduced.
|
||||||
--
|
--
|
||||||
|
|||||||
@@ -805,7 +805,9 @@ function RESCUEHELO:_OnEventCrashOrEject(EventData)
|
|||||||
self:T(self.lid..text)
|
self:T(self.lid..text)
|
||||||
|
|
||||||
-- Get coordinate of unit.
|
-- Get coordinate of unit.
|
||||||
local coord=unit:GetCoordinate()
|
--local coord=unit:GetCoordinate()
|
||||||
|
local Vec3 = EventData.IniDCSUnit:getPoint() -- Vec3
|
||||||
|
local coord = COORDINATE:NewFromVec3(Vec3)
|
||||||
|
|
||||||
if coord and self.rescuezone:IsCoordinateInZone(coord) then
|
if coord and self.rescuezone:IsCoordinateInZone(coord) then
|
||||||
|
|
||||||
|
|||||||
@@ -489,6 +489,7 @@ ENUMS.ReportingName =
|
|||||||
--Mosquito = "A-20",
|
--Mosquito = "A-20",
|
||||||
Skyhawk = "A-4E",
|
Skyhawk = "A-4E",
|
||||||
Viggen = "AJS37",
|
Viggen = "AJS37",
|
||||||
|
Harrier_B = "AV8BNA",
|
||||||
Harrier = "AV-8B",
|
Harrier = "AV-8B",
|
||||||
Spirit = "B-2",
|
Spirit = "B-2",
|
||||||
Aviojet = "C-101",
|
Aviojet = "C-101",
|
||||||
|
|||||||
@@ -44,21 +44,24 @@ SOCKET = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
--- Data type. This is the keyword the socket listener uses.
|
--- Data type. This is the keyword the socket listener uses.
|
||||||
|
-- @type SOCKET.DataType
|
||||||
-- @field #string TEXT Plain text.
|
-- @field #string TEXT Plain text.
|
||||||
-- @field #string BOMBRESULT Range bombing.
|
-- @field #string BOMBRESULT Range bombing.
|
||||||
-- @field #string STRAFERESULT Range strafeing result.
|
-- @field #string STRAFERESULT Range strafeing result.
|
||||||
-- @field #string LSOGRADE Airboss LSO grade.
|
-- @field #string LSOGRADE Airboss LSO grade.
|
||||||
|
-- @field #string TTS Text-To-Speech.
|
||||||
SOCKET.DataType={
|
SOCKET.DataType={
|
||||||
TEXT="moose_text",
|
TEXT="moose_text",
|
||||||
BOMBRESULT="moose_bomb_result",
|
BOMBRESULT="moose_bomb_result",
|
||||||
STRAFERESULT="moose_strafe_result",
|
STRAFERESULT="moose_strafe_result",
|
||||||
LSOGRADE="moose_lso_grade",
|
LSOGRADE="moose_lso_grade",
|
||||||
|
TTS="moose_text2speech"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
--- SOCKET class version.
|
--- SOCKET class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
SOCKET.version="0.2.0"
|
SOCKET.version="0.3.0"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- TODO list
|
-- TODO list
|
||||||
@@ -139,7 +142,7 @@ end
|
|||||||
|
|
||||||
--- Send a text message.
|
--- Send a text message.
|
||||||
-- @param #SOCKET self
|
-- @param #SOCKET self
|
||||||
-- @param #string Text Test message.
|
-- @param #string Text Text message.
|
||||||
-- @return #SOCKET self
|
-- @return #SOCKET self
|
||||||
function SOCKET:SendText(Text)
|
function SOCKET:SendText(Text)
|
||||||
|
|
||||||
@@ -153,4 +156,32 @@ function SOCKET:SendText(Text)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Send a text-to-speech message.
|
||||||
|
-- @param #SOCKET self
|
||||||
|
-- @param #string Text The text message to speek.
|
||||||
|
-- @param #number Provider The TTS provider: 0=Microsoft (default), 1=Google.
|
||||||
|
-- @param #string Voice The specific voice to use, e.g. `"Microsoft David Desktop"` or "`en-US-Standard-A`". If not set, the service will choose a voice based on the other parameters such as culture and gender.
|
||||||
|
-- @param #string Culture The Culture or language code, *e.g.* `"en-US"`.
|
||||||
|
-- @param #string Gender The Gender, *i.e.* "male", "female". Default "female".
|
||||||
|
-- @param #number Volume The volume. Microsoft: [0,100] default 50, Google: [-96, 10] default 0.
|
||||||
|
-- @return #SOCKET self
|
||||||
|
function SOCKET:SendTextToSpeech(Text, Provider, Voice, Culture, Gender, Volume)
|
||||||
|
|
||||||
|
Text=Text or "Hello World!"
|
||||||
|
|
||||||
|
local message={}
|
||||||
|
|
||||||
|
message.command = SOCKET.DataType.TTS
|
||||||
|
message.text = Text
|
||||||
|
message.provider=Provider
|
||||||
|
message.voice = Voice
|
||||||
|
message.culture = Culture
|
||||||
|
message.gender = Gender
|
||||||
|
message.volume = Volume
|
||||||
|
|
||||||
|
self:SendTable(message)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -385,6 +385,23 @@ UTILS.BasicSerialize = function(s)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function UTILS.PrintTableToLog(table, indent)
|
||||||
|
if not table then
|
||||||
|
BASE:E("No table passed!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if not indent then indent = 0 end
|
||||||
|
for k, v in pairs(table) do
|
||||||
|
if type(v) == "table" then
|
||||||
|
BASE:I(string.rep(" ", indent) .. tostring(k) .. " = {")
|
||||||
|
UTILS.PrintTableToLog(v, indent + 1)
|
||||||
|
BASE:I(string.rep(" ", indent) .. "}")
|
||||||
|
else
|
||||||
|
BASE:I(string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(v))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
UTILS.ToDegree = function(angle)
|
UTILS.ToDegree = function(angle)
|
||||||
return angle*180/math.pi
|
return angle*180/math.pi
|
||||||
@@ -489,6 +506,31 @@ UTILS.hPa2inHg = function( hPa )
|
|||||||
return hPa * 0.0295299830714
|
return hPa * 0.0295299830714
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Convert indicated airspeed (IAS) to true airspeed (TAS) for a given altitude above main sea level.
|
||||||
|
-- The conversion is based on the approximation that TAS is ~2% higher than IAS with every 1000 ft altitude above sea level.
|
||||||
|
-- @param #number ias Indicated air speed in any unit (m/s, km/h, knots, ...)
|
||||||
|
-- @param #number altitude Altitude above main sea level in meters.
|
||||||
|
-- @param #number oatcorr (Optional) Outside air temperature correction factor. Default 0.017.
|
||||||
|
-- @return #number True airspeed in the same unit the IAS has been given.
|
||||||
|
UTILS.IasToTas = function( ias, altitude, oatcorr )
|
||||||
|
oatcorr=oatcorr or 0.017
|
||||||
|
local tas=ias + (ias * oatcorr * UTILS.MetersToFeet(altitude) / 1000)
|
||||||
|
return tas
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Convert true airspeed (TAS) to indicated airspeed (IAS) for a given altitude above main sea level.
|
||||||
|
-- The conversion is based on the approximation that TAS is ~2% higher than IAS with every 1000 ft altitude above sea level.
|
||||||
|
-- @param #number tas True air speed in any unit (m/s, km/h, knots, ...)
|
||||||
|
-- @param #number altitude Altitude above main sea level in meters.
|
||||||
|
-- @param #number oatcorr (Optional) Outside air temperature correction factor. Default 0.017.
|
||||||
|
-- @return #number Indicated airspeed in the same unit the TAS has been given.
|
||||||
|
UTILS.TasToIas = function( tas, altitude, oatcorr )
|
||||||
|
oatcorr=oatcorr or 0.017
|
||||||
|
local ias=tas/(1+oatcorr*UTILS.MetersToFeet(altitude)/1000)
|
||||||
|
return ias
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Convert knots to altitude corrected KIAS, e.g. for tankers.
|
--- Convert knots to altitude corrected KIAS, e.g. for tankers.
|
||||||
-- @param #number knots Speed in knots.
|
-- @param #number knots Speed in knots.
|
||||||
-- @param #number altitude Altitude in feet
|
-- @param #number altitude Altitude in feet
|
||||||
@@ -642,7 +684,10 @@ function UTILS.Round( num, idp )
|
|||||||
return math.floor( num * mult + 0.5 ) / mult
|
return math.floor( num * mult + 0.5 ) / mult
|
||||||
end
|
end
|
||||||
|
|
||||||
-- porting in Slmod's dostring
|
--- Porting in Slmod's dostring - execute a string as LUA code with error handling.
|
||||||
|
-- @param #string s The code as string to be executed
|
||||||
|
-- @return #boolean success If true, code was successfully executed, else false
|
||||||
|
-- @return #string Outcome Code outcome if successful or error string if not successful
|
||||||
function UTILS.DoString( s )
|
function UTILS.DoString( s )
|
||||||
local f, err = loadstring( s )
|
local f, err = loadstring( s )
|
||||||
if f then
|
if f then
|
||||||
@@ -652,7 +697,15 @@ function UTILS.DoString( s )
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Here is a customized version of pairs, which I called spairs because it iterates over the table in a sorted order.
|
--- Here is a customized version of pairs, which I called spairs because it iterates over the table in a sorted order.
|
||||||
|
-- @param #table t The table
|
||||||
|
-- @param #string order (Optional) The sorting function
|
||||||
|
-- @return #string key The index key
|
||||||
|
-- @return #string value The value at the indexed key
|
||||||
|
-- @usage
|
||||||
|
-- for key,value in UTILS.spairs(mytable) do
|
||||||
|
-- -- your code here
|
||||||
|
-- end
|
||||||
function UTILS.spairs( t, order )
|
function UTILS.spairs( t, order )
|
||||||
-- collect the keys
|
-- collect the keys
|
||||||
local keys = {}
|
local keys = {}
|
||||||
@@ -677,7 +730,16 @@ function UTILS.spairs( t, order )
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Here is a customized version of pairs, which I called kpairs because it iterates over the table in a sorted order, based on a function that will determine the keys as reference first.
|
--- Here is a customized version of pairs, which I called kpairs because it iterates over the table in a sorted order, based on a function that will determine the keys as reference first.
|
||||||
|
-- @param #table t The table
|
||||||
|
-- @param #string getkey The function to determine the keys for sorting
|
||||||
|
-- @param #string order (Optional) The sorting function itself
|
||||||
|
-- @return #string key The index key
|
||||||
|
-- @return #string value The value at the indexed key
|
||||||
|
-- @usage
|
||||||
|
-- for key,value in UTILS.kpairs(mytable, getkeyfunc) do
|
||||||
|
-- -- your code here
|
||||||
|
-- end
|
||||||
function UTILS.kpairs( t, getkey, order )
|
function UTILS.kpairs( t, getkey, order )
|
||||||
-- collect the keys
|
-- collect the keys
|
||||||
local keys = {}
|
local keys = {}
|
||||||
@@ -702,7 +764,14 @@ function UTILS.kpairs( t, getkey, order )
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Here is a customized version of pairs, which I called rpairs because it iterates over the table in a random order.
|
--- Here is a customized version of pairs, which I called rpairs because it iterates over the table in a random order.
|
||||||
|
-- @param #table t The table
|
||||||
|
-- @return #string key The index key
|
||||||
|
-- @return #string value The value at the indexed key
|
||||||
|
-- @usage
|
||||||
|
-- for key,value in UTILS.rpairs(mytable) do
|
||||||
|
-- -- your code here
|
||||||
|
-- end
|
||||||
function UTILS.rpairs( t )
|
function UTILS.rpairs( t )
|
||||||
-- collect the keys
|
-- collect the keys
|
||||||
|
|
||||||
@@ -1197,6 +1266,20 @@ function UTILS.HdgDiff(h1, h2)
|
|||||||
return math.abs(delta)
|
return math.abs(delta)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Returns the heading from one vec3 to another vec3.
|
||||||
|
-- @param DCS#Vec3 a From vec3.
|
||||||
|
-- @param DCS#Vec3 b To vec3.
|
||||||
|
-- @return #number Heading in degrees.
|
||||||
|
function UTILS.HdgTo(a, b)
|
||||||
|
local dz=b.z-a.z
|
||||||
|
local dx=b.x-a.x
|
||||||
|
local heading=math.deg(math.atan2(dz, dx))
|
||||||
|
if heading < 0 then
|
||||||
|
heading = 360 + heading
|
||||||
|
end
|
||||||
|
return heading
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Translate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged.
|
--- Translate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged.
|
||||||
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
||||||
@@ -2001,9 +2084,9 @@ function UTILS.GenerateUHFrequencies()
|
|||||||
local _start = 220000000
|
local _start = 220000000
|
||||||
|
|
||||||
while _start < 399000000 do
|
while _start < 399000000 do
|
||||||
if _start ~= 243000000 then
|
if _start ~= 243000000 then
|
||||||
table.insert(FreeUHFFrequencies, _start)
|
table.insert(FreeUHFFrequencies, _start)
|
||||||
end
|
end
|
||||||
_start = _start + 500000
|
_start = _start + 500000
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2807,3 +2890,45 @@ function UTILS.IsAnyInTable(Table, Objects, Key)
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Helper function to plot a racetrack on the F10 Map - curtesy of Buur.
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate
|
||||||
|
-- @param #number Altitude Altitude in feet
|
||||||
|
-- @param #number Speed Speed in knots
|
||||||
|
-- @param #number Heading Heading in degrees
|
||||||
|
-- @param #number Leg Leg in NM
|
||||||
|
-- @param #number Coalition Coalition side, e.g. coaltion.side.RED or coaltion.side.BLUE
|
||||||
|
-- @param #table Color Color of the line in RGB, e.g. {1,0,0} for red
|
||||||
|
-- @param #number Alpha Transparency factor, between 0.1 and 1
|
||||||
|
-- @param #number LineType Line type to be used, line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||||
|
-- @param #boolean ReadOnly
|
||||||
|
function UTILS.PlotRacetrack(Coordinate, Altitude, Speed, Heading, Leg, Coalition, Color, Alpha, LineType, ReadOnly)
|
||||||
|
local fix_coordinate = Coordinate
|
||||||
|
local altitude = Altitude
|
||||||
|
local speed = Speed or 350
|
||||||
|
local heading = Heading or 270
|
||||||
|
local leg_distance = Leg or 10
|
||||||
|
|
||||||
|
local coalition = Coalition or -1
|
||||||
|
local color = Color or {1,0,0}
|
||||||
|
local alpha = Alpha or 1
|
||||||
|
local lineType = LineType or 1
|
||||||
|
|
||||||
|
|
||||||
|
speed = UTILS.IasToTas(speed, UTILS.FeetToMeters(altitude), oatcorr)
|
||||||
|
|
||||||
|
local turn_radius = 0.0211 * speed -3.01
|
||||||
|
|
||||||
|
local point_two = fix_coordinate:Translate(UTILS.NMToMeters(leg_distance), heading, true, false)
|
||||||
|
local point_three = point_two:Translate(UTILS.NMToMeters(turn_radius)*2, heading - 90, true, false)
|
||||||
|
local point_four = fix_coordinate:Translate(UTILS.NMToMeters(turn_radius)*2, heading - 90, true, false)
|
||||||
|
local circle_center_fix_four = point_two:Translate(UTILS.NMToMeters(turn_radius), heading - 90, true, false)
|
||||||
|
local circle_center_two_three = fix_coordinate:Translate(UTILS.NMToMeters(turn_radius), heading - 90, true, false)
|
||||||
|
|
||||||
|
|
||||||
|
fix_coordinate:LineToAll(point_two, coalition, color, alpha, lineType)
|
||||||
|
point_four:LineToAll(point_three, coalition, color, alpha, lineType)
|
||||||
|
circle_center_fix_four:CircleToAll(UTILS.NMToMeters(turn_radius), coalition, color, alpha, nil, 0, lineType)--, ReadOnly, Text)
|
||||||
|
circle_center_two_three:CircleToAll(UTILS.NMToMeters(turn_radius), coalition, color, alpha, nil, 0, lineType)--, ReadOnly, Text)
|
||||||
|
|
||||||
|
end
|
||||||
|
|||||||
@@ -198,8 +198,47 @@ AIRBASE.Nevada = {
|
|||||||
-- * AIRBASE.Normandy.Needs_Oar_Point
|
-- * AIRBASE.Normandy.Needs_Oar_Point
|
||||||
-- * AIRBASE.Normandy.Funtington
|
-- * AIRBASE.Normandy.Funtington
|
||||||
-- * AIRBASE.Normandy.Tangmere
|
-- * AIRBASE.Normandy.Tangmere
|
||||||
-- * AIRBASE.Normandy.Ford_AF
|
-- * AIRBASE.Normandy.Ford
|
||||||
--
|
-- * AIRBASE.Normandy.Argentan
|
||||||
|
-- * AIRBASE.Normandy.Goulet
|
||||||
|
-- * AIRBASE.Normandy.Barville
|
||||||
|
-- * AIRBASE.Normandy.Essay
|
||||||
|
-- * AIRBASE.Normandy.Hauterive
|
||||||
|
-- * AIRBASE.Normandy.Lymington
|
||||||
|
-- * AIRBASE.Normandy.Vrigny
|
||||||
|
-- * AIRBASE.Normandy.Odiham
|
||||||
|
-- * AIRBASE.Normandy.Conches
|
||||||
|
-- * AIRBASE.Normandy.West_Malling
|
||||||
|
-- * AIRBASE.Normandy.Villacoublay
|
||||||
|
-- * AIRBASE.Normandy.Kenley
|
||||||
|
-- * AIRBASE.Normandy.Beauvais_Tille
|
||||||
|
-- * AIRBASE.Normandy.Cormeilles_en_Vexin
|
||||||
|
-- * AIRBASE.Normandy.Creil
|
||||||
|
-- * AIRBASE.Normandy.Guyancourt
|
||||||
|
-- * AIRBASE.Normandy.Lonrai
|
||||||
|
-- * AIRBASE.Normandy.Dinan_Trelivan
|
||||||
|
-- * AIRBASE.Normandy.Heathrow
|
||||||
|
-- * AIRBASE.Normandy.Fecamp_Benouville
|
||||||
|
-- * AIRBASE.Normandy.Farnborough
|
||||||
|
-- * AIRBASE.Normandy.Friston
|
||||||
|
-- * AIRBASE.Normandy.Deanland
|
||||||
|
-- * AIRBASE.Normandy.Triqueville
|
||||||
|
-- * AIRBASE.Normandy.Poix
|
||||||
|
-- * AIRBASE.Normandy.Orly
|
||||||
|
-- * AIRBASE.Normandy.Stoney_Cross
|
||||||
|
-- * AIRBASE.Normandy.Amiens_Glisy
|
||||||
|
-- * AIRBASE.Normandy.Ronai
|
||||||
|
-- * AIRBASE.Normandy.Rouen_Boos
|
||||||
|
-- * AIRBASE.Normandy.Deauville
|
||||||
|
-- * AIRBASE.Normandy.Saint_Aubin
|
||||||
|
-- * AIRBASE.Normandy.Flers
|
||||||
|
-- * AIRBASE.Normandy.Avranches_Le_Val_Saint_Pere
|
||||||
|
-- * AIRBASE.Normandy.Gravesend
|
||||||
|
-- * AIRBASE.Normandy.Beaumont_le_Roger
|
||||||
|
-- * AIRBASE.Normandy.Broglie
|
||||||
|
-- * AIRBASE.Normandy.Bernay_Saint_Martin
|
||||||
|
-- * AIRBASE.Normandy.Saint_Andre_de_lEure
|
||||||
|
--
|
||||||
-- @field Normandy
|
-- @field Normandy
|
||||||
AIRBASE.Normandy = {
|
AIRBASE.Normandy = {
|
||||||
["Saint_Pierre_du_Mont"] = "Saint Pierre du Mont",
|
["Saint_Pierre_du_Mont"] = "Saint Pierre du Mont",
|
||||||
@@ -232,14 +271,46 @@ AIRBASE.Normandy = {
|
|||||||
["Needs_Oar_Point"] = "Needs Oar Point",
|
["Needs_Oar_Point"] = "Needs Oar Point",
|
||||||
["Funtington"] = "Funtington",
|
["Funtington"] = "Funtington",
|
||||||
["Tangmere"] = "Tangmere",
|
["Tangmere"] = "Tangmere",
|
||||||
["Ford_AF"] = "Ford_AF",
|
["Ford"] = "Ford",
|
||||||
["Goulet"] = "Goulet",
|
|
||||||
["Argentan"] = "Argentan",
|
["Argentan"] = "Argentan",
|
||||||
["Vrigny"] = "Vrigny",
|
["Goulet"] = "Goulet",
|
||||||
|
["Barville"] = "Barville",
|
||||||
["Essay"] = "Essay",
|
["Essay"] = "Essay",
|
||||||
["Hauterive"] = "Hauterive",
|
["Hauterive"] = "Hauterive",
|
||||||
["Barville"] = "Barville",
|
["Lymington"] = "Lymington",
|
||||||
|
["Vrigny"] = "Vrigny",
|
||||||
|
["Odiham"] = "Odiham",
|
||||||
["Conches"] = "Conches",
|
["Conches"] = "Conches",
|
||||||
|
["West_Malling"] = "West Malling",
|
||||||
|
["Villacoublay"] = "Villacoublay",
|
||||||
|
["Kenley"] = "Kenley",
|
||||||
|
["Beauvais_Tille"] = "Beauvais-Tille",
|
||||||
|
["Cormeilles_en_Vexin"] = "Cormeilles-en-Vexin",
|
||||||
|
["Creil"] = "Creil",
|
||||||
|
["Guyancourt"] = "Guyancourt",
|
||||||
|
["Lonrai"] = "Lonrai",
|
||||||
|
["Dinan_Trelivan"] = "Dinan-Trelivan",
|
||||||
|
["Heathrow"] = "Heathrow",
|
||||||
|
["Fecamp_Benouville"] = "Fecamp-Benouville",
|
||||||
|
["Farnborough"] = "Farnborough",
|
||||||
|
["Friston"] = "Friston",
|
||||||
|
["Deanland "] = "Deanland ",
|
||||||
|
["Triqueville"] = "Triqueville",
|
||||||
|
["Poix"] = "Poix",
|
||||||
|
["Orly"] = "Orly",
|
||||||
|
["Stoney_Cross"] = "Stoney Cross",
|
||||||
|
["Amiens_Glisy"] = "Amiens-Glisy",
|
||||||
|
["Ronai"] = "Ronai",
|
||||||
|
["Rouen_Boos"] = "Rouen-Boos",
|
||||||
|
["Deauville"] = "Deauville",
|
||||||
|
["Saint_Aubin"] = "Saint-Aubin",
|
||||||
|
["Flers"] = "Flers",
|
||||||
|
["Avranches_Le_Val_Saint_Pere"] = "Avranches Le Val-Saint-Pere",
|
||||||
|
["Gravesend"] = "Gravesend",
|
||||||
|
["Beaumont_le_Roger"] = "Beaumont-le-Roger",
|
||||||
|
["Broglie"] = "Broglie",
|
||||||
|
["Bernay_Saint_Martin"] = "Bernay Saint Martin",
|
||||||
|
["Saint_Andre_de_lEure"] = "Saint-Andre-de-lEure",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Airbases of the Persion Gulf Map:
|
--- Airbases of the Persion Gulf Map:
|
||||||
@@ -540,8 +611,12 @@ AIRBASE.SouthAtlantic={
|
|||||||
["Rio_Chico"] = "Rio Chico",
|
["Rio_Chico"] = "Rio Chico",
|
||||||
["Franco_Bianco"] = "Franco Bianco",
|
["Franco_Bianco"] = "Franco Bianco",
|
||||||
["Goose_Green"] = "Goose Green",
|
["Goose_Green"] = "Goose Green",
|
||||||
["Hipico"] = "Hipico",
|
["Hipico_Flying_Club"] = "Hipico Flying Club",
|
||||||
["CaletaTortel"] = "CaletaTortel",
|
["CaletaTortel"] = "CaletaTortel",
|
||||||
|
["Aeropuerto_de_Gobernador_Gregores"] = "Aeropuerto de Gobernador Gregores",
|
||||||
|
["Aerodromo_O_Higgins"] = "Aerodromo O'Higgins",
|
||||||
|
["Cullen_Airport"] = "Cullen Airport",
|
||||||
|
["Gull_Point"] = "Gull Point",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- AIRBASE.ParkingSpot ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy".
|
--- AIRBASE.ParkingSpot ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy".
|
||||||
|
|||||||
@@ -62,15 +62,15 @@
|
|||||||
--
|
--
|
||||||
-- @field #CLIENT
|
-- @field #CLIENT
|
||||||
CLIENT = {
|
CLIENT = {
|
||||||
ClassName = "CLIENT",
|
ClassName = "CLIENT",
|
||||||
ClientName = nil,
|
ClientName = nil,
|
||||||
ClientAlive = false,
|
ClientAlive = false,
|
||||||
ClientTransport = false,
|
ClientTransport = false,
|
||||||
ClientBriefingShown = false,
|
ClientBriefingShown = false,
|
||||||
_Menus = {},
|
_Menus = {},
|
||||||
_Tasks = {},
|
_Tasks = {},
|
||||||
Messages = {},
|
Messages = {},
|
||||||
Players = {},
|
Players = {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -95,6 +95,22 @@ function CLIENT:Find(DCSUnit, Error)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Finds a CLIENT from the _DATABASE using the relevant player name.
|
||||||
|
-- @param #CLIENT self
|
||||||
|
-- @param #string Name Name of the player
|
||||||
|
-- @return #CLIENT or nil if not found
|
||||||
|
function CLIENT:FindByPlayerName(Name)
|
||||||
|
|
||||||
|
local foundclient = nil
|
||||||
|
_DATABASE:ForEachClient(
|
||||||
|
function(client)
|
||||||
|
if client:GetPlayerName() == Name then
|
||||||
|
foundclient = client
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
return foundclient
|
||||||
|
end
|
||||||
|
|
||||||
--- Finds a CLIENT from the _DATABASE using the relevant Client Unit Name.
|
--- Finds a CLIENT from the _DATABASE using the relevant Client Unit Name.
|
||||||
-- As an optional parameter, a briefing text can be given also.
|
-- As an optional parameter, a briefing text can be given also.
|
||||||
@@ -105,13 +121,13 @@ end
|
|||||||
-- @return #CLIENT
|
-- @return #CLIENT
|
||||||
-- @usage
|
-- @usage
|
||||||
-- -- Create new Clients.
|
-- -- Create new Clients.
|
||||||
-- local Mission = MISSIONSCHEDULER.AddMission( 'Russia Transport Troops SA-6', 'Operational', 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.', 'Russia' )
|
-- local Mission = MISSIONSCHEDULER.AddMission( 'Russia Transport Troops SA-6', 'Operational', 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.', 'Russia' )
|
||||||
-- Mission:AddGoal( DeploySA6TroopsGoal )
|
-- Mission:AddGoal( DeploySA6TroopsGoal )
|
||||||
--
|
--
|
||||||
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 1' ):Transport() )
|
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 1' ):Transport() )
|
||||||
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 3' ):Transport() )
|
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 3' ):Transport() )
|
||||||
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 2' ):Transport() )
|
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 2' ):Transport() )
|
||||||
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 4' ):Transport() )
|
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 4' ):Transport() )
|
||||||
function CLIENT:FindByName( ClientName, ClientBriefing, Error )
|
function CLIENT:FindByName( ClientName, ClientBriefing, Error )
|
||||||
|
|
||||||
-- Client
|
-- Client
|
||||||
@@ -124,7 +140,7 @@ function CLIENT:FindByName( ClientName, ClientBriefing, Error )
|
|||||||
|
|
||||||
ClientFound.MessageSwitch = true
|
ClientFound.MessageSwitch = true
|
||||||
|
|
||||||
return ClientFound
|
return ClientFound
|
||||||
end
|
end
|
||||||
|
|
||||||
if not Error then
|
if not Error then
|
||||||
@@ -262,8 +278,8 @@ end
|
|||||||
-- @param #CLIENT self
|
-- @param #CLIENT self
|
||||||
-- @param #string ClientName Name of the Group as defined within the Mission Editor. The Group must have a Unit with the type Client.
|
-- @param #string ClientName Name of the Group as defined within the Mission Editor. The Group must have a Unit with the type Client.
|
||||||
function CLIENT:Reset( ClientName )
|
function CLIENT:Reset( ClientName )
|
||||||
self:F()
|
self:F()
|
||||||
self._Menus = {}
|
self._Menus = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Is Functions
|
-- Is Functions
|
||||||
@@ -347,85 +363,85 @@ function CLIENT:GetDCSGroup()
|
|||||||
self:F3()
|
self:F3()
|
||||||
|
|
||||||
-- local ClientData = Group.getByName( self.ClientName )
|
-- local ClientData = Group.getByName( self.ClientName )
|
||||||
-- if ClientData and ClientData:isExist() then
|
-- if ClientData and ClientData:isExist() then
|
||||||
-- self:T( self.ClientName .. " : group found!" )
|
-- self:T( self.ClientName .. " : group found!" )
|
||||||
-- return ClientData
|
-- return ClientData
|
||||||
-- else
|
-- else
|
||||||
-- return nil
|
-- return nil
|
||||||
-- end
|
-- end
|
||||||
|
|
||||||
local ClientUnit = Unit.getByName( self.ClientName )
|
local ClientUnit = Unit.getByName( self.ClientName )
|
||||||
|
|
||||||
local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) }
|
local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) }
|
||||||
|
|
||||||
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
|
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
|
||||||
self:T3( { "CoalitionData:", CoalitionData } )
|
self:T3( { "CoalitionData:", CoalitionData } )
|
||||||
for UnitId, UnitData in pairs( CoalitionData ) do
|
for UnitId, UnitData in pairs( CoalitionData ) do
|
||||||
self:T3( { "UnitData:", UnitData } )
|
self:T3( { "UnitData:", UnitData } )
|
||||||
if UnitData and UnitData:isExist() then
|
if UnitData and UnitData:isExist() then
|
||||||
|
|
||||||
--self:F(self.ClientName)
|
--self:F(self.ClientName)
|
||||||
if ClientUnit then
|
if ClientUnit then
|
||||||
|
|
||||||
local ClientGroup = ClientUnit:getGroup()
|
local ClientGroup = ClientUnit:getGroup()
|
||||||
|
|
||||||
if ClientGroup then
|
if ClientGroup then
|
||||||
self:T3( "ClientGroup = " .. self.ClientName )
|
self:T3( "ClientGroup = " .. self.ClientName )
|
||||||
|
|
||||||
if ClientGroup:isExist() and UnitData:getGroup():isExist() then
|
if ClientGroup:isExist() and UnitData:getGroup():isExist() then
|
||||||
|
|
||||||
if ClientGroup:getID() == UnitData:getGroup():getID() then
|
if ClientGroup:getID() == UnitData:getGroup():getID() then
|
||||||
self:T3( "Normal logic" )
|
self:T3( "Normal logic" )
|
||||||
self:T3( self.ClientName .. " : group found!" )
|
self:T3( self.ClientName .. " : group found!" )
|
||||||
self.ClientGroupID = ClientGroup:getID()
|
self.ClientGroupID = ClientGroup:getID()
|
||||||
self.ClientGroupName = ClientGroup:getName()
|
self.ClientGroupName = ClientGroup:getName()
|
||||||
return ClientGroup
|
return ClientGroup
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
-- Now we need to resolve the bugs in DCS 1.5 ...
|
-- Now we need to resolve the bugs in DCS 1.5 ...
|
||||||
-- Consult the database for the units of the Client Group. (ClientGroup:getUnits() returns nil)
|
-- Consult the database for the units of the Client Group. (ClientGroup:getUnits() returns nil)
|
||||||
self:T3( "Bug 1.5 logic" )
|
self:T3( "Bug 1.5 logic" )
|
||||||
|
|
||||||
local ClientGroupTemplate = _DATABASE.Templates.Units[self.ClientName].GroupTemplate
|
local ClientGroupTemplate = _DATABASE.Templates.Units[self.ClientName].GroupTemplate
|
||||||
|
|
||||||
self.ClientGroupID = ClientGroupTemplate.groupId
|
self.ClientGroupID = ClientGroupTemplate.groupId
|
||||||
|
|
||||||
self.ClientGroupName = _DATABASE.Templates.Units[self.ClientName].GroupName
|
self.ClientGroupName = _DATABASE.Templates.Units[self.ClientName].GroupName
|
||||||
|
|
||||||
self:T3( self.ClientName .. " : group found in bug 1.5 resolvement logic!" )
|
self:T3( self.ClientName .. " : group found in bug 1.5 resolvement logic!" )
|
||||||
return ClientGroup
|
return ClientGroup
|
||||||
|
|
||||||
end
|
end
|
||||||
-- else
|
-- else
|
||||||
-- error( "Client " .. self.ClientName .. " not found!" )
|
-- error( "Client " .. self.ClientName .. " not found!" )
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
--self:F( { "Client not found!", self.ClientName } )
|
--self:F( { "Client not found!", self.ClientName } )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
-- For non player clients
|
|
||||||
if ClientUnit then
|
|
||||||
local ClientGroup = ClientUnit:getGroup()
|
|
||||||
if ClientGroup then
|
|
||||||
self:T3( "ClientGroup = " .. self.ClientName )
|
|
||||||
if ClientGroup:isExist() then
|
|
||||||
self:T3( "Normal logic" )
|
|
||||||
self:T3( self.ClientName .. " : group found!" )
|
|
||||||
return ClientGroup
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Nothing could be found :(
|
-- For non player clients
|
||||||
self.ClientGroupID = nil
|
if ClientUnit then
|
||||||
self.ClientGroupName = nil
|
local ClientGroup = ClientUnit:getGroup()
|
||||||
|
if ClientGroup then
|
||||||
return nil
|
self:T3( "ClientGroup = " .. self.ClientName )
|
||||||
|
if ClientGroup:isExist() then
|
||||||
|
self:T3( "Normal logic" )
|
||||||
|
self:T3( self.ClientName .. " : group found!" )
|
||||||
|
return ClientGroup
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Nothing could be found :(
|
||||||
|
self.ClientGroupID = nil
|
||||||
|
self.ClientGroupName = nil
|
||||||
|
|
||||||
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -437,7 +453,7 @@ function CLIENT:GetClientGroupID()
|
|||||||
-- This updates the ID.
|
-- This updates the ID.
|
||||||
self:GetDCSGroup()
|
self:GetDCSGroup()
|
||||||
|
|
||||||
return self.ClientGroupID
|
return self.ClientGroupID
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -449,7 +465,7 @@ function CLIENT:GetClientGroupName()
|
|||||||
-- This updates the group name.
|
-- This updates the group name.
|
||||||
self:GetDCSGroup()
|
self:GetDCSGroup()
|
||||||
|
|
||||||
return self.ClientGroupName
|
return self.ClientGroupName
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns the UNIT of the CLIENT.
|
--- Returns the UNIT of the CLIENT.
|
||||||
@@ -458,23 +474,23 @@ end
|
|||||||
function CLIENT:GetClientGroupUnit()
|
function CLIENT:GetClientGroupUnit()
|
||||||
self:F2()
|
self:F2()
|
||||||
|
|
||||||
local ClientDCSUnit = Unit.getByName( self.ClientName )
|
local ClientDCSUnit = Unit.getByName( self.ClientName )
|
||||||
|
|
||||||
self:T( self.ClientDCSUnit )
|
self:T( self.ClientDCSUnit )
|
||||||
|
|
||||||
if ClientDCSUnit and ClientDCSUnit:isExist() then
|
if ClientDCSUnit and ClientDCSUnit:isExist() then
|
||||||
local ClientUnit=_DATABASE:FindUnit( self.ClientName )
|
local ClientUnit=_DATABASE:FindUnit( self.ClientName )
|
||||||
return ClientUnit
|
return ClientUnit
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns the DCSUnit of the CLIENT.
|
--- Returns the DCSUnit of the CLIENT.
|
||||||
-- @param #CLIENT self
|
-- @param #CLIENT self
|
||||||
-- @return DCS#Unit
|
-- @return DCS#Unit
|
||||||
function CLIENT:GetClientGroupDCSUnit()
|
function CLIENT:GetClientGroupDCSUnit()
|
||||||
self:F2()
|
self:F2()
|
||||||
|
|
||||||
local ClientDCSUnit = Unit.getByName( self.ClientName )
|
local ClientDCSUnit = Unit.getByName( self.ClientName )
|
||||||
|
|
||||||
@@ -489,29 +505,29 @@ end
|
|||||||
-- @param #CLIENT self
|
-- @param #CLIENT self
|
||||||
-- @return #boolean true is a transport.
|
-- @return #boolean true is a transport.
|
||||||
function CLIENT:IsTransport()
|
function CLIENT:IsTransport()
|
||||||
self:F()
|
self:F()
|
||||||
return self.ClientTransport
|
return self.ClientTransport
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Shows the @{AI.AI_Cargo#CARGO} contained within the CLIENT to the player as a message.
|
--- Shows the @{AI.AI_Cargo#CARGO} contained within the CLIENT to the player as a message.
|
||||||
-- The @{AI.AI_Cargo#CARGO} is shown using the @{Core.Message#MESSAGE} distribution system.
|
-- The @{AI.AI_Cargo#CARGO} is shown using the @{Core.Message#MESSAGE} distribution system.
|
||||||
-- @param #CLIENT self
|
-- @param #CLIENT self
|
||||||
function CLIENT:ShowCargo()
|
function CLIENT:ShowCargo()
|
||||||
self:F()
|
self:F()
|
||||||
|
|
||||||
local CargoMsg = ""
|
local CargoMsg = ""
|
||||||
|
|
||||||
for CargoName, Cargo in pairs( CARGOS ) do
|
for CargoName, Cargo in pairs( CARGOS ) do
|
||||||
if self == Cargo:IsLoadedInClient() then
|
if self == Cargo:IsLoadedInClient() then
|
||||||
CargoMsg = CargoMsg .. Cargo.CargoName .. " Type:" .. Cargo.CargoType .. " Weight: " .. Cargo.CargoWeight .. "\n"
|
CargoMsg = CargoMsg .. Cargo.CargoName .. " Type:" .. Cargo.CargoType .. " Weight: " .. Cargo.CargoWeight .. "\n"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if CargoMsg == "" then
|
if CargoMsg == "" then
|
||||||
CargoMsg = "empty"
|
CargoMsg = "empty"
|
||||||
end
|
end
|
||||||
|
|
||||||
self:Message( CargoMsg, 15, "Co-Pilot: Cargo Status", 30 )
|
self:Message( CargoMsg, 15, "Co-Pilot: Cargo Status", 30 )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -526,39 +542,39 @@ end
|
|||||||
-- @param #number MessageInterval is the interval in seconds between the display of the @{Core.Message#MESSAGE} when the CLIENT is in the air.
|
-- @param #number MessageInterval is the interval in seconds between the display of the @{Core.Message#MESSAGE} when the CLIENT is in the air.
|
||||||
-- @param #string MessageID is the identifier of the message when displayed with intervals.
|
-- @param #string MessageID is the identifier of the message when displayed with intervals.
|
||||||
function CLIENT:Message( Message, MessageDuration, MessageCategory, MessageInterval, MessageID )
|
function CLIENT:Message( Message, MessageDuration, MessageCategory, MessageInterval, MessageID )
|
||||||
self:F( { Message, MessageDuration, MessageCategory, MessageInterval } )
|
self:F( { Message, MessageDuration, MessageCategory, MessageInterval } )
|
||||||
|
|
||||||
if self.MessageSwitch == true then
|
if self.MessageSwitch == true then
|
||||||
if MessageCategory == nil then
|
if MessageCategory == nil then
|
||||||
MessageCategory = "Messages"
|
MessageCategory = "Messages"
|
||||||
end
|
end
|
||||||
if MessageID ~= nil then
|
if MessageID ~= nil then
|
||||||
if self.Messages[MessageID] == nil then
|
if self.Messages[MessageID] == nil then
|
||||||
self.Messages[MessageID] = {}
|
self.Messages[MessageID] = {}
|
||||||
self.Messages[MessageID].MessageId = MessageID
|
self.Messages[MessageID].MessageId = MessageID
|
||||||
self.Messages[MessageID].MessageTime = timer.getTime()
|
self.Messages[MessageID].MessageTime = timer.getTime()
|
||||||
self.Messages[MessageID].MessageDuration = MessageDuration
|
self.Messages[MessageID].MessageDuration = MessageDuration
|
||||||
if MessageInterval == nil then
|
if MessageInterval == nil then
|
||||||
self.Messages[MessageID].MessageInterval = 600
|
self.Messages[MessageID].MessageInterval = 600
|
||||||
else
|
else
|
||||||
self.Messages[MessageID].MessageInterval = MessageInterval
|
self.Messages[MessageID].MessageInterval = MessageInterval
|
||||||
end
|
end
|
||||||
MESSAGE:New( Message, MessageDuration, MessageCategory ):ToClient( self )
|
MESSAGE:New( Message, MessageDuration, MessageCategory ):ToClient( self )
|
||||||
else
|
else
|
||||||
if self:GetClientGroupDCSUnit() and not self:GetClientGroupDCSUnit():inAir() then
|
if self:GetClientGroupDCSUnit() and not self:GetClientGroupDCSUnit():inAir() then
|
||||||
if timer.getTime() - self.Messages[MessageID].MessageTime >= self.Messages[MessageID].MessageDuration + 10 then
|
if timer.getTime() - self.Messages[MessageID].MessageTime >= self.Messages[MessageID].MessageDuration + 10 then
|
||||||
MESSAGE:New( Message, MessageDuration , MessageCategory):ToClient( self )
|
MESSAGE:New( Message, MessageDuration , MessageCategory):ToClient( self )
|
||||||
self.Messages[MessageID].MessageTime = timer.getTime()
|
self.Messages[MessageID].MessageTime = timer.getTime()
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if timer.getTime() - self.Messages[MessageID].MessageTime >= self.Messages[MessageID].MessageDuration + self.Messages[MessageID].MessageInterval then
|
if timer.getTime() - self.Messages[MessageID].MessageTime >= self.Messages[MessageID].MessageDuration + self.Messages[MessageID].MessageInterval then
|
||||||
MESSAGE:New( Message, MessageDuration, MessageCategory ):ToClient( self )
|
MESSAGE:New( Message, MessageDuration, MessageCategory ):ToClient( self )
|
||||||
self.Messages[MessageID].MessageTime = timer.getTime()
|
self.Messages[MessageID].MessageTime = timer.getTime()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
MESSAGE:New( Message, MessageDuration, MessageCategory ):ToClient( self )
|
MESSAGE:New( Message, MessageDuration, MessageCategory ):ToClient( self )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -690,7 +690,8 @@ function CONTROLLABLE:CommandActivateACLS( UnitID, Name, Delay )
|
|||||||
if Delay and Delay > 0 then
|
if Delay and Delay > 0 then
|
||||||
SCHEDULER:New( nil, self.CommandActivateACLS, { self, UnitID, Name }, Delay )
|
SCHEDULER:New( nil, self.CommandActivateACLS, { self, UnitID, Name }, Delay )
|
||||||
else
|
else
|
||||||
self:SetCommand( CommandActivateACLS )
|
local controller = self:_GetController()
|
||||||
|
controller:setCommand( CommandActivateACLS )
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -711,7 +712,8 @@ function CONTROLLABLE:CommandDeactivateACLS( Delay )
|
|||||||
if Delay and Delay > 0 then
|
if Delay and Delay > 0 then
|
||||||
SCHEDULER:New( nil, self.CommandDeactivateACLS, { self }, Delay )
|
SCHEDULER:New( nil, self.CommandDeactivateACLS, { self }, Delay )
|
||||||
else
|
else
|
||||||
self:SetCommand( CommandDeactivateACLS )
|
local controller = self:_GetController()
|
||||||
|
controller:setCommand( CommandDeactivateACLS )
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -761,7 +763,7 @@ function CONTROLLABLE:CommandActivateLink4(Frequency, UnitID, Callsign, Delay)
|
|||||||
local CommandActivateLink4= {
|
local CommandActivateLink4= {
|
||||||
id = "ActivateLink4",
|
id = "ActivateLink4",
|
||||||
params= {
|
params= {
|
||||||
["frequency "] = freq*1000000,
|
["frequency"] = freq*1000000,
|
||||||
["unitId"] = UnitID or self:GetID(),
|
["unitId"] = UnitID or self:GetID(),
|
||||||
["name"] = Callsign or "LNK",
|
["name"] = Callsign or "LNK",
|
||||||
}
|
}
|
||||||
@@ -772,7 +774,8 @@ function CONTROLLABLE:CommandActivateLink4(Frequency, UnitID, Callsign, Delay)
|
|||||||
if Delay and Delay>0 then
|
if Delay and Delay>0 then
|
||||||
SCHEDULER:New(nil, self.CommandActivateLink4, {self, Frequency, UnitID, Callsign}, Delay)
|
SCHEDULER:New(nil, self.CommandActivateLink4, {self, Frequency, UnitID, Callsign}, Delay)
|
||||||
else
|
else
|
||||||
self:SetCommand(CommandActivateLink4)
|
local controller = self:_GetController()
|
||||||
|
controller:setCommand(CommandActivateLink4)
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -810,7 +813,8 @@ function CONTROLLABLE:CommandDeactivateLink4(Delay)
|
|||||||
if Delay and Delay>0 then
|
if Delay and Delay>0 then
|
||||||
SCHEDULER:New(nil, self.CommandDeactivateLink4, {self}, Delay)
|
SCHEDULER:New(nil, self.CommandDeactivateLink4, {self}, Delay)
|
||||||
else
|
else
|
||||||
self:SetCommand(CommandDeactivateLink4)
|
local controller = self:_GetController()
|
||||||
|
controller:setCommand(CommandDeactivateLink4)
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -1692,7 +1696,9 @@ function CONTROLLABLE:EnRouteTaskSEAD(TargetTypes, Priority)
|
|||||||
return DCSTask
|
return DCSTask
|
||||||
end
|
end
|
||||||
|
|
||||||
--- (AIR) Engaging a controllable. The task does not assign the target controllable to the unit/controllable to attack now; it just allows the unit/controllable to engage the target controllable as well as other assigned targets.
|
--- (AIR) Engaging a controllable. The task does not assign the target controllable to the unit/controllable to attack now;
|
||||||
|
-- it just allows the unit/controllable to engage the target controllable as well as other assigned targets.
|
||||||
|
-- See [hoggit](https://wiki.hoggitworld.com/view/DCS_task_engageGroup).
|
||||||
-- @param #CONTROLLABLE self
|
-- @param #CONTROLLABLE self
|
||||||
-- @param #CONTROLLABLE AttackGroup The Controllable to be attacked.
|
-- @param #CONTROLLABLE AttackGroup The Controllable to be attacked.
|
||||||
-- @param #number Priority All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first.
|
-- @param #number Priority All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first.
|
||||||
@@ -1741,6 +1747,7 @@ function CONTROLLABLE:EnRouteTaskEngageGroup( AttackGroup, Priority, WeaponType,
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- (AIR) Search and attack the Unit.
|
--- (AIR) Search and attack the Unit.
|
||||||
|
-- See [hoggit](https://wiki.hoggitworld.com/view/DCS_task_engageUnit).
|
||||||
-- @param #CONTROLLABLE self
|
-- @param #CONTROLLABLE self
|
||||||
-- @param Wrapper.Unit#UNIT EngageUnit The UNIT.
|
-- @param Wrapper.Unit#UNIT EngageUnit The UNIT.
|
||||||
-- @param #number Priority (optional) All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first.
|
-- @param #number Priority (optional) All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first.
|
||||||
@@ -1776,6 +1783,7 @@ function CONTROLLABLE:EnRouteTaskEngageUnit( EngageUnit, Priority, GroupAttack,
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- (AIR) Aircraft will act as an AWACS for friendly units (will provide them with information about contacts). No parameters.
|
--- (AIR) Aircraft will act as an AWACS for friendly units (will provide them with information about contacts). No parameters.
|
||||||
|
-- [hoggit](https://wiki.hoggitworld.com/view/DCS_task_awacs).
|
||||||
-- @param #CONTROLLABLE self
|
-- @param #CONTROLLABLE self
|
||||||
-- @return DCS#Task The DCS task structure.
|
-- @return DCS#Task The DCS task structure.
|
||||||
function CONTROLLABLE:EnRouteTaskAWACS()
|
function CONTROLLABLE:EnRouteTaskAWACS()
|
||||||
@@ -1789,6 +1797,7 @@ function CONTROLLABLE:EnRouteTaskAWACS()
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- (AIR) Aircraft will act as a tanker for friendly units. No parameters.
|
--- (AIR) Aircraft will act as a tanker for friendly units. No parameters.
|
||||||
|
-- See [hoggit](https://wiki.hoggitworld.com/view/DCS_task_tanker).
|
||||||
-- @param #CONTROLLABLE self
|
-- @param #CONTROLLABLE self
|
||||||
-- @return DCS#Task The DCS task structure.
|
-- @return DCS#Task The DCS task structure.
|
||||||
function CONTROLLABLE:EnRouteTaskTanker()
|
function CONTROLLABLE:EnRouteTaskTanker()
|
||||||
@@ -1804,6 +1813,7 @@ end
|
|||||||
-- En-route tasks for ground units/controllables
|
-- En-route tasks for ground units/controllables
|
||||||
|
|
||||||
--- (GROUND) Ground unit (EW-radar) will act as an EWR for friendly units (will provide them with information about contacts). No parameters.
|
--- (GROUND) Ground unit (EW-radar) will act as an EWR for friendly units (will provide them with information about contacts). No parameters.
|
||||||
|
-- See [hoggit](https://wiki.hoggitworld.com/view/DCS_task_ewr).
|
||||||
-- @param #CONTROLLABLE self
|
-- @param #CONTROLLABLE self
|
||||||
-- @return DCS#Task The DCS task structure.
|
-- @return DCS#Task The DCS task structure.
|
||||||
function CONTROLLABLE:EnRouteTaskEWR()
|
function CONTROLLABLE:EnRouteTaskEWR()
|
||||||
@@ -1821,14 +1831,17 @@ end
|
|||||||
--- (AIR + GROUND) The task makes the controllable/unit a FAC and lets the FAC to choose the target (enemy ground controllable) as well as other assigned targets.
|
--- (AIR + GROUND) The task makes the controllable/unit a FAC and lets the FAC to choose the target (enemy ground controllable) as well as other assigned targets.
|
||||||
-- The killer is player-controlled allied CAS-aircraft that is in contact with the FAC.
|
-- The killer is player-controlled allied CAS-aircraft that is in contact with the FAC.
|
||||||
-- If the task is assigned to the controllable lead unit will be a FAC.
|
-- If the task is assigned to the controllable lead unit will be a FAC.
|
||||||
|
-- See [hoggit](https://wiki.hoggitworld.com/view/DCS_task_fac_engageGroup).
|
||||||
-- @param #CONTROLLABLE self
|
-- @param #CONTROLLABLE self
|
||||||
-- @param #CONTROLLABLE AttackGroup Target CONTROLLABLE.
|
-- @param #CONTROLLABLE AttackGroup Target CONTROLLABLE.
|
||||||
-- @param #number Priority (Optional) All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. Default is 0.
|
-- @param #number Priority (Optional) All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. Default is 0.
|
||||||
-- @param #number WeaponType (Optional) Bitmask of weapon types those allowed to use. Default is "Auto".
|
-- @param #number WeaponType (Optional) Bitmask of weapon types those allowed to use. Default is "Auto".
|
||||||
-- @param DCS#AI.Task.Designation Designation (Optional) Designation type.
|
-- @param DCS#AI.Task.Designation Designation (Optional) Designation type.
|
||||||
-- @param #boolean Datalink (optional) Allows to use datalink to send the target information to attack aircraft. Enabled by default.
|
-- @param #boolean Datalink (optional) Allows to use datalink to send the target information to attack aircraft. Enabled by default.
|
||||||
|
-- @param #number CallsignID CallsignID, e.g. `CALLSIGN.JTAC.Anvil` for ground or `CALLSIGN.Aircraft.Ford` for air.
|
||||||
|
-- @param #number CallsignNumber Callsign first number, e.g. 2 for `Ford-2`.
|
||||||
-- @return DCS#Task The DCS task structure.
|
-- @return DCS#Task The DCS task structure.
|
||||||
function CONTROLLABLE:EnRouteTaskFAC_EngageGroup( AttackGroup, Priority, WeaponType, Designation, Datalink )
|
function CONTROLLABLE:EnRouteTaskFAC_EngageGroup( AttackGroup, Priority, WeaponType, Designation, Datalink, Frequency, Modulation, CallsignID, CallsignNumber )
|
||||||
|
|
||||||
local DCSTask = {
|
local DCSTask = {
|
||||||
id = 'FAC_EngageControllable',
|
id = 'FAC_EngageControllable',
|
||||||
@@ -1837,6 +1850,10 @@ function CONTROLLABLE:EnRouteTaskFAC_EngageGroup( AttackGroup, Priority, WeaponT
|
|||||||
weaponType = WeaponType or "Auto",
|
weaponType = WeaponType or "Auto",
|
||||||
designation = Designation,
|
designation = Designation,
|
||||||
datalink = Datalink and Datalink or false,
|
datalink = Datalink and Datalink or false,
|
||||||
|
frequency = (Frequency or 133)*1000000,
|
||||||
|
modulation = Modulation or radio.modulation.AM,
|
||||||
|
callname = CallsignID,
|
||||||
|
number = CallsignNumber,
|
||||||
priority = Priority or 0,
|
priority = Priority or 0,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -1845,27 +1862,26 @@ function CONTROLLABLE:EnRouteTaskFAC_EngageGroup( AttackGroup, Priority, WeaponT
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- (AIR + GROUND) The task makes the controllable/unit a FAC and lets the FAC to choose a targets (enemy ground controllable) around as well as other assigned targets.
|
--- (AIR + GROUND) The task makes the controllable/unit a FAC and lets the FAC to choose a targets (enemy ground controllable) around as well as other assigned targets.
|
||||||
-- The killer is player-controlled allied CAS-aircraft that is in contact with the FAC.
|
-- Assigns the controlled group to act as a Forward Air Controller or JTAC. Any detected targets will be assigned as targets to the player via the JTAC radio menu.
|
||||||
-- If the task is assigned to the controllable lead unit will be a FAC.
|
-- Target designation is set to auto and is dependent on the circumstances.
|
||||||
|
-- See [hoggit](https://wiki.hoggitworld.com/view/DCS_task_fac).
|
||||||
-- @param #CONTROLLABLE self
|
-- @param #CONTROLLABLE self
|
||||||
-- @param DCS#Distance Radius The maximal distance from the FAC to a target.
|
-- @param #number Frequency Frequency in MHz. Default 133 MHz.
|
||||||
|
-- @param #number Modulation Radio modulation. Default `radio.modulation.AM`.
|
||||||
|
-- @param #number CallsignID CallsignID, e.g. `CALLSIGN.JTAC.Anvil` for ground or `CALLSIGN.Aircraft.Ford` for air.
|
||||||
|
-- @param #number CallsignNumber Callsign first number, e.g. 2 for `Ford-2`.
|
||||||
-- @param #number Priority All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first.
|
-- @param #number Priority All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first.
|
||||||
-- @return DCS#Task The DCS task structure.
|
-- @return DCS#Task The DCS task structure.
|
||||||
function CONTROLLABLE:EnRouteTaskFAC( Radius, Priority )
|
function CONTROLLABLE:EnRouteTaskFAC( Frequency, Modulation, CallsignID, CallsignNumber, Priority )
|
||||||
|
|
||||||
-- FAC = {
|
|
||||||
-- id = 'FAC',
|
|
||||||
-- params = {
|
|
||||||
-- radius = Distance,
|
|
||||||
-- priority = number
|
|
||||||
-- }
|
|
||||||
-- }
|
|
||||||
|
|
||||||
local DCSTask = {
|
local DCSTask = {
|
||||||
id = 'FAC',
|
id = 'FAC',
|
||||||
params = {
|
params = {
|
||||||
radius = Radius,
|
frequency = (Frequency or 133)*1000000,
|
||||||
priority = Priority
|
modulation = Modulation or radio.modulation.AM,
|
||||||
|
callname = CallsignID,
|
||||||
|
number = CallsignNumber,
|
||||||
|
priority = Priority or 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1653,7 +1653,7 @@ function GROUP:GetMinHeight()
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns the current maximum height of the group.
|
--- Returns the current maximum height of the group, i.e. the highest unit height of that group.
|
||||||
-- Each unit within the group gets evaluated, and the maximum height (= the unit which is the highest elevated) is returned.
|
-- Each unit within the group gets evaluated, and the maximum height (= the unit which is the highest elevated) is returned.
|
||||||
-- @param #GROUP self
|
-- @param #GROUP self
|
||||||
-- @return #number Maximum height found.
|
-- @return #number Maximum height found.
|
||||||
@@ -1668,7 +1668,7 @@ function GROUP:GetMaxHeight()
|
|||||||
for Index, UnitData in pairs( DCSGroup:getUnits() ) do
|
for Index, UnitData in pairs( DCSGroup:getUnits() ) do
|
||||||
local UnitData = UnitData -- DCS#Unit
|
local UnitData = UnitData -- DCS#Unit
|
||||||
|
|
||||||
local UnitHeight = UnitData:getPoint()
|
local UnitHeight = UnitData:getPoint().p.y -- Height -- found by @Heavydrinker
|
||||||
|
|
||||||
if UnitHeight > GroupHeightMax then
|
if UnitHeight > GroupHeightMax then
|
||||||
GroupHeightMax = UnitHeight
|
GroupHeightMax = UnitHeight
|
||||||
@@ -2785,11 +2785,16 @@ end
|
|||||||
-- @param #GROUP self
|
-- @param #GROUP self
|
||||||
-- @param #boolean ShortCallsign Return a shortened customized callsign, i.e. "Ghostrider 9" and not "Ghostrider 9 1"
|
-- @param #boolean ShortCallsign Return a shortened customized callsign, i.e. "Ghostrider 9" and not "Ghostrider 9 1"
|
||||||
-- @param #boolean Keepnumber (Player only) Return customized callsign, incl optional numbers at the end, e.g. "Aerial 1-1#Ghostrider 109" results in "Ghostrider 109", if you want to e.g. use historical US Navy Callsigns
|
-- @param #boolean Keepnumber (Player only) Return customized callsign, incl optional numbers at the end, e.g. "Aerial 1-1#Ghostrider 109" results in "Ghostrider 109", if you want to e.g. use historical US Navy Callsigns
|
||||||
-- @param #table CallsignTranslations Table to translate between DCS standard callsigns and bespoke ones. Does not apply if using customized
|
-- @param #table CallsignTranslations Table to translate between DCS standard callsigns and bespoke ones. Overrides personal/parsed callsigns if set
|
||||||
-- callsigns from playername or group name.
|
-- callsigns from playername or group name.
|
||||||
-- @return #string Callsign
|
-- @return #string Callsign
|
||||||
-- @usage
|
-- @usage
|
||||||
-- -- Set Custom CAP Flight Callsigns for use with TTS
|
-- -- suppose there are three groups with one (client) unit each:
|
||||||
|
-- -- Slot 1 -- with mission editor callsign Enfield-1
|
||||||
|
-- -- Slot 2 # Apollo 403 -- with mission editor callsign Enfield-2
|
||||||
|
-- -- Slot 3 | Apollo -- with mission editor callsign Enfield-3
|
||||||
|
-- -- Slot 4 | Apollo -- with mission editor callsign Devil-4
|
||||||
|
-- -- and suppose these Custom CAP Flight Callsigns for use with TTS are set
|
||||||
-- mygroup:GetCustomCallSign(true,false,{
|
-- mygroup:GetCustomCallSign(true,false,{
|
||||||
-- Devil = 'Bengal',
|
-- Devil = 'Bengal',
|
||||||
-- Snake = 'Winder',
|
-- Snake = 'Winder',
|
||||||
@@ -2797,12 +2802,12 @@ end
|
|||||||
-- Enfield = 'Victory',
|
-- Enfield = 'Victory',
|
||||||
-- Uzi = 'Evil Eye'
|
-- Uzi = 'Evil Eye'
|
||||||
-- })
|
-- })
|
||||||
--
|
-- -- then GetCustomCallsign will return
|
||||||
-- results in this outcome if the group has Callsign "Enfield 9 1" on the 1st #UNIT of the group:
|
-- -- Enfield-1 for Slot 1
|
||||||
--
|
-- -- Apollo for Slot 2 or Apollo 403 if Keepnumber is set
|
||||||
-- 'Victory 9'
|
-- -- Apollo for Slot 3
|
||||||
--
|
-- -- Bengal-4 for Slot 4
|
||||||
--
|
|
||||||
function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations)
|
function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations)
|
||||||
--self:I("GetCustomCallSign")
|
--self:I("GetCustomCallSign")
|
||||||
|
|
||||||
@@ -2810,14 +2815,18 @@ function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations)
|
|||||||
if self:IsAlive() then
|
if self:IsAlive() then
|
||||||
local IsPlayer = self:IsPlayer()
|
local IsPlayer = self:IsPlayer()
|
||||||
local shortcallsign = self:GetCallsign() or "unknown91" -- e.g.Uzi91, but we want Uzi 9 1
|
local shortcallsign = self:GetCallsign() or "unknown91" -- e.g.Uzi91, but we want Uzi 9 1
|
||||||
local callsignroot = string.match(shortcallsign, '(%a+)') -- Uzi
|
local callsignroot = string.match(shortcallsign, '(%a+)') or "Ghost" -- Uzi
|
||||||
--self:I("CallSign = " .. callsignroot)
|
--self:I("CallSign = " .. callsignroot)
|
||||||
local groupname = self:GetName()
|
local groupname = self:GetName()
|
||||||
local callnumber = string.match(shortcallsign, "(%d+)$" ) or "91" -- 91
|
local callnumber = string.match(shortcallsign, "(%d+)$" ) or "91" -- 91
|
||||||
local callnumbermajor = string.char(string.byte(callnumber,1)) -- 9
|
local callnumbermajor = string.char(string.byte(callnumber,1)) -- 9
|
||||||
local callnumberminor = string.char(string.byte(callnumber,2)) -- 1
|
local callnumberminor = string.char(string.byte(callnumber,2)) -- 1
|
||||||
local personalized = false
|
local personalized = false
|
||||||
if IsPlayer and string.find(groupname,"#") then
|
|
||||||
|
-- prioritize bespoke callsigns over parsing, prefer parsing over default callsigns
|
||||||
|
if CallsignTranslations and CallsignTranslations[callsignroot] then
|
||||||
|
callsignroot = CallsignTranslations[callsignroot]
|
||||||
|
elseif IsPlayer and string.find(groupname,"#") then
|
||||||
-- personalized flight name in group naming
|
-- personalized flight name in group naming
|
||||||
if Keepnumber then
|
if Keepnumber then
|
||||||
shortcallsign = string.match(groupname,"#(.+)") or "Ghost 111" -- Ghostrider 219
|
shortcallsign = string.match(groupname,"#(.+)") or "Ghost 111" -- Ghostrider 219
|
||||||
@@ -2830,32 +2839,28 @@ function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations)
|
|||||||
shortcallsign = string.match(self:GetPlayerName(),"|%s*([%a]+)") or string.match(self:GetPlayerName(),"|%s*([%d]+)") or "Ghost" -- Ghostrider
|
shortcallsign = string.match(self:GetPlayerName(),"|%s*([%a]+)") or string.match(self:GetPlayerName(),"|%s*([%d]+)") or "Ghost" -- Ghostrider
|
||||||
personalized = true
|
personalized = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if (not personalized) and CallsignTranslations and CallsignTranslations[callsignroot] then
|
if personalized then
|
||||||
callsignroot = CallsignTranslations[callsignroot]
|
-- player personalized callsign
|
||||||
|
-- remove trailing/leading spaces
|
||||||
|
shortcallsign=string.gsub(shortcallsign,"^%s*","")
|
||||||
|
shortcallsign=string.gsub(shortcallsign,"%s*$","")
|
||||||
|
if Keepnumber then
|
||||||
|
return shortcallsign -- Ghostrider 219
|
||||||
|
elseif ShortCallsign then
|
||||||
|
callsign = shortcallsign.." "..callnumbermajor -- Ghostrider 9
|
||||||
|
else
|
||||||
|
callsign = shortcallsign.." "..callnumbermajor.." "..callnumberminor -- Ghostrider 9 1
|
||||||
|
end
|
||||||
|
return callsign
|
||||||
end
|
end
|
||||||
|
|
||||||
if personalized then
|
-- AI or not personalized
|
||||||
-- player personalized callsign
|
if ShortCallsign then
|
||||||
-- remove trailing/leading spaces
|
callsign = callsignroot.." "..callnumbermajor -- Uzi/Victory 9
|
||||||
shortcallsign=string.gsub(shortcallsign,"^%s*","")
|
|
||||||
shortcallsign=string.gsub(shortcallsign,"%s*$","")
|
|
||||||
if Keepnumber then
|
|
||||||
return shortcallsign -- Ghostrider 219
|
|
||||||
elseif ShortCallsign then
|
|
||||||
callsign = shortcallsign.." "..callnumbermajor -- Ghostrider 9
|
|
||||||
else
|
else
|
||||||
callsign = shortcallsign.." "..callnumbermajor.." "..callnumberminor -- Ghostrider 9 1
|
callsign = callsignroot.." "..callnumbermajor.." "..callnumberminor -- Uzi/Victory 9 1
|
||||||
end
|
end
|
||||||
return callsign
|
|
||||||
end
|
|
||||||
|
|
||||||
-- AI or not personalized
|
|
||||||
if ShortCallsign then
|
|
||||||
callsign = callsignroot.." "..callnumbermajor -- Uzi/Victory 9
|
|
||||||
else
|
|
||||||
callsign = callsignroot.." "..callnumbermajor.." "..callnumberminor -- Uzi/Victory 9 1
|
|
||||||
end
|
|
||||||
|
|
||||||
--self:I("Generated Callsign = " .. callsign)
|
--self:I("Generated Callsign = " .. callsign)
|
||||||
end
|
end
|
||||||
|
|||||||
833
Moose Development/Moose/Wrapper/Net.lua
Normal file
833
Moose Development/Moose/Wrapper/Net.lua
Normal file
@@ -0,0 +1,833 @@
|
|||||||
|
--- **Wrapper** - DCS net functions.
|
||||||
|
--
|
||||||
|
-- Encapsules **multiplayer server** environment scripting functions from [net](https://wiki.hoggitworld.com/view/DCS_singleton_net)
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Author: **Applevangelist**
|
||||||
|
-- # Last Update Apr 2023
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- @module Wrapper.Net
|
||||||
|
-- @image Utils_Profiler.jpg
|
||||||
|
|
||||||
|
do
|
||||||
|
--- The NET class
|
||||||
|
-- @type NET
|
||||||
|
-- @field #string ClassName
|
||||||
|
-- @field #string Version
|
||||||
|
-- @field #string lid
|
||||||
|
-- @field #number BlockTime
|
||||||
|
-- @field #table BlockedPilots
|
||||||
|
-- @field #table KnownPilots
|
||||||
|
-- @field #string BlockMessage
|
||||||
|
-- @field #string UnblockMessage
|
||||||
|
-- @field #table BlockedUCIDs
|
||||||
|
-- @field #table BlockedSlots
|
||||||
|
-- @field #table BlockedSides
|
||||||
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @type NET.PlayerData
|
||||||
|
-- @field #string name
|
||||||
|
-- @field #string ucid
|
||||||
|
-- @field #number id
|
||||||
|
-- @field #number side
|
||||||
|
-- @field #number slot
|
||||||
|
|
||||||
|
--- Encapsules multiplayer environment scripting functions from [net](https://wiki.hoggitworld.com/view/DCS_singleton_net)
|
||||||
|
-- with some added FSM functions and options to block/unblock players in MP environments.
|
||||||
|
--
|
||||||
|
-- @field #NET
|
||||||
|
NET = {
|
||||||
|
ClassName = "NET",
|
||||||
|
Version = "0.1.1",
|
||||||
|
BlockTime = 600,
|
||||||
|
BlockedPilots = {},
|
||||||
|
BlockedUCIDs = {},
|
||||||
|
BlockedSides = {},
|
||||||
|
BlockedSlots = {},
|
||||||
|
KnownPilots = {},
|
||||||
|
BlockMessage = nil,
|
||||||
|
UnblockMessage = nil,
|
||||||
|
lid = nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Instantiate a new NET object.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:New()
|
||||||
|
-- Inherit base.
|
||||||
|
local self = BASE:Inherit(self, FSM:New()) -- #NET
|
||||||
|
|
||||||
|
self.BlockTime = 600
|
||||||
|
self.BlockedPilots = {}
|
||||||
|
self.KnownPilots = {}
|
||||||
|
self:SetBlockMessage()
|
||||||
|
self:SetUnblockMessage()
|
||||||
|
|
||||||
|
-- Start State.
|
||||||
|
self:SetStartState("Stopped")
|
||||||
|
|
||||||
|
-- Add FSM transitions.
|
||||||
|
-- From State --> Event --> To State
|
||||||
|
self:AddTransition("Stopped", "Run", "Running") -- Start FSM.
|
||||||
|
self:AddTransition("*", "PlayerJoined", "*")
|
||||||
|
self:AddTransition("*", "PlayerLeft", "*")
|
||||||
|
self:AddTransition("*", "PlayerDied", "*")
|
||||||
|
self:AddTransition("*", "PlayerEjected", "*")
|
||||||
|
self:AddTransition("*", "PlayerBlocked", "*")
|
||||||
|
self:AddTransition("*", "PlayerUnblocked", "*")
|
||||||
|
self:AddTransition("*", "Status", "*")
|
||||||
|
self:AddTransition("*", "Stop", "Stopped")
|
||||||
|
|
||||||
|
self.lid = string.format("NET %s | ",self.Version)
|
||||||
|
|
||||||
|
--- FSM Function OnAfterPlayerJoined.
|
||||||
|
-- @function [parent=#NET] OnAfterPlayerJoined
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string From State.
|
||||||
|
-- @param #string Event Trigger.
|
||||||
|
-- @param #string To State.
|
||||||
|
-- @param Wrapper.Unit#UNIT Client Unit Object.
|
||||||
|
-- @param #string Name Name of joining Pilot.
|
||||||
|
-- @return #NET self
|
||||||
|
|
||||||
|
--- FSM Function OnAfterPlayerLeft.
|
||||||
|
-- @function [parent=#NET] OnAfterPlayerLeft
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string From State.
|
||||||
|
-- @param #string Event Trigger.
|
||||||
|
-- @param #string To State.
|
||||||
|
-- @param Wrapper.Unit#UNIT Client Unit Object, might be nil.
|
||||||
|
-- @param #string Name Name of leaving Pilot.
|
||||||
|
-- @return #NET self
|
||||||
|
|
||||||
|
--- FSM Function OnAfterPlayerEjected.
|
||||||
|
-- @function [parent=#NET] OnAfterPlayerEjected
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string From State.
|
||||||
|
-- @param #string Event Trigger.
|
||||||
|
-- @param #string To State.
|
||||||
|
-- @param Wrapper.Unit#UNIT Client Unit Object, might be nil.
|
||||||
|
-- @param #string Name Name of leaving Pilot.
|
||||||
|
-- @return #NET self
|
||||||
|
|
||||||
|
--- FSM Function OnAfterPlayerDied.
|
||||||
|
-- @function [parent=#NET] OnAfterPlayerDied
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string From State.
|
||||||
|
-- @param #string Event Trigger.
|
||||||
|
-- @param #string To State.
|
||||||
|
-- @param Wrapper.Unit#UNIT Client Unit Object, might be nil.
|
||||||
|
-- @param #string Name Name of dead Pilot.
|
||||||
|
-- @return #NET self
|
||||||
|
|
||||||
|
--- FSM Function OnAfterPlayerBlocked.
|
||||||
|
-- @function [parent=#NET] OnAfterPlayerBlocked
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string From State.
|
||||||
|
-- @param #string Event Trigger.
|
||||||
|
-- @param #string To State.
|
||||||
|
-- @param Wrapper.Client#CLIENT Client Client Object, might be nil.
|
||||||
|
-- @param #string Name Name of blocked Pilot.
|
||||||
|
-- @param #number Seconds Blocked for this number of seconds
|
||||||
|
-- @return #NET self
|
||||||
|
|
||||||
|
--- FSM Function OnAfterPlayerUnblocked.
|
||||||
|
-- @function [parent=#NET] OnAfterPlayerUnblocked
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string From State.
|
||||||
|
-- @param #string Event Trigger.
|
||||||
|
-- @param #string To State.
|
||||||
|
-- @param Wrapper.Client#CLIENT Client Client Object, might be nil.
|
||||||
|
-- @param #string Name Name of unblocked Pilot.
|
||||||
|
-- @return #NET self
|
||||||
|
|
||||||
|
self:Run()
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- [Internal] Check any blockers
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string UCID
|
||||||
|
-- @param #string Name
|
||||||
|
-- @param #number PlayerID
|
||||||
|
-- @param #number PlayerSide
|
||||||
|
-- @param #string PlayerSlot
|
||||||
|
-- @return #boolean IsBlocked
|
||||||
|
function NET:IsAnyBlocked(UCID,Name,PlayerID,PlayerSide,PlayerSlot)
|
||||||
|
local blocked = false
|
||||||
|
local TNow = timer.getTime()
|
||||||
|
-- UCID
|
||||||
|
if UCID and self.BlockedUCIDs[UCID] and TNow < self.BlockedUCIDs[UCID] then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
-- ID/Name
|
||||||
|
if PlayerID and not Name then
|
||||||
|
Name = self:GetPlayerIDByName(Name)
|
||||||
|
end
|
||||||
|
-- Name
|
||||||
|
if Name and self.BlockedPilots[Name] and TNow < self.BlockedPilots[Name] then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
-- Side
|
||||||
|
if PlayerSide and self.BlockedSides[PlayerSide] and TNow < self.BlockedSides[PlayerSide] then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
-- Slot
|
||||||
|
if PlayerSlot and self.BlockedSlots[PlayerSlot] and TNow < self.BlockedSlots[PlayerSlot] then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return blocked
|
||||||
|
end
|
||||||
|
|
||||||
|
--- [Internal] Event Handler
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:_EventHandler(EventData)
|
||||||
|
self:T(self.lid .. " _EventHandler")
|
||||||
|
self:T2({Event = EventData.id})
|
||||||
|
local data = EventData -- Core.Event#EVENTDATA EventData
|
||||||
|
if data.id and data.IniUnit and (data.IniPlayerName or data.IniUnit:GetPlayerName()) then
|
||||||
|
|
||||||
|
-- Get Player Data
|
||||||
|
local name = data.IniPlayerName and data.IniPlayerName or data.IniUnit:GetPlayerName()
|
||||||
|
local ucid = self:GetPlayerUCID(nil,name) or "none"
|
||||||
|
local PlayerID = self:GetPlayerIDByName(name) or "none"
|
||||||
|
local PlayerSide, PlayerSlot = self:GetSlot(data.IniUnit)
|
||||||
|
local TNow = timer.getTime()
|
||||||
|
|
||||||
|
self:T(self.lid.."Event for: "..name.." | UCID: "..ucid)
|
||||||
|
|
||||||
|
-- Joining
|
||||||
|
if data.id == EVENTS.PlayerEnterUnit or data.id == EVENTS.PlayerEnterAircraft then
|
||||||
|
self:T(self.lid.."Pilot Joining: "..name.." | UCID: "..ucid)
|
||||||
|
-- Check for blockages
|
||||||
|
local blocked = self:IsAnyBlocked(ucid,name,PlayerID,PlayerSide,PlayerSlot)
|
||||||
|
|
||||||
|
if blocked and PlayerID and tonumber(PlayerID) ~= 1 then
|
||||||
|
-- block pilot
|
||||||
|
local outcome = net.force_player_slot(tonumber(PlayerID), 0, '' )
|
||||||
|
else
|
||||||
|
self.KnownPilots[name] = {
|
||||||
|
name = name,
|
||||||
|
ucid = ucid,
|
||||||
|
id = PlayerID,
|
||||||
|
side = PlayerSide,
|
||||||
|
slot = PlayerSlot,
|
||||||
|
}
|
||||||
|
self:__PlayerJoined(1,data.IniUnit,name)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Leaving
|
||||||
|
if data.id == EVENTS.PlayerLeaveUnit and self.KnownPilots[name] then
|
||||||
|
self:T(self.lid.."Pilot Leaving: "..name.." | UCID: "..ucid)
|
||||||
|
self:__PlayerLeft(1,data.IniUnit,name)
|
||||||
|
self.KnownPilots[name] = false
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Ejected
|
||||||
|
if data.id == EVENTS.Ejection and self.KnownPilots[name] then
|
||||||
|
self:T(self.lid.."Pilot Ejecting: "..name.." | UCID: "..ucid)
|
||||||
|
self:__PlayerEjected(1,data.IniUnit,name)
|
||||||
|
self.KnownPilots[name] = false
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Dead, Crash, Suicide
|
||||||
|
if (data.id == EVENTS.PilotDead or data.id == EVENTS.SelfKillPilot or data.id == EVENTS.Crash) and self.KnownPilots[name] then
|
||||||
|
self:T(self.lid.."Pilot Dead: "..name.." | UCID: "..ucid)
|
||||||
|
self:__PlayerDied(1,data.IniUnit,name)
|
||||||
|
self.KnownPilots[name] = false
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Block a player.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client CLIENT object.
|
||||||
|
-- @param #string PlayerName (optional) Name of the player.
|
||||||
|
-- @param #number Seconds (optional) Number of seconds the player has to wait before rejoining.
|
||||||
|
-- @param #string Message (optional) Message to be sent via chat.
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:BlockPlayer(Client,PlayerName,Seconds,Message)
|
||||||
|
self:T({PlayerName,Seconds,Message})
|
||||||
|
local name = PlayerName
|
||||||
|
if Client and (not PlayerName) then
|
||||||
|
name = Client:GetPlayerName()
|
||||||
|
elseif PlayerName then
|
||||||
|
name = PlayerName
|
||||||
|
else
|
||||||
|
self:F(self.lid.."Block: No Client or PlayerName given or nothing found!")
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
local ucid = self:GetPlayerUCID(Client,name)
|
||||||
|
local addon = Seconds or self.BlockTime
|
||||||
|
self.BlockedPilots[name] = timer.getTime()+addon
|
||||||
|
self.BlockedUCIDs[ucid] = timer.getTime()+addon
|
||||||
|
local message = Message or self.BlockMessage
|
||||||
|
if name then
|
||||||
|
self:SendChatToPlayer(message,name)
|
||||||
|
else
|
||||||
|
self:SendChat(name..": "..message)
|
||||||
|
end
|
||||||
|
self:__PlayerBlocked(1,Client,name,Seconds)
|
||||||
|
local PlayerID = self:GetPlayerIDByName(name)
|
||||||
|
if PlayerID and tonumber(PlayerID) ~= 1 then
|
||||||
|
local outcome = net.force_player_slot(tonumber(PlayerID), 0, '' )
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Block a SET_CLIENT of players
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Core.Set#SET_CLIENT PlayerSet The SET to block.
|
||||||
|
-- @param #number Seconds Seconds (optional) Number of seconds the player has to wait before rejoining.
|
||||||
|
-- @param #string Message (optional) Message to be sent via chat.
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:BlockPlayerSet(PlayerSet,Seconds,Message)
|
||||||
|
self:T({PlayerSet.Set,Seconds,Message})
|
||||||
|
local addon = Seconds or self.BlockTime
|
||||||
|
local message = Message or self.BlockMessage
|
||||||
|
for _,_client in pairs(PlayerSet.Set) do
|
||||||
|
local name = _client:GetPlayerName()
|
||||||
|
self:BlockPlayer(_client,name,addon,message)
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Unblock a SET_CLIENT of players
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Core.Set#SET_CLIENT PlayerSet The SET to unblock.
|
||||||
|
-- @param #string Message (optional) Message to be sent via chat.
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:UnblockPlayerSet(PlayerSet,Message)
|
||||||
|
self:T({PlayerSet.Set,Seconds,Message})
|
||||||
|
local message = Message or self.UnblockMessage
|
||||||
|
for _,_client in pairs(PlayerSet.Set) do
|
||||||
|
local name = _client:GetPlayerName()
|
||||||
|
self:UnblockPlayer(_client,name,message)
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Block a specific UCID of a player, does NOT automatically kick the player with the UCID if already joined.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string ucid
|
||||||
|
-- @param #number Seconds Seconds (optional) Number of seconds the player has to wait before rejoining.
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:BlockUCID(ucid,Seconds)
|
||||||
|
self:T({ucid,Seconds})
|
||||||
|
local addon = Seconds or self.BlockTime
|
||||||
|
self.BlockedUCIDs[ucid] = timer.getTime()+addon
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Unblock a specific UCID of a player
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string ucid
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:UnblockUCID(ucid)
|
||||||
|
self:T({ucid})
|
||||||
|
self.BlockedUCIDs[ucid] = nil
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Block a specific coalition side, does NOT automatically kick all players of that side or kick out joined players
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #number side The side to block - 1 : Red, 2 : Blue
|
||||||
|
-- @param #number Seconds Seconds (optional) Number of seconds the player has to wait before rejoining.
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:BlockSide(Side,Seconds)
|
||||||
|
self:T({Side,Seconds})
|
||||||
|
local addon = Seconds or self.BlockTime
|
||||||
|
if Side == 1 or Side == 2 then
|
||||||
|
self.BlockedSides[Side] = timer.getTime()+addon
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Unblock a specific coalition side. Does NOT unblock specifically blocked playernames or UCIDs.
|
||||||
|
-- @param #number side The side to block - 1 : Red, 2 : Blue
|
||||||
|
-- @param #number Seconds Seconds (optional) Number of seconds the player has to wait before rejoining.
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:UnblockSide(Side,Seconds)
|
||||||
|
self:T({Side,Seconds})
|
||||||
|
local addon = Seconds or self.BlockTime
|
||||||
|
if Side == 1 or Side == 2 then
|
||||||
|
self.BlockedSides[Side] = nil
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Block a specific player slot, does NOT automatically kick a player in that slot or kick out joined players
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string slot The slot to block
|
||||||
|
-- @param #number Seconds Seconds (optional) Number of seconds the player has to wait before rejoining.
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:BlockSlot(Slot,Seconds)
|
||||||
|
self:T({Slot,Seconds})
|
||||||
|
local addon = Seconds or self.BlockTime
|
||||||
|
self.BlockedSlots[Slot] = timer.getTime()+addon
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Unblock a specific slot.
|
||||||
|
-- @param #string slot The slot to block
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:UnblockSlot(Slot)
|
||||||
|
self:T({Slot})
|
||||||
|
self.BlockedSlots[Slot] = nil
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Unblock a player.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client CLIENT object
|
||||||
|
-- @param #string PlayerName (optional) Name of the player.
|
||||||
|
-- @param #string Message (optional) Message to be sent via chat.
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:UnblockPlayer(Client,PlayerName,Message)
|
||||||
|
local name = PlayerName
|
||||||
|
if Client then
|
||||||
|
name = Client:GetPlayerName()
|
||||||
|
elseif PlayerName then
|
||||||
|
name = PlayerName
|
||||||
|
else
|
||||||
|
self:F(self.lid.."Unblock: No PlayerName given or not found!")
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
local ucid = self:GetPlayerUCID(Client,name)
|
||||||
|
self.BlockedPilots[name] = nil
|
||||||
|
self.BlockedUCIDs[ucid] = nil
|
||||||
|
local message = Message or self.UnblockMessage
|
||||||
|
if name then
|
||||||
|
self:SendChatToPlayer(message,name)
|
||||||
|
else
|
||||||
|
self:SendChat(name..": "..message)
|
||||||
|
end
|
||||||
|
self:__PlayerUnblocked(1,Client,name)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set block chat message.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string Text The message
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:SetBlockMessage(Text)
|
||||||
|
self.BlockMessage = Text or "You are blocked from joining. Wait time is: "..self.BlockTime.." seconds!"
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set block time in seconds.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #number Seconds Numnber of seconds this block will last. Defaults to 600.
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:SetBlockTime(Seconds)
|
||||||
|
self.BlockTime = Seconds or 600
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set unblock chat message.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string Text The message
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:SetUnblockMessage(Text)
|
||||||
|
self.UnblockMessage = Text or "You are unblocked now and can join again."
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Send chat message.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string Message Message to send
|
||||||
|
-- @param #boolean ToAll (Optional)
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:SendChat(Message,ToAll)
|
||||||
|
if Message then
|
||||||
|
net.send_chat(Message, ToAll)
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Find the PlayerID by name
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string Name The player name whose ID to find
|
||||||
|
-- @return #number PlayerID or nil
|
||||||
|
function NET:GetPlayerIDByName(Name)
|
||||||
|
if not Name then return nil end
|
||||||
|
local playerList = self:GetPlayerList()
|
||||||
|
self:T({playerList})
|
||||||
|
for i=1,#playerList do
|
||||||
|
local playerName = net.get_name(i)
|
||||||
|
self:T({playerName})
|
||||||
|
if playerName == Name then
|
||||||
|
return playerList[i]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Find the PlayerID from a CLIENT object.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client The client
|
||||||
|
-- @return #number PlayerID or nil
|
||||||
|
function NET:GetPlayerIDFromClient(Client)
|
||||||
|
if Client then
|
||||||
|
local name = Client:GetPlayerName()
|
||||||
|
local id = self:GetPlayerIDByName(name)
|
||||||
|
return id
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Send chat message to a specific player using the CLIENT object.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string Message The text message
|
||||||
|
-- @param Wrapper.Client#CLIENT ToClient Client receiving the message
|
||||||
|
-- @param Wrapper.Client#CLIENT FromClient (Optional) Client sending the message
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:SendChatToClient(Message, ToClient, FromClient)
|
||||||
|
local PlayerId = self:GetPlayerIDFromClient(ToClient)
|
||||||
|
local FromId = self:GetPlayerIDFromClient(FromClient)
|
||||||
|
if Message and PlayerId and FromId then
|
||||||
|
net.send_chat_to(Message, tonumber(PlayerId) , tonumber(FromId))
|
||||||
|
elseif Message and PlayerId then
|
||||||
|
net.send_chat_to(Message, tonumber(PlayerId))
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Send chat message to a specific player using the player name
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string Message The text message
|
||||||
|
-- @param #string ToPlayer Player receiving the message
|
||||||
|
-- @param #string FromPlayer(Optional) Player sending the message
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:SendChatToPlayer(Message, ToPlayer, FromPlayer)
|
||||||
|
local PlayerId = self:GetPlayerIDByName(ToPlayer)
|
||||||
|
local FromId = self:GetPlayerIDByName(FromPlayer)
|
||||||
|
if Message and PlayerId and FromId then
|
||||||
|
net.send_chat_to(Message, tonumber(PlayerId) , tonumber(FromId))
|
||||||
|
elseif Message and PlayerId then
|
||||||
|
net.send_chat_to(Message, tonumber(PlayerId))
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Load a specific mission.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string Path and Mission
|
||||||
|
-- @return #boolean success
|
||||||
|
-- @usage
|
||||||
|
-- mynet:LoadMission(lfs.writeDir() .. 'Missions\\' .. 'MyTotallyAwesomeMission.miz')
|
||||||
|
function NET:LoadMission(Path)
|
||||||
|
local outcome = false
|
||||||
|
if Path then
|
||||||
|
outcome = net.load_mission(Path)
|
||||||
|
end
|
||||||
|
return outcome
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Load next mission. Returns false if at the end of list.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @return #boolean success
|
||||||
|
function NET:LoadNextMission()
|
||||||
|
local outcome = false
|
||||||
|
outcome = net.load_next_mission()
|
||||||
|
return outcome
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Return a table of players currently connected to the server.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @return #table PlayerList
|
||||||
|
function NET:GetPlayerList()
|
||||||
|
local plist = nil
|
||||||
|
plist = net.get_player_list()
|
||||||
|
return plist
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns the playerID of the local player. Always returns 1 for server.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @return #number ID
|
||||||
|
function NET:GetMyPlayerID()
|
||||||
|
return net.get_my_player_id()
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns the playerID of the server. Currently always returns 1.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @return #number ID
|
||||||
|
function NET:GetServerID()
|
||||||
|
return net.get_server_id()
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Return a table of attributes for a given client. If optional attribute is present, only that value is returned.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client The client.
|
||||||
|
-- @param #string Attribute (Optional) The attribute to obtain. List see below.
|
||||||
|
-- @return #table PlayerInfo or nil if it cannot be found
|
||||||
|
-- @usage
|
||||||
|
-- Table holds these attributes:
|
||||||
|
--
|
||||||
|
-- 'id' : playerID
|
||||||
|
-- 'name' : player name
|
||||||
|
-- 'side' : 0 - spectators, 1 - red, 2 - blue
|
||||||
|
-- 'slot' : slotID of the player or
|
||||||
|
-- 'ping' : ping of the player in ms
|
||||||
|
-- 'ipaddr': IP address of the player, SERVER ONLY
|
||||||
|
-- 'ucid' : Unique Client Identifier, SERVER ONLY
|
||||||
|
--
|
||||||
|
function NET:GetPlayerInfo(Client,Attribute)
|
||||||
|
local PlayerID = self:GetPlayerIDFromClient(Client)
|
||||||
|
if PlayerID then
|
||||||
|
return net.get_player_info(tonumber(PlayerID), Attribute)
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get player UCID from player CLIENT object or player name. Provide either one.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client The client object to be used.
|
||||||
|
-- @param #string Name Player name to be used.
|
||||||
|
-- @return #boolean success
|
||||||
|
function NET:GetPlayerUCID(Client,Name)
|
||||||
|
local PlayerID = nil
|
||||||
|
if Client then
|
||||||
|
PlayerID = self:GetPlayerIDFromClient(Client)
|
||||||
|
elseif Name then
|
||||||
|
PlayerID = self:GetPlayerIDByName(Name)
|
||||||
|
else
|
||||||
|
self:E(self.lid.."Neither client nor name provided!")
|
||||||
|
end
|
||||||
|
local ucid = net.get_player_info(tonumber(PlayerID), 'ucid')
|
||||||
|
return ucid
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Kicks a player from the server. Can display a message to the user.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client The client
|
||||||
|
-- @param #string Message (Optional) The message to send.
|
||||||
|
-- @return #boolean success
|
||||||
|
function NET:Kick(Client,Message)
|
||||||
|
local PlayerID = self:GetPlayerIDFromClient(Client)
|
||||||
|
if PlayerID and tonumber(PlayerID) ~= 1 then
|
||||||
|
return net.kick(tonumber(PlayerID), Message)
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Return a statistic for a given client.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client The client
|
||||||
|
-- @param #number StatisticID The statistic to obtain
|
||||||
|
-- @return #number Statistic or nil
|
||||||
|
-- @usage
|
||||||
|
-- StatisticIDs are:
|
||||||
|
--
|
||||||
|
-- net.PS_PING (0) - ping (in ms)
|
||||||
|
-- net.PS_CRASH (1) - number of crashes
|
||||||
|
-- net.PS_CAR (2) - number of destroyed vehicles
|
||||||
|
-- net.PS_PLANE (3) - ... planes/helicopters
|
||||||
|
-- net.PS_SHIP (4) - ... ships
|
||||||
|
-- net.PS_SCORE (5) - total score
|
||||||
|
-- net.PS_LAND (6) - number of landings
|
||||||
|
-- net.PS_EJECT (7) - of ejects
|
||||||
|
--
|
||||||
|
-- mynet:GetPlayerStatistic(Client,7) -- return number of ejects
|
||||||
|
function NET:GetPlayerStatistic(Client,StatisticID)
|
||||||
|
local PlayerID = self:GetPlayerIDFromClient(Client)
|
||||||
|
local stats = StatisticID or 0
|
||||||
|
if stats > 7 or stats < 0 then stats = 0 end
|
||||||
|
if PlayerID then
|
||||||
|
return net.get_stat(tonumber(PlayerID),stats)
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Return the name of a given client. Effectively the same as CLIENT:GetPlayerName().
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client The client
|
||||||
|
-- @return #string Name or nil if not obtainable
|
||||||
|
function NET:GetName(Client)
|
||||||
|
local PlayerID = self:GetPlayerIDFromClient(Client)
|
||||||
|
if PlayerID then
|
||||||
|
return net.get_name(tonumber(PlayerID))
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns the SideId and SlotId of a given client.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client The client
|
||||||
|
-- @return #number SideID i.e. 0 : spectators, 1 : Red, 2 : Blue
|
||||||
|
-- @return #number SlotID
|
||||||
|
function NET:GetSlot(Client)
|
||||||
|
local PlayerID = self:GetPlayerIDFromClient(Client)
|
||||||
|
if PlayerID then
|
||||||
|
local side,slot = net.get_slot(tonumber(PlayerID))
|
||||||
|
return side,slot
|
||||||
|
else
|
||||||
|
return nil,nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Force the slot for a specific client.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client The client
|
||||||
|
-- @param #number SideID i.e. 0 : spectators, 1 : Red, 2 : Blue
|
||||||
|
-- @param #number SlotID Slot number
|
||||||
|
-- @return #boolean Success
|
||||||
|
function NET:ForceSlot(Client,SideID,SlotID)
|
||||||
|
local PlayerID = self:GetPlayerIDFromClient(Client)
|
||||||
|
if PlayerID and tonumber(PlayerID) ~= 1 then
|
||||||
|
return net.force_player_slot(tonumber(PlayerID), SideID, SlotID or '' )
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Force a client back to spectators.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client The client
|
||||||
|
-- @return #boolean Succes
|
||||||
|
function NET:ReturnToSpectators(Client)
|
||||||
|
local outcome = self:ForceSlot(Client,0)
|
||||||
|
return outcome
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Converts a lua value to a JSON string.
|
||||||
|
-- @param #string Lua Anything lua
|
||||||
|
-- @return #table Json
|
||||||
|
function NET.Lua2Json(Lua)
|
||||||
|
return net.lua2json(Lua)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Converts a JSON string to a lua value.
|
||||||
|
-- @param #string Json Anything JSON
|
||||||
|
-- @return #table Lua
|
||||||
|
function NET.Lua2Json(Json)
|
||||||
|
return net.json2lua(Json)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Executes a lua string in a given lua environment in the game.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string State The state in which to execute - see below.
|
||||||
|
-- @param #string DoString The lua string to be executed.
|
||||||
|
-- @return #string Output
|
||||||
|
-- @usage
|
||||||
|
-- States are:
|
||||||
|
-- 'config': the state in which $INSTALL_DIR/Config/main.cfg is executed, as well as $WRITE_DIR/Config/autoexec.cfg - used for configuration settings
|
||||||
|
-- 'mission': holds current mission
|
||||||
|
-- 'export': runs $WRITE_DIR/Scripts/Export.lua and the relevant export API
|
||||||
|
function NET:DoStringIn(State,DoString)
|
||||||
|
return net.dostring_in(State,DoString)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Write an "INFO" entry to the DCS log file, with the message Message.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string Message The message to be logged.
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:Log(Message)
|
||||||
|
net.log(Message)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get some data of pilots who have currently joined
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client Provide either the client object whose data to find **or**
|
||||||
|
-- @param #string Name The player name whose data to find
|
||||||
|
-- @return #table Table of #NET.PlayerData or nil if not found
|
||||||
|
function NET:GetKnownPilotData(Client,Name)
|
||||||
|
local name = Name
|
||||||
|
if Client and not Name then
|
||||||
|
name = Client:GetPlayerName()
|
||||||
|
end
|
||||||
|
if name then
|
||||||
|
return self.KnownPilots[name]
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Status - housekeeping
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string From
|
||||||
|
-- @param #string Event
|
||||||
|
-- @param #string To
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:onafterStatus(From,Event,To)
|
||||||
|
self:I({From,Event,To})
|
||||||
|
|
||||||
|
local function HouseHold(tavolo)
|
||||||
|
local TNow = timer.getTime()
|
||||||
|
for _,entry in pairs (tavolo) do
|
||||||
|
if entry >= TNow then entry = nil end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
HouseHold(self.BlockedPilots)
|
||||||
|
HouseHold(self.BlockedSides)
|
||||||
|
HouseHold(self.BlockedSlots)
|
||||||
|
HouseHold(self.BlockedUCIDs)
|
||||||
|
|
||||||
|
if self:Is("Running") then
|
||||||
|
self:__Status(-60)
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Stop the event functions
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string From
|
||||||
|
-- @param #string Event
|
||||||
|
-- @param #string To
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:onafterRun(From,Event,To)
|
||||||
|
self:I({From,Event,To})
|
||||||
|
self:HandleEvent(EVENTS.PlayerEnterUnit,self._EventHandler)
|
||||||
|
self:HandleEvent(EVENTS.PlayerEnterAircraft,self._EventHandler)
|
||||||
|
self:HandleEvent(EVENTS.PlayerLeaveUnit,self._EventHandler)
|
||||||
|
self:HandleEvent(EVENTS.PilotDead,self._EventHandler)
|
||||||
|
self:HandleEvent(EVENTS.Ejection,self._EventHandler)
|
||||||
|
self:HandleEvent(EVENTS.Crash,self._EventHandler)
|
||||||
|
self:HandleEvent(EVENTS.SelfKillPilot,self._EventHandler)
|
||||||
|
self:__Status(-30)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Stop the event functions
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string From
|
||||||
|
-- @param #string Event
|
||||||
|
-- @param #string To
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:onafterStop(From,Event,To)
|
||||||
|
self:T({From,Event,To})
|
||||||
|
self:UnHandleEvent(EVENTS.PlayerEnterUnit)
|
||||||
|
self:UnHandleEvent(EVENTS.PlayerEnterAircraft)
|
||||||
|
self:UnHandleEvent(EVENTS.PlayerLeaveUnit)
|
||||||
|
self:UnHandleEvent(EVENTS.PilotDead)
|
||||||
|
self:UnHandleEvent(EVENTS.Ejection)
|
||||||
|
self:UnHandleEvent(EVENTS.Crash)
|
||||||
|
self:UnHandleEvent(EVENTS.SelfKillPilot)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- End of NET
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
end
|
||||||
@@ -394,6 +394,35 @@ function POSITIONABLE:GetCoordinate()
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Triggers an explosion at the coordinates of the positionable.
|
||||||
|
-- @param #POSITIONABLE self
|
||||||
|
-- @param #number power Power of the explosion in kg TNT. Default 100 kg TNT.
|
||||||
|
-- @param #number delay (Optional) Delay of explosion in seconds.
|
||||||
|
-- @return #POSITIONABLE self
|
||||||
|
function POSITIONABLE:Explode(power, delay)
|
||||||
|
|
||||||
|
-- Default.
|
||||||
|
power=power or 100
|
||||||
|
|
||||||
|
-- Check if delay or not.
|
||||||
|
if delay and delay>0 then
|
||||||
|
-- Delayed call.
|
||||||
|
self:ScheduleOnce(delay, POSITIONABLE.Explode, self, power, 0)
|
||||||
|
else
|
||||||
|
|
||||||
|
local coord=self:GetCoord()
|
||||||
|
|
||||||
|
if coord then
|
||||||
|
-- Create an explotion at the coordinate of the positionable.
|
||||||
|
coord:Explosion(power)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Returns a COORDINATE object, which is offset with respect to the orientation of the POSITIONABLE.
|
--- Returns a COORDINATE object, which is offset with respect to the orientation of the POSITIONABLE.
|
||||||
-- @param #POSITIONABLE self
|
-- @param #POSITIONABLE self
|
||||||
-- @param #number x Offset in the direction "the nose" of the unit is pointing in meters. Default 0 m.
|
-- @param #number x Offset in the direction "the nose" of the unit is pointing in meters. Default 0 m.
|
||||||
@@ -703,11 +732,11 @@ function POSITIONABLE:IsSubmarine()
|
|||||||
|
|
||||||
if DCSUnit then
|
if DCSUnit then
|
||||||
local UnitDescriptor = DCSUnit:getDesc()
|
local UnitDescriptor = DCSUnit:getDesc()
|
||||||
if UnitDescriptor.attributes["Submarines"] == true then
|
if UnitDescriptor.attributes["Submarines"] == true then
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self:E( { "Cannot check IsSubmarine", Positionable = self, Alive = self:IsAlive() } )
|
self:E( { "Cannot check IsSubmarine", Positionable = self, Alive = self:IsAlive() } )
|
||||||
@@ -845,6 +874,78 @@ function POSITIONABLE:GetVelocityKNOTS()
|
|||||||
return UTILS.MpsToKnots( self:GetVelocityMPS() )
|
return UTILS.MpsToKnots( self:GetVelocityMPS() )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Returns the true airspeed (TAS). This is calculated from the current velocity minus wind in 3D.
|
||||||
|
-- @param #POSITIONABLE self
|
||||||
|
-- @return #number TAS in m/s. Returns 0 if the POSITIONABLE does not exist.
|
||||||
|
function POSITIONABLE:GetAirspeedTrue()
|
||||||
|
|
||||||
|
-- TAS
|
||||||
|
local tas=0
|
||||||
|
|
||||||
|
-- Get current coordinate.
|
||||||
|
local coord=self:GetCoord()
|
||||||
|
|
||||||
|
if coord then
|
||||||
|
|
||||||
|
-- Altitude in meters.
|
||||||
|
local alt=coord.y
|
||||||
|
|
||||||
|
-- Wind velocity vector.
|
||||||
|
local wvec3=coord:GetWindVec3(alt, false)
|
||||||
|
|
||||||
|
-- Velocity vector.
|
||||||
|
local vvec3=self:GetVelocityVec3()
|
||||||
|
|
||||||
|
--GS=TAS+WIND ==> TAS=GS-WIND
|
||||||
|
local tasvec3=UTILS.VecSubstract(vvec3, wvec3)
|
||||||
|
|
||||||
|
-- True airspeed in m/s
|
||||||
|
tas=UTILS.VecNorm(tasvec3)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return tas
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns the indicated airspeed (IAS).
|
||||||
|
-- The IAS is calculated from the TAS under the approximation that TAS increases by ~2% with every 1000 feet altitude ASL.
|
||||||
|
-- @param #POSITIONABLE self
|
||||||
|
-- @param #number oatcorr (Optional) Outside air temperature (OAT) correction factor. Default 0.017 (=1.7%).
|
||||||
|
-- @return #number IAS in m/s. Returns 0 if the POSITIONABLE does not exist.
|
||||||
|
function POSITIONABLE:GetAirspeedIndicated(oatcorr)
|
||||||
|
|
||||||
|
-- Get true airspeed.
|
||||||
|
local tas=self:GetAirspeedTrue()
|
||||||
|
|
||||||
|
-- Get altitude.
|
||||||
|
local altitude=self:GetAltitude()
|
||||||
|
|
||||||
|
-- Convert TAS to IAS.
|
||||||
|
local ias=UTILS.TasToIas(tas, altitude, oatcorr)
|
||||||
|
|
||||||
|
return ias
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns the horizonal speed relative to eath's surface. The vertical component of the velocity vector is projected out (set to zero).
|
||||||
|
-- @param #POSITIONABLE self
|
||||||
|
-- @return #number Ground speed in m/s. Returns 0 if the POSITIONABLE does not exist.
|
||||||
|
function POSITIONABLE:GetGroundSpeed()
|
||||||
|
|
||||||
|
local gs=0
|
||||||
|
|
||||||
|
local vel=self:GetVelocityVec3()
|
||||||
|
|
||||||
|
if vel then
|
||||||
|
|
||||||
|
local vec2={x=vel.x, y=vel.z}
|
||||||
|
|
||||||
|
gs=UTILS.Vec2Norm(vel)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return gs
|
||||||
|
end
|
||||||
|
|
||||||
--- Returns the Angle of Attack of a POSITIONABLE.
|
--- Returns the Angle of Attack of a POSITIONABLE.
|
||||||
-- @param #POSITIONABLE self
|
-- @param #POSITIONABLE self
|
||||||
-- @return #number Angle of attack in degrees.
|
-- @return #number Angle of attack in degrees.
|
||||||
@@ -1696,6 +1797,7 @@ do -- Cargo
|
|||||||
["tt_DSHK"] = 6,
|
["tt_DSHK"] = 6,
|
||||||
["HL_KORD"] = 6,
|
["HL_KORD"] = 6,
|
||||||
["HL_DSHK"] = 6,
|
["HL_DSHK"] = 6,
|
||||||
|
["CCKW_353"] = 16, --GMC CCKW 2½-ton 6×6 truck, estimating 16 soldiers
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Assuming that each passenger weighs 95 kg on average.
|
-- Assuming that each passenger weighs 95 kg on average.
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
--
|
--
|
||||||
-- ### Author: **FlightControl**
|
-- ### Author: **FlightControl**
|
||||||
--
|
--
|
||||||
-- ### Contributions: **Applevangelist**
|
-- ### Contributions: **Applevangelist**, **funkyfranky**
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -12,12 +12,12 @@
|
|||||||
-- @image Wrapper_Scenery.JPG
|
-- @image Wrapper_Scenery.JPG
|
||||||
|
|
||||||
|
|
||||||
|
--- SCENERY Class
|
||||||
--- @type SCENERY
|
-- @type SCENERY
|
||||||
-- @field #string ClassName
|
-- @field #string ClassName Name of the class.
|
||||||
-- @field #string SceneryName
|
-- @field #string SceneryName Name of the scenery object.
|
||||||
-- @field #DCS.Object SceneryObject
|
-- @field DCS#Object SceneryObject DCS scenery object.
|
||||||
-- @field #number Life0
|
-- @field #number Life0 Initial life points.
|
||||||
-- @extends Wrapper.Positionable#POSITIONABLE
|
-- @extends Wrapper.Positionable#POSITIONABLE
|
||||||
|
|
||||||
|
|
||||||
@@ -37,12 +37,16 @@ SCENERY = {
|
|||||||
--- Register scenery object as POSITIONABLE.
|
--- Register scenery object as POSITIONABLE.
|
||||||
--@param #SCENERY self
|
--@param #SCENERY self
|
||||||
--@param #string SceneryName Scenery name.
|
--@param #string SceneryName Scenery name.
|
||||||
--@param #DCS.Object SceneryObject DCS scenery object.
|
--@param DCS#Object SceneryObject DCS scenery object.
|
||||||
--@return #SCENERY Scenery object.
|
--@return #SCENERY Scenery object.
|
||||||
function SCENERY:Register( SceneryName, SceneryObject )
|
function SCENERY:Register( SceneryName, SceneryObject )
|
||||||
|
|
||||||
local self = BASE:Inherit( self, POSITIONABLE:New( SceneryName ) )
|
local self = BASE:Inherit( self, POSITIONABLE:New( SceneryName ) )
|
||||||
|
|
||||||
self.SceneryName = SceneryName
|
self.SceneryName = SceneryName
|
||||||
|
|
||||||
self.SceneryObject = SceneryObject
|
self.SceneryObject = SceneryObject
|
||||||
|
|
||||||
if self.SceneryObject then
|
if self.SceneryObject then
|
||||||
self.Life0 = self.SceneryObject:getLife()
|
self.Life0 = self.SceneryObject:getLife()
|
||||||
else
|
else
|
||||||
@@ -53,23 +57,29 @@ end
|
|||||||
|
|
||||||
--- Obtain DCS Object from the SCENERY Object.
|
--- Obtain DCS Object from the SCENERY Object.
|
||||||
--@param #SCENERY self
|
--@param #SCENERY self
|
||||||
--@return #DCS.Object DCS scenery object.
|
--@return DCS#Object DCS scenery object.
|
||||||
function SCENERY:GetDCSObject()
|
function SCENERY:GetDCSObject()
|
||||||
return self.SceneryObject
|
return self.SceneryObject
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get current life points from the SCENERY Object.
|
--- Get current life points from the SCENERY Object.
|
||||||
|
-- **CAVEAT**: Some objects change their life value or "hitpoints" **after** the first hit. Hence we will adjust the life0 value to 120%
|
||||||
|
-- of the last life value if life exceeds life0 (initial life) at any point. Thus will will get a smooth percentage decrease, if you use this e.g. as success
|
||||||
|
-- criteria for a bombing task.
|
||||||
--@param #SCENERY self
|
--@param #SCENERY self
|
||||||
--@return #number life
|
--@return #number life
|
||||||
function SCENERY:GetLife()
|
function SCENERY:GetLife()
|
||||||
local life = 0
|
local life = 0
|
||||||
if self.SceneryObject then
|
if self.SceneryObject then
|
||||||
life = self.SceneryObject:getLife()
|
life = self.SceneryObject:getLife()
|
||||||
|
if life > self.Life0 then
|
||||||
|
self.Life0 = math.floor(life * 1.2)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return life
|
return life
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get current initial life points from the SCENERY Object.
|
--- Get initial life points of the SCENERY Object.
|
||||||
--@param #SCENERY self
|
--@param #SCENERY self
|
||||||
--@return #number life
|
--@return #number life
|
||||||
function SCENERY:GetLife0()
|
function SCENERY:GetLife0()
|
||||||
@@ -90,7 +100,7 @@ function SCENERY:IsDead()
|
|||||||
return self:GetLife() < 1 and true or false
|
return self:GetLife() < 1 and true or false
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get the threat level of a SCENERY object. Always 0.
|
--- Get the threat level of a SCENERY object. Always 0 as scenery does not pose a threat to anyone.
|
||||||
--@param #SCENERY self
|
--@param #SCENERY self
|
||||||
--@return #number Threat level 0.
|
--@return #number Threat level 0.
|
||||||
--@return #string "Scenery".
|
--@return #string "Scenery".
|
||||||
@@ -106,11 +116,13 @@ end
|
|||||||
--@param #number Radius (optional) Search radius around coordinate, defaults to 100
|
--@param #number Radius (optional) Search radius around coordinate, defaults to 100
|
||||||
--@return #SCENERY Scenery Object or `nil` if it cannot be found
|
--@return #SCENERY Scenery Object or `nil` if it cannot be found
|
||||||
function SCENERY:FindByName(Name, Coordinate, Radius)
|
function SCENERY:FindByName(Name, Coordinate, Radius)
|
||||||
|
|
||||||
local radius = Radius or 100
|
local radius = Radius or 100
|
||||||
local name = Name or "unknown"
|
local name = Name or "unknown"
|
||||||
local scenery = nil
|
local scenery = nil
|
||||||
|
|
||||||
|
BASE:T({name, radius, Coordinate:GetVec2()})
|
||||||
|
|
||||||
---
|
---
|
||||||
-- @param Core.Point#COORDINATE coordinate
|
-- @param Core.Point#COORDINATE coordinate
|
||||||
-- @param #number radius
|
-- @param #number radius
|
||||||
@@ -166,6 +178,7 @@ function SCENERY:FindByZoneName( ZoneName )
|
|||||||
zone = ZONE:FindByName(ZoneName)
|
zone = ZONE:FindByName(ZoneName)
|
||||||
end
|
end
|
||||||
local _id = zone:GetProperty('OBJECT ID')
|
local _id = zone:GetProperty('OBJECT ID')
|
||||||
|
BASE:T("Object ID ".._id)
|
||||||
if not _id then
|
if not _id then
|
||||||
-- this zone has no object ID
|
-- this zone has no object ID
|
||||||
BASE:E("**** Zone without object ID: "..ZoneName.." | Type: "..tostring(zone.ClassName))
|
BASE:E("**** Zone without object ID: "..ZoneName.." | Type: "..tostring(zone.ClassName))
|
||||||
@@ -231,4 +244,4 @@ end
|
|||||||
--@return #SCENERY self
|
--@return #SCENERY self
|
||||||
function SCENERY:Destroy()
|
function SCENERY:Destroy()
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -89,9 +89,9 @@
|
|||||||
--
|
--
|
||||||
-- @field #UNIT
|
-- @field #UNIT
|
||||||
UNIT = {
|
UNIT = {
|
||||||
ClassName="UNIT",
|
ClassName="UNIT",
|
||||||
UnitName=nil,
|
UnitName=nil,
|
||||||
GroupName=nil,
|
GroupName=nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -167,7 +167,7 @@ end
|
|||||||
|
|
||||||
--- Get the DCS unit object.
|
--- Get the DCS unit object.
|
||||||
-- @param #UNIT self
|
-- @param #UNIT self
|
||||||
-- @return DCS#Unit
|
-- @return DCS#Unit The DCS unit object.
|
||||||
function UNIT:GetDCSObject()
|
function UNIT:GetDCSObject()
|
||||||
|
|
||||||
local DCSUnit = Unit.getByName( self.UnitName )
|
local DCSUnit = Unit.getByName( self.UnitName )
|
||||||
@@ -325,14 +325,19 @@ function UNIT:IsAlive()
|
|||||||
local DCSUnit = self:GetDCSObject() -- DCS#Unit
|
local DCSUnit = self:GetDCSObject() -- DCS#Unit
|
||||||
|
|
||||||
if DCSUnit then
|
if DCSUnit then
|
||||||
local UnitIsAlive = DCSUnit:isExist() and DCSUnit:isActive()
|
local UnitIsAlive = DCSUnit:isExist() and DCSUnit:isActive() -- and DCSUnit:getLife() > 1
|
||||||
return UnitIsAlive
|
return UnitIsAlive
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Returns if the Unit is dead.
|
||||||
|
-- @param #UNIT self
|
||||||
|
-- @return #boolean `true` if Unit is dead, else false or nil if the unit does not exist
|
||||||
|
function UNIT:IsDead()
|
||||||
|
return not self:IsAlive()
|
||||||
|
end
|
||||||
|
|
||||||
--- Returns the Unit's callsign - the localized string.
|
--- Returns the Unit's callsign - the localized string.
|
||||||
-- @param #UNIT self
|
-- @param #UNIT self
|
||||||
@@ -626,7 +631,7 @@ function UNIT:IsFuelSupply()
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns the unit's group if it exist and nil otherwise.
|
--- Returns the unit's group if it exists and nil otherwise.
|
||||||
-- @param Wrapper.Unit#UNIT self
|
-- @param Wrapper.Unit#UNIT self
|
||||||
-- @return Wrapper.Group#GROUP The Group of the Unit or `nil` if the unit does not exist.
|
-- @return Wrapper.Group#GROUP The Group of the Unit or `nil` if the unit does not exist.
|
||||||
function UNIT:GetGroup()
|
function UNIT:GetGroup()
|
||||||
@@ -915,7 +920,7 @@ function UNIT:GetLife()
|
|||||||
|
|
||||||
local DCSUnit = self:GetDCSObject()
|
local DCSUnit = self:GetDCSObject()
|
||||||
|
|
||||||
if DCSUnit then
|
if DCSUnit and DCSUnit:isExist() then
|
||||||
local UnitLife = DCSUnit:getLife()
|
local UnitLife = DCSUnit:getLife()
|
||||||
return UnitLife
|
return UnitLife
|
||||||
end
|
end
|
||||||
@@ -967,6 +972,24 @@ function UNIT:GetDamageRelative()
|
|||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Returns the current value for an animation argument on the external model of the given object.
|
||||||
|
-- Each model animation has an id tied to with different values representing different states of the model.
|
||||||
|
-- Animation arguments can be figured out by opening the respective 3d model in the modelviewer.
|
||||||
|
-- @param #UNIT self
|
||||||
|
-- @param #number AnimationArgument Number corresponding to the animated part of the unit.
|
||||||
|
-- @return #number Value of the animation argument [-1, 1]. If draw argument value is invalid for the unit in question a value of 0 will be returned.
|
||||||
|
function UNIT:GetDrawArgumentValue(AnimationArgument)
|
||||||
|
|
||||||
|
local DCSUnit = self:GetDCSObject()
|
||||||
|
|
||||||
|
if DCSUnit then
|
||||||
|
local value = DCSUnit:getDrawArgumentValue(AnimationArgument or 0)
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
--- Returns the category of the #UNIT from descriptor. Returns one of
|
--- Returns the category of the #UNIT from descriptor. Returns one of
|
||||||
--
|
--
|
||||||
-- * Unit.Category.AIRPLANE
|
-- * Unit.Category.AIRPLANE
|
||||||
|
|||||||
839
Moose Development/Moose/Wrapper/Weapon.lua
Normal file
839
Moose Development/Moose/Wrapper/Weapon.lua
Normal file
@@ -0,0 +1,839 @@
|
|||||||
|
--- **Wrapper** - Weapon functions.
|
||||||
|
--
|
||||||
|
-- ## Main Features:
|
||||||
|
--
|
||||||
|
-- * Convenient access to DCS API functions
|
||||||
|
-- * Track weapon and get impact position
|
||||||
|
-- * Get launcher and target of weapon
|
||||||
|
-- * Define callback function when weapon impacts
|
||||||
|
-- * Define callback function when tracking weapon
|
||||||
|
-- * Mark impact points on F10 map
|
||||||
|
-- * Put coloured smoke on impact points
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ## Example Missions:
|
||||||
|
--
|
||||||
|
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Wrapper%20-%20Weapon).
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Author: **funkyfranky**
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
-- @module Wrapper.Weapon
|
||||||
|
-- @image Wrapper_Weapon.png
|
||||||
|
|
||||||
|
|
||||||
|
--- WEAPON class.
|
||||||
|
-- @type WEAPON
|
||||||
|
-- @field #string ClassName Name of the class.
|
||||||
|
-- @field #number verbose Verbosity level.
|
||||||
|
-- @field #string lid Class id string for output to DCS log file.
|
||||||
|
-- @field DCS#Weapon weapon The DCS weapon object.
|
||||||
|
-- @field #string name Name of the weapon object.
|
||||||
|
-- @field #string typeName Type name of the weapon.
|
||||||
|
-- @field #number category Weapon category 0=SHELL, 1=MISSILE, 2=ROCKET, 3=BOMB, 4=TORPEDO (Weapon.Category.X).
|
||||||
|
-- @field #number categoryMissile Missile category 0=AAM, 1=SAM, 2=BM, 3=ANTI_SHIP, 4=CRUISE, 5=OTHER (Weapon.MissileCategory.X).
|
||||||
|
-- @field #number coalition Coalition ID.
|
||||||
|
-- @field #number country Country ID.
|
||||||
|
-- @field DCS#Desc desc Descriptor table.
|
||||||
|
-- @field DCS#Unit launcher Launcher DCS unit.
|
||||||
|
-- @field Wrapper.Unit#UNIT launcherUnit Launcher Unit.
|
||||||
|
-- @field #string launcherName Name of launcher unit.
|
||||||
|
-- @field #number dtTrack Time step in seconds for tracking scheduler.
|
||||||
|
-- @field #function impactFunc Callback function for weapon impact.
|
||||||
|
-- @field #table impactArg Optional arguments for the impact callback function.
|
||||||
|
-- @field #function trackFunc Callback function when weapon is tracked and alive.
|
||||||
|
-- @field #table trackArg Optional arguments for the track callback function.
|
||||||
|
-- @field DCS#Vec3 vec3 Last known 3D position vector of the tracked weapon.
|
||||||
|
-- @field DCS#Position3 pos3 Last known 3D position and direction vector of the tracked weapon.
|
||||||
|
-- @field Core.Point#COORDINATE coordinate Coordinate object of the weapon. Can be used in other classes.
|
||||||
|
-- @field DCS#Vec3 impactVec3 Impact 3D vector.
|
||||||
|
-- @field Core.Point#COORDINATE impactCoord Impact coordinate.
|
||||||
|
-- @field #number trackScheduleID Tracking scheduler ID. Can be used to remove/destroy the scheduler function.
|
||||||
|
-- @field #boolean tracking If `true`, scheduler will keep tracking. Otherwise, function will return nil and stop tracking.
|
||||||
|
-- @field #boolean impactMark If `true`, the impact point is marked on the F10 map. Requires tracking to be started.
|
||||||
|
-- @field #boolean impactSmoke If `true`, the impact point is marked by smoke. Requires tracking to be started.
|
||||||
|
-- @field #number impactSmokeColor Colour of impact point smoke.
|
||||||
|
-- @field #boolean impactDestroy If `true`, destroy weapon before impact. Requires tracking to be started and sufficiently small time step.
|
||||||
|
-- @field #number impactDestroyDist Distance in meters to the estimated impact point. If smaller, then weapon is destroyed.
|
||||||
|
-- @field #number distIP Distance in meters for the intercept point estimation.
|
||||||
|
-- @field Wrapper.Unit#UNIT target Last known target.
|
||||||
|
-- @extends Wrapper.Positionable#POSITIONABLE
|
||||||
|
|
||||||
|
--- *In the long run, the sharpest weapon of all is a kind and gentle spirit.* -- Anne Frank
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- # The WEAPON Concept
|
||||||
|
--
|
||||||
|
-- The WEAPON class offers an easy-to-use wrapper interface to all DCS API functions.
|
||||||
|
--
|
||||||
|
-- Probably, the most striking highlight is that the position of the weapon can be tracked and its impact position can be determined, which is not
|
||||||
|
-- possible with the native DCS scripting engine functions.
|
||||||
|
--
|
||||||
|
-- **Note** that this wrapper class is different from most others as weapon objects cannot be found with a DCS API function like `getByName()`.
|
||||||
|
-- They can only be found in DCS events like the "Shot" event, where the weapon object is contained in the event data.
|
||||||
|
--
|
||||||
|
-- # Tracking
|
||||||
|
--
|
||||||
|
-- The status of the weapon can be tracked with the @{#WEAPON.StartTrack} function. This function will try to determin the position of the weapon in (normally) relatively
|
||||||
|
-- small time steps. The time step can be set via the @{#WEAPON.SetTimeStepTrack} function and is by default set to 0.01 seconds.
|
||||||
|
--
|
||||||
|
-- Once the position cannot be retrieved any more, the weapon has impacted (or was destroyed otherwise) and the last known position is safed as the impact point.
|
||||||
|
-- The impact point can be accessed with the @{#WEAPON.GetImpactVec3} or @{#WEAPON.GetImpactCoordinate} functions.
|
||||||
|
--
|
||||||
|
-- ## Impact Point Marking
|
||||||
|
--
|
||||||
|
-- You can mark the impact point on the F10 map with @{#WEAPON.SetMarkImpact}.
|
||||||
|
--
|
||||||
|
-- You can also trigger coloured smoke at the impact point via @{#WEAPON.SetSmokeImpact}.
|
||||||
|
--
|
||||||
|
-- ## Callback functions
|
||||||
|
--
|
||||||
|
-- It is possible to define functions that are called during the tracking of the weapon and upon impact, which help you to customize further actions.
|
||||||
|
--
|
||||||
|
-- ### Callback on Impact
|
||||||
|
--
|
||||||
|
-- The function called on impact can be set with @{#WEAPON.SetFuncImpact}
|
||||||
|
--
|
||||||
|
-- ### Callback when Tracking
|
||||||
|
--
|
||||||
|
-- The function called each time the weapon status is tracked can be set with @{#WEAPON.SetFuncTrack}
|
||||||
|
--
|
||||||
|
-- # Target
|
||||||
|
--
|
||||||
|
-- If the weapon has a specific target, you can get it with the @{#WEAPON.GetTarget} function. Note that the object, which is returned can vary. Normally, it is a UNIT
|
||||||
|
-- but it could also be a STATIC object.
|
||||||
|
--
|
||||||
|
-- Also note that the weapon does not always have a target, it can loose a target and re-aquire it and the target might change to another unit.
|
||||||
|
--
|
||||||
|
-- You can get the target name with the @{#WEAPON.GetTargetName} function.
|
||||||
|
--
|
||||||
|
-- The distance to the target is returned by the @{#WEAPON.GetTargetDistance} function.
|
||||||
|
--
|
||||||
|
-- # Category
|
||||||
|
--
|
||||||
|
-- The category (bomb, rocket, missile, shell, torpedo) of the weapon can be retrieved with the @{#WEAPON.GetCategory} function.
|
||||||
|
--
|
||||||
|
-- You can check if the weapon is a
|
||||||
|
--
|
||||||
|
-- * bomb with @{#WEAPON.IsBomb}
|
||||||
|
-- * rocket with @{#WEAPON.IsRocket}
|
||||||
|
-- * missile with @{#WEAPON.IsMissile}
|
||||||
|
-- * shell with @{#WEAPON.IsShell}
|
||||||
|
-- * torpedo with @{#WEAPON.IsTorpedo}
|
||||||
|
--
|
||||||
|
-- # Parameters
|
||||||
|
--
|
||||||
|
-- You can get various parameters of the weapon, *e.g.*
|
||||||
|
--
|
||||||
|
-- * position: @{#WEAPON.GetVec3}, @{#WEAPON.GetVec2 }, @{#WEAPON.GetCoordinate}
|
||||||
|
-- * speed: @{#WEAPON.GetSpeed}
|
||||||
|
-- * coalition: @{#WEAPON.GetCoalition}
|
||||||
|
-- * country: @{#WEAPON.GetCountry}
|
||||||
|
--
|
||||||
|
-- # Dependencies
|
||||||
|
--
|
||||||
|
-- This class is used (at least) in the MOOSE classes:
|
||||||
|
--
|
||||||
|
-- * RANGE (to determine the impact points of bombs and missiles)
|
||||||
|
-- * ARTY (to destroy and replace shells with smoke or illumination)
|
||||||
|
-- * FOX (to destroy the missile before it hits the target)
|
||||||
|
--
|
||||||
|
-- @field #WEAPON
|
||||||
|
WEAPON = {
|
||||||
|
ClassName = "WEAPON",
|
||||||
|
verbose = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
--- WEAPON class version.
|
||||||
|
-- @field #string version
|
||||||
|
WEAPON.version="0.1.0"
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- TODO list
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- TODO: A lot...
|
||||||
|
-- TODO: Destroy before impact.
|
||||||
|
-- TODO: Monitor target.
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Constructor
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Create a new WEAPON object from the DCS weapon object.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param DCS#Weapon WeaponObject The DCS weapon object.
|
||||||
|
-- @return #WEAPON self
|
||||||
|
function WEAPON:New(WeaponObject)
|
||||||
|
|
||||||
|
-- Nil check on object.
|
||||||
|
if WeaponObject==nil then
|
||||||
|
env.error("ERROR: Weapon object does NOT exist")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Inherit everything from FSM class.
|
||||||
|
local self=BASE:Inherit(self, POSITIONABLE:New("Weapon")) -- #WEAPON
|
||||||
|
|
||||||
|
-- Set DCS weapon object.
|
||||||
|
self.weapon=WeaponObject
|
||||||
|
|
||||||
|
-- Descriptors containing a lot of info.
|
||||||
|
self.desc=WeaponObject:getDesc()
|
||||||
|
|
||||||
|
-- This gives the object category which is always Object.Category.WEAPON!
|
||||||
|
--self.category=WeaponObject:getCategory()
|
||||||
|
|
||||||
|
-- Weapon category: 0=SHELL, 1=MISSILE, 2=ROCKET, 3=BOMB (Weapon.Category.X)
|
||||||
|
self.category = self.desc.category
|
||||||
|
|
||||||
|
if self:IsMissile() and self.desc.missileCategory then
|
||||||
|
self.categoryMissile=self.desc.missileCategory
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get type name.
|
||||||
|
self.typeName=WeaponObject:getTypeName() or "Unknown Type"
|
||||||
|
|
||||||
|
-- Get name of object. Usually a number like "1234567".
|
||||||
|
self.name=WeaponObject:getName()
|
||||||
|
|
||||||
|
-- Get coaliton of weapon.
|
||||||
|
self.coalition=WeaponObject:getCoalition()
|
||||||
|
|
||||||
|
-- Get country of weapon.
|
||||||
|
self.country=WeaponObject:getCountry()
|
||||||
|
|
||||||
|
-- Get DCS unit of the launcher.
|
||||||
|
self.launcher=WeaponObject:getLauncher()
|
||||||
|
|
||||||
|
-- Get launcher of weapon.
|
||||||
|
self.launcherName="Unknown Launcher"
|
||||||
|
if self.launcher then
|
||||||
|
self.launcherName=self.launcher:getName()
|
||||||
|
self.launcherUnit=UNIT:Find(self.launcher)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Init the coordinate of the weapon from that of the launcher.
|
||||||
|
self.coordinate=COORDINATE:NewFromVec3(self.launcher:getPoint())
|
||||||
|
|
||||||
|
-- Set log ID.
|
||||||
|
self.lid=string.format("[%s] %s | ", self.typeName, self.name)
|
||||||
|
|
||||||
|
-- Set default parameters
|
||||||
|
self:SetTimeStepTrack()
|
||||||
|
self:SetDistanceInterceptPoint()
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
local text=string.format("Weapon v%s\nName=%s, TypeName=%s, Category=%s, Coalition=%d, Country=%d, Launcher=%s",
|
||||||
|
self.version, self.name, self.typeName, self.category, self.coalition, self.country, self.launcherName)
|
||||||
|
self:T(self.lid..text)
|
||||||
|
|
||||||
|
-- Descriptors.
|
||||||
|
self:T2(self.desc)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- User API Functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Set verbosity level.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param #number VerbosityLevel Level of output (higher=more). Default 0.
|
||||||
|
-- @return #WEAPON self
|
||||||
|
function WEAPON:SetVerbosity(VerbosityLevel)
|
||||||
|
self.verbose=VerbosityLevel or 0
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set track position time step.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param #number TimeStep Time step in seconds when the position is updated. Default 0.01 sec ==> 100 evaluations per second.
|
||||||
|
-- @return #WEAPON self
|
||||||
|
function WEAPON:SetTimeStepTrack(TimeStep)
|
||||||
|
self.dtTrack=TimeStep or 0.01
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set distance of intercept point for estimated impact point.
|
||||||
|
-- If the weapon cannot be tracked any more, the intercept point from its last known position and direction is used to get
|
||||||
|
-- a better approximation of the impact point. Can be useful when using longer time steps in the tracking and still achieve
|
||||||
|
-- a good result on the impact point.
|
||||||
|
-- It uses the DCS function [getIP](https://wiki.hoggitworld.com/view/DCS_func_getIP).
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param #number Distance Distance in meters. Default is 50 m. Set to 0 to deactivate.
|
||||||
|
-- @return #WEAPON self
|
||||||
|
function WEAPON:SetDistanceInterceptPoint(Distance)
|
||||||
|
self.distIP=Distance or 50
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Mark impact point on the F10 map. This requires that the tracking has been started.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param #boolean Switch If `true` or nil, impact is marked.
|
||||||
|
-- @return #WEAPON self
|
||||||
|
function WEAPON:SetMarkImpact(Switch)
|
||||||
|
|
||||||
|
if Switch==false then
|
||||||
|
self.impactMark=false
|
||||||
|
else
|
||||||
|
self.impactMark=true
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Put smoke on impact point. This requires that the tracking has been started.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param #boolean Switch If `true` or nil, impact is smoked.
|
||||||
|
-- @param #number SmokeColor Color of smoke. Default is `SMOKECOLOR.Red`.
|
||||||
|
-- @return #WEAPON self
|
||||||
|
function WEAPON:SetSmokeImpact(Switch, SmokeColor)
|
||||||
|
|
||||||
|
if Switch==false then
|
||||||
|
self.impactSmoke=false
|
||||||
|
else
|
||||||
|
self.impactSmoke=true
|
||||||
|
end
|
||||||
|
|
||||||
|
self.impactSmokeColor=SmokeColor or SMOKECOLOR.Red
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set callback function when weapon is tracked and still alive. The first argument will be the WEAPON object.
|
||||||
|
-- Note that this can be called many times per second. So be careful for performance reasons.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param #function FuncTrack Function called during tracking.
|
||||||
|
-- @param ... Optional function arguments.
|
||||||
|
-- @return #WEAPON self
|
||||||
|
function WEAPON:SetFuncTrack(FuncTrack, ...)
|
||||||
|
self.trackFunc=FuncTrack
|
||||||
|
self.trackArg=arg or {}
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set callback function when weapon impacted or was destroyed otherwise, *i.e.* cannot be tracked any more.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param #function FuncImpact Function called once the weapon impacted.
|
||||||
|
-- @param ... Optional function arguments.
|
||||||
|
-- @return #WEAPON self
|
||||||
|
--
|
||||||
|
-- @usage
|
||||||
|
-- -- Function called on impact.
|
||||||
|
-- local function OnImpact(Weapon)
|
||||||
|
-- Weapon:GetImpactCoordinate():MarkToAll("Impact Coordinate of weapon")
|
||||||
|
-- end
|
||||||
|
--
|
||||||
|
-- -- Set which function to call.
|
||||||
|
-- myweapon:SetFuncImpact(OnImpact)
|
||||||
|
--
|
||||||
|
-- -- Start tracking.
|
||||||
|
-- myweapon:Track()
|
||||||
|
--
|
||||||
|
function WEAPON:SetFuncImpact(FuncImpact, ...)
|
||||||
|
self.impactFunc=FuncImpact
|
||||||
|
self.impactArg=arg or {}
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get the unit that launched the weapon.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return Wrapper.Unit#UNIT Laucher unit.
|
||||||
|
function WEAPON:GetLauncher()
|
||||||
|
return self.launcherUnit
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the target, which the weapon is guiding to.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return Wrapper.Object#OBJECT The target object, which can be a UNIT or STATIC object.
|
||||||
|
function WEAPON:GetTarget()
|
||||||
|
|
||||||
|
local target=nil --Wrapper.Object#OBJECT
|
||||||
|
|
||||||
|
if self.weapon then
|
||||||
|
|
||||||
|
-- Get the DCS target object, which can be a Unit, Weapon, Static, Scenery, Airbase.
|
||||||
|
local object=self.weapon:getTarget()
|
||||||
|
|
||||||
|
if object then
|
||||||
|
|
||||||
|
-- Get object category.
|
||||||
|
local category=object:getCategory()
|
||||||
|
|
||||||
|
--Target name
|
||||||
|
local name=object:getName()
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T(self.lid..string.format("Got Target Object %s, category=%d", object:getName(), category))
|
||||||
|
|
||||||
|
if category==Object.Category.UNIT then
|
||||||
|
|
||||||
|
target=UNIT:FindByName(name)
|
||||||
|
|
||||||
|
elseif category==Object.Category.STATIC then
|
||||||
|
|
||||||
|
target=STATIC:FindByName(name, false)
|
||||||
|
|
||||||
|
elseif category==Object.Category.SCENERY then
|
||||||
|
self:E(self.lid..string.format("ERROR: Scenery target not implemented yet!"))
|
||||||
|
else
|
||||||
|
self:E(self.lid..string.format("ERROR: Object category=%d is not implemented yet!", category))
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return target
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the distance to the current target the weapon is guiding to.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param #function ConversionFunction (Optional) Conversion function from meters to desired unit, *e.g.* `UTILS.MpsToKmph`.
|
||||||
|
-- @return #number Distance from weapon to target in meters.
|
||||||
|
function WEAPON:GetTargetDistance(ConversionFunction)
|
||||||
|
|
||||||
|
-- Get the target of the weapon.
|
||||||
|
local target=self:GetTarget() --Wrapper.Unit#UNIT
|
||||||
|
|
||||||
|
local distance=nil
|
||||||
|
if target then
|
||||||
|
|
||||||
|
-- Current position of target.
|
||||||
|
local tv3=target:GetVec3()
|
||||||
|
|
||||||
|
-- Current position of weapon.
|
||||||
|
local wv3=self:GetVec3()
|
||||||
|
|
||||||
|
if tv3 and wv3 then
|
||||||
|
distance=UTILS.VecDist3D(tv3, wv3)
|
||||||
|
|
||||||
|
if ConversionFunction then
|
||||||
|
distance=ConversionFunction(distance)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return distance
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get name the current target the weapon is guiding to.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return #string Name of the target or "None" if no target.
|
||||||
|
function WEAPON:GetTargetName()
|
||||||
|
|
||||||
|
-- Get the target of the weapon.
|
||||||
|
local target=self:GetTarget() --Wrapper.Unit#UNIT
|
||||||
|
|
||||||
|
local name="None"
|
||||||
|
if target then
|
||||||
|
name=target:GetName()
|
||||||
|
end
|
||||||
|
|
||||||
|
return name
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get velocity vector of weapon.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return DCS#Vec3 Velocity vector with x, y and z components in meters/second.
|
||||||
|
function WEAPON:GetVelocityVec3()
|
||||||
|
local Vvec3=nil
|
||||||
|
if self.weapon then
|
||||||
|
Vvec3=self.weapon:getVelocity()
|
||||||
|
end
|
||||||
|
return Vvec3
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get speed of weapon.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param #function ConversionFunction (Optional) Conversion function from m/s to desired unit, *e.g.* `UTILS.MpsToKmph`.
|
||||||
|
-- @return #number Speed in meters per second.
|
||||||
|
function WEAPON:GetSpeed(ConversionFunction)
|
||||||
|
|
||||||
|
local speed=nil
|
||||||
|
|
||||||
|
if self.weapon then
|
||||||
|
|
||||||
|
local v=self:GetVelocityVec3()
|
||||||
|
|
||||||
|
speed=UTILS.VecNorm(v)
|
||||||
|
|
||||||
|
if ConversionFunction then
|
||||||
|
speed=ConversionFunction(speed)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return speed
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the current 3D position vector.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return DCS#Vec3 Current position vector in 3D.
|
||||||
|
function WEAPON:GetVec3()
|
||||||
|
|
||||||
|
local vec3=nil
|
||||||
|
if self.weapon then
|
||||||
|
vec3=self.weapon:getPoint()
|
||||||
|
end
|
||||||
|
|
||||||
|
return vec3
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get the current 2D position vector.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return DCS#Vec2 Current position vector in 2D.
|
||||||
|
function WEAPON:GetVec2()
|
||||||
|
|
||||||
|
local vec3=self:GetVec3()
|
||||||
|
|
||||||
|
if vec3 then
|
||||||
|
|
||||||
|
local vec2={x=vec3.x, y=vec3.z}
|
||||||
|
|
||||||
|
return vec2
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get type name.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return #string The type name.
|
||||||
|
function WEAPON:GetTypeName()
|
||||||
|
return self.typeName
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get coalition.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return #number Coalition ID.
|
||||||
|
function WEAPON:GetCoalition()
|
||||||
|
return self.coalition
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get country.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return #number Country ID.
|
||||||
|
function WEAPON:GetCountry()
|
||||||
|
return self.country
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get DCS object.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return DCS#Weapon The weapon object.
|
||||||
|
function WEAPON:GetDCSObject()
|
||||||
|
-- This polymorphic function is used in Wrapper.Identifiable#IDENTIFIABLE
|
||||||
|
return self.weapon
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the impact position vector. Note that this might not exist if the weapon has not impacted yet!
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return DCS#Vec3 Impact position vector (if any).
|
||||||
|
function WEAPON:GetImpactVec3()
|
||||||
|
return self.impactVec3
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the impact coordinate. Note that this might not exist if the weapon has not impacted yet!
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return Core.Point#COORDINATE Impact coordinate (if any).
|
||||||
|
function WEAPON:GetImpactCoordinate()
|
||||||
|
return self.impactCoord
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if weapon is in the air. Obviously not really useful for torpedos. Well, then again, this is DCS...
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return #boolean If `true`, weapon is in the air and `false` if not. Returns `nil` if weapon object itself is `nil`.
|
||||||
|
function WEAPON:InAir()
|
||||||
|
local inAir=nil
|
||||||
|
if self.weapon then
|
||||||
|
inAir=self.weapon:inAir()
|
||||||
|
end
|
||||||
|
return inAir
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Check if weapon object (still) exists.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return #boolean If `true`, the weapon object still exists and `false` otherwise. Returns `nil` if weapon object itself is `nil`.
|
||||||
|
function WEAPON:IsExist()
|
||||||
|
local isExist=nil
|
||||||
|
if self.weapon then
|
||||||
|
isExist=self.weapon:isExist()
|
||||||
|
end
|
||||||
|
return isExist
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Check if weapon is a bomb.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return #boolean If `true`, is a bomb.
|
||||||
|
function WEAPON:IsBomb()
|
||||||
|
return self.category==Weapon.Category.BOMB
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if weapon is a missile.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return #boolean If `true`, is a missile.
|
||||||
|
function WEAPON:IsMissile()
|
||||||
|
return self.category==Weapon.Category.MISSILE
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if weapon is a rocket.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return #boolean If `true`, is a missile.
|
||||||
|
function WEAPON:IsRocket()
|
||||||
|
return self.category==Weapon.Category.ROCKET
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if weapon is a shell.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return #boolean If `true`, is a shell.
|
||||||
|
function WEAPON:IsShell()
|
||||||
|
return self.category==Weapon.Category.SHELL
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if weapon is a torpedo.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return #boolean If `true`, is a torpedo.
|
||||||
|
function WEAPON:IsTorpedo()
|
||||||
|
return self.category==Weapon.Category.TORPEDO
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Destroy the weapon object.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param #number Delay Delay before destroy in seconds.
|
||||||
|
-- @return #WEAPON self
|
||||||
|
function WEAPON:Destroy(Delay)
|
||||||
|
|
||||||
|
if Delay and Delay>0 then
|
||||||
|
self:ScheduleOnce(Delay, WEAPON.Destroy, self, 0)
|
||||||
|
else
|
||||||
|
if self.weapon then
|
||||||
|
self:T(self.lid.."Destroying Weapon NOW!")
|
||||||
|
self:StopTrack()
|
||||||
|
self.weapon:destroy()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Start tracking the weapon until it impacts or is destroyed otherwise.
|
||||||
|
-- The position of the weapon is monitored in small time steps. Once the position cannot be determined anymore, the monitoring is stopped and the last known position is
|
||||||
|
-- the (approximate) impact point. Of course, the smaller the time step, the better the position can be determined. However, this can hit the performance as many
|
||||||
|
-- calculations per second need to be carried out.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param #number Delay Delay in seconds before the tracking starts. Default 0.001 sec.
|
||||||
|
-- @return #WEAPON self
|
||||||
|
function WEAPON:StartTrack(Delay)
|
||||||
|
|
||||||
|
-- Set delay before start.
|
||||||
|
Delay=math.max(Delay or 0.001, 0.001)
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T(self.lid..string.format("Start tracking weapon in %.4f sec", Delay))
|
||||||
|
|
||||||
|
-- Weapon is not yet "alife" just yet. Start timer in 0.001 seconds.
|
||||||
|
self.trackScheduleID=timer.scheduleFunction(WEAPON._TrackWeapon, self, timer.getTime() + Delay)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Stop tracking the weapon by removing the scheduler function.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param #number Delay (Optional) Delay in seconds before the tracking is stopped.
|
||||||
|
-- @return #WEAPON self
|
||||||
|
function WEAPON:StopTrack(Delay)
|
||||||
|
|
||||||
|
if Delay and Delay>0 then
|
||||||
|
-- Delayed call.
|
||||||
|
self:ScheduleOnce(Delay, WEAPON.StopTrack, self, 0)
|
||||||
|
else
|
||||||
|
|
||||||
|
if self.trackScheduleID then
|
||||||
|
|
||||||
|
timer.removeFunction(self.trackScheduleID)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Private Functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Track weapon until impact.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param DCS#Time time Time in seconds.
|
||||||
|
-- @return #number Time when called next or nil if not called again.
|
||||||
|
function WEAPON:_TrackWeapon(time)
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
if self.verbose>=20 then
|
||||||
|
self:I(self.lid..string.format("Tracking at T=%.5f", time))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Protected call to get the weapon position. If the position cannot be determined any more, the weapon has impacted and status is nil.
|
||||||
|
local status, pos3= pcall(
|
||||||
|
function()
|
||||||
|
local point=self.weapon:getPosition()
|
||||||
|
return point
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
if status then
|
||||||
|
|
||||||
|
-------------------------------
|
||||||
|
-- Weapon is still in exists --
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
-- Update last known position.
|
||||||
|
self.pos3 = pos3
|
||||||
|
|
||||||
|
-- Update last known vec3.
|
||||||
|
self.vec3 = UTILS.DeepCopy(self.pos3.p)
|
||||||
|
|
||||||
|
-- Update coordinate.
|
||||||
|
self.coordinate:UpdateFromVec3(self.vec3)
|
||||||
|
|
||||||
|
-- Keep on tracking by returning the next time below.
|
||||||
|
self.tracking=true
|
||||||
|
|
||||||
|
-- Callback function.
|
||||||
|
if self.trackFunc then
|
||||||
|
self.trackFunc(self, unpack(self.trackArg))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Verbose output.
|
||||||
|
if self.verbose>=5 then
|
||||||
|
|
||||||
|
-- Get vec2 of current position.
|
||||||
|
local vec2={x=self.vec3.x, y=self.vec3.z}
|
||||||
|
|
||||||
|
-- Land hight.
|
||||||
|
local height=land.getHeight(vec2)
|
||||||
|
|
||||||
|
-- Current height above ground level.
|
||||||
|
local agl=self.vec3.y-height
|
||||||
|
|
||||||
|
-- Estimated IP (if any)
|
||||||
|
local ip=self:_GetIP(self.distIP)
|
||||||
|
|
||||||
|
-- Distance between positon and estimated impact.
|
||||||
|
local d=0
|
||||||
|
if ip then
|
||||||
|
d=UTILS.VecDist3D(self.vec3, ip)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Output.
|
||||||
|
self:I(self.lid..string.format("T=%.3f: Height=%.3f m AGL=%.3f m, dIP=%.3f", time, height, agl, d))
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
---------------------------
|
||||||
|
-- Weapon does NOT exist --
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
-- Get intercept point from position (p) and direction (x) in 50 meters.
|
||||||
|
local ip = self:_GetIP(self.distIP)
|
||||||
|
|
||||||
|
if self.verbose>=10 and ip then
|
||||||
|
|
||||||
|
-- Output.
|
||||||
|
self:I(self.lid.."Got intercept point!")
|
||||||
|
|
||||||
|
-- Coordinate of the impact point.
|
||||||
|
local coord=COORDINATE:NewFromVec3(ip)
|
||||||
|
|
||||||
|
-- Mark coordinate.
|
||||||
|
coord:MarkToAll("Intercept point")
|
||||||
|
coord:SmokeBlue()
|
||||||
|
|
||||||
|
-- Distance to last known pos.
|
||||||
|
local d=UTILS.VecDist3D(ip, self.vec3)
|
||||||
|
|
||||||
|
-- Output.
|
||||||
|
self:I(self.lid..string.format("FF d(ip, vec3)=%.3f meters", d))
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Safe impact vec3.
|
||||||
|
self.impactVec3=ip or self.vec3
|
||||||
|
|
||||||
|
-- Safe impact coordinate.
|
||||||
|
self.impactCoord=COORDINATE:NewFromVec3(self.vec3)
|
||||||
|
|
||||||
|
-- Mark impact point on F10 map.
|
||||||
|
if self.impactMark then
|
||||||
|
self.impactCoord:MarkToAll(string.format("Impact point of weapon %s\ntype=%s\nlauncher=%s", self.name, self.typeName, self.launcherName))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Smoke on impact point.
|
||||||
|
if self.impactSmoke then
|
||||||
|
self.impactCoord:Smoke(self.impactSmokeColor)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Call callback function.
|
||||||
|
if self.impactFunc then
|
||||||
|
self.impactFunc(self, unpack(self.impactArg or {}))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Stop tracking by returning nil below.
|
||||||
|
self.tracking=false
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Return next time the function is called or nil to stop the scheduler.
|
||||||
|
if self.tracking then
|
||||||
|
if self.dtTrack and self.dtTrack>=0.00001 then
|
||||||
|
return time+self.dtTrack
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Compute estimated intercept/impact point (IP) based on last known position and direction.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param #number Distance Distance in meters. Default 50 m.
|
||||||
|
-- @return DCS#Vec3 Estimated intercept/impact point. Can also return `nil`, if no IP can be determined.
|
||||||
|
function WEAPON:_GetIP(Distance)
|
||||||
|
|
||||||
|
Distance=Distance or 50
|
||||||
|
|
||||||
|
local ip=nil --DCS#Vec3
|
||||||
|
|
||||||
|
if Distance>0 and self.pos3 then
|
||||||
|
|
||||||
|
-- Get intercept point from position (p) and direction (x) in 20 meters.
|
||||||
|
ip = land.getIP(self.pos3.p, self.pos3.x, Distance or 20) --DCS#Vec3
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return ip
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -34,6 +34,7 @@ Core/MarkerOps_Base.lua
|
|||||||
Core/Astar.lua
|
Core/Astar.lua
|
||||||
Core/Condition.lua
|
Core/Condition.lua
|
||||||
Core/TextAndSound.lua
|
Core/TextAndSound.lua
|
||||||
|
Core/Pathline.lua
|
||||||
|
|
||||||
Wrapper/Object.lua
|
Wrapper/Object.lua
|
||||||
Wrapper/Identifiable.lua
|
Wrapper/Identifiable.lua
|
||||||
@@ -46,6 +47,8 @@ Wrapper/Static.lua
|
|||||||
Wrapper/Airbase.lua
|
Wrapper/Airbase.lua
|
||||||
Wrapper/Scenery.lua
|
Wrapper/Scenery.lua
|
||||||
Wrapper/Marker.lua
|
Wrapper/Marker.lua
|
||||||
|
Wrapper/Weapon.lua
|
||||||
|
Wrapper/Net.lua
|
||||||
|
|
||||||
Cargo/Cargo.lua
|
Cargo/Cargo.lua
|
||||||
Cargo/CargoUnit.lua
|
Cargo/CargoUnit.lua
|
||||||
|
|||||||
Reference in New Issue
Block a user