mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
Compare commits
143 Commits
v2.5.0pre1
...
v2.5.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e677e2acf4 | ||
|
|
2658230331 | ||
|
|
bb0827bb8c | ||
|
|
be1f8cde34 | ||
|
|
29028bf128 | ||
|
|
c30ca2b18a | ||
|
|
d1b40b63c2 | ||
|
|
0b7ac754c5 | ||
|
|
5b514d223b | ||
|
|
7d8e27c83e | ||
|
|
dcd1060496 | ||
|
|
533e2abf17 | ||
|
|
6b318d0309 | ||
|
|
bee44b97fd | ||
|
|
77de1dda2b | ||
|
|
5b03bc5066 | ||
|
|
0d6f32581c | ||
|
|
2620370890 | ||
|
|
f866af0d4a | ||
|
|
b86eac33b6 | ||
|
|
c78bd2652a | ||
|
|
392d465b30 | ||
|
|
056cc1630d | ||
|
|
364ce927e7 | ||
|
|
517d5860fb | ||
|
|
4e49184da2 | ||
|
|
68ece29ab5 | ||
|
|
c3cc76c15b | ||
|
|
0a570da986 | ||
|
|
b25697e261 | ||
|
|
863e239204 | ||
|
|
457b30ed07 | ||
|
|
7360c51a8f | ||
|
|
df512f7a58 | ||
|
|
da05c41c27 | ||
|
|
3c428723f8 | ||
|
|
4c3fd1d867 | ||
|
|
909c028e48 | ||
|
|
14e12dc36b | ||
|
|
0502e0a020 | ||
|
|
36aebd3c04 | ||
|
|
007831bb9a | ||
|
|
def44bde89 | ||
|
|
4b369fae95 | ||
|
|
cb4a44b512 | ||
|
|
53fbec9c15 | ||
|
|
d476922af2 | ||
|
|
861f2da4d6 | ||
|
|
6d75b25f57 | ||
|
|
bfb3ae33f4 | ||
|
|
a796c7a594 | ||
|
|
af63c1a219 | ||
|
|
72da7a2c04 | ||
|
|
f9bdbf4e3f | ||
|
|
850951bcb4 | ||
|
|
3d0f002f73 | ||
|
|
ff8c454752 | ||
|
|
6882c4b42f | ||
|
|
9b0f474edf | ||
|
|
ceaae5421c | ||
|
|
36666df76f | ||
|
|
3616e4264a | ||
|
|
a704693abf | ||
|
|
4dc303a166 | ||
|
|
71b5140e69 | ||
|
|
00e5499f81 | ||
|
|
7cf73af707 | ||
|
|
4ef827836c | ||
|
|
e3d6337481 | ||
|
|
f258e9263e | ||
|
|
48d99a19c5 | ||
|
|
f34511bd8c | ||
|
|
63a5d2e3ac | ||
|
|
3fb52d4fa1 | ||
|
|
463403cef4 | ||
|
|
fedee49eb1 | ||
|
|
042cc7a85f | ||
|
|
9583168c4d | ||
|
|
0c32c35c27 | ||
|
|
6ab85072d2 | ||
|
|
04da941c36 | ||
|
|
d6c919a097 | ||
|
|
1f9ffcd92d | ||
|
|
12ef34acaa | ||
|
|
ce60e7ce74 | ||
|
|
aa5d36db88 | ||
|
|
a2759b1cc0 | ||
|
|
0da0b025b5 | ||
|
|
f685064c0c | ||
|
|
b68e254bc0 | ||
|
|
d43f77706e | ||
|
|
5e1fb36d01 | ||
|
|
6d3bbe88e6 | ||
|
|
e4906eb083 | ||
|
|
19eb81866a | ||
|
|
cd34755036 | ||
|
|
c72625c60f | ||
|
|
1e1154d190 | ||
|
|
ff0f62c01b | ||
|
|
e6e5787fc6 | ||
|
|
431e8a05f9 | ||
|
|
b91a8cf4c8 | ||
|
|
3e8455410e | ||
|
|
3a9ad1b7cc | ||
|
|
6000421957 | ||
|
|
231ceef41f | ||
|
|
4661f6981e | ||
|
|
fcfcfeae00 | ||
|
|
09ed562f61 | ||
|
|
b1f2bab87e | ||
|
|
aa6515e1ca | ||
|
|
d5b8ed62ae | ||
|
|
6208c79d1f | ||
|
|
740ee74f61 | ||
|
|
0bb0c70667 | ||
|
|
9fb311bf7d | ||
|
|
8915657e91 | ||
|
|
8b506a2f20 | ||
|
|
6dd73dba44 | ||
|
|
d3a3d96436 | ||
|
|
8337058306 | ||
|
|
389931fbf0 | ||
|
|
b9fd6c0e31 | ||
|
|
f48f53fb37 | ||
|
|
07edcd7e73 | ||
|
|
a193d342d7 | ||
|
|
5b9978e390 | ||
|
|
c3c984381e | ||
|
|
ca49e8f8ae | ||
|
|
59c46f9b4b | ||
|
|
b9384831fb | ||
|
|
0d57ad5584 | ||
|
|
cf34ec0905 | ||
|
|
1c99e474b2 | ||
|
|
e45134f086 | ||
|
|
1b5e9df586 | ||
|
|
4b6612e114 | ||
|
|
e4473010eb | ||
|
|
bb259b2572 | ||
|
|
e16e2851c1 | ||
|
|
169c5a674c | ||
|
|
60042e14dc | ||
|
|
14de37f390 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -221,3 +221,8 @@ _gsdata_/
|
||||
.gitattributes
|
||||
.gitignore
|
||||
Moose Test Missions/MOOSE_Test_Template.miz
|
||||
Moose Development/Moose/.vscode/launch.json
|
||||
MooseCodeWS.code-workspace
|
||||
.gitignore
|
||||
.gitignore
|
||||
/.gitignore
|
||||
|
||||
0
.scannerwork/.sonar_lock
Normal file
0
.scannerwork/.sonar_lock
Normal file
6
.scannerwork/report-task.txt
Normal file
6
.scannerwork/report-task.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
projectKey=Test
|
||||
serverUrl=http://localhost:9000
|
||||
serverVersion=8.1.0.31237
|
||||
dashboardUrl=http://localhost:9000/dashboard?id=Test
|
||||
ceTaskId=AXAlUJO97YLjwz1VUDXR
|
||||
ceTaskUrl=http://localhost:9000/api/ce/task?id=AXAlUJO97YLjwz1VUDXR
|
||||
@@ -1,10 +1,10 @@
|
||||
--- **AI** -- (R2.2) - Models the process of Combat Air Patrol (CAP) for airplanes.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_A2A_Cap
|
||||
-- @image AI_Combat_Air_Patrol.JPG
|
||||
@@ -14,54 +14,54 @@
|
||||
-- @extends AI.AI_Air_Engage#AI_AIR_ENGAGE
|
||||
|
||||
|
||||
--- The AI_A2A_CAP class implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group}
|
||||
--- The AI_A2A_CAP class implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group}
|
||||
-- and automatically engage any airborne enemies that are within a certain range or within a certain zone.
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
--
|
||||
-- The AI_A2A_CAP is assigned a @{Wrapper.Group} and this must be done before the AI_A2A_CAP process can be started using the **Start** event.
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
--
|
||||
-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits.
|
||||
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
--
|
||||
-- This cycle will continue.
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
--
|
||||
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
--
|
||||
-- When enemies are detected, the AI will automatically engage the enemy.
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
--
|
||||
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
--
|
||||
-- ## 1. AI_A2A_CAP constructor
|
||||
--
|
||||
--
|
||||
-- * @{#AI_A2A_CAP.New}(): Creates a new AI_A2A_CAP object.
|
||||
--
|
||||
--
|
||||
-- ## 2. AI_A2A_CAP is a FSM
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
--
|
||||
-- ### 2.1 AI_A2A_CAP States
|
||||
--
|
||||
--
|
||||
-- * **None** ( Group ): The process is not started yet.
|
||||
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
|
||||
-- * **Engaging** ( Group ): The AI is engaging the bogeys.
|
||||
-- * **Returning** ( Group ): The AI is returning to Base..
|
||||
--
|
||||
--
|
||||
-- ### 2.2 AI_A2A_CAP Events
|
||||
--
|
||||
--
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone.
|
||||
-- * **@{#AI_A2A_CAP.Engage}**: Let the AI engage the bogeys.
|
||||
@@ -74,25 +74,25 @@
|
||||
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
|
||||
--
|
||||
-- ## 3. Set the Range of Engagement
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- An optional range can be set in meters,
|
||||
--
|
||||
-- An optional range can be set in meters,
|
||||
-- that will define when the AI will engage with the detected airborne enemy targets.
|
||||
-- The range can be beyond or smaller than the range of the Patrol Zone.
|
||||
-- The range is applied at the position of the AI.
|
||||
-- Use the method @{AI.AI_CAP#AI_A2A_CAP.SetEngageRange}() to define that range.
|
||||
--
|
||||
-- ## 4. Set the Zone of Engagement
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- An optional @{Zone} can be set,
|
||||
--
|
||||
-- An optional @{Zone} can be set,
|
||||
-- that will define when the AI will engage with the detected airborne enemy targets.
|
||||
-- Use the method @{AI.AI_Cap#AI_A2A_CAP.SetEngageZone}() to define that Zone.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- @field #AI_A2A_CAP
|
||||
AI_A2A_CAP = {
|
||||
ClassName = "AI_A2A_CAP",
|
||||
@@ -119,12 +119,12 @@ function AI_A2A_CAP:New2( AICap, EngageMinSpeed, EngageMaxSpeed, EngageFloorAlti
|
||||
local AI_Air = AI_AIR:New( AICap )
|
||||
local AI_Air_Patrol = AI_AIR_PATROL:New( AI_Air, AICap, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) -- #AI_AIR_PATROL
|
||||
local AI_Air_Engage = AI_AIR_ENGAGE:New( AI_Air_Patrol, AICap, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
|
||||
local self = BASE:Inherit( self, AI_Air_Engage )
|
||||
local self = BASE:Inherit( self, AI_Air_Engage ) --#AI_A2A_CAP
|
||||
|
||||
self:SetFuelThreshold( .2, 60 )
|
||||
self:SetDamageThreshold( 0.4 )
|
||||
self:SetDisengageRadius( 70000 )
|
||||
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -160,35 +160,35 @@ function AI_A2A_CAP:onafterStart( AICap, From, Event, To )
|
||||
|
||||
end
|
||||
|
||||
--- Set the Engage Zone which defines where the AI will engage bogies.
|
||||
--- Set the Engage Zone which defines where the AI will engage bogies.
|
||||
-- @param #AI_A2A_CAP self
|
||||
-- @param Core.Zone#ZONE EngageZone The zone where the AI is performing CAP.
|
||||
-- @return #AI_A2A_CAP self
|
||||
function AI_A2A_CAP:SetEngageZone( EngageZone )
|
||||
self:F2()
|
||||
|
||||
if EngageZone then
|
||||
if EngageZone then
|
||||
self.EngageZone = EngageZone
|
||||
else
|
||||
self.EngageZone = nil
|
||||
end
|
||||
end
|
||||
|
||||
--- Set the Engage Range when the AI will engage with airborne enemies.
|
||||
--- Set the Engage Range when the AI will engage with airborne enemies.
|
||||
-- @param #AI_A2A_CAP self
|
||||
-- @param #number EngageRange The Engage Range.
|
||||
-- @return #AI_A2A_CAP self
|
||||
function AI_A2A_CAP:SetEngageRange( EngageRange )
|
||||
self:F2()
|
||||
|
||||
if EngageRange then
|
||||
if EngageRange then
|
||||
self.EngageRange = EngageRange
|
||||
else
|
||||
self.EngageRange = nil
|
||||
end
|
||||
end
|
||||
|
||||
--- Evaluate the attack and create an AttackUnitTask list.
|
||||
--- Evaluate the attack and create an AttackUnitTask list.
|
||||
-- @param #AI_A2A_CAP self
|
||||
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
|
||||
-- @param Wrappper.Group#GROUP DefenderGroup The group of defenders.
|
||||
@@ -200,15 +200,13 @@ function AI_A2A_CAP:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageA
|
||||
|
||||
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do
|
||||
local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT
|
||||
self:T( { "Attacking Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } )
|
||||
if AttackUnit:IsAlive() and AttackUnit:IsAir() then
|
||||
if AttackUnit and AttackUnit:IsAlive() and AttackUnit:IsAir() then
|
||||
-- TODO: Add coalition check? Only attack units of if AttackUnit:GetCoalition()~=AICap:GetCoalition()
|
||||
-- Maybe the detected set also contains
|
||||
-- Maybe the detected set also contains
|
||||
self:T( { "Attacking Task:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } )
|
||||
AttackUnitTasks[#AttackUnitTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return AttackUnitTasks
|
||||
end
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,12 @@
|
||||
--- **AI** -- (R2.2) - Models the process of Ground Controlled Interception (GCI) for airplanes.
|
||||
--
|
||||
-- This is a class used in the @{AI_A2A_Dispatcher}.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_A2A_GCI
|
||||
-- @image AI_Ground_Control_Intercept.JPG
|
||||
@@ -18,52 +18,52 @@
|
||||
|
||||
|
||||
--- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders.
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
--
|
||||
-- The AI_A2A_GCI is assigned a @{Wrapper.Group} and this must be done before the AI_A2A_GCI process can be started using the **Start** event.
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
--
|
||||
-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits.
|
||||
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
--
|
||||
-- This cycle will continue.
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
--
|
||||
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
--
|
||||
-- When enemies are detected, the AI will automatically engage the enemy.
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
--
|
||||
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
--
|
||||
-- ## 1. AI_A2A_GCI constructor
|
||||
--
|
||||
--
|
||||
-- * @{#AI_A2A_GCI.New}(): Creates a new AI_A2A_GCI object.
|
||||
--
|
||||
--
|
||||
-- ## 2. AI_A2A_GCI is a FSM
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
--
|
||||
-- ### 2.1 AI_A2A_GCI States
|
||||
--
|
||||
--
|
||||
-- * **None** ( Group ): The process is not started yet.
|
||||
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
|
||||
-- * **Engaging** ( Group ): The AI is engaging the bogeys.
|
||||
-- * **Returning** ( Group ): The AI is returning to Base..
|
||||
--
|
||||
--
|
||||
-- ### 2.2 AI_A2A_GCI Events
|
||||
--
|
||||
--
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone.
|
||||
-- * **@{#AI_A2A_GCI.Engage}**: Let the AI engage the bogeys.
|
||||
@@ -76,25 +76,25 @@
|
||||
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
|
||||
--
|
||||
-- ## 3. Set the Range of Engagement
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- An optional range can be set in meters,
|
||||
--
|
||||
-- An optional range can be set in meters,
|
||||
-- that will define when the AI will engage with the detected airborne enemy targets.
|
||||
-- The range can be beyond or smaller than the range of the Patrol Zone.
|
||||
-- The range is applied at the position of the AI.
|
||||
-- Use the method @{AI.AI_GCI#AI_A2A_GCI.SetEngageRange}() to define that range.
|
||||
--
|
||||
-- ## 4. Set the Zone of Engagement
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- An optional @{Zone} can be set,
|
||||
--
|
||||
-- An optional @{Zone} can be set,
|
||||
-- that will define when the AI will engage with the detected airborne enemy targets.
|
||||
-- Use the method @{AI.AI_Cap#AI_A2A_GCI.SetEngageZone}() to define that Zone.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- @field #AI_A2A_GCI
|
||||
AI_A2A_GCI = {
|
||||
ClassName = "AI_A2A_GCI",
|
||||
@@ -116,7 +116,7 @@ function AI_A2A_GCI:New2( AIIntercept, EngageMinSpeed, EngageMaxSpeed, EngageFlo
|
||||
local AI_Air = AI_AIR:New( AIIntercept )
|
||||
local AI_Air_Engage = AI_AIR_ENGAGE:New( AI_Air, AIIntercept, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
|
||||
local self = BASE:Inherit( self, AI_Air_Engage ) -- #AI_A2A_GCI
|
||||
|
||||
|
||||
self:SetFuelThreshold( .2, 60 )
|
||||
self:SetDamageThreshold( 0.4 )
|
||||
self:SetDisengageRadius( 70000 )
|
||||
@@ -150,7 +150,7 @@ function AI_A2A_GCI:onafterStart( AIIntercept, From, Event, To )
|
||||
end
|
||||
|
||||
|
||||
--- Evaluate the attack and create an AttackUnitTask list.
|
||||
--- Evaluate the attack and create an AttackUnitTask list.
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
|
||||
-- @param Wrappper.Group#GROUP DefenderGroup The group of defenders.
|
||||
@@ -165,14 +165,10 @@ function AI_A2A_GCI:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageA
|
||||
self:T( { "Attacking Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } )
|
||||
if AttackUnit:IsAlive() and AttackUnit:IsAir() then
|
||||
-- TODO: Add coalition check? Only attack units of if AttackUnit:GetCoalition()~=AICap:GetCoalition()
|
||||
-- Maybe the detected set also contains
|
||||
-- Maybe the detected set also contains
|
||||
AttackUnitTasks[#AttackUnitTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return AttackUnitTasks
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -121,13 +121,13 @@ AI_A2A_PATROL = {
|
||||
|
||||
--- Creates a new AI_A2A_PATROL object
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param Wrapper.Group#GROUP AIPatrol
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The patrol group object.
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
|
||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
|
||||
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to BARO
|
||||
-- @return #AI_A2A_PATROL self
|
||||
-- @usage
|
||||
-- -- Define a new AI_A2A_PATROL Object. This PatrolArea will patrol a Group within PatrolZone between 3000 and 6000 meters, with a variying speed between 600 and 900 km/h.
|
||||
@@ -137,7 +137,7 @@ AI_A2A_PATROL = {
|
||||
function AI_A2A_PATROL:New( AIPatrol, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
|
||||
|
||||
local AI_Air = AI_AIR:New( AIPatrol )
|
||||
local AI_Air_Patrol = AI_A2A_PATROL:New( AI_Air, AIPatrol, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
|
||||
local AI_Air_Patrol = AI_AIR_PATROL:New( AI_Air, AIPatrol, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
|
||||
local self = BASE:Inherit( self, AI_Air_Patrol ) -- #AI_A2A_PATROL
|
||||
|
||||
self:SetFuelThreshold( .2, 60 )
|
||||
@@ -151,8 +151,8 @@ function AI_A2A_PATROL:New( AIPatrol, PatrolZone, PatrolFloorAltitude, PatrolCei
|
||||
self.PatrolMinSpeed = PatrolMinSpeed
|
||||
self.PatrolMaxSpeed = PatrolMaxSpeed
|
||||
|
||||
-- defafult PatrolAltType to "RADIO" if not specified
|
||||
self.PatrolAltType = PatrolAltType or "RADIO"
|
||||
-- defafult PatrolAltType to "BARO" if not specified
|
||||
self.PatrolAltType = PatrolAltType or "BARO"
|
||||
|
||||
self:AddTransition( { "Started", "Airborne", "Refuelling" }, "Patrol", "Patrolling" )
|
||||
|
||||
@@ -287,15 +287,15 @@ function AI_A2A_PATROL:onafterPatrol( AIPatrol, From, Event, To )
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- @param Wrapper.Group#GROUP AIPatrol
|
||||
-- This statis method is called from the route path within the last task at the last waaypoint of the AIPatrol.
|
||||
--- This statis method is called from the route path within the last task at the last waaypoint of the AIPatrol.
|
||||
-- Note that this method is required, as triggers the next route when patrolling for the AIPatrol.
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The AI group.
|
||||
-- @param #AI_A2A_PATROL Fsm The FSM.
|
||||
function AI_A2A_PATROL.PatrolRoute( AIPatrol, Fsm )
|
||||
|
||||
AIPatrol:F( { "AI_A2A_PATROL.PatrolRoute:", AIPatrol:GetName() } )
|
||||
|
||||
if AIPatrol:IsAlive() then
|
||||
if AIPatrol and AIPatrol:IsAlive() then
|
||||
Fsm:Route()
|
||||
end
|
||||
|
||||
@@ -309,7 +309,6 @@ end
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_A2A_PATROL:onafterRoute( AIPatrol, From, Event, To )
|
||||
|
||||
self:F2()
|
||||
|
||||
-- When RTB, don't allow anymore the routing.
|
||||
|
||||
@@ -4375,7 +4375,7 @@ do -- AI_A2G_DISPATCHER
|
||||
|
||||
Report:Add( string.format( "\n - Squadron Resources: ", #self.DefenseQueue ) )
|
||||
for DefenderSquadronName, DefenderSquadron in pairs( self.DefenderSquadrons ) do
|
||||
Report:Add( string.format( " - %s - %d", DefenderSquadronName, DefenderSquadron.ResourceCount and DefenderSquadron.ResourceCount or "n/a" ) )
|
||||
Report:Add( string.format( " - %s - %s", DefenderSquadronName, DefenderSquadron.ResourceCount and tostring(DefenderSquadron.ResourceCount) or "n/a" ) )
|
||||
end
|
||||
|
||||
self:F( Report:Text( "\n" ) )
|
||||
@@ -4591,10 +4591,10 @@ do -- AI_A2G_DISPATCHER
|
||||
Report:Add( string.format( " - %s - %s", DefenseQueueItem.SquadronName, DefenseQueueItem.DefenderSquadron.TakeoffTime, DefenseQueueItem.DefenderSquadron.TakeoffInterval) )
|
||||
|
||||
end
|
||||
|
||||
|
||||
Report:Add( string.format( "\n - Squadron Resources: ", #self.DefenseQueue ) )
|
||||
for DefenderSquadronName, DefenderSquadron in pairs( self.DefenderSquadrons ) do
|
||||
Report:Add( string.format( " - %s - %d", DefenderSquadronName, DefenderSquadron.ResourceCount and DefenderSquadron.ResourceCount or "n/a" ) )
|
||||
Report:Add( string.format( " - %s - %s", DefenderSquadronName, DefenderSquadron.ResourceCount and tostring(DefenderSquadron.ResourceCount) or "n/a" ) )
|
||||
end
|
||||
|
||||
self:F( Report:Text( "\n" ) )
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- **AI** -- Models the process of AI air operations.
|
||||
--- **AI** - Models the process of AI air operations.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -492,6 +492,7 @@ function AI_AIR:onafterStatus()
|
||||
OldAIControllable:SetTask( TimedOrbitTask, 10 )
|
||||
|
||||
self:Fuel()
|
||||
RTB = true
|
||||
end
|
||||
else
|
||||
end
|
||||
@@ -707,13 +708,14 @@ end
|
||||
function AI_AIR:onafterRefuel( AIGroup, From, Event, To )
|
||||
self:F( { AIGroup, From, Event, To } )
|
||||
|
||||
|
||||
if AIGroup and AIGroup:IsAlive() then
|
||||
|
||||
-- Get tanker group.
|
||||
local Tanker = GROUP:FindByName( self.TankerName )
|
||||
|
||||
if Tanker:IsAlive() and Tanker:IsAirPlane() then
|
||||
if Tanker and Tanker:IsAlive() and Tanker:IsAirPlane() then
|
||||
|
||||
self:I( "Group " .. self.Controllable:GetName() .. " ... Refuelling! ( " .. self:GetState() .. "), at tanker " .. self.TankerName )
|
||||
self:I( "Group " .. self.Controllable:GetName() .. " ... Refuelling! State=" .. self:GetState() .. ", Refuelling tanker " .. self.TankerName )
|
||||
|
||||
local RefuelRoute = {}
|
||||
|
||||
@@ -724,40 +726,43 @@ function AI_AIR:onafterRefuel( AIGroup, From, Event, To )
|
||||
local ToRefuelSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local FromRefuelRoutePoint = FromRefuelCoord:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
ToRefuelSpeed,
|
||||
true
|
||||
)
|
||||
local FromRefuelRoutePoint = FromRefuelCoord:WaypointAir(self.PatrolAltType, POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, ToRefuelSpeed, true)
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToRefuelRoutePoint = FromRefuelCoord:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
ToRefuelSpeed,
|
||||
true
|
||||
)
|
||||
--- Create a route point of type air. NOT used!
|
||||
local ToRefuelRoutePoint = Tanker:GetCoordinate():WaypointAir(self.PatrolAltType, POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, ToRefuelSpeed, true)
|
||||
|
||||
self:F( { ToRefuelSpeed = ToRefuelSpeed } )
|
||||
|
||||
RefuelRoute[#RefuelRoute+1] = FromRefuelRoutePoint
|
||||
--RefuelRoute[#RefuelRoute+1] = ToRefuelRoutePoint
|
||||
RefuelRoute[#RefuelRoute+1] = ToRefuelRoutePoint
|
||||
|
||||
AIGroup:OptionROEHoldFire()
|
||||
AIGroup:OptionROTEvadeFire()
|
||||
|
||||
-- Get Class name for .Resume function
|
||||
local classname=self:GetClassName()
|
||||
|
||||
-- AI_A2A_CAP can call this function but does not have a .Resume function. Try to fix.
|
||||
if classname=="AI_A2A_CAP" then
|
||||
classname="AI_AIR_PATROL"
|
||||
end
|
||||
|
||||
env.info("FF refueling classname="..classname)
|
||||
|
||||
local Tasks = {}
|
||||
Tasks[#Tasks+1] = AIGroup:TaskRefueling()
|
||||
Tasks[#Tasks+1] = AIGroup:TaskFunction( self:GetClassName() .. ".Resume", self )
|
||||
Tasks[#Tasks+1] = AIGroup:TaskFunction( classname .. ".Resume", self )
|
||||
RefuelRoute[#RefuelRoute].task = AIGroup:TaskCombo( Tasks )
|
||||
|
||||
AIGroup:Route( RefuelRoute, self.TaskDelay )
|
||||
|
||||
else
|
||||
|
||||
-- No tanker defined ==> RTB!
|
||||
self:RTB()
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -80,7 +80,8 @@ AI_AIR_ENGAGE = {
|
||||
|
||||
--- Creates a new AI_AIR_ENGAGE object
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
-- @param AI.AI_Air#AI_AIR AI_Air The AI_AIR FSM.
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AI group.
|
||||
-- @param DCS#Speed EngageMinSpeed (optional, default = 50% of max speed) The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Speed EngageMaxSpeed (optional, default = 75% of max speed) The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Altitude EngageFloorAltitude (optional, default = 1000m ) The lowest altitude in meters where to execute the engagement.
|
||||
@@ -334,9 +335,8 @@ end
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_AIR_ENGAGE:onafterEngage( AIGroup, From, Event, To )
|
||||
|
||||
-- TODO: This function is overwritten below!
|
||||
self:HandleEvent( EVENTS.Dead )
|
||||
|
||||
end
|
||||
|
||||
-- todo: need to fix this global function
|
||||
@@ -349,10 +349,10 @@ end
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_AIR_ENGAGE:onbeforeEngage( AIGroup, From, Event, To )
|
||||
|
||||
if self.Accomplished == true then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- onafter event handler for Abort event.
|
||||
@@ -374,7 +374,7 @@ end
|
||||
-- @param #string To The To State string.
|
||||
function AI_AIR_ENGAGE:onafterAccomplish( AIGroup, From, Event, To )
|
||||
self.Accomplished = true
|
||||
self:SetDetectionOff()
|
||||
--self:SetDetectionOff()
|
||||
end
|
||||
|
||||
--- @param #AI_AIR_ENGAGE self
|
||||
@@ -405,14 +405,10 @@ end
|
||||
|
||||
--- @param Wrapper.Group#GROUP AIControllable
|
||||
function AI_AIR_ENGAGE.___EngageRoute( AIGroup, Fsm, AttackSetUnit )
|
||||
|
||||
Fsm:I( { "AI_AIR_ENGAGE.___EngageRoute:", AIGroup:GetName() } )
|
||||
Fsm:I(string.format("AI_AIR_ENGAGE.___EngageRoute: %s", tostring(AIGroup:GetName())))
|
||||
|
||||
if AIGroup:IsAlive() then
|
||||
Fsm:__EngageRoute( Fsm.TaskDelay, AttackSetUnit )
|
||||
|
||||
--local Task = AIGroup:TaskOrbitCircle( 4000, 400 )
|
||||
--AIGroup:SetTask( Task )
|
||||
if AIGroup and AIGroup:IsAlive() then
|
||||
Fsm:__EngageRoute( Fsm.TaskDelay or 0.1, AttackSetUnit )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -423,7 +419,6 @@ end
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_AIR_ENGAGE:onafterEngageRoute( DefenderGroup, From, Event, To, AttackSetUnit )
|
||||
|
||||
self:I( { DefenderGroup, From, Event, To, AttackSetUnit } )
|
||||
|
||||
local DefenderGroupName = DefenderGroup:GetName()
|
||||
@@ -450,39 +445,33 @@ function AI_AIR_ENGAGE:onafterEngageRoute( DefenderGroup, From, Event, To, Attac
|
||||
TargetCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
|
||||
|
||||
local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord )
|
||||
local EngageDistance = ( DefenderGroup:IsHelicopter() and 5000 ) or ( DefenderGroup:IsAirPlane() and 10000 )
|
||||
local EngageDistance = ( DefenderGroup:IsHelicopter() and 5000 ) or ( DefenderGroup:IsAirPlane() and 10000 )
|
||||
|
||||
if TargetDistance <= EngageDistance * 3 then
|
||||
-- TODO: A factor of * 3 is way too close. This causes the AI not to engange until merged sometimes!
|
||||
if TargetDistance <= EngageDistance * 9 then
|
||||
|
||||
self:I(string.format("AI_AIR_ENGAGE onafterEngageRoute ==> __Engage - target distance = %.1f km", TargetDistance/1000))
|
||||
self:__Engage( 0.1, AttackSetUnit )
|
||||
|
||||
else
|
||||
|
||||
self:I(string.format("FF AI_AIR_ENGAGE onafterEngageRoute ==> Routing - target distance = %.1f km", TargetDistance/1000))
|
||||
|
||||
local EngageRoute = {}
|
||||
local AttackTasks = {}
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
local FromWP = DefenderCoord:WaypointAir(
|
||||
self.PatrolAltType or "RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
EngageSpeed,
|
||||
true
|
||||
)
|
||||
local FromWP = DefenderCoord:WaypointAir(self.PatrolAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, EngageSpeed, true)
|
||||
|
||||
EngageRoute[#EngageRoute+1] = FromWP
|
||||
|
||||
self:SetTargetDistance( TargetCoord ) -- For RTB status check
|
||||
|
||||
local FromEngageAngle = DefenderCoord:GetAngleDegrees( DefenderCoord:GetDirectionVec3( TargetCoord ) )
|
||||
local ToWP = DefenderCoord:Translate( EngageDistance, FromEngageAngle, true ):WaypointAir(
|
||||
self.PatrolAltType or "RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
EngageSpeed,
|
||||
true
|
||||
)
|
||||
local ToCoord=DefenderCoord:Translate( EngageDistance, FromEngageAngle, true )
|
||||
|
||||
local ToWP = ToCoord:WaypointAir(self.PatrolAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, EngageSpeed, true)
|
||||
|
||||
EngageRoute[#EngageRoute+1] = ToWP
|
||||
|
||||
@@ -492,11 +481,12 @@ function AI_AIR_ENGAGE:onafterEngageRoute( DefenderGroup, From, Event, To, Attac
|
||||
DefenderGroup:OptionROEReturnFire()
|
||||
DefenderGroup:OptionROTEvadeFire()
|
||||
|
||||
DefenderGroup:Route( EngageRoute, self.TaskDelay )
|
||||
DefenderGroup:Route( EngageRoute, self.TaskDelay or 0.1 )
|
||||
end
|
||||
|
||||
end
|
||||
else
|
||||
-- TODO: This will make an A2A Dispatcher CAP flight to return rather than going back to patrolling!
|
||||
self:I( DefenderGroupName .. ": No targets found -> Going RTB")
|
||||
self:Return()
|
||||
end
|
||||
@@ -506,10 +496,11 @@ end
|
||||
--- @param Wrapper.Group#GROUP AIControllable
|
||||
function AI_AIR_ENGAGE.___Engage( AIGroup, Fsm, AttackSetUnit )
|
||||
|
||||
Fsm:I( { "AI_AIR_ENGAGE.___Engage:", AIGroup:GetName() } )
|
||||
Fsm:I(string.format("AI_AIR_ENGAGE.___Engage: %s", tostring(AIGroup:GetName())))
|
||||
|
||||
if AIGroup:IsAlive() then
|
||||
Fsm:__Engage( Fsm.TaskDelay, AttackSetUnit )
|
||||
if AIGroup and AIGroup:IsAlive() then
|
||||
local delay=Fsm.TaskDelay or 0.1
|
||||
Fsm:__Engage(delay, AttackSetUnit)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -520,7 +511,6 @@ end
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit )
|
||||
|
||||
self:F( { DefenderGroup, From, Event, To, AttackSetUnit} )
|
||||
|
||||
local DefenderGroupName = DefenderGroup:GetName()
|
||||
@@ -532,7 +522,7 @@ function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetU
|
||||
|
||||
if AttackCount > 0 then
|
||||
|
||||
if DefenderGroup:IsAlive() then
|
||||
if DefenderGroup and DefenderGroup:IsAlive() then
|
||||
|
||||
local EngageAltitude = math.random( self.EngageFloorAltitude or 500, self.EngageCeilingAltitude or 1000 )
|
||||
local EngageSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
|
||||
@@ -544,33 +534,25 @@ function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetU
|
||||
TargetCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
|
||||
|
||||
local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord )
|
||||
|
||||
local EngageDistance = ( DefenderGroup:IsHelicopter() and 5000 ) or ( DefenderGroup:IsAirPlane() and 10000 )
|
||||
|
||||
local EngageRoute = {}
|
||||
local AttackTasks = {}
|
||||
|
||||
local FromWP = DefenderCoord:WaypointAir(
|
||||
self.EngageAltType or "RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
EngageSpeed,
|
||||
true
|
||||
)
|
||||
local FromWP = DefenderCoord:WaypointAir(self.EngageAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, EngageSpeed, true)
|
||||
EngageRoute[#EngageRoute+1] = FromWP
|
||||
|
||||
self:SetTargetDistance( TargetCoord ) -- For RTB status check
|
||||
|
||||
local FromEngageAngle = DefenderCoord:GetAngleDegrees( DefenderCoord:GetDirectionVec3( TargetCoord ) )
|
||||
local ToWP = DefenderCoord:Translate( EngageDistance, FromEngageAngle, true ):WaypointAir(
|
||||
self.EngageAltType or "RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
EngageSpeed,
|
||||
true
|
||||
)
|
||||
local ToCoord=DefenderCoord:Translate( EngageDistance, FromEngageAngle, true )
|
||||
|
||||
local ToWP = ToCoord:WaypointAir(self.EngageAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, EngageSpeed, true)
|
||||
EngageRoute[#EngageRoute+1] = ToWP
|
||||
|
||||
if TargetDistance <= EngageDistance * 3 then
|
||||
-- TODO: A factor of * 3 this way too low. This causes the AI NOT to engage until very close or even merged sometimes. Some A2A missiles have a much longer range! Needs more frequent updates of the task!
|
||||
if TargetDistance <= EngageDistance * 9 then
|
||||
|
||||
local AttackUnitTasks = self:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude ) -- Polymorphic
|
||||
|
||||
@@ -579,7 +561,8 @@ function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetU
|
||||
self:Return()
|
||||
return
|
||||
else
|
||||
self:I( DefenderGroupName .. ": Engaging targets " )
|
||||
local text=string.format("%s: Engaging targets at distance %.2f NM", DefenderGroupName, UTILS.MetersToNM(TargetDistance))
|
||||
self:I(text)
|
||||
DefenderGroup:OptionROEOpenFire()
|
||||
DefenderGroup:OptionROTEvadeFire()
|
||||
DefenderGroup:OptionKeepWeaponsOnThreat()
|
||||
@@ -591,10 +574,11 @@ function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetU
|
||||
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskFunction( "AI_AIR_ENGAGE.___Engage", self, AttackSetUnit )
|
||||
EngageRoute[#EngageRoute].task = DefenderGroup:TaskCombo( AttackTasks )
|
||||
|
||||
DefenderGroup:Route( EngageRoute, self.TaskDelay )
|
||||
DefenderGroup:Route( EngageRoute, self.TaskDelay or 0.1 )
|
||||
|
||||
end
|
||||
else
|
||||
-- TODO: This will make an A2A Dispatcher CAP flight to return rather than going back to patrolling!
|
||||
self:I( DefenderGroupName .. ": No targets found -> returning.")
|
||||
self:Return()
|
||||
return
|
||||
@@ -605,9 +589,9 @@ end
|
||||
function AI_AIR_ENGAGE.Resume( AIEngage, Fsm )
|
||||
|
||||
AIEngage:F( { "Resume:", AIEngage:GetName() } )
|
||||
if AIEngage:IsAlive() then
|
||||
Fsm:__Reset( Fsm.TaskDelay )
|
||||
Fsm:__EngageRoute( Fsm.TaskDelay, Fsm.AttackSetUnit )
|
||||
if AIEngage and AIEngage:IsAlive() then
|
||||
Fsm:__Reset( Fsm.TaskDelay or 0.1 )
|
||||
Fsm:__EngageRoute( Fsm.TaskDelay or 0.2, Fsm.AttackSetUnit )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -99,7 +99,8 @@ AI_AIR_PATROL = {
|
||||
|
||||
--- Creates a new AI_AIR_PATROL object
|
||||
-- @param #AI_AIR_PATROL self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
-- @param AI.AI_Air#AI_AIR AI_Air The AI_AIR FSM.
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AI group.
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
|
||||
-- @param DCS#Altitude PatrolFloorAltitude (optional, default = 1000m ) The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude (optional, default = 1500m ) The highest altitude in meters where to execute the patrol.
|
||||
@@ -270,14 +271,15 @@ function AI_AIR_PATROL:onafterPatrol( AIPatrol, From, Event, To )
|
||||
)
|
||||
end
|
||||
|
||||
--- @param Wrapper.Group#GROUP AIPatrol
|
||||
-- This statis method is called from the route path within the last task at the last waaypoint of the AIPatrol.
|
||||
--- This statis method is called from the route path within the last task at the last waaypoint of the AIPatrol.
|
||||
-- Note that this method is required, as triggers the next route when patrolling for the AIPatrol.
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The AI group.
|
||||
-- @param #AI_AIR_PATROL Fsm The FSM.
|
||||
function AI_AIR_PATROL.___PatrolRoute( AIPatrol, Fsm )
|
||||
|
||||
AIPatrol:F( { "AI_AIR_PATROL.___PatrolRoute:", AIPatrol:GetName() } )
|
||||
|
||||
if AIPatrol:IsAlive() then
|
||||
if AIPatrol and AIPatrol:IsAlive() then
|
||||
Fsm:PatrolRoute()
|
||||
end
|
||||
|
||||
@@ -299,7 +301,7 @@ function AI_AIR_PATROL:onafterPatrolRoute( AIPatrol, From, Event, To )
|
||||
end
|
||||
|
||||
|
||||
if AIPatrol:IsAlive() then
|
||||
if AIPatrol and AIPatrol:IsAlive() then
|
||||
|
||||
local PatrolRoute = {}
|
||||
|
||||
@@ -316,13 +318,7 @@ function AI_AIR_PATROL:onafterPatrolRoute( AIPatrol, From, Event, To )
|
||||
local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
|
||||
local speedkmh=ToTargetSpeed
|
||||
|
||||
local FromWP = CurrentCoord:WaypointAir(
|
||||
self.PatrolAltType or "RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
ToTargetSpeed,
|
||||
true
|
||||
)
|
||||
local FromWP = CurrentCoord:WaypointAir(self.PatrolAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, ToTargetSpeed, true)
|
||||
PatrolRoute[#PatrolRoute+1] = FromWP
|
||||
|
||||
if self.racetrack then
|
||||
@@ -360,7 +356,7 @@ function AI_AIR_PATROL:onafterPatrolRoute( AIPatrol, From, Event, To )
|
||||
local taskOrbit=AIPatrol:TaskOrbit(c1, altitude, UTILS.KmphToMps(speedkmh), c2)
|
||||
|
||||
-- Task function to redo the patrol at other random position.
|
||||
local taskPatrol=AIPatrol:TaskFunction("AI_A2A_PATROL.PatrolRoute", self)
|
||||
local taskPatrol=AIPatrol:TaskFunction("AI_AIR_PATROL.___PatrolRoute", self)
|
||||
|
||||
-- Controlled task with task condition.
|
||||
local taskCond=AIPatrol:TaskCondition(nil, nil, nil, nil, duration, nil)
|
||||
@@ -372,18 +368,11 @@ function AI_AIR_PATROL:onafterPatrolRoute( AIPatrol, From, Event, To )
|
||||
else
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToWP = ToTargetCoord:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
ToTargetSpeed,
|
||||
true
|
||||
)
|
||||
|
||||
local ToWP = ToTargetCoord:WaypointAir(self.PatrolAltType, POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, ToTargetSpeed, true)
|
||||
PatrolRoute[#PatrolRoute+1] = ToWP
|
||||
|
||||
local Tasks = {}
|
||||
Tasks[#Tasks+1] = AIPatrol:TaskFunction( "AI_AIR_PATROL.___PatrolRoute", self )
|
||||
Tasks[#Tasks+1] = AIPatrol:TaskFunction("AI_AIR_PATROL.___PatrolRoute", self)
|
||||
PatrolRoute[#PatrolRoute].task = AIPatrol:TaskCombo( Tasks )
|
||||
|
||||
end
|
||||
@@ -401,7 +390,7 @@ end
|
||||
function AI_AIR_PATROL.Resume( AIPatrol, Fsm )
|
||||
|
||||
AIPatrol:F( { "AI_AIR_PATROL.Resume:", AIPatrol:GetName() } )
|
||||
if AIPatrol:IsAlive() then
|
||||
if AIPatrol and AIPatrol:IsAlive() then
|
||||
Fsm:__Reset( Fsm.TaskDelay )
|
||||
Fsm:__PatrolRoute( Fsm.TaskDelay )
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- **AI** -- Models squadrons for airplanes and helicopters.
|
||||
--- **AI** - Models squadrons for airplanes and helicopters.
|
||||
--
|
||||
-- This is a class used in the @{AI_Air_Dispatcher} and derived dispatcher classes.
|
||||
--
|
||||
@@ -9,7 +9,7 @@
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Air_Squadron
|
||||
-- @image AI_Air_To_Air_Engage.JPG
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -196,8 +196,12 @@ function AI_BALANCER:onenterDestroying( SetGroup, From, Event, To, ClientName, A
|
||||
SetGroup:Flush( self )
|
||||
end
|
||||
|
||||
--- @param #AI_BALANCER self
|
||||
--- RTB
|
||||
-- @param #AI_BALANCER self
|
||||
-- @param Core.Set#SET_GROUP SetGroup
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
function AI_BALANCER:onenterReturning( SetGroup, From, Event, To, AIGroup )
|
||||
|
||||
@@ -213,10 +217,13 @@ function AI_BALANCER:onenterReturning( SetGroup, From, Event, To, AIGroup )
|
||||
local PointVec2 = POINT_VEC2:New( AIGroup:GetVec2().x, AIGroup:GetVec2().y )
|
||||
local ClosestAirbase = self.ReturnAirbaseSet:FindNearestAirbaseFromPointVec2( PointVec2 )
|
||||
self:T( ClosestAirbase.AirbaseName )
|
||||
--[[
|
||||
AIGroup:MessageToRed( "Returning to " .. ClosestAirbase:GetName().. " ...", 30 )
|
||||
local RTBRoute = AIGroup:RouteReturnToAirbase( ClosestAirbase )
|
||||
AIGroupTemplate.route = RTBRoute
|
||||
AIGroup:Respawn( AIGroupTemplate )
|
||||
]]
|
||||
AIGroup:RouteRTB(ClosestAirbase)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -421,7 +421,7 @@ end
|
||||
-- @param #string To The To State string.
|
||||
function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
|
||||
|
||||
if Controllable:IsAlive() then
|
||||
if Controllable and Controllable:IsAlive() then
|
||||
|
||||
local EngageRoute = {}
|
||||
|
||||
|
||||
@@ -141,6 +141,17 @@ function AI_CARGO:New( Carrier, CargoSet )
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- On after Deployed event.
|
||||
-- @function [parent=#AI_CARGO] OnAfterDeployed
|
||||
-- @param #AI_CARGO self
|
||||
-- @param Wrapper.Group#GROUP Carrier
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
-- @param #boolean Defend Defend for APCs.
|
||||
|
||||
|
||||
for _, CarrierUnit in pairs( Carrier:GetUnits() ) do
|
||||
local CarrierUnit = CarrierUnit -- Wrapper.Unit#UNIT
|
||||
@@ -256,9 +267,10 @@ function AI_CARGO:onbeforeLoad( Carrier, From, Event, To, PickupZone )
|
||||
self:F( { "In radius", CarrierUnit:GetName() } )
|
||||
|
||||
local CargoWeight = Cargo:GetWeight()
|
||||
local CarrierSpace=Carrier_Weight[CarrierUnit]
|
||||
|
||||
-- Only when there is space within the bay to load the next cargo item!
|
||||
if Carrier_Weight[CarrierUnit] > CargoWeight then --and CargoBayFreeVolume > CargoVolume then
|
||||
if CarrierSpace > CargoWeight then
|
||||
Carrier:RouteStop()
|
||||
--Cargo:Ungroup()
|
||||
Cargo:__Board( -LoadDelay, CarrierUnit )
|
||||
@@ -275,6 +287,8 @@ function AI_CARGO:onbeforeLoad( Carrier, From, Event, To, PickupZone )
|
||||
|
||||
-- Ok, we loaded a cargo, now we can stop the loop.
|
||||
break
|
||||
else
|
||||
self:T(string.format("WARNING: Cargo too heavy for carrier %s. Cargo=%.1f > %.1f free space", tostring(CarrierUnit:GetName()), CargoWeight, CarrierSpace))
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -554,6 +568,7 @@ end
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
-- @param #boolean Defend Defend for APCs.
|
||||
function AI_CARGO:onafterDeployed( Carrier, From, Event, To, DeployZone, Defend )
|
||||
self:F( { Carrier, From, Event, To, DeployZone = DeployZone, Defend = Defend } )
|
||||
|
||||
|
||||
@@ -76,25 +76,31 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet )
|
||||
--- Pickup Handler OnAfter for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] OnAfterPickup
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane Cargo plane.
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase Airbase where troops are picked up.
|
||||
-- @param #number Speed in km/h for travelling to pickup base.
|
||||
-- @param Wrapper.Group#GROUP Airplane Cargo transport plane.
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate where to pickup stuff.
|
||||
-- @param #number Speed Speed in km/h for travelling to pickup base.
|
||||
-- @param #number Height Height in meters to move to the pickup coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE PickupZone The airbase zone where the cargo will be picked up.
|
||||
|
||||
--- Pickup Trigger for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] Pickup
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase Airbase where troops are picked up.
|
||||
-- @param #number Speed in km/h for travelling to pickup base.
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate where to pickup stuff.
|
||||
-- @param #number Speed Speed in km/h for travelling to pickup base.
|
||||
-- @param #number Height Height in meters to move to the pickup coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE PickupZone The airbase zone where the cargo will be picked up.
|
||||
|
||||
--- Pickup Asynchronous Trigger for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] __Pickup
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param #number Delay Delay in seconds.
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase Airbase where troops are picked up.
|
||||
-- @param #number Speed in km/h for travelling to pickup base.
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate where to pickup stuff.
|
||||
-- @param #number Speed Speed in km/h for travelling to pickup base.
|
||||
-- @param #number Height Height in meters to move to the pickup coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE PickupZone The airbase zone where the cargo will be picked up.
|
||||
|
||||
--- Deploy Handler OnBefore for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] OnBeforeDeploy
|
||||
@@ -111,24 +117,30 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet )
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] OnAfterDeploy
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane Cargo plane.
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase Destination airbase where troops are deployed.
|
||||
-- @param #number Speed Speed in km/h for travelling to deploy base.
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate where to deploy stuff.
|
||||
-- @param #number Speed Speed in km/h for travelling to the deploy base.
|
||||
-- @param #number Height Height in meters to move to the home coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE DeployZone The airbase zone where the cargo will be deployed.
|
||||
|
||||
--- Deploy Trigger for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] Deploy
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase Destination airbase where troops are deployed.
|
||||
-- @param #number Speed Speed in km/h for travelling to deploy base.
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate where to deploy stuff.
|
||||
-- @param #number Speed Speed in km/h for travelling to the deploy base.
|
||||
-- @param #number Height Height in meters to move to the home coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE DeployZone The airbase zone where the cargo will be deployed.
|
||||
|
||||
--- Deploy Asynchronous Trigger for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] __Deploy
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param #number Delay Delay in seconds.
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase Destination airbase where troops are deployed.
|
||||
-- @param #number Speed Speed in km/h for travelling to deploy base.
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate where to deploy stuff.
|
||||
-- @param #number Speed Speed in km/h for travelling to the deploy base.
|
||||
-- @param #number Height Height in meters to move to the home coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE DeployZone The airbase zone where the cargo will be deployed.
|
||||
|
||||
--- On after Loaded event, i.e. triggered when the cargo is inside the carrier.
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] OnAfterLoaded
|
||||
@@ -137,6 +149,16 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet )
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
|
||||
|
||||
--- On after Deployed event.
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] OnAfterDeployed
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane Cargo plane.
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed.
|
||||
|
||||
-- Set carrier.
|
||||
self:SetCarrier( Airplane )
|
||||
@@ -259,15 +281,17 @@ end
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed in km/h for travelling to pickup base.
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate where to pickup stuff.
|
||||
-- @param #number Speed Speed in km/h for travelling to pickup base.
|
||||
-- @param #number Height Height in meters to move to the pickup coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE (optional) PickupZone The zone where the cargo will be picked up.
|
||||
-- @param Core.Zone#ZONE_AIRBASE PickupZone The airbase zone where the cargo will be picked up.
|
||||
function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Coordinate, Speed, Height, PickupZone )
|
||||
|
||||
if Airplane and Airplane:IsAlive() then
|
||||
|
||||
self.PickupZone = PickupZone
|
||||
local airbasepickup=Coordinate:GetClosestAirbase()
|
||||
|
||||
self.PickupZone = PickupZone or ZONE_AIRBASE:New(airbasepickup:GetName())
|
||||
|
||||
-- Get closest airbase of current position.
|
||||
local ClosestAirbase, DistToAirbase=Airplane:GetCoordinate():GetClosestAirbase()
|
||||
@@ -280,11 +304,10 @@ function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Coordinate,
|
||||
end
|
||||
|
||||
-- Set pickup airbase.
|
||||
local Airbase = PickupZone:GetAirbase()
|
||||
local Airbase = self.PickupZone:GetAirbase()
|
||||
|
||||
-- Distance from closest to pickup airbase ==> we need to know if we are already at the pickup airbase.
|
||||
local Dist = Airbase:GetCoordinate():Get2DDistance(ClosestAirbase:GetCoordinate())
|
||||
--env.info("Distance closest to pickup airbase = "..Dist)
|
||||
|
||||
if Airplane:InAir() or Dist>500 then
|
||||
|
||||
@@ -305,7 +328,7 @@ function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Coordinate,
|
||||
|
||||
end
|
||||
|
||||
self:GetParent( self, AI_CARGO_AIRPLANE ).onafterPickup( self, Airplane, From, Event, To, Coordinate, Speed, Height, PickupZone )
|
||||
self:GetParent( self, AI_CARGO_AIRPLANE ).onafterPickup( self, Airplane, From, Event, To, Coordinate, Speed, Height, self.PickupZone )
|
||||
|
||||
end
|
||||
|
||||
@@ -318,15 +341,19 @@ end
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed in km/h for travelling to pickup base.
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate where to deploy stuff.
|
||||
-- @param #number Speed Speed in km/h for travelling to the deploy base.
|
||||
-- @param #number Height Height in meters to move to the home coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE DeployZone The zone where the cargo will be deployed.
|
||||
-- @param Core.Zone#ZONE_AIRBASE DeployZone The airbase zone where the cargo will be deployed.
|
||||
function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Coordinate, Speed, Height, DeployZone )
|
||||
|
||||
if Airplane and Airplane:IsAlive()~=nil then
|
||||
|
||||
local Airbase = DeployZone:GetAirbase()
|
||||
local Airbase = Coordinate:GetClosestAirbase()
|
||||
|
||||
if DeployZone then
|
||||
Airbase=DeployZone:GetAirbase()
|
||||
end
|
||||
|
||||
-- Activate uncontrolled airplane.
|
||||
if Airplane:IsAlive()==false then
|
||||
@@ -354,6 +381,7 @@ end
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Zone#ZONE_AIRBASE DeployZone The airbase zone where the cargo will be deployed.
|
||||
function AI_CARGO_AIRPLANE:onafterUnload( Airplane, From, Event, To, DeployZone )
|
||||
|
||||
local UnboardInterval = 10
|
||||
|
||||
@@ -1151,6 +1151,10 @@ function AI_CARGO_DISPATCHER:onafterMonitor()
|
||||
self.PickupCargo[Carrier] = CargoCoordinate
|
||||
PickupCargo = Cargo
|
||||
break
|
||||
else
|
||||
local text=string.format("WARNING: Cargo %s is too heavy to be loaded into transport. Cargo weight %.1f > %.1f load capacity of carrier %s.",
|
||||
tostring(Cargo:GetName()), Cargo:GetWeight(), LargestLoadCapacity, tostring(Carrier:GetName()))
|
||||
self:I(text)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- **AI** -- (R2.5) - Models the automatic assignment of AI escorts to player flights.
|
||||
--- **AI** - Models the automatic assignment of AI escorts to player flights.
|
||||
--
|
||||
-- ## Features:
|
||||
-- --
|
||||
@@ -12,7 +12,7 @@
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Escort_Dispatcher
|
||||
-- @image AI_Escort_Dispatcher.JPG
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
|
||||
--- @type AI_ESCORT_DISPATCHER
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- **AI** -- (R2.5) - Models the assignment of AI escorts to player flights upon request using the radio menu.
|
||||
--- **AI** - Models the assignment of AI escorts to player flights upon request using the radio menu.
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
@@ -12,7 +12,7 @@
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_ESCORT_DISPATCHER_REQUEST
|
||||
-- @image AI_ESCORT_DISPATCHER_REQUEST.JPG
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
|
||||
--- @type AI_ESCORT_DISPATCHER_REQUEST
|
||||
|
||||
@@ -178,8 +178,8 @@ function AI_PATROL_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltit
|
||||
self.PatrolMinSpeed = PatrolMinSpeed
|
||||
self.PatrolMaxSpeed = PatrolMaxSpeed
|
||||
|
||||
-- defafult PatrolAltType to "RADIO" if not specified
|
||||
self.PatrolAltType = PatrolAltType or "RADIO"
|
||||
-- defafult PatrolAltType to "BARO" if not specified
|
||||
self.PatrolAltType = PatrolAltType or "BARO"
|
||||
|
||||
self:SetRefreshTimeInterval( 30 )
|
||||
|
||||
@@ -636,7 +636,7 @@ function AI_PATROL_ZONE:onafterStart( Controllable, From, Event, To )
|
||||
|
||||
self.Controllable:OnReSpawn(
|
||||
function( PatrolGroup )
|
||||
self:E( "ReSpawn" )
|
||||
self:T( "ReSpawn" )
|
||||
self:__Reset( 1 )
|
||||
self:__Route( 5 )
|
||||
end
|
||||
@@ -667,21 +667,27 @@ function AI_PATROL_ZONE:onafterDetect( Controllable, From, Event, To )
|
||||
if TargetObject and TargetObject:isExist() and TargetObject.id_ < 50000000 then
|
||||
|
||||
local TargetUnit = UNIT:Find( TargetObject )
|
||||
local TargetUnitName = TargetUnit:GetName()
|
||||
|
||||
if self.DetectionZone then
|
||||
if TargetUnit:IsInZone( self.DetectionZone ) then
|
||||
self:T( {"Detected ", TargetUnit } )
|
||||
-- Check that target is alive due to issue https://github.com/FlightControl-Master/MOOSE/issues/1234
|
||||
if TargetUnit and TargetUnit:IsAlive() then
|
||||
|
||||
local TargetUnitName = TargetUnit:GetName()
|
||||
|
||||
if self.DetectionZone then
|
||||
if TargetUnit:IsInZone( self.DetectionZone ) then
|
||||
self:T( {"Detected ", TargetUnit } )
|
||||
if self.DetectedUnits[TargetUnit] == nil then
|
||||
self.DetectedUnits[TargetUnit] = true
|
||||
end
|
||||
Detected = true
|
||||
end
|
||||
else
|
||||
if self.DetectedUnits[TargetUnit] == nil then
|
||||
self.DetectedUnits[TargetUnit] = true
|
||||
end
|
||||
Detected = true
|
||||
Detected = true
|
||||
end
|
||||
else
|
||||
if self.DetectedUnits[TargetUnit] == nil then
|
||||
self.DetectedUnits[TargetUnit] = true
|
||||
end
|
||||
Detected = true
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -735,7 +741,7 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
|
||||
-- This will make the plane fly immediately to the patrol zone.
|
||||
|
||||
if self.Controllable:InAir() == false then
|
||||
self:E( "Not in the air, finding route path within PatrolZone" )
|
||||
self:T( "Not in the air, finding route path within PatrolZone" )
|
||||
local CurrentVec2 = self.Controllable:GetVec2()
|
||||
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
||||
@@ -750,7 +756,7 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
|
||||
)
|
||||
PatrolRoute[#PatrolRoute+1] = CurrentRoutePoint
|
||||
else
|
||||
self:E( "In the air, finding route path within PatrolZone" )
|
||||
self:T( "In the air, finding route path within PatrolZone" )
|
||||
local CurrentVec2 = self.Controllable:GetVec2()
|
||||
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
--- **Actions** - ACT_ACCOUNT_ classes **account for** (detect, count & report) various DCS events occuring on @{Wrapper.Unit}s.
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Actions.Account
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
do -- ACT_ACCOUNT
|
||||
|
||||
|
||||
--- # @{#ACT_ACCOUNT} FSM class, extends @{Core.Fsm#FSM_PROCESS}
|
||||
--
|
||||
-- ## ACT_ACCOUNT state machine:
|
||||
--
|
||||
--
|
||||
-- ## ACT_ACCOUNT state machine:
|
||||
--
|
||||
-- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur.
|
||||
-- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below.
|
||||
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
|
||||
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
|
||||
-- but will have **different implementation behaviour** upon each event or state transition.
|
||||
--
|
||||
-- ### ACT_ACCOUNT States
|
||||
--
|
||||
--
|
||||
-- ### ACT_ACCOUNT States
|
||||
--
|
||||
-- * **Asigned**: The player is assigned.
|
||||
-- * **Waiting**: Waiting for an event.
|
||||
-- * **Report**: Reporting.
|
||||
-- * **Account**: Account for an event.
|
||||
-- * **Accounted**: All events have been accounted for, end of the process.
|
||||
-- * **Failed**: Failed the process.
|
||||
--
|
||||
-- ### ACT_ACCOUNT Events
|
||||
--
|
||||
--
|
||||
-- ### ACT_ACCOUNT Events
|
||||
--
|
||||
-- * **Start**: Start the process.
|
||||
-- * **Wait**: Wait for an event.
|
||||
-- * **Report**: Report the status of the accounting.
|
||||
@@ -36,32 +36,32 @@ do -- ACT_ACCOUNT
|
||||
-- * **More**: More targets.
|
||||
-- * **NoMore (*)**: No more targets.
|
||||
-- * **Fail (*)**: The action process has failed.
|
||||
--
|
||||
--
|
||||
-- (*) End states of the process.
|
||||
--
|
||||
--
|
||||
-- ### ACT_ACCOUNT state transition methods:
|
||||
--
|
||||
--
|
||||
-- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state.
|
||||
-- There are 2 moments when state transition methods will be called by the state machine:
|
||||
--
|
||||
-- * **Before** the state transition.
|
||||
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
|
||||
--
|
||||
-- * **Before** the state transition.
|
||||
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
|
||||
-- If the state transition method returns false, then the processing of the state transition will not be done!
|
||||
-- If you want to change the behaviour of the AIControllable at this event, return false,
|
||||
-- If you want to change the behaviour of the AIControllable at this event, return false,
|
||||
-- but then you'll need to specify your own logic using the AIControllable!
|
||||
--
|
||||
-- * **After** the state transition.
|
||||
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
|
||||
--
|
||||
-- * **After** the state transition.
|
||||
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
|
||||
-- These state transition methods need to provide a return value, which is specified at the function description.
|
||||
--
|
||||
--
|
||||
-- @type ACT_ACCOUNT
|
||||
-- @field Core.Set#SET_UNIT TargetSetUnit
|
||||
-- @extends Core.Fsm#FSM_PROCESS
|
||||
ACT_ACCOUNT = {
|
||||
ACT_ACCOUNT = {
|
||||
ClassName = "ACT_ACCOUNT",
|
||||
TargetSetUnit = nil,
|
||||
}
|
||||
|
||||
|
||||
--- Creates a new DESTROY process.
|
||||
-- @param #ACT_ACCOUNT self
|
||||
-- @return #ACT_ACCOUNT
|
||||
@@ -69,7 +69,7 @@ do -- ACT_ACCOUNT
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, FSM_PROCESS:New() ) -- Core.Fsm#FSM_PROCESS
|
||||
|
||||
|
||||
self:AddTransition( "Assigned", "Start", "Waiting" )
|
||||
self:AddTransition( "*", "Wait", "Waiting" )
|
||||
self:AddTransition( "*", "Report", "Report" )
|
||||
@@ -79,16 +79,16 @@ do -- ACT_ACCOUNT
|
||||
self:AddTransition( { "Account", "AccountForPlayer", "AccountForOther" }, "More", "Wait" )
|
||||
self:AddTransition( { "Account", "AccountForPlayer", "AccountForOther" }, "NoMore", "Accounted" )
|
||||
self:AddTransition( "*", "Fail", "Failed" )
|
||||
|
||||
|
||||
self:AddEndState( "Failed" )
|
||||
|
||||
self:SetStartState( "Assigned" )
|
||||
|
||||
|
||||
self:SetStartState( "Assigned" )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Process Events
|
||||
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
@@ -104,7 +104,7 @@ do -- ACT_ACCOUNT
|
||||
self:__Wait( 1 )
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
@@ -112,17 +112,17 @@ do -- ACT_ACCOUNT
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ACCOUNT:onenterWaiting( ProcessUnit, From, Event, To )
|
||||
|
||||
|
||||
if self.DisplayCount >= self.DisplayInterval then
|
||||
self:Report()
|
||||
self.DisplayCount = 1
|
||||
else
|
||||
self.DisplayCount = self.DisplayCount + 1
|
||||
end
|
||||
|
||||
|
||||
return true -- Process always the event.
|
||||
end
|
||||
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
@@ -130,30 +130,30 @@ do -- ACT_ACCOUNT
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ACCOUNT:onafterEvent( ProcessUnit, From, Event, To, Event )
|
||||
|
||||
|
||||
self:__NoMore( 1 )
|
||||
end
|
||||
|
||||
|
||||
end -- ACT_ACCOUNT
|
||||
|
||||
do -- ACT_ACCOUNT_DEADS
|
||||
|
||||
--- # @{#ACT_ACCOUNT_DEADS} FSM class, extends @{Core.Fsm.Account#ACT_ACCOUNT}
|
||||
--
|
||||
--
|
||||
-- The ACT_ACCOUNT_DEADS class accounts (detects, counts and reports) successful kills of DCS units.
|
||||
-- The process is given a @{Set} of units that will be tracked upon successful destruction.
|
||||
-- The process will end after each target has been successfully destroyed.
|
||||
-- Each successful dead will trigger an Account state transition that can be scored, modified or administered.
|
||||
--
|
||||
--
|
||||
--
|
||||
--
|
||||
-- ## ACT_ACCOUNT_DEADS constructor:
|
||||
--
|
||||
--
|
||||
-- * @{#ACT_ACCOUNT_DEADS.New}(): Creates a new ACT_ACCOUNT_DEADS object.
|
||||
--
|
||||
--
|
||||
-- @type ACT_ACCOUNT_DEADS
|
||||
-- @field Core.Set#SET_UNIT TargetSetUnit
|
||||
-- @extends #ACT_ACCOUNT
|
||||
ACT_ACCOUNT_DEADS = {
|
||||
ACT_ACCOUNT_DEADS = {
|
||||
ClassName = "ACT_ACCOUNT_DEADS",
|
||||
}
|
||||
|
||||
@@ -165,24 +165,24 @@ do -- ACT_ACCOUNT_DEADS
|
||||
function ACT_ACCOUNT_DEADS:New()
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, ACT_ACCOUNT:New() ) -- #ACT_ACCOUNT_DEADS
|
||||
|
||||
|
||||
self.DisplayInterval = 30
|
||||
self.DisplayCount = 30
|
||||
self.DisplayMessage = true
|
||||
self.DisplayTime = 10 -- 10 seconds is the default
|
||||
self.DisplayCategory = "HQ" -- Targets is the default display category
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
function ACT_ACCOUNT_DEADS:Init( FsmAccount )
|
||||
|
||||
self.Task = self:GetTask()
|
||||
|
||||
self.Task = self:GetTask()
|
||||
self.TaskName = self.Task:GetName()
|
||||
end
|
||||
|
||||
--- Process Events
|
||||
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
@@ -190,12 +190,12 @@ do -- ACT_ACCOUNT_DEADS
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ACCOUNT_DEADS:onenterReport( ProcessUnit, Task, From, Event, To )
|
||||
|
||||
|
||||
local MessageText = "Your group with assigned " .. self.TaskName .. " task has " .. Task.TargetSetUnit:GetUnitTypesText() .. " targets left to be destroyed."
|
||||
self:GetCommandCenter():MessageTypeToGroup( MessageText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
@@ -206,7 +206,7 @@ do -- ACT_ACCOUNT_DEADS
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function ACT_ACCOUNT_DEADS:onafterEvent( ProcessUnit, Task, From, Event, To, EventData )
|
||||
self:T( { ProcessUnit:GetName(), Task:GetName(), From, Event, To, EventData } )
|
||||
|
||||
|
||||
if Task.TargetSetUnit:FindUnit( EventData.IniUnitName ) then
|
||||
local PlayerName = ProcessUnit:GetPlayerName()
|
||||
local PlayerHit = self.PlayerHits and self.PlayerHits[EventData.IniUnitName]
|
||||
@@ -228,14 +228,14 @@ do -- ACT_ACCOUNT_DEADS
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function ACT_ACCOUNT_DEADS:onenterAccountForPlayer( ProcessUnit, Task, From, Event, To, EventData )
|
||||
self:T( { ProcessUnit:GetName(), Task:GetName(), From, Event, To, EventData } )
|
||||
|
||||
|
||||
local TaskGroup = ProcessUnit:GetGroup()
|
||||
|
||||
Task.TargetSetUnit:Remove( EventData.IniUnitName )
|
||||
|
||||
|
||||
local MessageText = "You have destroyed a target.\nYour group assigned with task " .. self.TaskName .. " has\n" .. Task.TargetSetUnit:Count() .. " targets ( " .. Task.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed."
|
||||
self:GetCommandCenter():MessageTypeToGroup( MessageText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
|
||||
|
||||
|
||||
local PlayerName = ProcessUnit:GetPlayerName()
|
||||
Task:AddProgress( PlayerName, "Destroyed " .. EventData.IniTypeName, timer.getTime(), 1 )
|
||||
|
||||
@@ -256,10 +256,10 @@ do -- ACT_ACCOUNT_DEADS
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function ACT_ACCOUNT_DEADS:onenterAccountForOther( ProcessUnit, Task, From, Event, To, EventData )
|
||||
self:T( { ProcessUnit:GetName(), Task:GetName(), From, Event, To, EventData } )
|
||||
|
||||
|
||||
local TaskGroup = ProcessUnit:GetGroup()
|
||||
Task.TargetSetUnit:Remove( EventData.IniUnitName )
|
||||
|
||||
|
||||
local MessageText = "One of the task targets has been destroyed.\nYour group assigned with task " .. self.TaskName .. " has\n" .. Task.TargetSetUnit:Count() .. " targets ( " .. Task.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed."
|
||||
self:GetCommandCenter():MessageTypeToGroup( MessageText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
|
||||
|
||||
@@ -270,9 +270,9 @@ do -- ACT_ACCOUNT_DEADS
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- DCS Events
|
||||
|
||||
|
||||
--- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function ACT_ACCOUNT_DEADS:OnEventHit( EventData )
|
||||
@@ -282,8 +282,8 @@ do -- ACT_ACCOUNT_DEADS
|
||||
self.PlayerHits = self.PlayerHits or {}
|
||||
self.PlayerHits[EventData.TgtDCSUnitName] = EventData.IniPlayerName
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function ACT_ACCOUNT_DEADS:onfuncEventDead( EventData )
|
||||
@@ -295,7 +295,7 @@ do -- ACT_ACCOUNT_DEADS
|
||||
end
|
||||
|
||||
--- DCS Events
|
||||
|
||||
|
||||
--- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function ACT_ACCOUNT_DEADS:onfuncEventCrash( EventData )
|
||||
|
||||
@@ -1,82 +1,82 @@
|
||||
--- (SP) (MP) (FSM) Accept or reject process for player (task) assignments.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- # @{#ACT_ASSIGN} FSM template class, extends @{Core.Fsm#FSM_PROCESS}
|
||||
--
|
||||
--
|
||||
-- ## ACT_ASSIGN state machine:
|
||||
--
|
||||
--
|
||||
-- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur.
|
||||
-- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below.
|
||||
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
|
||||
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
|
||||
-- but will have **different implementation behaviour** upon each event or state transition.
|
||||
--
|
||||
--
|
||||
-- ### ACT_ASSIGN **Events**:
|
||||
--
|
||||
--
|
||||
-- These are the events defined in this class:
|
||||
--
|
||||
--
|
||||
-- * **Start**: Start the tasking acceptance process.
|
||||
-- * **Assign**: Assign the task.
|
||||
-- * **Reject**: Reject the task..
|
||||
--
|
||||
--
|
||||
-- ### ACT_ASSIGN **Event methods**:
|
||||
--
|
||||
--
|
||||
-- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process.
|
||||
-- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine:
|
||||
--
|
||||
--
|
||||
-- * **Immediate**: The event method has exactly the name of the event.
|
||||
-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed.
|
||||
--
|
||||
-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed.
|
||||
--
|
||||
-- ### ACT_ASSIGN **States**:
|
||||
--
|
||||
--
|
||||
-- * **UnAssigned**: The player has not accepted the task.
|
||||
-- * **Assigned (*)**: The player has accepted the task.
|
||||
-- * **Rejected (*)**: The player has not accepted the task.
|
||||
-- * **Waiting**: The process is awaiting player feedback.
|
||||
-- * **Failed (*)**: The process has failed.
|
||||
--
|
||||
--
|
||||
-- (*) End states of the process.
|
||||
--
|
||||
--
|
||||
-- ### ACT_ASSIGN state transition methods:
|
||||
--
|
||||
--
|
||||
-- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state.
|
||||
-- There are 2 moments when state transition methods will be called by the state machine:
|
||||
--
|
||||
-- * **Before** the state transition.
|
||||
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
|
||||
--
|
||||
-- * **Before** the state transition.
|
||||
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
|
||||
-- If the state transition method returns false, then the processing of the state transition will not be done!
|
||||
-- If you want to change the behaviour of the AIControllable at this event, return false,
|
||||
-- If you want to change the behaviour of the AIControllable at this event, return false,
|
||||
-- but then you'll need to specify your own logic using the AIControllable!
|
||||
--
|
||||
-- * **After** the state transition.
|
||||
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
|
||||
--
|
||||
-- * **After** the state transition.
|
||||
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
|
||||
-- These state transition methods need to provide a return value, which is specified at the function description.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- # 1) @{#ACT_ASSIGN_ACCEPT} class, extends @{Core.Fsm.Assign#ACT_ASSIGN}
|
||||
--
|
||||
--
|
||||
-- The ACT_ASSIGN_ACCEPT class accepts by default a task for a player. No player intervention is allowed to reject the task.
|
||||
--
|
||||
--
|
||||
-- ## 1.1) ACT_ASSIGN_ACCEPT constructor:
|
||||
--
|
||||
--
|
||||
-- * @{#ACT_ASSIGN_ACCEPT.New}(): Creates a new ACT_ASSIGN_ACCEPT object.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- # 2) @{#ACT_ASSIGN_MENU_ACCEPT} class, extends @{Core.Fsm.Assign#ACT_ASSIGN}
|
||||
--
|
||||
--
|
||||
-- The ACT_ASSIGN_MENU_ACCEPT class accepts a task when the player accepts the task through an added menu option.
|
||||
-- This assignment type is useful to conditionally allow the player to choose whether or not he would accept the task.
|
||||
-- The assignment type also allows to reject the task.
|
||||
--
|
||||
--
|
||||
-- ## 2.1) ACT_ASSIGN_MENU_ACCEPT constructor:
|
||||
-- -----------------------------------------
|
||||
--
|
||||
--
|
||||
-- * @{#ACT_ASSIGN_MENU_ACCEPT.New}(): Creates a new ACT_ASSIGN_MENU_ACCEPT object.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- @module Actions.Assign
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
@@ -89,11 +89,11 @@ do -- ACT_ASSIGN
|
||||
-- @field Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @field Core.Zone#ZONE_BASE TargetZone
|
||||
-- @extends Core.Fsm#FSM_PROCESS
|
||||
ACT_ASSIGN = {
|
||||
ACT_ASSIGN = {
|
||||
ClassName = "ACT_ASSIGN",
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
--- Creates a new task assignment state machine. The process will accept the task by default, no player intervention accepted.
|
||||
-- @param #ACT_ASSIGN self
|
||||
-- @return #ACT_ASSIGN The task acceptance process.
|
||||
@@ -106,16 +106,16 @@ do -- ACT_ASSIGN
|
||||
self:AddTransition( "Waiting", "Assign", "Assigned" )
|
||||
self:AddTransition( "Waiting", "Reject", "Rejected" )
|
||||
self:AddTransition( "*", "Fail", "Failed" )
|
||||
|
||||
|
||||
self:AddEndState( "Assigned" )
|
||||
self:AddEndState( "Rejected" )
|
||||
self:AddEndState( "Failed" )
|
||||
|
||||
self:SetStartState( "UnAssigned" )
|
||||
|
||||
|
||||
self:SetStartState( "UnAssigned" )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
end -- ACT_ASSIGN
|
||||
|
||||
|
||||
@@ -128,26 +128,26 @@ do -- ACT_ASSIGN_ACCEPT
|
||||
-- @field Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @field Core.Zone#ZONE_BASE TargetZone
|
||||
-- @extends #ACT_ASSIGN
|
||||
ACT_ASSIGN_ACCEPT = {
|
||||
ACT_ASSIGN_ACCEPT = {
|
||||
ClassName = "ACT_ASSIGN_ACCEPT",
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
--- Creates a new task assignment state machine. The process will accept the task by default, no player intervention accepted.
|
||||
-- @param #ACT_ASSIGN_ACCEPT self
|
||||
-- @param #string TaskBriefing
|
||||
function ACT_ASSIGN_ACCEPT:New( TaskBriefing )
|
||||
|
||||
|
||||
local self = BASE:Inherit( self, ACT_ASSIGN:New() ) -- #ACT_ASSIGN_ACCEPT
|
||||
|
||||
self.TaskBriefing = TaskBriefing
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function ACT_ASSIGN_ACCEPT:Init( FsmAssign )
|
||||
|
||||
self.TaskBriefing = FsmAssign.TaskBriefing
|
||||
|
||||
self.TaskBriefing = FsmAssign.TaskBriefing
|
||||
end
|
||||
|
||||
--- StateMachine callback function
|
||||
@@ -157,8 +157,8 @@ do -- ACT_ASSIGN_ACCEPT
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ASSIGN_ACCEPT:onafterStart( ProcessUnit, Task, From, Event, To )
|
||||
|
||||
self:__Assign( 1 )
|
||||
|
||||
self:__Assign( 1 )
|
||||
end
|
||||
|
||||
--- StateMachine callback function
|
||||
@@ -168,10 +168,10 @@ do -- ACT_ASSIGN_ACCEPT
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ASSIGN_ACCEPT:onenterAssigned( ProcessUnit, Task, From, Event, To, TaskGroup )
|
||||
|
||||
|
||||
self.Task:Assign( ProcessUnit, ProcessUnit:GetPlayerName() )
|
||||
end
|
||||
|
||||
|
||||
end -- ACT_ASSIGN_ACCEPT
|
||||
|
||||
|
||||
@@ -183,7 +183,7 @@ do -- ACT_ASSIGN_MENU_ACCEPT
|
||||
-- @field Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @field Core.Zone#ZONE_BASE TargetZone
|
||||
-- @extends #ACT_ASSIGN
|
||||
ACT_ASSIGN_MENU_ACCEPT = {
|
||||
ACT_ASSIGN_MENU_ACCEPT = {
|
||||
ClassName = "ACT_ASSIGN_MENU_ACCEPT",
|
||||
}
|
||||
|
||||
@@ -197,7 +197,7 @@ do -- ACT_ASSIGN_MENU_ACCEPT
|
||||
local self = BASE:Inherit( self, ACT_ASSIGN:New() ) -- #ACT_ASSIGN_MENU_ACCEPT
|
||||
|
||||
self.TaskBriefing = TaskBriefing
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -207,12 +207,12 @@ do -- ACT_ASSIGN_MENU_ACCEPT
|
||||
-- @param #string TaskBriefing
|
||||
-- @return #ACT_ASSIGN_MENU_ACCEPT self
|
||||
function ACT_ASSIGN_MENU_ACCEPT:Init( TaskBriefing )
|
||||
|
||||
|
||||
self.TaskBriefing = TaskBriefing
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ASSIGN_MENU_ACCEPT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
@@ -222,30 +222,30 @@ do -- ACT_ASSIGN_MENU_ACCEPT
|
||||
function ACT_ASSIGN_MENU_ACCEPT:onafterStart( ProcessUnit, Task, From, Event, To )
|
||||
|
||||
self:GetCommandCenter():MessageToGroup( "Task " .. self.Task:GetName() .. " has been assigned to you and your group!\nRead the briefing and use the Radio Menu (F10) / Task ... CONFIRMATION menu to accept or reject the task.\nYou have 2 minutes to accept, or the task assignment will be cancelled!", ProcessUnit:GetGroup(), 120 )
|
||||
|
||||
local TaskGroup = ProcessUnit:GetGroup()
|
||||
|
||||
local TaskGroup = ProcessUnit:GetGroup()
|
||||
|
||||
self.Menu = MENU_GROUP:New( TaskGroup, "Task " .. self.Task:GetName() .. " CONFIRMATION" )
|
||||
self.MenuAcceptTask = MENU_GROUP_COMMAND:New( TaskGroup, "Accept task " .. self.Task:GetName(), self.Menu, self.MenuAssign, self, TaskGroup )
|
||||
self.MenuRejectTask = MENU_GROUP_COMMAND:New( TaskGroup, "Reject task " .. self.Task:GetName(), self.Menu, self.MenuReject, self, TaskGroup )
|
||||
|
||||
|
||||
self:__Reject( 120, TaskGroup )
|
||||
end
|
||||
|
||||
|
||||
--- Menu function.
|
||||
-- @param #ACT_ASSIGN_MENU_ACCEPT self
|
||||
function ACT_ASSIGN_MENU_ACCEPT:MenuAssign( TaskGroup )
|
||||
|
||||
|
||||
self:__Assign( -1, TaskGroup )
|
||||
end
|
||||
|
||||
|
||||
--- Menu function.
|
||||
-- @param #ACT_ASSIGN_MENU_ACCEPT self
|
||||
function ACT_ASSIGN_MENU_ACCEPT:MenuReject( TaskGroup )
|
||||
|
||||
|
||||
self:__Reject( -1, TaskGroup )
|
||||
end
|
||||
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ASSIGN_MENU_ACCEPT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
@@ -253,10 +253,10 @@ do -- ACT_ASSIGN_MENU_ACCEPT
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ASSIGN_MENU_ACCEPT:onafterAssign( ProcessUnit, Task, From, Event, To, TaskGroup )
|
||||
|
||||
|
||||
self.Menu:Remove()
|
||||
end
|
||||
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ASSIGN_MENU_ACCEPT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
@@ -265,7 +265,7 @@ do -- ACT_ASSIGN_MENU_ACCEPT
|
||||
-- @param #string To
|
||||
function ACT_ASSIGN_MENU_ACCEPT:onafterReject( ProcessUnit, Task, From, Event, To, TaskGroup )
|
||||
self:F( { TaskGroup = TaskGroup } )
|
||||
|
||||
|
||||
self.Menu:Remove()
|
||||
--TODO: need to resolve this problem ... it has to do with the events ...
|
||||
--self.Task:UnAssignFromUnit( ProcessUnit )needs to become a callback funtion call upon the event
|
||||
@@ -279,7 +279,7 @@ do -- ACT_ASSIGN_MENU_ACCEPT
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ASSIGN_MENU_ACCEPT:onenterAssigned( ProcessUnit, Task, From, Event, To, TaskGroup )
|
||||
|
||||
|
||||
--self.Task:AssignToGroup( TaskGroup )
|
||||
self.Task:Assign( ProcessUnit, ProcessUnit:GetPlayerName() )
|
||||
end
|
||||
|
||||
@@ -1,65 +1,65 @@
|
||||
--- (SP) (MP) (FSM) Route AI or players through waypoints or to zones.
|
||||
--
|
||||
--
|
||||
-- ## ACT_ASSIST state machine:
|
||||
--
|
||||
--
|
||||
-- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur.
|
||||
-- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below.
|
||||
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
|
||||
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
|
||||
-- but will have **different implementation behaviour** upon each event or state transition.
|
||||
--
|
||||
--
|
||||
-- ### ACT_ASSIST **Events**:
|
||||
--
|
||||
--
|
||||
-- These are the events defined in this class:
|
||||
--
|
||||
--
|
||||
-- * **Start**: The process is started.
|
||||
-- * **Next**: The process is smoking the targets in the given zone.
|
||||
--
|
||||
--
|
||||
-- ### ACT_ASSIST **Event methods**:
|
||||
--
|
||||
--
|
||||
-- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process.
|
||||
-- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine:
|
||||
--
|
||||
--
|
||||
-- * **Immediate**: The event method has exactly the name of the event.
|
||||
-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed.
|
||||
--
|
||||
-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed.
|
||||
--
|
||||
-- ### ACT_ASSIST **States**:
|
||||
--
|
||||
--
|
||||
-- * **None**: The controllable did not receive route commands.
|
||||
-- * **AwaitSmoke (*)**: The process is awaiting to smoke the targets in the zone.
|
||||
-- * **Smoking (*)**: The process is smoking the targets in the zone.
|
||||
-- * **Failed (*)**: The process has failed.
|
||||
--
|
||||
--
|
||||
-- (*) End states of the process.
|
||||
--
|
||||
--
|
||||
-- ### ACT_ASSIST state transition methods:
|
||||
--
|
||||
--
|
||||
-- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state.
|
||||
-- There are 2 moments when state transition methods will be called by the state machine:
|
||||
--
|
||||
-- * **Before** the state transition.
|
||||
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
|
||||
--
|
||||
-- * **Before** the state transition.
|
||||
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
|
||||
-- If the state transition method returns false, then the processing of the state transition will not be done!
|
||||
-- If you want to change the behaviour of the AIControllable at this event, return false,
|
||||
-- If you want to change the behaviour of the AIControllable at this event, return false,
|
||||
-- but then you'll need to specify your own logic using the AIControllable!
|
||||
--
|
||||
-- * **After** the state transition.
|
||||
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
|
||||
--
|
||||
-- * **After** the state transition.
|
||||
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
|
||||
-- These state transition methods need to provide a return value, which is specified at the function description.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- # 1) @{#ACT_ASSIST_SMOKE_TARGETS_ZONE} class, extends @{Core.Fsm.Route#ACT_ASSIST}
|
||||
--
|
||||
--
|
||||
-- The ACT_ASSIST_SMOKE_TARGETS_ZONE class implements the core functions to smoke targets in a @{Zone}.
|
||||
-- The targets are smoked within a certain range around each target, simulating a realistic smoking behaviour.
|
||||
-- The targets are smoked within a certain range around each target, simulating a realistic smoking behaviour.
|
||||
-- At random intervals, a new target is smoked.
|
||||
--
|
||||
--
|
||||
-- # 1.1) ACT_ASSIST_SMOKE_TARGETS_ZONE constructor:
|
||||
--
|
||||
--
|
||||
-- * @{#ACT_ASSIST_SMOKE_TARGETS_ZONE.New}(): Creates a new ACT_ASSIST_SMOKE_TARGETS_ZONE object.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- @module Actions.Assist
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
@@ -69,7 +69,7 @@ do -- ACT_ASSIST
|
||||
--- ACT_ASSIST class
|
||||
-- @type ACT_ASSIST
|
||||
-- @extends Core.Fsm#FSM_PROCESS
|
||||
ACT_ASSIST = {
|
||||
ACT_ASSIST = {
|
||||
ClassName = "ACT_ASSIST",
|
||||
}
|
||||
|
||||
@@ -86,15 +86,15 @@ do -- ACT_ASSIST
|
||||
self:AddTransition( "Smoking", "Next", "AwaitSmoke" )
|
||||
self:AddTransition( "*", "Stop", "Success" )
|
||||
self:AddTransition( "*", "Fail", "Failed" )
|
||||
|
||||
|
||||
self:AddEndState( "Failed" )
|
||||
self:AddEndState( "Success" )
|
||||
|
||||
self:SetStartState( "None" )
|
||||
|
||||
self:SetStartState( "None" )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Task Events
|
||||
|
||||
--- StateMachine callback function
|
||||
@@ -104,17 +104,17 @@ do -- ACT_ASSIST
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ASSIST:onafterStart( ProcessUnit, From, Event, To )
|
||||
|
||||
|
||||
local ProcessGroup = ProcessUnit:GetGroup()
|
||||
local MissionMenu = self:GetMission():GetMenu( ProcessGroup )
|
||||
|
||||
|
||||
local function MenuSmoke( MenuParam )
|
||||
local self = MenuParam.self
|
||||
local SmokeColor = MenuParam.SmokeColor
|
||||
self.SmokeColor = SmokeColor
|
||||
self:__Next( 1 )
|
||||
end
|
||||
|
||||
|
||||
self.Menu = MENU_GROUP:New( ProcessGroup, "Target acquisition", MissionMenu )
|
||||
self.MenuSmokeBlue = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop blue smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.Blue } )
|
||||
self.MenuSmokeGreen = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop green smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.Green } )
|
||||
@@ -130,10 +130,10 @@ do -- ACT_ASSIST
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ASSIST:onafterStop( ProcessUnit, From, Event, To )
|
||||
|
||||
|
||||
self.Menu:Remove() -- When stopped, remove the menus
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
do -- ACT_ASSIST_SMOKE_TARGETS_ZONE
|
||||
@@ -143,17 +143,17 @@ do -- ACT_ASSIST_SMOKE_TARGETS_ZONE
|
||||
-- @field Core.Set#SET_UNIT TargetSetUnit
|
||||
-- @field Core.Zone#ZONE_BASE TargetZone
|
||||
-- @extends #ACT_ASSIST
|
||||
ACT_ASSIST_SMOKE_TARGETS_ZONE = {
|
||||
ACT_ASSIST_SMOKE_TARGETS_ZONE = {
|
||||
ClassName = "ACT_ASSIST_SMOKE_TARGETS_ZONE",
|
||||
}
|
||||
|
||||
|
||||
-- function ACT_ASSIST_SMOKE_TARGETS_ZONE:_Destructor()
|
||||
-- self:E("_Destructor")
|
||||
--
|
||||
--
|
||||
-- self.Menu:Remove()
|
||||
-- self:EventRemoveAll()
|
||||
-- end
|
||||
|
||||
|
||||
--- Creates a new target smoking state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator.
|
||||
-- @param #ACT_ASSIST_SMOKE_TARGETS_ZONE self
|
||||
-- @param Core.Set#SET_UNIT TargetSetUnit
|
||||
@@ -163,29 +163,29 @@ do -- ACT_ASSIST_SMOKE_TARGETS_ZONE
|
||||
|
||||
self.TargetSetUnit = TargetSetUnit
|
||||
self.TargetZone = TargetZone
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function ACT_ASSIST_SMOKE_TARGETS_ZONE:Init( FsmSmoke )
|
||||
|
||||
|
||||
self.TargetSetUnit = FsmSmoke.TargetSetUnit
|
||||
self.TargetZone = FsmSmoke.TargetZone
|
||||
end
|
||||
|
||||
|
||||
--- Creates a new target smoking state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator.
|
||||
-- @param #ACT_ASSIST_SMOKE_TARGETS_ZONE self
|
||||
-- @param Core.Set#SET_UNIT TargetSetUnit
|
||||
-- @param Core.Zone#ZONE_BASE TargetZone
|
||||
-- @return #ACT_ASSIST_SMOKE_TARGETS_ZONE self
|
||||
function ACT_ASSIST_SMOKE_TARGETS_ZONE:Init( TargetSetUnit, TargetZone )
|
||||
|
||||
|
||||
self.TargetSetUnit = TargetSetUnit
|
||||
self.TargetZone = TargetZone
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ASSIST_SMOKE_TARGETS_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
@@ -193,7 +193,7 @@ do -- ACT_ASSIST_SMOKE_TARGETS_ZONE
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ASSIST_SMOKE_TARGETS_ZONE:onenterSmoking( ProcessUnit, From, Event, To )
|
||||
|
||||
|
||||
self.TargetSetUnit:ForEachUnit(
|
||||
--- @param Wrapper.Unit#UNIT SmokeUnit
|
||||
function( SmokeUnit )
|
||||
@@ -203,12 +203,12 @@ do -- ACT_ASSIST_SMOKE_TARGETS_ZONE
|
||||
if SmokeUnit:IsAlive() then
|
||||
SmokeUnit:Smoke( self.SmokeColor, 150 )
|
||||
end
|
||||
end, {}, math.random( 10, 60 )
|
||||
end, {}, math.random( 10, 60 )
|
||||
)
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
--- (SP) (MP) (FSM) Route AI or players through waypoints or to zones.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- # @{#ACT_ROUTE} FSM class, extends @{Core.Fsm#FSM_PROCESS}
|
||||
--
|
||||
--
|
||||
-- ## ACT_ROUTE state machine:
|
||||
--
|
||||
--
|
||||
-- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur.
|
||||
-- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below.
|
||||
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
|
||||
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
|
||||
-- but will have **different implementation behaviour** upon each event or state transition.
|
||||
--
|
||||
--
|
||||
-- ### ACT_ROUTE **Events**:
|
||||
--
|
||||
--
|
||||
-- These are the events defined in this class:
|
||||
--
|
||||
--
|
||||
-- * **Start**: The process is started. The process will go into the Report state.
|
||||
-- * **Report**: The process is reporting to the player the route to be followed.
|
||||
-- * **Route**: The process is routing the controllable.
|
||||
@@ -22,56 +22,56 @@
|
||||
-- * **Arrive**: The controllable has arrived at a route point.
|
||||
-- * **More**: There are more route points that need to be followed. The process will go back into the Report state.
|
||||
-- * **NoMore**: There are no more route points that need to be followed. The process will go into the Success state.
|
||||
--
|
||||
--
|
||||
-- ### ACT_ROUTE **Event methods**:
|
||||
--
|
||||
--
|
||||
-- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process.
|
||||
-- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine:
|
||||
--
|
||||
--
|
||||
-- * **Immediate**: The event method has exactly the name of the event.
|
||||
-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed.
|
||||
--
|
||||
-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed.
|
||||
--
|
||||
-- ### ACT_ROUTE **States**:
|
||||
--
|
||||
--
|
||||
-- * **None**: The controllable did not receive route commands.
|
||||
-- * **Arrived (*)**: The controllable has arrived at a route point.
|
||||
-- * **Aborted (*)**: The controllable has aborted the route path.
|
||||
-- * **Routing**: The controllable is understay to the route point.
|
||||
-- * **Pausing**: The process is pausing the routing. AI air will go into hover, AI ground will stop moving. Players can fly around.
|
||||
-- * **Success (*)**: All route points were reached.
|
||||
-- * **Success (*)**: All route points were reached.
|
||||
-- * **Failed (*)**: The process has failed.
|
||||
--
|
||||
--
|
||||
-- (*) End states of the process.
|
||||
--
|
||||
--
|
||||
-- ### ACT_ROUTE state transition methods:
|
||||
--
|
||||
--
|
||||
-- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state.
|
||||
-- There are 2 moments when state transition methods will be called by the state machine:
|
||||
--
|
||||
-- * **Before** the state transition.
|
||||
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
|
||||
--
|
||||
-- * **Before** the state transition.
|
||||
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
|
||||
-- If the state transition method returns false, then the processing of the state transition will not be done!
|
||||
-- If you want to change the behaviour of the AIControllable at this event, return false,
|
||||
-- If you want to change the behaviour of the AIControllable at this event, return false,
|
||||
-- but then you'll need to specify your own logic using the AIControllable!
|
||||
--
|
||||
-- * **After** the state transition.
|
||||
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
|
||||
--
|
||||
-- * **After** the state transition.
|
||||
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
|
||||
-- These state transition methods need to provide a return value, which is specified at the function description.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- # 1) @{#ACT_ROUTE_ZONE} class, extends @{Core.Fsm.Route#ACT_ROUTE}
|
||||
--
|
||||
--
|
||||
-- The ACT_ROUTE_ZONE class implements the core functions to route an AIR @{Wrapper.Controllable} player @{Wrapper.Unit} to a @{Zone}.
|
||||
-- The player receives on perioding times messages with the coordinates of the route to follow.
|
||||
-- The player receives on perioding times messages with the coordinates of the route to follow.
|
||||
-- Upon arrival at the zone, a confirmation of arrival is sent, and the process will be ended.
|
||||
--
|
||||
--
|
||||
-- # 1.1) ACT_ROUTE_ZONE constructor:
|
||||
--
|
||||
--
|
||||
-- * @{#ACT_ROUTE_ZONE.New}(): Creates a new ACT_ROUTE_ZONE object.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- @module Actions.Route
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
@@ -85,11 +85,11 @@ do -- ACT_ROUTE
|
||||
-- @field Core.Zone#ZONE_BASE Zone
|
||||
-- @field Core.Point#COORDINATE Coordinate
|
||||
-- @extends Core.Fsm#FSM_PROCESS
|
||||
ACT_ROUTE = {
|
||||
ACT_ROUTE = {
|
||||
ClassName = "ACT_ROUTE",
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
--- Creates a new routing state machine. The process will route a CLIENT to a ZONE until the CLIENT is within that ZONE.
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @return #ACT_ROUTE self
|
||||
@@ -97,7 +97,7 @@ do -- ACT_ROUTE
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, FSM_PROCESS:New( "ACT_ROUTE" ) ) -- Core.Fsm#FSM_PROCESS
|
||||
|
||||
|
||||
self:AddTransition( "*", "Reset", "None" )
|
||||
self:AddTransition( "None", "Start", "Routing" )
|
||||
self:AddTransition( "*", "Report", "*" )
|
||||
@@ -109,23 +109,23 @@ do -- ACT_ROUTE
|
||||
self:AddTransition( "*", "Fail", "Failed" )
|
||||
self:AddTransition( "", "", "" )
|
||||
self:AddTransition( "", "", "" )
|
||||
|
||||
|
||||
self:AddEndState( "Arrived" )
|
||||
self:AddEndState( "Failed" )
|
||||
self:AddEndState( "Cancelled" )
|
||||
|
||||
|
||||
self:SetStartState( "None" )
|
||||
|
||||
self:SetRouteMode( "C" )
|
||||
|
||||
|
||||
self:SetRouteMode( "C" )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set a Cancel Menu item.
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @return #ACT_ROUTE
|
||||
function ACT_ROUTE:SetMenuCancel( MenuGroup, MenuText, ParentMenu, MenuTime, MenuTag )
|
||||
|
||||
|
||||
self.CancelMenuGroupCommand = MENU_GROUP_COMMAND:New(
|
||||
MenuGroup,
|
||||
MenuText,
|
||||
@@ -135,47 +135,47 @@ do -- ACT_ROUTE
|
||||
):SetTime( MenuTime ):SetTag( MenuTag )
|
||||
|
||||
ParentMenu:SetTime( MenuTime )
|
||||
|
||||
|
||||
ParentMenu:Remove( MenuTime, MenuTag )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set the route mode.
|
||||
-- There are 2 route modes supported:
|
||||
--
|
||||
--
|
||||
-- * SetRouteMode( "B" ): Route mode is Bearing and Range.
|
||||
-- * SetRouteMode( "C" ): Route mode is LL or MGRS according coordinate system setup.
|
||||
--
|
||||
--
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @return #ACT_ROUTE
|
||||
function ACT_ROUTE:SetRouteMode( RouteMode )
|
||||
|
||||
|
||||
self.RouteMode = RouteMode
|
||||
|
||||
return self
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Get the routing text to be displayed.
|
||||
-- The route mode determines the text displayed.
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @param Wrapper.Unit#UNIT Controllable
|
||||
-- @return #string
|
||||
function ACT_ROUTE:GetRouteText( Controllable )
|
||||
|
||||
|
||||
local RouteText = ""
|
||||
|
||||
local Coordinate = nil -- Core.Point#COORDINATE
|
||||
|
||||
|
||||
if self.Coordinate then
|
||||
Coordinate = self.Coordinate
|
||||
end
|
||||
|
||||
|
||||
if self.Zone then
|
||||
Coordinate = self.Zone:GetPointVec3( self.Altitude )
|
||||
Coordinate:SetHeading( self.Heading )
|
||||
end
|
||||
|
||||
|
||||
|
||||
local Task = self:GetTask() -- This is to dermine that the coordinates are for a specific task mode (A2A or A2G).
|
||||
local CC = self:GetTask():GetMission():GetCommandCenter()
|
||||
@@ -209,7 +209,7 @@ do -- ACT_ROUTE
|
||||
return RouteText
|
||||
end
|
||||
|
||||
|
||||
|
||||
function ACT_ROUTE:MenuCancel()
|
||||
self:F("Cancelled")
|
||||
self.CancelMenuGroupCommand:Remove()
|
||||
@@ -225,11 +225,11 @@ do -- ACT_ROUTE
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ROUTE:onafterStart( ProcessUnit, From, Event, To )
|
||||
|
||||
|
||||
|
||||
self:__Route( 1 )
|
||||
end
|
||||
|
||||
|
||||
--- Check if the controllable has arrived.
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
@@ -237,7 +237,7 @@ do -- ACT_ROUTE
|
||||
function ACT_ROUTE:onfuncHasArrived( ProcessUnit )
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
@@ -245,7 +245,7 @@ do -- ACT_ROUTE
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ROUTE:onbeforeRoute( ProcessUnit, From, Event, To )
|
||||
|
||||
|
||||
if ProcessUnit:IsAlive() then
|
||||
local HasArrived = self:onfuncHasArrived( ProcessUnit ) -- Polymorphic
|
||||
if self.DisplayCount >= self.DisplayInterval then
|
||||
@@ -257,18 +257,18 @@ do -- ACT_ROUTE
|
||||
else
|
||||
self.DisplayCount = self.DisplayCount + 1
|
||||
end
|
||||
|
||||
|
||||
if HasArrived then
|
||||
self:__Arrive( 1 )
|
||||
else
|
||||
self:__Route( 1 )
|
||||
end
|
||||
|
||||
|
||||
return HasArrived -- if false, then the event will not be executed...
|
||||
end
|
||||
|
||||
|
||||
return false
|
||||
|
||||
|
||||
end
|
||||
|
||||
end -- ACT_ROUTE
|
||||
@@ -280,12 +280,12 @@ do -- ACT_ROUTE_POINT
|
||||
-- @type ACT_ROUTE_POINT
|
||||
-- @field Tasking.Task#TASK TASK
|
||||
-- @extends #ACT_ROUTE
|
||||
ACT_ROUTE_POINT = {
|
||||
ACT_ROUTE_POINT = {
|
||||
ClassName = "ACT_ROUTE_POINT",
|
||||
}
|
||||
|
||||
|
||||
--- Creates a new routing state machine.
|
||||
--- Creates a new routing state machine.
|
||||
-- The task will route a controllable to a Coordinate until the controllable is within the Range.
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
-- @param Core.Point#COORDINATE The Coordinate to Target.
|
||||
@@ -296,29 +296,29 @@ do -- ACT_ROUTE_POINT
|
||||
|
||||
self.Coordinate = Coordinate
|
||||
self.Range = Range or 0
|
||||
|
||||
|
||||
self.DisplayInterval = 30
|
||||
self.DisplayCount = 30
|
||||
self.DisplayMessage = true
|
||||
self.DisplayTime = 10 -- 10 seconds is the default
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Creates a new routing state machine.
|
||||
|
||||
--- Creates a new routing state machine.
|
||||
-- The task will route a controllable to a Coordinate until the controllable is within the Range.
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
function ACT_ROUTE_POINT:Init( FsmRoute )
|
||||
|
||||
|
||||
self.Coordinate = FsmRoute.Coordinate
|
||||
self.Range = FsmRoute.Range or 0
|
||||
|
||||
|
||||
self.DisplayInterval = 30
|
||||
self.DisplayCount = 30
|
||||
self.DisplayMessage = true
|
||||
self.DisplayTime = 10 -- 10 seconds is the default
|
||||
self:SetStartState("None")
|
||||
end
|
||||
end
|
||||
|
||||
--- Set Coordinate
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
@@ -326,7 +326,7 @@ do -- ACT_ROUTE_POINT
|
||||
function ACT_ROUTE_POINT:SetCoordinate( Coordinate )
|
||||
self:F2( { Coordinate } )
|
||||
self.Coordinate = Coordinate
|
||||
end
|
||||
end
|
||||
|
||||
--- Get Coordinate
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
@@ -334,7 +334,7 @@ do -- ACT_ROUTE_POINT
|
||||
function ACT_ROUTE_POINT:GetCoordinate()
|
||||
self:F2( { self.Coordinate } )
|
||||
return self.Coordinate
|
||||
end
|
||||
end
|
||||
|
||||
--- Set Range around Coordinate
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
@@ -342,16 +342,16 @@ do -- ACT_ROUTE_POINT
|
||||
function ACT_ROUTE_POINT:SetRange( Range )
|
||||
self:F2( { Range } )
|
||||
self.Range = Range or 10000
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Get Range around Coordinate
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
-- @return #number The Range to consider the arrival. Default is 10000 meters.
|
||||
function ACT_ROUTE_POINT:GetRange()
|
||||
self:F2( { self.Range } )
|
||||
return self.Range
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Method override to check if the controllable has arrived.
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
@@ -360,7 +360,7 @@ do -- ACT_ROUTE_POINT
|
||||
|
||||
if ProcessUnit:IsAlive() then
|
||||
local Distance = self.Coordinate:Get2DDistance( ProcessUnit:GetCoordinate() )
|
||||
|
||||
|
||||
if Distance <= self.Range then
|
||||
local RouteText = "Task \"" .. self:GetTask():GetName() .. "\", you have arrived."
|
||||
self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
|
||||
@@ -370,9 +370,9 @@ do -- ACT_ROUTE_POINT
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
--- Task Events
|
||||
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
@@ -380,9 +380,9 @@ do -- ACT_ROUTE_POINT
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ROUTE_POINT:onafterReport( ProcessUnit, From, Event, To )
|
||||
|
||||
|
||||
local RouteText = "Task \"" .. self:GetTask():GetName() .. "\", " .. self:GetRouteText( ProcessUnit )
|
||||
|
||||
|
||||
self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Update )
|
||||
end
|
||||
|
||||
@@ -397,7 +397,7 @@ do -- ACT_ROUTE_ZONE
|
||||
-- @field Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @field Core.Zone#ZONE_BASE Zone
|
||||
-- @extends #ACT_ROUTE
|
||||
ACT_ROUTE_ZONE = {
|
||||
ACT_ROUTE_ZONE = {
|
||||
ClassName = "ACT_ROUTE_ZONE",
|
||||
}
|
||||
|
||||
@@ -409,25 +409,25 @@ do -- ACT_ROUTE_ZONE
|
||||
local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_ZONE
|
||||
|
||||
self.Zone = Zone
|
||||
|
||||
|
||||
self.DisplayInterval = 30
|
||||
self.DisplayCount = 30
|
||||
self.DisplayMessage = true
|
||||
self.DisplayTime = 10 -- 10 seconds is the default
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
function ACT_ROUTE_ZONE:Init( FsmRoute )
|
||||
|
||||
|
||||
self.Zone = FsmRoute.Zone
|
||||
|
||||
|
||||
self.DisplayInterval = 30
|
||||
self.DisplayCount = 30
|
||||
self.DisplayMessage = true
|
||||
self.DisplayTime = 10 -- 10 seconds is the default
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Set Zone
|
||||
-- @param #ACT_ROUTE_ZONE self
|
||||
-- @param Core.Zone#ZONE_BASE Zone The Zone object where to route to.
|
||||
@@ -437,14 +437,14 @@ do -- ACT_ROUTE_ZONE
|
||||
self.Zone = Zone
|
||||
self.Altitude = Altitude
|
||||
self.Heading = Heading
|
||||
end
|
||||
end
|
||||
|
||||
--- Get Zone
|
||||
-- @param #ACT_ROUTE_ZONE self
|
||||
-- @return Core.Zone#ZONE_BASE Zone The Zone object where to route to.
|
||||
function ACT_ROUTE_ZONE:GetZone()
|
||||
return self.Zone
|
||||
end
|
||||
return self.Zone
|
||||
end
|
||||
|
||||
--- Method override to check if the controllable has arrived.
|
||||
-- @param #ACT_ROUTE self
|
||||
@@ -459,9 +459,9 @@ do -- ACT_ROUTE_ZONE
|
||||
|
||||
return ProcessUnit:IsInZone( self.Zone )
|
||||
end
|
||||
|
||||
|
||||
--- Task Events
|
||||
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ROUTE_ZONE self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
@@ -470,7 +470,7 @@ do -- ACT_ROUTE_ZONE
|
||||
-- @param #string To
|
||||
function ACT_ROUTE_ZONE:onafterReport( ProcessUnit, From, Event, To )
|
||||
self:F( { ProcessUnit = ProcessUnit } )
|
||||
|
||||
|
||||
local RouteText = "Task \"" .. self:GetTask():GetName() .. "\", " .. self:GetRouteText( ProcessUnit )
|
||||
self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Update )
|
||||
end
|
||||
|
||||
@@ -298,7 +298,8 @@ end
|
||||
--
|
||||
--
|
||||
-- @param #BASE self
|
||||
-- @param #BASE Child is the Child class from which the Parent class needs to be retrieved.
|
||||
-- @param #BASE Child This is the Child class from which the Parent class needs to be retrieved.
|
||||
-- @param #BASE FromClass (Optional) The class from which to get the parent.
|
||||
-- @return #BASE
|
||||
function BASE:GetParent( Child, FromClass )
|
||||
|
||||
@@ -625,6 +626,55 @@ do -- Event Handling
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
|
||||
--- Unknown precisely what creates this event, likely tied into newer damage model. Will update this page when new information become available.
|
||||
--
|
||||
-- * initiator: The unit that had the failure.
|
||||
--
|
||||
-- @function [parent=#BASE] OnEventDetailedFailure
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
--- Occurs when any modification to the "Score" as seen on the debrief menu would occur.
|
||||
-- There is no information on what values the score was changed to. Event is likely similar to player_comment in this regard.
|
||||
-- @function [parent=#BASE] OnEventScore
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
--- Occurs on the death of a unit. Contains more and different information. Similar to unit_lost it will occur for aircraft before the aircraft crash event occurs.
|
||||
--
|
||||
-- * initiator: The unit that killed the target
|
||||
-- * target: Target Object
|
||||
-- * weapon: Weapon Object
|
||||
--
|
||||
-- @function [parent=#BASE] OnEventKill
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
--- Occurs when any modification to the "Score" as seen on the debrief menu would occur.
|
||||
-- There is no information on what values the score was changed to. Event is likely similar to player_comment in this regard.
|
||||
-- @function [parent=#BASE] OnEventScore
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
--- Occurs when the game thinks an object is destroyed.
|
||||
--
|
||||
-- * initiator: The unit that is was destroyed.
|
||||
--
|
||||
-- @function [parent=#BASE] OnEventUnitLost
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
--- Occurs shortly after the landing animation of an ejected pilot touching the ground and standing up. Event does not occur if the pilot lands in the water and sub combs to Davey Jones Locker.
|
||||
--
|
||||
-- * initiator: Static object representing the ejected pilot. Place : Aircraft that the pilot ejected from.
|
||||
-- * place: may not return as a valid object if the aircraft has crashed into the ground and no longer exists.
|
||||
-- * subplace: is always 0 for unknown reasons.
|
||||
--
|
||||
-- @function [parent=#BASE] OnEventLandingAfterEjection
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -764,9 +814,7 @@ do -- Scheduling
|
||||
if not self.Scheduler then
|
||||
self.Scheduler = SCHEDULER:New( self )
|
||||
end
|
||||
|
||||
self.Scheduler.SchedulerObject = self.Scheduler
|
||||
|
||||
|
||||
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule(
|
||||
self,
|
||||
SchedulerFunction,
|
||||
@@ -804,9 +852,6 @@ do -- Scheduling
|
||||
self.Scheduler = SCHEDULER:New( self )
|
||||
end
|
||||
|
||||
self.Scheduler.SchedulerObject = self.Scheduler
|
||||
--self.MasterObject = self
|
||||
|
||||
local ScheduleID = self.Scheduler:Schedule(
|
||||
self,
|
||||
SchedulerFunction,
|
||||
@@ -925,7 +970,13 @@ end
|
||||
-- -- Switch the tracing Off
|
||||
-- BASE:TraceOnOff( false )
|
||||
function BASE:TraceOnOff( TraceOnOff )
|
||||
_TraceOnOff = TraceOnOff or true
|
||||
if TraceOnOff==false then
|
||||
self:I( "Tracing in MOOSE is OFF" )
|
||||
_TraceOnOff = false
|
||||
else
|
||||
self:I( "Tracing in MOOSE is ON" )
|
||||
_TraceOnOff = true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -954,7 +1005,11 @@ end
|
||||
-- @param #boolean TraceAll true = trace all methods in MOOSE.
|
||||
function BASE:TraceAll( TraceAll )
|
||||
|
||||
_TraceAll = TraceAll or true
|
||||
if TraceAll==false then
|
||||
_TraceAll=false
|
||||
else
|
||||
_TraceAll = true
|
||||
end
|
||||
|
||||
if _TraceAll then
|
||||
self:I( "Tracing all methods in MOOSE " )
|
||||
|
||||
@@ -187,7 +187,7 @@ end
|
||||
function DATABASE:AddUnit( DCSUnitName )
|
||||
|
||||
if not self.UNITS[DCSUnitName] then
|
||||
self:I( { "Add UNIT:", DCSUnitName } )
|
||||
self:T( { "Add UNIT:", DCSUnitName } )
|
||||
local UnitRegister = UNIT:Register( DCSUnitName )
|
||||
self.UNITS[DCSUnitName] = UNIT:Register( DCSUnitName )
|
||||
|
||||
@@ -247,12 +247,15 @@ end
|
||||
|
||||
--- Adds a Airbase based on the Airbase Name in the DATABASE.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string AirbaseName The name of the airbase
|
||||
-- @param #string AirbaseName The name of the airbase.
|
||||
-- @return Wrapper.Airbase#AIRBASE Airbase object.
|
||||
function DATABASE:AddAirbase( AirbaseName )
|
||||
|
||||
if not self.AIRBASES[AirbaseName] then
|
||||
self.AIRBASES[AirbaseName] = AIRBASE:Register( AirbaseName )
|
||||
end
|
||||
|
||||
return self.AIRBASES[AirbaseName]
|
||||
end
|
||||
|
||||
|
||||
@@ -509,7 +512,7 @@ end
|
||||
function DATABASE:AddGroup( GroupName )
|
||||
|
||||
if not self.GROUPS[GroupName] then
|
||||
self:I( { "Add GROUP:", GroupName } )
|
||||
self:T( { "Add GROUP:", GroupName } )
|
||||
self.GROUPS[GroupName] = GROUP:Register( GroupName )
|
||||
end
|
||||
|
||||
@@ -521,7 +524,7 @@ end
|
||||
function DATABASE:AddPlayer( UnitName, PlayerName )
|
||||
|
||||
if PlayerName then
|
||||
self:I( { "Add player for unit:", UnitName, PlayerName } )
|
||||
self:T( { "Add player for unit:", UnitName, PlayerName } )
|
||||
self.PLAYERS[PlayerName] = UnitName
|
||||
self.PLAYERUNITS[PlayerName] = self:FindUnit( UnitName )
|
||||
self.PLAYERSJOINED[PlayerName] = PlayerName
|
||||
@@ -533,7 +536,7 @@ end
|
||||
function DATABASE:DeletePlayer( UnitName, PlayerName )
|
||||
|
||||
if PlayerName then
|
||||
self:I( { "Clean player:", PlayerName } )
|
||||
self:T( { "Clean player:", PlayerName } )
|
||||
self.PLAYERS[PlayerName] = nil
|
||||
self.PLAYERUNITS[PlayerName] = nil
|
||||
end
|
||||
@@ -698,11 +701,11 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category
|
||||
UnitNames[#UnitNames+1] = self.Templates.Units[UnitTemplate.name].UnitName
|
||||
end
|
||||
|
||||
self:I( { Group = self.Templates.Groups[GroupTemplateName].GroupName,
|
||||
self:T( { Group = self.Templates.Groups[GroupTemplateName].GroupName,
|
||||
Coalition = self.Templates.Groups[GroupTemplateName].CoalitionID,
|
||||
Category = self.Templates.Groups[GroupTemplateName].CategoryID,
|
||||
Country = self.Templates.Groups[GroupTemplateName].CountryID,
|
||||
Units = UnitNames
|
||||
Category = self.Templates.Groups[GroupTemplateName].CategoryID,
|
||||
Country = self.Templates.Groups[GroupTemplateName].CountryID,
|
||||
Units = UnitNames
|
||||
}
|
||||
)
|
||||
end
|
||||
@@ -847,9 +850,9 @@ function DATABASE:_RegisterGroupsAndUnits()
|
||||
end
|
||||
end
|
||||
|
||||
self:I("Groups:")
|
||||
self:T("Groups:")
|
||||
for GroupName, Group in pairs( self.GROUPS ) do
|
||||
self:I( { "Group:", GroupName } )
|
||||
self:T( { "Group:", GroupName } )
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -861,7 +864,7 @@ end
|
||||
function DATABASE:_RegisterClients()
|
||||
|
||||
for ClientName, ClientTemplate in pairs( self.Templates.ClientsByName ) do
|
||||
self:I( { "Register Client:", ClientName } )
|
||||
self:T( { "Register Client:", ClientName } )
|
||||
self:AddClient( ClientName )
|
||||
end
|
||||
|
||||
@@ -879,7 +882,7 @@ function DATABASE:_RegisterStatics()
|
||||
if DCSStatic:isExist() then
|
||||
local DCSStaticName = DCSStatic:getName()
|
||||
|
||||
self:I( { "Register Static:", DCSStaticName } )
|
||||
self:T( { "Register Static:", DCSStaticName } )
|
||||
self:AddStatic( DCSStaticName )
|
||||
else
|
||||
self:E( { "Static does not exist: ", DCSStatic } )
|
||||
@@ -893,16 +896,29 @@ end
|
||||
--- @param #DATABASE self
|
||||
function DATABASE:_RegisterAirbases()
|
||||
|
||||
--[[
|
||||
local CoalitionsData = { AirbasesRed = coalition.getAirbases( coalition.side.RED ), AirbasesBlue = coalition.getAirbases( coalition.side.BLUE ), AirbasesNeutral = coalition.getAirbases( coalition.side.NEUTRAL ) }
|
||||
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
|
||||
for DCSAirbaseId, DCSAirbase in pairs( CoalitionData ) do
|
||||
|
||||
local DCSAirbaseName = DCSAirbase:getName()
|
||||
|
||||
self:I( { "Register Airbase:", DCSAirbaseName, DCSAirbase:getID() } )
|
||||
self:T( { "Register Airbase:", DCSAirbaseName, DCSAirbase:getID() } )
|
||||
self:AddAirbase( DCSAirbaseName )
|
||||
end
|
||||
end
|
||||
]]
|
||||
|
||||
for DCSAirbaseId, DCSAirbase in pairs(world.getAirbases()) do
|
||||
local DCSAirbaseName = DCSAirbase:getName()
|
||||
|
||||
-- This gives the incorrect value to be inserted into the airdromeID for DCS 2.5.6!
|
||||
local airbaseID=DCSAirbase:getID()
|
||||
|
||||
local airbase=self:AddAirbase( DCSAirbaseName )
|
||||
|
||||
self:I(string.format("Register Airbase: %s, getID=%d, GetID=%d (unique=%d)", DCSAirbaseName, DCSAirbase:getID(), airbase:GetID(), airbase:GetID(true)))
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -221,9 +221,11 @@ EVENTS = {
|
||||
PlayerComment = world.event.S_EVENT_PLAYER_COMMENT,
|
||||
ShootingStart = world.event.S_EVENT_SHOOTING_START,
|
||||
ShootingEnd = world.event.S_EVENT_SHOOTING_END,
|
||||
-- Added with DCS 2.5.1
|
||||
MarkAdded = world.event.S_EVENT_MARK_ADDED,
|
||||
MarkChange = world.event.S_EVENT_MARK_CHANGE,
|
||||
MarkRemoved = world.event.S_EVENT_MARK_REMOVED,
|
||||
-- Moose Events
|
||||
NewCargo = world.event.S_EVENT_NEW_CARGO,
|
||||
DeleteCargo = world.event.S_EVENT_DELETE_CARGO,
|
||||
NewZone = world.event.S_EVENT_NEW_ZONE,
|
||||
@@ -231,6 +233,12 @@ EVENTS = {
|
||||
NewZoneGoal = world.event.S_EVENT_NEW_ZONE_GOAL,
|
||||
DeleteZoneGoal = world.event.S_EVENT_DELETE_ZONE_GOAL,
|
||||
RemoveUnit = world.event.S_EVENT_REMOVE_UNIT,
|
||||
-- Added with DCS 2.5.6
|
||||
DetailedFailure = world.event.S_EVENT_DETAILED_FAILURE or -1, --We set this to -1 for backward compatibility to DCS 2.5.5 and earlier
|
||||
Kill = world.event.S_EVENT_KILL or -1,
|
||||
Score = world.event.S_EVENT_SCORE or -1,
|
||||
UnitLost = world.event.S_EVENT_UNIT_LOST or -1,
|
||||
LandingAfterEjection = world.event.S_EVENT_LANDING_AFTER_EJECTION or -1,
|
||||
}
|
||||
|
||||
--- The Event structure
|
||||
@@ -481,6 +489,32 @@ local _EVENTMETA = {
|
||||
Event = "OnEventRemoveUnit",
|
||||
Text = "S_EVENT_REMOVE_UNIT"
|
||||
},
|
||||
-- Added with DCS 2.5.6
|
||||
[EVENTS.DetailedFailure] = {
|
||||
Order = 1,
|
||||
Event = "OnEventDetailedFailure",
|
||||
Text = "S_EVENT_DETAILED_FAILURE"
|
||||
},
|
||||
[EVENTS.Kill] = {
|
||||
Order = 1,
|
||||
Event = "OnEventKill",
|
||||
Text = "S_EVENT_KILL"
|
||||
},
|
||||
[EVENTS.Score] = {
|
||||
Order = 1,
|
||||
Event = "OnEventScore",
|
||||
Text = "S_EVENT_SCORE"
|
||||
},
|
||||
[EVENTS.UnitLost] = {
|
||||
Order = 1,
|
||||
Event = "OnEventUnitLost",
|
||||
Text = "S_EVENT_UNIT_LOST"
|
||||
},
|
||||
[EVENTS.LandingAfterEjection] = {
|
||||
Order = 1,
|
||||
Event = "OnEventLandingAfterEjection",
|
||||
Text = "S_EVENT_LANDING_AFTER_EJECTION"
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -488,6 +522,9 @@ local _EVENTMETA = {
|
||||
-- @type EVENT.Events
|
||||
-- @field #number IniUnit
|
||||
|
||||
--- Create new event handler.
|
||||
-- @param #EVENT self
|
||||
-- @return #EVENT self
|
||||
function EVENT:New()
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F2()
|
||||
@@ -518,6 +555,8 @@ function EVENT:Init( EventID, EventClass )
|
||||
if not self.Events[EventID][EventPriority][EventClass] then
|
||||
self.Events[EventID][EventPriority][EventClass] = {}
|
||||
end
|
||||
|
||||
|
||||
return self.Events[EventID][EventPriority][EventClass]
|
||||
end
|
||||
|
||||
@@ -604,7 +643,7 @@ end
|
||||
-- @param EventID
|
||||
-- @return #EVENT
|
||||
function EVENT:OnEventGeneric( EventFunction, EventClass, EventID )
|
||||
self:F2( { EventID } )
|
||||
self:F2( { EventID, EventClass, EventFunction } )
|
||||
|
||||
local EventData = self:Init( EventID, EventClass )
|
||||
EventData.EventFunction = EventFunction
|
||||
@@ -878,258 +917,227 @@ function EVENT:onEvent( Event )
|
||||
end
|
||||
|
||||
|
||||
-- Get event meta data.
|
||||
local EventMeta = _EVENTMETA[Event.id]
|
||||
|
||||
--self:E( { EventMeta.Text, Event } ) -- Activate the see all incoming events ...
|
||||
|
||||
if self and
|
||||
self.Events and
|
||||
self.Events[Event.id] and
|
||||
self.MissionEnd == false and
|
||||
( Event.initiator ~= nil or ( Event.initiator == nil and Event.id ~= EVENTS.PlayerLeaveUnit ) ) then
|
||||
|
||||
if Event.id and Event.id == EVENTS.MissionEnd then
|
||||
self.MissionEnd = true
|
||||
end
|
||||
|
||||
if Event.initiator then
|
||||
|
||||
Event.IniObjectCategory = Event.initiator:getCategory()
|
||||
|
||||
if Event.IniObjectCategory == Object.Category.UNIT then
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniDCSGroup = Event.IniDCSUnit:getGroup()
|
||||
Event.IniUnit = UNIT:FindByName( Event.IniDCSUnitName )
|
||||
if not Event.IniUnit then
|
||||
-- Unit can be a CLIENT. Most likely this will be the case ...
|
||||
Event.IniUnit = CLIENT:FindByName( Event.IniDCSUnitName, '', true )
|
||||
end
|
||||
Event.IniDCSGroupName = ""
|
||||
if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then
|
||||
Event.IniDCSGroupName = Event.IniDCSGroup:getName()
|
||||
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
|
||||
--if Event.IniGroup then
|
||||
Event.IniGroupName = Event.IniDCSGroupName
|
||||
--end
|
||||
end
|
||||
Event.IniPlayerName = Event.IniDCSUnit:getPlayerName()
|
||||
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
||||
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||
end
|
||||
|
||||
if Event.IniObjectCategory == Object.Category.STATIC then
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniUnit = STATIC:FindByName( Event.IniDCSUnitName, false )
|
||||
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
||||
end
|
||||
|
||||
if Event.IniObjectCategory == Object.Category.CARGO then
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniUnit = CARGO:FindByName( Event.IniDCSUnitName )
|
||||
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
||||
end
|
||||
|
||||
if Event.IniObjectCategory == Object.Category.SCENERY then
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniUnit = SCENERY:Register( Event.IniDCSUnitName, Event.initiator )
|
||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||
Event.IniTypeName = Event.initiator:isExist() and Event.IniDCSUnit:getTypeName() or "SCENERY" -- TODO: Bug fix for 2.1!
|
||||
end
|
||||
end
|
||||
|
||||
if Event.target then
|
||||
|
||||
Event.TgtObjectCategory = Event.target:getCategory()
|
||||
|
||||
if Event.TgtObjectCategory == Object.Category.UNIT then
|
||||
Event.TgtDCSUnit = Event.target
|
||||
Event.TgtDCSGroup = Event.TgtDCSUnit:getGroup()
|
||||
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
||||
Event.TgtUnitName = Event.TgtDCSUnitName
|
||||
Event.TgtUnit = UNIT:FindByName( Event.TgtDCSUnitName )
|
||||
Event.TgtDCSGroupName = ""
|
||||
if Event.TgtDCSGroup and Event.TgtDCSGroup:isExist() then
|
||||
Event.TgtDCSGroupName = Event.TgtDCSGroup:getName()
|
||||
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
|
||||
--if Event.TgtGroup then
|
||||
Event.TgtGroupName = Event.TgtDCSGroupName
|
||||
--end
|
||||
end
|
||||
Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName()
|
||||
Event.TgtCoalition = Event.TgtDCSUnit:getCoalition()
|
||||
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
|
||||
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
|
||||
end
|
||||
|
||||
if Event.TgtObjectCategory == Object.Category.STATIC then
|
||||
Event.TgtDCSUnit = Event.target
|
||||
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
||||
Event.TgtUnitName = Event.TgtDCSUnitName
|
||||
Event.TgtUnit = STATIC:FindByName( Event.TgtDCSUnitName, false )
|
||||
Event.TgtCoalition = Event.TgtDCSUnit:getCoalition()
|
||||
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
|
||||
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
|
||||
end
|
||||
|
||||
if Event.TgtObjectCategory == Object.Category.SCENERY then
|
||||
Event.TgtDCSUnit = Event.target
|
||||
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
||||
Event.TgtUnitName = Event.TgtDCSUnitName
|
||||
Event.TgtUnit = SCENERY:Register( Event.TgtDCSUnitName, Event.target )
|
||||
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
|
||||
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
|
||||
end
|
||||
end
|
||||
|
||||
if Event.weapon then
|
||||
Event.Weapon = Event.weapon
|
||||
Event.WeaponName = Event.Weapon:getTypeName()
|
||||
Event.WeaponUNIT = CLIENT:Find( Event.Weapon, '', true ) -- Sometimes, the weapon is a player unit!
|
||||
Event.WeaponPlayerName = Event.WeaponUNIT and Event.Weapon:getPlayerName()
|
||||
Event.WeaponCoalition = Event.WeaponUNIT and Event.Weapon:getCoalition()
|
||||
Event.WeaponCategory = Event.WeaponUNIT and Event.Weapon:getDesc().category
|
||||
Event.WeaponTypeName = Event.WeaponUNIT and Event.Weapon:getTypeName()
|
||||
--Event.WeaponTgtDCSUnit = Event.Weapon:getTarget()
|
||||
end
|
||||
|
||||
-- Place should be given for takeoff and landing events as well as base captured. It should be a DCS airbase.
|
||||
if Event.place then
|
||||
Event.Place=AIRBASE:Find(Event.place)
|
||||
Event.PlaceName=Event.Place:GetName()
|
||||
end
|
||||
|
||||
-- Mark points.
|
||||
if Event.idx then
|
||||
Event.MarkID=Event.idx
|
||||
Event.MarkVec3=Event.pos
|
||||
Event.MarkCoordinate=COORDINATE:NewFromVec3(Event.pos)
|
||||
Event.MarkText=Event.text
|
||||
Event.MarkCoalition=Event.coalition
|
||||
Event.MarkGroupID = Event.groupID
|
||||
end
|
||||
|
||||
if Event.cargo then
|
||||
Event.Cargo = Event.cargo
|
||||
Event.CargoName = Event.cargo.Name
|
||||
end
|
||||
|
||||
if Event.zone then
|
||||
Event.Zone = Event.zone
|
||||
Event.ZoneName = Event.zone.ZoneName
|
||||
end
|
||||
|
||||
local PriorityOrder = EventMeta.Order
|
||||
local PriorityBegin = PriorityOrder == -1 and 5 or 1
|
||||
local PriorityEnd = PriorityOrder == -1 and 1 or 5
|
||||
|
||||
if Event.IniObjectCategory ~= Object.Category.STATIC then
|
||||
self:F( { EventMeta.Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } )
|
||||
end
|
||||
|
||||
for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do
|
||||
|
||||
if self.Events[Event.id][EventPriority] then
|
||||
|
||||
-- Okay, we got the event from DCS. Now loop the SORTED self.EventSorted[] table for the received Event.id, and for each EventData registered, check if a function needs to be called.
|
||||
for EventClass, EventData in pairs( self.Events[Event.id][EventPriority] ) do
|
||||
|
||||
--if Event.IniObjectCategory ~= Object.Category.STATIC then
|
||||
-- self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } )
|
||||
--end
|
||||
|
||||
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
|
||||
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
|
||||
|
||||
-- If the EventData is for a UNIT, the call directly the EventClass EventFunction for that UNIT.
|
||||
if EventData.EventUnit then
|
||||
|
||||
-- So now the EventClass must be a UNIT class!!! We check if it is still "Alive".
|
||||
if EventClass:IsAlive() or
|
||||
Event.id == EVENTS.PlayerEnterUnit or
|
||||
Event.id == EVENTS.Crash or
|
||||
Event.id == EVENTS.Dead or
|
||||
Event.id == EVENTS.RemoveUnit then
|
||||
|
||||
local UnitName = EventClass:GetName()
|
||||
|
||||
if ( EventMeta.Side == "I" and UnitName == Event.IniDCSUnitName ) or
|
||||
( EventMeta.Side == "T" and UnitName == Event.TgtDCSUnitName ) then
|
||||
|
||||
-- First test if a EventFunction is Set, otherwise search for the default function
|
||||
if EventData.EventFunction then
|
||||
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventData.EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
|
||||
else
|
||||
|
||||
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
||||
local EventFunction = EventClass[ EventMeta.Event ]
|
||||
if EventFunction and type( EventFunction ) == "function" then
|
||||
|
||||
-- Now call the default event function.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
-- The EventClass is not alive anymore, we remove it from the EventHandlers...
|
||||
self:RemoveEvent( EventClass, Event.id )
|
||||
end
|
||||
else
|
||||
|
||||
-- If the EventData is for a GROUP, the call directly the EventClass EventFunction for the UNIT in that GROUP.
|
||||
if EventData.EventGroup then
|
||||
|
||||
-- So now the EventClass must be a GROUP class!!! We check if it is still "Alive".
|
||||
if EventClass:IsAlive() or
|
||||
Event.id == EVENTS.PlayerEnterUnit or
|
||||
Event.id == EVENTS.Crash or
|
||||
Event.id == EVENTS.Dead or
|
||||
Event.id == EVENTS.RemoveUnit then
|
||||
|
||||
-- We can get the name of the EventClass, which is now always a GROUP object.
|
||||
local GroupName = EventClass:GetName()
|
||||
|
||||
if ( EventMeta.Side == "I" and GroupName == Event.IniDCSGroupName ) or
|
||||
( EventMeta.Side == "T" and GroupName == Event.TgtDCSGroupName ) then
|
||||
|
||||
-- Check if this is a known event?
|
||||
if EventMeta then
|
||||
|
||||
if self and
|
||||
self.Events and
|
||||
self.Events[Event.id] and
|
||||
self.MissionEnd == false and
|
||||
( Event.initiator ~= nil or ( Event.initiator == nil and Event.id ~= EVENTS.PlayerLeaveUnit ) ) then
|
||||
|
||||
if Event.id and Event.id == EVENTS.MissionEnd then
|
||||
self.MissionEnd = true
|
||||
end
|
||||
|
||||
if Event.initiator then
|
||||
|
||||
Event.IniObjectCategory = Event.initiator:getCategory()
|
||||
|
||||
if Event.IniObjectCategory == Object.Category.UNIT then
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniDCSGroup = Event.IniDCSUnit:getGroup()
|
||||
Event.IniUnit = UNIT:FindByName( Event.IniDCSUnitName )
|
||||
if not Event.IniUnit then
|
||||
-- Unit can be a CLIENT. Most likely this will be the case ...
|
||||
Event.IniUnit = CLIENT:FindByName( Event.IniDCSUnitName, '', true )
|
||||
end
|
||||
Event.IniDCSGroupName = ""
|
||||
if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then
|
||||
Event.IniDCSGroupName = Event.IniDCSGroup:getName()
|
||||
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
|
||||
--if Event.IniGroup then
|
||||
Event.IniGroupName = Event.IniDCSGroupName
|
||||
--end
|
||||
end
|
||||
Event.IniPlayerName = Event.IniDCSUnit:getPlayerName()
|
||||
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
||||
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||
end
|
||||
|
||||
if Event.IniObjectCategory == Object.Category.STATIC then
|
||||
if Event.id==31 then
|
||||
--env.info("FF event 31")
|
||||
-- Event.initiator is a Static object representing the pilot. But getName() error due to DCS bug.
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
local ID=Event.initiator.id_
|
||||
Event.IniDCSUnitName = string.format("Ejected Pilot ID %s", tostring(ID))
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniCoalition = 0
|
||||
Event.IniCategory = 0
|
||||
Event.IniTypeName = "Ejected Pilot"
|
||||
else
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniUnit = STATIC:FindByName( Event.IniDCSUnitName, false )
|
||||
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
||||
end
|
||||
end
|
||||
|
||||
if Event.IniObjectCategory == Object.Category.CARGO then
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniUnit = CARGO:FindByName( Event.IniDCSUnitName )
|
||||
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
||||
end
|
||||
|
||||
if Event.IniObjectCategory == Object.Category.SCENERY then
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniUnit = SCENERY:Register( Event.IniDCSUnitName, Event.initiator )
|
||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||
Event.IniTypeName = Event.initiator:isExist() and Event.IniDCSUnit:getTypeName() or "SCENERY" -- TODO: Bug fix for 2.1!
|
||||
end
|
||||
end
|
||||
|
||||
if Event.target then
|
||||
|
||||
Event.TgtObjectCategory = Event.target:getCategory()
|
||||
|
||||
if Event.TgtObjectCategory == Object.Category.UNIT then
|
||||
Event.TgtDCSUnit = Event.target
|
||||
Event.TgtDCSGroup = Event.TgtDCSUnit:getGroup()
|
||||
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
||||
Event.TgtUnitName = Event.TgtDCSUnitName
|
||||
Event.TgtUnit = UNIT:FindByName( Event.TgtDCSUnitName )
|
||||
Event.TgtDCSGroupName = ""
|
||||
if Event.TgtDCSGroup and Event.TgtDCSGroup:isExist() then
|
||||
Event.TgtDCSGroupName = Event.TgtDCSGroup:getName()
|
||||
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
|
||||
--if Event.TgtGroup then
|
||||
Event.TgtGroupName = Event.TgtDCSGroupName
|
||||
--end
|
||||
end
|
||||
Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName()
|
||||
Event.TgtCoalition = Event.TgtDCSUnit:getCoalition()
|
||||
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
|
||||
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
|
||||
end
|
||||
|
||||
if Event.TgtObjectCategory == Object.Category.STATIC then
|
||||
Event.TgtDCSUnit = Event.target
|
||||
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
||||
Event.TgtUnitName = Event.TgtDCSUnitName
|
||||
Event.TgtUnit = STATIC:FindByName( Event.TgtDCSUnitName, false )
|
||||
Event.TgtCoalition = Event.TgtDCSUnit:getCoalition()
|
||||
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
|
||||
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
|
||||
end
|
||||
|
||||
if Event.TgtObjectCategory == Object.Category.SCENERY then
|
||||
Event.TgtDCSUnit = Event.target
|
||||
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
||||
Event.TgtUnitName = Event.TgtDCSUnitName
|
||||
Event.TgtUnit = SCENERY:Register( Event.TgtDCSUnitName, Event.target )
|
||||
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
|
||||
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
|
||||
end
|
||||
end
|
||||
|
||||
if Event.weapon then
|
||||
Event.Weapon = Event.weapon
|
||||
Event.WeaponName = Event.Weapon:getTypeName()
|
||||
Event.WeaponUNIT = CLIENT:Find( Event.Weapon, '', true ) -- Sometimes, the weapon is a player unit!
|
||||
Event.WeaponPlayerName = Event.WeaponUNIT and Event.Weapon:getPlayerName()
|
||||
Event.WeaponCoalition = Event.WeaponUNIT and Event.Weapon:getCoalition()
|
||||
Event.WeaponCategory = Event.WeaponUNIT and Event.Weapon:getDesc().category
|
||||
Event.WeaponTypeName = Event.WeaponUNIT and Event.Weapon:getTypeName()
|
||||
--Event.WeaponTgtDCSUnit = Event.Weapon:getTarget()
|
||||
end
|
||||
|
||||
-- Place should be given for takeoff and landing events as well as base captured. It should be a DCS airbase.
|
||||
if Event.place then
|
||||
if Event.id==EVENTS.LandingAfterEjection then
|
||||
-- Place is here the UNIT of which the pilot ejected.
|
||||
--local name=Event.place:getName() -- This returns a DCS error "Airbase doesn't exit" :(
|
||||
-- However, this is not a big thing, as the aircraft the pilot ejected from is usually long crashed before the ejected pilot touches the ground.
|
||||
--Event.Place=UNIT:Find(Event.place)
|
||||
else
|
||||
Event.Place=AIRBASE:Find(Event.place)
|
||||
Event.PlaceName=Event.Place:GetName()
|
||||
end
|
||||
end
|
||||
|
||||
-- Mark points.
|
||||
if Event.idx then
|
||||
Event.MarkID=Event.idx
|
||||
Event.MarkVec3=Event.pos
|
||||
Event.MarkCoordinate=COORDINATE:NewFromVec3(Event.pos)
|
||||
Event.MarkText=Event.text
|
||||
Event.MarkCoalition=Event.coalition
|
||||
Event.MarkGroupID = Event.groupID
|
||||
end
|
||||
|
||||
if Event.cargo then
|
||||
Event.Cargo = Event.cargo
|
||||
Event.CargoName = Event.cargo.Name
|
||||
end
|
||||
|
||||
if Event.zone then
|
||||
Event.Zone = Event.zone
|
||||
Event.ZoneName = Event.zone.ZoneName
|
||||
end
|
||||
|
||||
local PriorityOrder = EventMeta.Order
|
||||
local PriorityBegin = PriorityOrder == -1 and 5 or 1
|
||||
local PriorityEnd = PriorityOrder == -1 and 1 or 5
|
||||
|
||||
if Event.IniObjectCategory ~= Object.Category.STATIC then
|
||||
self:F( { EventMeta.Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } )
|
||||
end
|
||||
|
||||
for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do
|
||||
|
||||
if self.Events[Event.id][EventPriority] then
|
||||
|
||||
-- Okay, we got the event from DCS. Now loop the SORTED self.EventSorted[] table for the received Event.id, and for each EventData registered, check if a function needs to be called.
|
||||
for EventClass, EventData in pairs( self.Events[Event.id][EventPriority] ) do
|
||||
|
||||
--if Event.IniObjectCategory ~= Object.Category.STATIC then
|
||||
-- self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } )
|
||||
--end
|
||||
|
||||
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
|
||||
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
|
||||
|
||||
-- If the EventData is for a UNIT, the call directly the EventClass EventFunction for that UNIT.
|
||||
if EventData.EventUnit then
|
||||
|
||||
-- So now the EventClass must be a UNIT class!!! We check if it is still "Alive".
|
||||
if EventClass:IsAlive() or
|
||||
Event.id == EVENTS.PlayerEnterUnit or
|
||||
Event.id == EVENTS.Crash or
|
||||
Event.id == EVENTS.Dead or
|
||||
Event.id == EVENTS.RemoveUnit then
|
||||
|
||||
local UnitName = EventClass:GetName()
|
||||
|
||||
if ( EventMeta.Side == "I" and UnitName == Event.IniDCSUnitName ) or
|
||||
( EventMeta.Side == "T" and UnitName == Event.TgtDCSUnitName ) then
|
||||
|
||||
-- First test if a EventFunction is Set, otherwise search for the default function
|
||||
if EventData.EventFunction then
|
||||
|
||||
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
||||
self:F( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
||||
end
|
||||
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) )
|
||||
return EventData.EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
|
||||
else
|
||||
@@ -1140,74 +1148,130 @@ function EVENT:onEvent( Event )
|
||||
|
||||
-- Now call the default event function.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F( { "Calling " .. EventMeta.Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
self:F( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventFunction( EventClass, Event, unpack( EventData.Params ) )
|
||||
return EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
-- The EventClass is not alive anymore, we remove it from the EventHandlers...
|
||||
--self:RemoveEvent( EventClass, Event.id )
|
||||
self:RemoveEvent( EventClass, Event.id )
|
||||
end
|
||||
else
|
||||
|
||||
-- If the EventData is not bound to a specific unit, then call the EventClass EventFunction.
|
||||
-- Note that here the EventFunction will need to implement and determine the logic for the relevant source- or target unit, or weapon.
|
||||
if not EventData.EventUnit then
|
||||
|
||||
-- First test if a EventFunction is Set, otherwise search for the default function
|
||||
if EventData.EventFunction then
|
||||
|
||||
-- There is an EventFunction defined, so call the EventFunction.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F2( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventData.EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
else
|
||||
|
||||
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
||||
local EventFunction = EventClass[ EventMeta.Event ]
|
||||
if EventFunction and type( EventFunction ) == "function" then
|
||||
|
||||
-- Now call the default event function.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F2( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
else
|
||||
|
||||
--- If the EventData is for a GROUP, the call directly the EventClass EventFunction for the UNIT in that GROUP.
|
||||
if EventData.EventGroup then
|
||||
|
||||
-- So now the EventClass must be a GROUP class!!! We check if it is still "Alive".
|
||||
if EventClass:IsAlive() or
|
||||
Event.id == EVENTS.PlayerEnterUnit or
|
||||
Event.id == EVENTS.Crash or
|
||||
Event.id == EVENTS.Dead or
|
||||
Event.id == EVENTS.RemoveUnit then
|
||||
|
||||
-- We can get the name of the EventClass, which is now always a GROUP object.
|
||||
local GroupName = EventClass:GetName()
|
||||
|
||||
if ( EventMeta.Side == "I" and GroupName == Event.IniDCSGroupName ) or
|
||||
( EventMeta.Side == "T" and GroupName == Event.TgtDCSGroupName ) then
|
||||
|
||||
-- First test if a EventFunction is Set, otherwise search for the default function
|
||||
if EventData.EventFunction then
|
||||
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) )
|
||||
end, ErrorHandler )
|
||||
|
||||
else
|
||||
|
||||
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
||||
local EventFunction = EventClass[ EventMeta.Event ]
|
||||
if EventFunction and type( EventFunction ) == "function" then
|
||||
|
||||
-- Now call the default event function.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F( { "Calling " .. EventMeta.Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventFunction( EventClass, Event, unpack( EventData.Params ) )
|
||||
end, ErrorHandler )
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
else
|
||||
-- The EventClass is not alive anymore, we remove it from the EventHandlers...
|
||||
--self:RemoveEvent( EventClass, Event.id )
|
||||
end
|
||||
else
|
||||
|
||||
-- If the EventData is not bound to a specific unit, then call the EventClass EventFunction.
|
||||
-- Note that here the EventFunction will need to implement and determine the logic for the relevant source- or target unit, or weapon.
|
||||
if not EventData.EventUnit then
|
||||
|
||||
-- First test if a EventFunction is Set, otherwise search for the default function
|
||||
if EventData.EventFunction then
|
||||
|
||||
-- There is an EventFunction defined, so call the EventFunction.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F2( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
local Result, Value = EventFunction( EventClass, Event )
|
||||
return Result, Value
|
||||
return EventData.EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
else
|
||||
|
||||
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
||||
local EventFunction = EventClass[ EventMeta.Event ]
|
||||
if EventFunction and type( EventFunction ) == "function" then
|
||||
|
||||
-- Now call the default event function.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F2( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
local Result, Value = EventFunction( EventClass, Event )
|
||||
return Result, Value
|
||||
end, ErrorHandler )
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- When cargo was deleted, it may probably be because of an S_EVENT_DEAD.
|
||||
-- However, in the loading logic, an S_EVENT_DEAD is also generated after a Destroy() call.
|
||||
-- And this is a problem because it will remove all entries from the SET_CARGOs.
|
||||
-- To prevent this from happening, the Cargo object has a flag NoDestroy.
|
||||
-- When true, the SET_CARGO won't Remove the Cargo object from the set.
|
||||
-- But we need to switch that flag off after the event handlers have been called.
|
||||
if Event.id == EVENTS.DeleteCargo then
|
||||
Event.Cargo.NoDestroy = nil
|
||||
|
||||
-- When cargo was deleted, it may probably be because of an S_EVENT_DEAD.
|
||||
-- However, in the loading logic, an S_EVENT_DEAD is also generated after a Destroy() call.
|
||||
-- And this is a problem because it will remove all entries from the SET_CARGOs.
|
||||
-- To prevent this from happening, the Cargo object has a flag NoDestroy.
|
||||
-- When true, the SET_CARGO won't Remove the Cargo object from the set.
|
||||
-- But we need to switch that flag off after the event handlers have been called.
|
||||
if Event.id == EVENTS.DeleteCargo then
|
||||
Event.Cargo.NoDestroy = nil
|
||||
end
|
||||
else
|
||||
self:T( { EventMeta.Text, Event } )
|
||||
end
|
||||
else
|
||||
self:T( { EventMeta.Text, Event } )
|
||||
self:E(string.format("WARNING: Could not get EVENTMETA data for event ID=%d! Is this an unknown/new DCS event?", tostring(Event.id)))
|
||||
end
|
||||
|
||||
Event = nil
|
||||
@@ -1223,7 +1287,7 @@ EVENTHANDLER = {
|
||||
|
||||
--- The EVENTHANDLER constructor
|
||||
-- @param #EVENTHANDLER self
|
||||
-- @return #EVENTHANDLER
|
||||
-- @return #EVENTHANDLER self
|
||||
function EVENTHANDLER:New()
|
||||
self = BASE:Inherit( self, BASE:New() ) -- #EVENTHANDLER
|
||||
return self
|
||||
|
||||
@@ -727,14 +727,17 @@ do -- FSM
|
||||
if DelaySeconds ~= nil then
|
||||
if DelaySeconds < 0 then -- Only call the event ONCE!
|
||||
DelaySeconds = math.abs( DelaySeconds )
|
||||
if not self._EventSchedules[EventName] then
|
||||
if not self._EventSchedules[EventName] then
|
||||
CallID = self.CallScheduler:Schedule( self, self._handler, { EventName, ... }, DelaySeconds or 1, nil, nil, nil, 4, true )
|
||||
self._EventSchedules[EventName] = CallID
|
||||
self:T2(string.format("NEGATIVE Event %s delayed by %.1f sec SCHEDULED with CallID=%s", EventName, DelaySeconds, tostring(CallID)))
|
||||
else
|
||||
self:T2(string.format("NEGATIVE Event %s delayed by %.1f sec CANCELLED as we already have such an event in the queue.", EventName, DelaySeconds))
|
||||
-- reschedule
|
||||
end
|
||||
else
|
||||
CallID = self.CallScheduler:Schedule( self, self._handler, { EventName, ... }, DelaySeconds or 1, nil, nil, nil, 4, true )
|
||||
self:T2(string.format("Event %s delayed by %.1f sec SCHEDULED with CallID=%s", EventName, DelaySeconds, tostring(CallID)))
|
||||
end
|
||||
else
|
||||
error( "FSM: An asynchronous event trigger requires a DelaySeconds parameter!!! This can be positive or negative! Sorry, but will not process this." )
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions: **funkyfranky**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
|
||||
@@ -357,7 +357,7 @@ do -- COORDINATE
|
||||
-- @return #table Table of DCS static objects found.
|
||||
-- @return #table Table of DCS scenery objects found.
|
||||
function COORDINATE:ScanObjects(radius, scanunits, scanstatics, scanscenery)
|
||||
self:F(string.format("Scanning in radius %.1f m.", radius))
|
||||
self:F(string.format("Scanning in radius %.1f m.", radius or 100))
|
||||
|
||||
local SphereSearch = {
|
||||
id = world.VolumeType.SPHERE,
|
||||
@@ -437,13 +437,56 @@ do -- COORDINATE
|
||||
end
|
||||
for _,static in pairs(Statics) do
|
||||
self:T(string.format("Scan found static %s", static:getName()))
|
||||
_DATABASE:AddStatic(static:getName())
|
||||
end
|
||||
for _,scenery in pairs(Scenery) do
|
||||
self:T(string.format("Scan found scenery %s", scenery:getTypeName()))
|
||||
self:T(string.format("Scan found scenery %s typename=%s", scenery:getName(), scenery:getTypeName()))
|
||||
SCENERY:Register(scenery:getName(), scenery)
|
||||
end
|
||||
|
||||
return gotunits, gotstatics, gotscenery, Units, Statics, Scenery
|
||||
end
|
||||
|
||||
--- Scan/find UNITS 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:ScanUnits(radius)
|
||||
|
||||
local _,_,_,units=self:ScanObjects(radius, true, false, false)
|
||||
|
||||
local set=SET_UNIT:New()
|
||||
|
||||
for _,unit in pairs(units) do
|
||||
set:AddUnit(unit)
|
||||
end
|
||||
|
||||
return set
|
||||
end
|
||||
|
||||
--- Find the closest unit to the COORDINATE within a certain radius.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number radius Scan radius in meters. Default 100 m.
|
||||
-- @return Wrapper.Unit#UNIT The closest unit or #nil if no unit is inside the given radius.
|
||||
function COORDINATE:FindClosestUnit(radius)
|
||||
|
||||
local units=self:ScanUnits(radius)
|
||||
|
||||
local umin=nil --Wrapper.Unit#UNIT
|
||||
local dmin=math.huge
|
||||
for _,_unit in pairs(units.Set) do
|
||||
local unit=_unit --Wrapper.Unit#UNIT
|
||||
local coordinate=unit:GetCoordinate()
|
||||
local d=self:Get2DDistance(coordinate)
|
||||
if d<dmin then
|
||||
dmin=d
|
||||
umin=unit
|
||||
end
|
||||
end
|
||||
|
||||
return umin
|
||||
end
|
||||
|
||||
|
||||
--- Calculate the distance from a reference @{#COORDINATE}.
|
||||
-- @param #COORDINATE self
|
||||
@@ -653,6 +696,28 @@ do -- COORDINATE
|
||||
return Angle
|
||||
end
|
||||
|
||||
--- Return an intermediate COORDINATE between this an another coordinate.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE ToCoordinate The other coordinate.
|
||||
-- @param #number Fraction The fraction (0,1) where the new coordinate is created. Default 0.5, i.e. in the middle.
|
||||
-- @return #COORDINATE Coordinate between this and the other coordinate.
|
||||
function COORDINATE:GetIntermediateCoordinate( ToCoordinate, Fraction )
|
||||
|
||||
local f=Fraction or 0.5
|
||||
|
||||
-- Get the vector from A to B
|
||||
local vec=UTILS.VecSubstract(ToCoordinate, self)
|
||||
|
||||
-- Scale the vector.
|
||||
vec.x=f*vec.x
|
||||
vec.y=f*vec.y
|
||||
vec.z=f*vec.z
|
||||
|
||||
-- Move the vector to start at the end of A.
|
||||
vec=UTILS.VecAdd(self, vec)
|
||||
|
||||
return self:New(vec.x,vec.y,vec.z)
|
||||
end
|
||||
|
||||
--- Return the 2D distance in meters between the target COORDINATE and the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
@@ -1093,7 +1158,7 @@ do -- COORDINATE
|
||||
-- Airbase parameters for takeoff and landing points.
|
||||
if airbase then
|
||||
local AirbaseID = airbase:GetID()
|
||||
local AirbaseCategory = airbase:GetDesc().category
|
||||
local AirbaseCategory = airbase:GetAirbaseCategory()
|
||||
if AirbaseCategory == Airbase.Category.SHIP or AirbaseCategory == Airbase.Category.HELIPAD then
|
||||
RoutePoint.linkUnit = AirbaseID
|
||||
RoutePoint.helipadId = AirbaseID
|
||||
@@ -1213,41 +1278,69 @@ do -- COORDINATE
|
||||
|
||||
--- Build an ground type route point.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number Speed (optional) Speed in km/h. The default speed is 20 km/h.
|
||||
-- @param #string Formation (optional) The route point Formation, which is a text string that specifies exactly the Text in the Type of the route point, like "Vee", "Echelon Right".
|
||||
-- @param #number Speed (Optional) Speed in km/h. The default speed is 20 km/h.
|
||||
-- @param #string Formation (Optional) The route point Formation, which is a text string that specifies exactly the Text in the Type of the route point, like "Vee", "Echelon Right".
|
||||
-- @param #table DCSTasks (Optional) A table of DCS tasks that are executed at the waypoints. Mind the curly brackets {}!
|
||||
-- @return #table The route point.
|
||||
function COORDINATE:WaypointGround( Speed, Formation )
|
||||
self:F2( { Formation, Speed } )
|
||||
function COORDINATE:WaypointGround( Speed, Formation, DCSTasks )
|
||||
self:F2( { Speed, Formation, DCSTasks } )
|
||||
|
||||
|
||||
local RoutePoint = {}
|
||||
RoutePoint.x = self.x
|
||||
RoutePoint.y = self.z
|
||||
|
||||
RoutePoint.action = Formation or ""
|
||||
--RoutePoint.formation_template = Formation and "" or nil
|
||||
|
||||
|
||||
RoutePoint.x = self.x
|
||||
RoutePoint.y = self.z
|
||||
|
||||
RoutePoint.alt = self:GetLandHeight()+1 -- self.y
|
||||
RoutePoint.alt_type = COORDINATE.WaypointAltType.BARO
|
||||
|
||||
RoutePoint.action = Formation or "Off Road"
|
||||
RoutePoint.formation_template=""
|
||||
|
||||
RoutePoint.ETA=0
|
||||
RoutePoint.ETA_locked=true
|
||||
|
||||
RoutePoint.speed = ( Speed or 20 ) / 3.6
|
||||
RoutePoint.speed_locked = true
|
||||
|
||||
-- ["task"] =
|
||||
-- {
|
||||
-- ["id"] = "ComboTask",
|
||||
-- ["params"] =
|
||||
-- {
|
||||
-- ["tasks"] =
|
||||
-- {
|
||||
-- }, -- end of ["tasks"]
|
||||
-- }, -- end of ["params"]
|
||||
-- }, -- end of ["task"]
|
||||
RoutePoint.task = {}
|
||||
RoutePoint.task.id = "ComboTask"
|
||||
RoutePoint.task.params = {}
|
||||
RoutePoint.task.params.tasks = DCSTasks or {}
|
||||
|
||||
return RoutePoint
|
||||
end
|
||||
|
||||
--- Build route waypoint point for Naval units.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number Speed (Optional) Speed in km/h. The default speed is 20 km/h.
|
||||
-- @param #string Depth (Optional) Dive depth in meters. Only for submarines. Default is COORDINATE.y component.
|
||||
-- @param #table DCSTasks (Optional) A table of DCS tasks that are executed at the waypoints. Mind the curly brackets {}!
|
||||
-- @return #table The route point.
|
||||
function COORDINATE:WaypointNaval( Speed, Depth, DCSTasks )
|
||||
self:F2( { Speed, Depth, DCSTasks } )
|
||||
|
||||
local RoutePoint = {}
|
||||
|
||||
RoutePoint.x = self.x
|
||||
RoutePoint.y = self.z
|
||||
|
||||
RoutePoint.alt = Depth or self.y -- Depth is for submarines only. Ships should have alt=0.
|
||||
RoutePoint.alt_type = "BARO"
|
||||
|
||||
RoutePoint.type = "Turning Point"
|
||||
RoutePoint.action = "Turning Point"
|
||||
RoutePoint.formation_template = ""
|
||||
|
||||
RoutePoint.ETA=0
|
||||
RoutePoint.ETA_locked=true
|
||||
|
||||
RoutePoint.speed = ( Speed or 20 ) / 3.6
|
||||
RoutePoint.speed_locked = true
|
||||
|
||||
RoutePoint.task = {}
|
||||
RoutePoint.task.id = "ComboTask"
|
||||
RoutePoint.task.params = {}
|
||||
RoutePoint.task.params.tasks = {}
|
||||
|
||||
RoutePoint.task.params.tasks = DCSTasks or {}
|
||||
|
||||
return RoutePoint
|
||||
end
|
||||
|
||||
@@ -113,18 +113,20 @@ end
|
||||
-- @param #number dt (Optional) Time step in seconds for checking the queue. Default 0.01 sec.
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:Start(delay, dt)
|
||||
|
||||
|
||||
-- Delay before start.
|
||||
self.delay=delay or 1
|
||||
|
||||
-- Time interval for queue check.
|
||||
self.dt=dt or 0.01
|
||||
|
||||
self:I(self.lid..string.format("Starting RADIOQUEUE %s on Frequency %.2f MHz [modulation=%d] in %.1f seconds (dt=%.3f sec)", self.alias, self.frequency/1000000, self.modulation, delay, dt))
|
||||
-- Debug message.
|
||||
self:I(self.lid..string.format("Starting RADIOQUEUE %s on Frequency %.2f MHz [modulation=%d] in %.1f seconds (dt=%.3f sec)", self.alias, self.frequency/1000000, self.modulation, self.delay, self.dt))
|
||||
|
||||
|
||||
-- Start Scheduler.
|
||||
if self.schedonce then
|
||||
self:_CheckRadioQueueDelayed(self.delta)
|
||||
self:_CheckRadioQueueDelayed(delay)
|
||||
else
|
||||
--self.RQid=self.scheduler:Schedule(self, self._CheckRadioQueue, {}, delay, dt)
|
||||
self.RQid=self.scheduler:Schedule(nil, RADIOQUEUE._CheckRadioQueue, {self}, delay, dt)
|
||||
end
|
||||
|
||||
@@ -351,13 +353,21 @@ function RADIOQUEUE:Broadcast(transmission)
|
||||
self.senderinit=true
|
||||
end
|
||||
|
||||
-- Set subtitle only if duration>0 sec.
|
||||
local subtitle=nil
|
||||
local duration=nil
|
||||
if transmission.subtitle and transmission.subduration and transmission.subduration>0 then
|
||||
subtitle=transmission.subtitle
|
||||
duration=transmission.subduration
|
||||
end
|
||||
|
||||
-- Command to tranmit the call.
|
||||
local commandTransmit={
|
||||
id = "TransmitMessage",
|
||||
params = {
|
||||
file=filename,
|
||||
duration=transmission.subduration,
|
||||
subtitle=transmission.subtitle or "",
|
||||
duration=duration,
|
||||
subtitle=subtitle,
|
||||
loop=false,
|
||||
}}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
--
|
||||
-- Takes care of the creation and dispatching of scheduled functions for SCHEDULER objects.
|
||||
--
|
||||
-- This class is tricky and needs some thorought explanation.
|
||||
-- This class is tricky and needs some thorough explanation.
|
||||
-- SCHEDULE classes are used to schedule functions for objects, or as persistent objects.
|
||||
-- The SCHEDULEDISPATCHER class ensures that:
|
||||
--
|
||||
@@ -13,9 +13,10 @@
|
||||
-- - Scheduled functions are automatically removed when the schedule is finished, according the SCHEDULER object parameters.
|
||||
--
|
||||
-- The SCHEDULEDISPATCHER class will manage SCHEDULER object in memory during garbage collection:
|
||||
-- - When a SCHEDULER object is not attached to another object (that is, it's first :Schedule() parameter is nil), then the SCHEDULER
|
||||
-- object is _persistent_ within memory.
|
||||
--
|
||||
-- - When a SCHEDULER object is not attached to another object (that is, it's first :Schedule() parameter is nil), then the SCHEDULER object is _persistent_ within memory.
|
||||
-- - When a SCHEDULER object *is* attached to another object, then the SCHEDULER object is _not persistent_ within memory after a garbage collection!
|
||||
--
|
||||
-- The none persistency of SCHEDULERS attached to objects is required to allow SCHEDULER objects to be garbage collectged, when the parent object is also desroyed or nillified and garbage collected.
|
||||
-- Even when there are pending timer scheduled functions to be executed for the SCHEDULER object,
|
||||
-- these will not be executed anymore when the SCHEDULER object has been destroyed.
|
||||
@@ -33,13 +34,41 @@
|
||||
-- @module Core.ScheduleDispatcher
|
||||
-- @image Core_Schedule_Dispatcher.JPG
|
||||
|
||||
--- SCHEDULEDISPATCHER class.
|
||||
-- @type SCHEDULEDISPATCHER
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #number CallID Call ID counter.
|
||||
-- @field #table PersistentSchedulers Persistant schedulers.
|
||||
-- @field #table ObjectSchedulers Schedulers that only exist as long as the master object exists.
|
||||
-- @field #table Schedule Meta table setmetatable( {}, { __mode = "k" } ).
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- The SCHEDULEDISPATCHER structure
|
||||
-- @type SCHEDULEDISPATCHER
|
||||
SCHEDULEDISPATCHER = {
|
||||
ClassName = "SCHEDULEDISPATCHER",
|
||||
CallID = 0,
|
||||
ClassName = "SCHEDULEDISPATCHER",
|
||||
CallID = 0,
|
||||
PersistentSchedulers = {},
|
||||
ObjectSchedulers = {},
|
||||
Schedule = nil,
|
||||
}
|
||||
|
||||
--- Player data table holding all important parameters of each player.
|
||||
-- @type SCHEDULEDISPATCHER.ScheduleData
|
||||
-- @field #function Function The schedule function to be called.
|
||||
-- @field #table Arguments Schedule function arguments.
|
||||
-- @field #number Start Start time in seconds.
|
||||
-- @field #number Repeat Repeat time intervall in seconds.
|
||||
-- @field #number Randomize Randomization factor [0,1].
|
||||
-- @field #number Stop Stop time in seconds.
|
||||
-- @field #number StartTime Time in seconds when the scheduler is created.
|
||||
-- @field #number ScheduleID Schedule ID.
|
||||
-- @field #function CallHandler Function to be passed to the DCS timer.scheduleFunction().
|
||||
-- @field #boolean ShowTrace If true, show tracing info.
|
||||
|
||||
--- Create a new schedule dispatcher object.
|
||||
-- @param #SCHEDULEDISPATCHER self
|
||||
-- @return #SCHEDULEDISPATCHER self
|
||||
function SCHEDULEDISPATCHER:New()
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F3()
|
||||
@@ -51,15 +80,28 @@ end
|
||||
-- It is constructed as such that a garbage collection is executed on the weak tables, when the Scheduler is nillified.
|
||||
-- Nothing of this code should be modified without testing it thoroughly.
|
||||
-- @param #SCHEDULEDISPATCHER self
|
||||
-- @param Core.Scheduler#SCHEDULER Scheduler
|
||||
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
||||
-- @param #function ScheduleFunction Scheduler function.
|
||||
-- @param #table ScheduleArguments Table of arguments passed to the ScheduleFunction.
|
||||
-- @param #number Start Start time in seconds.
|
||||
-- @param #number Repeat Repeat interval in seconds.
|
||||
-- @param #number Randomize Radomization factor [0,1].
|
||||
-- @param #number Stop Stop time in seconds.
|
||||
-- @param #number TraceLevel Trace level [0,3].
|
||||
-- @param Core.Fsm#FSM Fsm Finite state model.
|
||||
-- @return #string Call ID or nil.
|
||||
function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop, TraceLevel, Fsm )
|
||||
self:F2( { Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop, TraceLevel } )
|
||||
self:F2( { Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop, TraceLevel, Fsm } )
|
||||
|
||||
-- Increase counter.
|
||||
self.CallID = self.CallID + 1
|
||||
|
||||
-- Create ID.
|
||||
local CallID = self.CallID .. "#" .. ( Scheduler.MasterObject and Scheduler.MasterObject.GetClassNameAndID and Scheduler.MasterObject:GetClassNameAndID() or "" ) or ""
|
||||
|
||||
self:T2(string.format("Adding schedule #%d CallID=%s", self.CallID, CallID))
|
||||
|
||||
-- Initialize the ObjectSchedulers array, which is a weakly coupled table.
|
||||
-- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array.
|
||||
-- Initialize PersistentSchedulers
|
||||
self.PersistentSchedulers = self.PersistentSchedulers or {}
|
||||
|
||||
-- Initialize the ObjectSchedulers array, which is a weakly coupled table.
|
||||
@@ -76,11 +118,11 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
|
||||
self.Schedule = self.Schedule or setmetatable( {}, { __mode = "k" } )
|
||||
self.Schedule[Scheduler] = self.Schedule[Scheduler] or {}
|
||||
self.Schedule[Scheduler][CallID] = {}
|
||||
self.Schedule[Scheduler][CallID] = {} --#SCHEDULEDISPATCHER.ScheduleData
|
||||
self.Schedule[Scheduler][CallID].Function = ScheduleFunction
|
||||
self.Schedule[Scheduler][CallID].Arguments = ScheduleArguments
|
||||
self.Schedule[Scheduler][CallID].StartTime = timer.getTime() + ( Start or 0 )
|
||||
self.Schedule[Scheduler][CallID].Start = Start + .1
|
||||
self.Schedule[Scheduler][CallID].Start = Start + 0.1
|
||||
self.Schedule[Scheduler][CallID].Repeat = Repeat or 0
|
||||
self.Schedule[Scheduler][CallID].Randomize = Randomize or 0
|
||||
self.Schedule[Scheduler][CallID].Stop = Stop
|
||||
@@ -118,15 +160,15 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
if name_fsm then
|
||||
Info.name = name_fsm
|
||||
end
|
||||
--env.info( debug.traceback() )
|
||||
end
|
||||
|
||||
self:T3( self.Schedule[Scheduler][CallID] )
|
||||
|
||||
--- Function passed to the DCS timer.scheduleFunction()
|
||||
self.Schedule[Scheduler][CallID].CallHandler = function( Params )
|
||||
|
||||
local CallID = Params.CallID
|
||||
local Info = Params.Info
|
||||
local Info = Params.Info or {}
|
||||
local Source = Info.source or "?"
|
||||
local Line = Info.currentline or "?"
|
||||
local Name = Info.name or "?"
|
||||
@@ -139,7 +181,8 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
return errmsg
|
||||
end
|
||||
|
||||
local Scheduler = self.ObjectSchedulers[CallID]
|
||||
-- Get object or persistant scheduler object.
|
||||
local Scheduler = self.ObjectSchedulers[CallID] --Core.Scheduler#SCHEDULER
|
||||
if not Scheduler then
|
||||
Scheduler = self.PersistentSchedulers[CallID]
|
||||
end
|
||||
@@ -148,21 +191,24 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
|
||||
if Scheduler then
|
||||
|
||||
local MasterObject = tostring(Scheduler.MasterObject)
|
||||
local Schedule = self.Schedule[Scheduler][CallID]
|
||||
local MasterObject = tostring(Scheduler.MasterObject)
|
||||
|
||||
-- Schedule object.
|
||||
local Schedule = self.Schedule[Scheduler][CallID] --#SCHEDULEDISPATCHER.ScheduleData
|
||||
|
||||
--self:T3( { Schedule = Schedule } )
|
||||
|
||||
local SchedulerObject = Scheduler.SchedulerObject
|
||||
--local ScheduleObjectName = Scheduler.SchedulerObject:GetNameAndClassID()
|
||||
local ScheduleFunction = Schedule.Function
|
||||
local ScheduleArguments = Schedule.Arguments
|
||||
local Start = Schedule.Start
|
||||
local Repeat = Schedule.Repeat or 0
|
||||
local Randomize = Schedule.Randomize or 0
|
||||
local Stop = Schedule.Stop or 0
|
||||
local ScheduleID = Schedule.ScheduleID
|
||||
local ShowTrace = Scheduler.ShowTrace
|
||||
local SchedulerObject = Scheduler.MasterObject --Scheduler.SchedulerObject Now is this the Maste or Scheduler object?
|
||||
local ShowTrace = Scheduler.ShowTrace
|
||||
|
||||
local ScheduleFunction = Schedule.Function
|
||||
local ScheduleArguments = Schedule.Arguments or {}
|
||||
local Start = Schedule.Start
|
||||
local Repeat = Schedule.Repeat or 0
|
||||
local Randomize = Schedule.Randomize or 0
|
||||
local Stop = Schedule.Stop or 0
|
||||
local ScheduleID = Schedule.ScheduleID
|
||||
|
||||
|
||||
local Prefix = ( Repeat == 0 ) and "--->" or "+++>"
|
||||
|
||||
@@ -189,24 +235,20 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
local CurrentTime = timer.getTime()
|
||||
local StartTime = Schedule.StartTime
|
||||
|
||||
self:F3( { Master = MasterObject, CurrentTime = CurrentTime, StartTime = StartTime, Start = Start, Repeat = Repeat, Randomize = Randomize, Stop = Stop } )
|
||||
-- Debug info.
|
||||
self:F3( { CallID=CallID, ScheduleID=ScheduleID, Master = MasterObject, CurrentTime = CurrentTime, StartTime = StartTime, Start = Start, Repeat = Repeat, Randomize = Randomize, Stop = Stop } )
|
||||
|
||||
|
||||
if Status and (( Result == nil ) or ( Result and Result ~= false ) ) then
|
||||
|
||||
if Repeat ~= 0 and ( ( Stop == 0 ) or ( Stop ~= 0 and CurrentTime <= StartTime + Stop ) ) then
|
||||
local ScheduleTime =
|
||||
CurrentTime +
|
||||
Repeat +
|
||||
math.random(
|
||||
- ( Randomize * Repeat / 2 ),
|
||||
( Randomize * Repeat / 2 )
|
||||
) +
|
||||
0.01
|
||||
local ScheduleTime = CurrentTime + Repeat + math.random(- ( Randomize * Repeat / 2 ), ( Randomize * Repeat / 2 )) + 0.0001 -- Accuracy
|
||||
--self:T3( { Repeat = CallID, CurrentTime, ScheduleTime, ScheduleArguments } )
|
||||
return ScheduleTime -- returns the next time the function needs to be called.
|
||||
else
|
||||
self:Stop( Scheduler, CallID )
|
||||
end
|
||||
|
||||
else
|
||||
self:Stop( Scheduler, CallID )
|
||||
end
|
||||
@@ -222,6 +264,10 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
return CallID
|
||||
end
|
||||
|
||||
--- Remove schedule.
|
||||
-- @param #SCHEDULEDISPATCHER self
|
||||
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
||||
-- @param #table CallID Call ID.
|
||||
function SCHEDULEDISPATCHER:RemoveSchedule( Scheduler, CallID )
|
||||
self:F2( { Remove = CallID, Scheduler = Scheduler } )
|
||||
|
||||
@@ -231,46 +277,80 @@ function SCHEDULEDISPATCHER:RemoveSchedule( Scheduler, CallID )
|
||||
end
|
||||
end
|
||||
|
||||
--- Start dispatcher.
|
||||
-- @param #SCHEDULEDISPATCHER self
|
||||
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
||||
-- @param #table CallID (Optional) Call ID.
|
||||
-- @param #string Info (Optional) Debug info.
|
||||
function SCHEDULEDISPATCHER:Start( Scheduler, CallID, Info )
|
||||
self:F2( { Start = CallID, Scheduler = Scheduler } )
|
||||
|
||||
|
||||
if CallID then
|
||||
local Schedule = self.Schedule[Scheduler]
|
||||
|
||||
local Schedule = self.Schedule[Scheduler][CallID] --#SCHEDULEDISPATCHER.ScheduleData
|
||||
|
||||
-- Only start when there is no ScheduleID defined!
|
||||
-- This prevents to "Start" the scheduler twice with the same CallID...
|
||||
if not Schedule[CallID].ScheduleID then
|
||||
Schedule[CallID].StartTime = timer.getTime() -- Set the StartTime field to indicate when the scheduler started.
|
||||
Schedule[CallID].ScheduleID = timer.scheduleFunction(
|
||||
Schedule[CallID].CallHandler,
|
||||
{ CallID = CallID, Info = Info },
|
||||
timer.getTime() + Schedule[CallID].Start
|
||||
)
|
||||
if not Schedule.ScheduleID then
|
||||
|
||||
-- Current time in seconds.
|
||||
local Tnow=timer.getTime()
|
||||
|
||||
Schedule.StartTime = Tnow -- Set the StartTime field to indicate when the scheduler started.
|
||||
|
||||
-- Start DCS schedule function https://wiki.hoggitworld.com/view/DCS_func_scheduleFunction
|
||||
Schedule.ScheduleID = timer.scheduleFunction(Schedule.CallHandler, { CallID = CallID, Info = Info }, Tnow + Schedule.Start)
|
||||
|
||||
self:T(string.format("Starting scheduledispatcher Call ID=%s ==> Schedule ID=%s", tostring(CallID), tostring(Schedule.ScheduleID)))
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
-- Recursive.
|
||||
for CallID, Schedule in pairs( self.Schedule[Scheduler] or {} ) do
|
||||
self:Start( Scheduler, CallID, Info ) -- Recursive
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
--- Stop dispatcher.
|
||||
-- @param #SCHEDULEDISPATCHER self
|
||||
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
||||
-- @param #table CallID Call ID.
|
||||
function SCHEDULEDISPATCHER:Stop( Scheduler, CallID )
|
||||
self:F2( { Stop = CallID, Scheduler = Scheduler } )
|
||||
|
||||
if CallID then
|
||||
local Schedule = self.Schedule[Scheduler]
|
||||
-- Only stop when there is a ScheduleID defined for the CallID.
|
||||
-- So, when the scheduler was stopped before, do nothing.
|
||||
if Schedule[CallID].ScheduleID then
|
||||
timer.removeFunction( Schedule[CallID].ScheduleID )
|
||||
Schedule[CallID].ScheduleID = nil
|
||||
|
||||
local Schedule = self.Schedule[Scheduler][CallID] --#SCHEDULEDISPATCHER.ScheduleData
|
||||
|
||||
-- Only stop when there is a ScheduleID defined for the CallID. So, when the scheduler was stopped before, do nothing.
|
||||
if Schedule.ScheduleID then
|
||||
|
||||
self:T(string.format("scheduledispatcher stopping scheduler CallID=%s, ScheduleID=%s", tostring(CallID), tostring(Schedule.ScheduleID)))
|
||||
|
||||
-- Remove schedule function https://wiki.hoggitworld.com/view/DCS_func_removeFunction
|
||||
timer.removeFunction(Schedule.ScheduleID)
|
||||
|
||||
Schedule.ScheduleID = nil
|
||||
|
||||
else
|
||||
self:T(string.format("Error no ScheduleID for CallID=%s", tostring(CallID)))
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
for CallID, Schedule in pairs( self.Schedule[Scheduler] or {} ) do
|
||||
self:Stop( Scheduler, CallID ) -- Recursive
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
--- Clear all schedules by stopping all dispatchers.
|
||||
-- @param #SCHEDULEDISPATCHER self
|
||||
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
||||
function SCHEDULEDISPATCHER:Clear( Scheduler )
|
||||
self:F2( { Scheduler = Scheduler } )
|
||||
|
||||
@@ -279,9 +359,19 @@ function SCHEDULEDISPATCHER:Clear( Scheduler )
|
||||
end
|
||||
end
|
||||
|
||||
function SCHEDULEDISPATCHER:NoTrace( Scheduler )
|
||||
--- Shopw tracing info.
|
||||
-- @param #SCHEDULEDISPATCHER self
|
||||
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
||||
function SCHEDULEDISPATCHER:ShowTrace( Scheduler )
|
||||
self:F2( { Scheduler = Scheduler } )
|
||||
|
||||
Scheduler.ShowTrace = nil
|
||||
Scheduler.ShowTrace = true
|
||||
end
|
||||
|
||||
--- No tracing info.
|
||||
-- @param #SCHEDULEDISPATCHER self
|
||||
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
||||
function SCHEDULEDISPATCHER:NoTrace( Scheduler )
|
||||
self:F2( { Scheduler = Scheduler } )
|
||||
Scheduler.ShowTrace = false
|
||||
end
|
||||
|
||||
|
||||
@@ -43,7 +43,9 @@
|
||||
|
||||
--- The SCHEDULER class
|
||||
-- @type SCHEDULER
|
||||
-- @field #number ScheduleID the ID of the scheduler.
|
||||
-- @field #table Schedules Table of schedules.
|
||||
-- @field #table MasterObject Master object.
|
||||
-- @field #boolean ShowTrace Trace info if true.
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
@@ -69,53 +71,53 @@
|
||||
--
|
||||
-- * @{#SCHEDULER.New}( nil ): Setup a new SCHEDULER object, which is persistently executed after garbage collection.
|
||||
--
|
||||
-- SchedulerObject = SCHEDULER:New()
|
||||
-- SchedulerID = SchedulerObject:Schedule( nil, ScheduleFunction, {} )
|
||||
-- MasterObject = SCHEDULER:New()
|
||||
-- SchedulerID = MasterObject:Schedule( nil, ScheduleFunction, {} )
|
||||
--
|
||||
-- The above example creates a new SchedulerObject, but does not schedule anything.
|
||||
-- A separate schedule is created by using the SchedulerObject using the method :Schedule..., which returns a ScheduleID
|
||||
-- The above example creates a new MasterObject, but does not schedule anything.
|
||||
-- A separate schedule is created by using the MasterObject using the method :Schedule..., which returns a ScheduleID
|
||||
--
|
||||
-- ### Construct a SCHEDULER object without a volatile schedule, but volatile to the Object existence...
|
||||
--
|
||||
-- * @{#SCHEDULER.New}( Object ): Setup a new SCHEDULER object, which is linked to the Object. When the Object is nillified or destroyed, the SCHEDULER object will also be destroyed and stopped after garbage collection.
|
||||
--
|
||||
-- ZoneObject = ZONE:New( "ZoneName" )
|
||||
-- SchedulerObject = SCHEDULER:New( ZoneObject )
|
||||
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {} )
|
||||
-- MasterObject = SCHEDULER:New( ZoneObject )
|
||||
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {} )
|
||||
-- ...
|
||||
-- ZoneObject = nil
|
||||
-- garbagecollect()
|
||||
--
|
||||
-- The above example creates a new SchedulerObject, but does not schedule anything, and is bound to the existence of ZoneObject, which is a ZONE.
|
||||
-- A separate schedule is created by using the SchedulerObject using the method :Schedule()..., which returns a ScheduleID
|
||||
-- The above example creates a new MasterObject, but does not schedule anything, and is bound to the existence of ZoneObject, which is a ZONE.
|
||||
-- A separate schedule is created by using the MasterObject using the method :Schedule()..., which returns a ScheduleID
|
||||
-- Later in the logic, the ZoneObject is put to nil, and garbage is collected.
|
||||
-- As a result, the ScheduleObject will cancel any planned schedule.
|
||||
-- As a result, the MasterObject will cancel any planned schedule.
|
||||
--
|
||||
-- ### Construct a SCHEDULER object with a persistent schedule.
|
||||
--
|
||||
-- * @{#SCHEDULER.New}( nil, Function, FunctionArguments, Start, ... ): Setup a new persistent SCHEDULER object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters.
|
||||
--
|
||||
-- SchedulerObject, SchedulerID = SCHEDULER:New( nil, ScheduleFunction, {} )
|
||||
-- MasterObject, SchedulerID = SCHEDULER:New( nil, ScheduleFunction, {} )
|
||||
--
|
||||
-- The above example creates a new SchedulerObject, and does schedule the first schedule as part of the call.
|
||||
-- Note that 2 variables are returned here: SchedulerObject, ScheduleID...
|
||||
-- The above example creates a new MasterObject, and does schedule the first schedule as part of the call.
|
||||
-- Note that 2 variables are returned here: MasterObject, ScheduleID...
|
||||
--
|
||||
-- ### Construct a SCHEDULER object without a schedule, but volatile to the Object existence...
|
||||
--
|
||||
-- * @{#SCHEDULER.New}( Object, Function, FunctionArguments, Start, ... ): Setup a new SCHEDULER object, linked to Object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters.
|
||||
--
|
||||
-- ZoneObject = ZONE:New( "ZoneName" )
|
||||
-- SchedulerObject, SchedulerID = SCHEDULER:New( ZoneObject, ScheduleFunction, {} )
|
||||
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {} )
|
||||
-- MasterObject, SchedulerID = SCHEDULER:New( ZoneObject, ScheduleFunction, {} )
|
||||
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {} )
|
||||
-- ...
|
||||
-- ZoneObject = nil
|
||||
-- garbagecollect()
|
||||
--
|
||||
-- The above example creates a new SchedulerObject, and schedules a method call (ScheduleFunction),
|
||||
-- The above example creates a new MasterObject, and schedules a method call (ScheduleFunction),
|
||||
-- and is bound to the existence of ZoneObject, which is a ZONE object (ZoneObject).
|
||||
-- Both a ScheduleObject and a SchedulerID variable are returned.
|
||||
-- Both a MasterObject and a SchedulerID variable are returned.
|
||||
-- Later in the logic, the ZoneObject is put to nil, and garbage is collected.
|
||||
-- As a result, the ScheduleObject will cancel the planned schedule.
|
||||
-- As a result, the MasterObject will cancel the planned schedule.
|
||||
--
|
||||
-- ## SCHEDULER timer stopping and (re-)starting.
|
||||
--
|
||||
@@ -125,15 +127,15 @@
|
||||
-- * @{#SCHEDULER.Stop}(): Stop the schedules within the SCHEDULER object. If a CallID is provided to :Stop(), then only the schedule referenced by CallID will be stopped.
|
||||
--
|
||||
-- ZoneObject = ZONE:New( "ZoneName" )
|
||||
-- SchedulerObject, SchedulerID = SCHEDULER:New( ZoneObject, ScheduleFunction, {} )
|
||||
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 10 )
|
||||
-- MasterObject, SchedulerID = SCHEDULER:New( ZoneObject, ScheduleFunction, {} )
|
||||
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 10 )
|
||||
-- ...
|
||||
-- SchedulerObject:Stop( SchedulerID )
|
||||
-- MasterObject:Stop( SchedulerID )
|
||||
-- ...
|
||||
-- SchedulerObject:Start( SchedulerID )
|
||||
-- MasterObject:Start( SchedulerID )
|
||||
--
|
||||
-- The above example creates a new SchedulerObject, and does schedule the first schedule as part of the call.
|
||||
-- Note that 2 variables are returned here: SchedulerObject, ScheduleID...
|
||||
-- The above example creates a new MasterObject, and does schedule the first schedule as part of the call.
|
||||
-- Note that 2 variables are returned here: MasterObject, ScheduleID...
|
||||
-- Later in the logic, the repeating schedule with SchedulerID is stopped.
|
||||
-- A bit later, the repeating schedule with SchedulerId is (re)-started.
|
||||
--
|
||||
@@ -145,32 +147,32 @@
|
||||
-- Consider the following code fragment of the SCHEDULER object creation.
|
||||
--
|
||||
-- ZoneObject = ZONE:New( "ZoneName" )
|
||||
-- SchedulerObject = SCHEDULER:New( ZoneObject )
|
||||
-- MasterObject = SCHEDULER:New( ZoneObject )
|
||||
--
|
||||
-- Several parameters can be specified that influence the behaviour of a Schedule.
|
||||
--
|
||||
-- ### A single schedule, immediately executed
|
||||
--
|
||||
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {} )
|
||||
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {} )
|
||||
--
|
||||
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within milleseconds ...
|
||||
--
|
||||
-- ### A single schedule, planned over time
|
||||
--
|
||||
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10 )
|
||||
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10 )
|
||||
--
|
||||
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds ...
|
||||
--
|
||||
-- ### A schedule with a repeating time interval, planned over time
|
||||
--
|
||||
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60 )
|
||||
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60 )
|
||||
--
|
||||
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
|
||||
-- and repeating 60 every seconds ...
|
||||
--
|
||||
-- ### A schedule with a repeating time interval, planned over time, with time interval randomization
|
||||
--
|
||||
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60, 0.5 )
|
||||
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60, 0.5 )
|
||||
--
|
||||
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
|
||||
-- and repeating 60 seconds, with a 50% time interval randomization ...
|
||||
@@ -180,7 +182,7 @@
|
||||
--
|
||||
-- ### A schedule with a repeating time interval, planned over time, with time interval randomization, and stop after a time interval
|
||||
--
|
||||
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60, 0.5, 300 )
|
||||
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60, 0.5, 300 )
|
||||
--
|
||||
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
|
||||
-- The schedule will repeat every 60 seconds.
|
||||
@@ -191,13 +193,15 @@
|
||||
--
|
||||
-- @field #SCHEDULER
|
||||
SCHEDULER = {
|
||||
ClassName = "SCHEDULER",
|
||||
Schedules = {},
|
||||
ClassName = "SCHEDULER",
|
||||
Schedules = {},
|
||||
MasterObject = nil,
|
||||
ShowTrace = nil,
|
||||
}
|
||||
|
||||
--- SCHEDULER constructor.
|
||||
-- @param #SCHEDULER self
|
||||
-- @param #table SchedulerObject Specified for which Moose object the timer is setup. If a value of nil is provided, a scheduler will be setup without an object reference.
|
||||
-- @param #table MasterObject Specified for which Moose object the timer is setup. If a value of nil is provided, a scheduler will be setup without an object reference.
|
||||
-- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments.
|
||||
-- @param #table SchedulerArguments Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }.
|
||||
-- @param #number Start Specifies the amount of seconds that will be waited before the scheduling is started, and the event function is called.
|
||||
@@ -205,51 +209,51 @@ SCHEDULER = {
|
||||
-- @param #number RandomizeFactor Specifies a randomization factor between 0 and 1 to randomize the Repeat.
|
||||
-- @param #number Stop Specifies the amount of seconds when the scheduler will be stopped.
|
||||
-- @return #SCHEDULER self.
|
||||
-- @return #number The ScheduleID of the planned schedule.
|
||||
function SCHEDULER:New( SchedulerObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop )
|
||||
-- @return #table The ScheduleID of the planned schedule.
|
||||
function SCHEDULER:New( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop )
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #SCHEDULER
|
||||
self:F2( { Start, Repeat, RandomizeFactor, Stop } )
|
||||
|
||||
local ScheduleID = nil
|
||||
|
||||
self.MasterObject = SchedulerObject
|
||||
self.ShowTrace = true
|
||||
self.MasterObject = MasterObject
|
||||
self.ShowTrace = false
|
||||
|
||||
if SchedulerFunction then
|
||||
ScheduleID = self:Schedule( SchedulerObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop, 4 )
|
||||
ScheduleID = self:Schedule( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop, 3 )
|
||||
end
|
||||
|
||||
return self, ScheduleID
|
||||
end
|
||||
|
||||
--function SCHEDULER:_Destructor()
|
||||
-- --self:E("_Destructor")
|
||||
--
|
||||
-- _SCHEDULEDISPATCHER:RemoveSchedule( self.CallID )
|
||||
--end
|
||||
|
||||
--- Schedule a new time event. Note that the schedule will only take place if the scheduler is *started*. Even for a single schedule event, the scheduler needs to be started also.
|
||||
-- @param #SCHEDULER self
|
||||
-- @param #table SchedulerObject Specified for which Moose object the timer is setup. If a value of nil is provided, a scheduler will be setup without an object reference.
|
||||
-- @param #table MasterObject Specified for which Moose object the timer is setup. If a value of nil is provided, a scheduler will be setup without an object reference.
|
||||
-- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments.
|
||||
-- @param #table SchedulerArguments Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }.
|
||||
-- @param #number Start Specifies the amount of seconds that will be waited before the scheduling is started, and the event function is called.
|
||||
-- @param #number Repeat Specifies the interval in seconds when the scheduler will call the event function.
|
||||
-- @param #number Repeat Specifies the time interval in seconds when the scheduler will call the event function.
|
||||
-- @param #number RandomizeFactor Specifies a randomization factor between 0 and 1 to randomize the Repeat.
|
||||
-- @param #number Stop Specifies the amount of seconds when the scheduler will be stopped.
|
||||
-- @return #number The ScheduleID of the planned schedule.
|
||||
function SCHEDULER:Schedule( SchedulerObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop, TraceLevel, Fsm )
|
||||
-- @param #number Stop Time interval in seconds after which the scheduler will be stoppe.
|
||||
-- @param #number TraceLevel Trace level [0,3]. Default 3.
|
||||
-- @param Core.Fsm#FSM Fsm Finite state model.
|
||||
-- @return #table The ScheduleID of the planned schedule.
|
||||
function SCHEDULER:Schedule( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop, TraceLevel, Fsm )
|
||||
self:F2( { Start, Repeat, RandomizeFactor, Stop } )
|
||||
self:T3( { SchedulerArguments } )
|
||||
|
||||
-- Debug info.
|
||||
local ObjectName = "-"
|
||||
if SchedulerObject and SchedulerObject.ClassName and SchedulerObject.ClassID then
|
||||
ObjectName = SchedulerObject.ClassName .. SchedulerObject.ClassID
|
||||
if MasterObject and MasterObject.ClassName and MasterObject.ClassID then
|
||||
ObjectName = MasterObject.ClassName .. MasterObject.ClassID
|
||||
end
|
||||
self:F3( { "Schedule :", ObjectName, tostring( SchedulerObject ), Start, Repeat, RandomizeFactor, Stop } )
|
||||
self.SchedulerObject = SchedulerObject
|
||||
self:F3( { "Schedule :", ObjectName, tostring( MasterObject ), Start, Repeat, RandomizeFactor, Stop } )
|
||||
|
||||
-- Set master object.
|
||||
self.MasterObject = MasterObject
|
||||
|
||||
-- Add schedule.
|
||||
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule(
|
||||
self,
|
||||
SchedulerFunction,
|
||||
@@ -269,57 +273,47 @@ end
|
||||
|
||||
--- (Re-)Starts the schedules or a specific schedule if a valid ScheduleID is provided.
|
||||
-- @param #SCHEDULER self
|
||||
-- @param #number ScheduleID (optional) The ScheduleID of the planned (repeating) schedule.
|
||||
-- @param #string ScheduleID (Optional) The ScheduleID of the planned (repeating) schedule.
|
||||
function SCHEDULER:Start( ScheduleID )
|
||||
self:F3( { ScheduleID } )
|
||||
|
||||
self:T(string.format("Starting scheduler ID=%s", tostring(ScheduleID)))
|
||||
_SCHEDULEDISPATCHER:Start( self, ScheduleID )
|
||||
end
|
||||
|
||||
--- Stops the schedules or a specific schedule if a valid ScheduleID is provided.
|
||||
-- @param #SCHEDULER self
|
||||
-- @param #number ScheduleID (optional) The ScheduleID of the planned (repeating) schedule.
|
||||
-- @param #string ScheduleID (Optional) The ScheduleID of the planned (repeating) schedule.
|
||||
function SCHEDULER:Stop( ScheduleID )
|
||||
self:F3( { ScheduleID } )
|
||||
|
||||
self:T(string.format("Stopping scheduler ID=%s", tostring(ScheduleID)))
|
||||
_SCHEDULEDISPATCHER:Stop( self, ScheduleID )
|
||||
end
|
||||
|
||||
--- Removes a specific schedule if a valid ScheduleID is provided.
|
||||
-- @param #SCHEDULER self
|
||||
-- @param #number ScheduleID (optional) The ScheduleID of the planned (repeating) schedule.
|
||||
-- @param #string ScheduleID (optional) The ScheduleID of the planned (repeating) schedule.
|
||||
function SCHEDULER:Remove( ScheduleID )
|
||||
self:F3( { ScheduleID } )
|
||||
|
||||
_SCHEDULEDISPATCHER:Remove( self, ScheduleID )
|
||||
self:T(string.format("Removing scheduler ID=%s", tostring(ScheduleID)))
|
||||
_SCHEDULEDISPATCHER:RemoveSchedule( self, ScheduleID )
|
||||
end
|
||||
|
||||
--- Clears all pending schedules.
|
||||
-- @param #SCHEDULER self
|
||||
function SCHEDULER:Clear()
|
||||
self:F3( )
|
||||
|
||||
self:T(string.format("Clearing scheduler"))
|
||||
_SCHEDULEDISPATCHER:Clear( self )
|
||||
end
|
||||
|
||||
--- Show tracing for this scheduler.
|
||||
-- @param #SCHEDULER self
|
||||
function SCHEDULER:ShowTrace()
|
||||
_SCHEDULEDISPATCHER:ShowTrace( self )
|
||||
end
|
||||
|
||||
--- No tracing for this scheduler.
|
||||
-- @param #SCHEDULER self
|
||||
-- @param #number ScheduleID (optional) The ScheduleID of the planned (repeating) schedule.
|
||||
function SCHEDULER:NoTrace()
|
||||
|
||||
_SCHEDULEDISPATCHER:NoTrace( self )
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1576,8 +1576,8 @@ do -- SET_GROUP
|
||||
|
||||
--- Iterate the SET_GROUP and count how many GROUPs and UNITs are alive.
|
||||
-- @param #SET_GROUP self
|
||||
-- @return #number The number of GROUPs completely in the Zone
|
||||
-- @return #number The number of UNITS alive.
|
||||
-- @return #number The number of GROUPs alive.
|
||||
-- @return #number The number of UNITs alive.
|
||||
function SET_GROUP:CountAlive()
|
||||
local CountG = 0
|
||||
local CountU = 0
|
||||
@@ -2186,20 +2186,6 @@ do -- SET_UNIT
|
||||
return IsNotInZone
|
||||
end
|
||||
|
||||
|
||||
--- Check if minimal one element of the SET_UNIT is in the Zone.
|
||||
-- @param #SET_UNIT self
|
||||
-- @param #function IteratorFunction The function that will be called when there is an alive UNIT in the SET_UNIT. The function needs to accept a UNIT parameter.
|
||||
-- @return #SET_UNIT self
|
||||
function SET_UNIT:ForEachUnitInZone( IteratorFunction, ... )
|
||||
self:F2( arg )
|
||||
|
||||
self:ForEach( IteratorFunction, arg, self:GetSet() )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,7 +16,7 @@
|
||||
-- * Spawn late activated.
|
||||
-- * Spawn with or without an initial delay.
|
||||
-- * Respawn after landing, on the runway or at the ramp after engine shutdown.
|
||||
-- * Spawn with custom heading.
|
||||
-- * Spawn with custom heading, both for a group formation and for the units in the group.
|
||||
-- * Spawn with different skills.
|
||||
-- * Spawn with different liveries.
|
||||
-- * Spawn with an inner and outer radius to set the initial position.
|
||||
@@ -214,9 +214,8 @@
|
||||
-- ### **Scheduled** spawning methods
|
||||
--
|
||||
-- * @{#SPAWN.SpawnScheduled}(): Spawn groups at scheduled but randomized intervals.
|
||||
-- * @{#SPAWN.SpawnScheduledStart}(): Start or continue to spawn groups at scheduled time intervals.
|
||||
-- * @{#SPAWN.SpawnScheduledStop}(): Stop the spawning of groups at scheduled time intervals.
|
||||
--
|
||||
--- * @{#SPAWN.SpawnScheduleStart}(): Start or continue to spawn groups at scheduled time intervals.
|
||||
-- * @{#SPAWN.SpawnScheduleStop}(): Stop the spawning of groups at scheduled time intervals.
|
||||
--
|
||||
--
|
||||
-- ## Retrieve alive GROUPs spawned by the SPAWN object
|
||||
@@ -260,7 +259,7 @@
|
||||
-- immediately when :SpawnScheduled() is initiated. The methods @{#SPAWN.InitDelayOnOff}() and @{#SPAWN.InitDelayOn}() can be used to
|
||||
-- activate a delay before the first @{Wrapper.Group} is spawned. For completeness, a method @{#SPAWN.InitDelayOff}() is also available, that
|
||||
-- can be used to switch off the initial delay. Because there is no delay by default, this method would only be used when a
|
||||
-- @{#SPAWN.SpawnScheduledStop}() ; @{#SPAWN.SpawnScheduledStart}() sequence would have been used.
|
||||
-- @{#SPAWN.SpawnScheduleStop}() ; @{#SPAWN.SpawnScheduleStart}() sequence would have been used.
|
||||
--
|
||||
--
|
||||
-- @field #SPAWN SPAWN
|
||||
@@ -326,6 +325,8 @@ function SPAWN:New( SpawnTemplatePrefix )
|
||||
self.SpawnInitModu = nil -- No special modulation.
|
||||
self.SpawnInitRadio = nil -- No radio comms setting.
|
||||
self.SpawnInitModex = nil
|
||||
self.SpawnInitAirbase = nil
|
||||
self.TweakedTemplate = false -- Check if the user is using self made template.
|
||||
|
||||
self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned.
|
||||
else
|
||||
@@ -378,7 +379,9 @@ function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix )
|
||||
self.SpawnInitModu = nil -- No special modulation.
|
||||
self.SpawnInitRadio = nil -- No radio comms setting.
|
||||
self.SpawnInitModex = nil
|
||||
|
||||
self.SpawnInitAirbase = nil
|
||||
self.TweakedTemplate = false -- Check if the user is using self made template.
|
||||
|
||||
self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned.
|
||||
else
|
||||
error( "SPAWN:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" )
|
||||
@@ -406,7 +409,11 @@ end
|
||||
function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix )
|
||||
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 paramter SpawnAliasPrefix is not set")
|
||||
return nil
|
||||
end
|
||||
|
||||
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.SpawnTemplatePrefix = SpawnTemplatePrefix
|
||||
@@ -433,6 +440,8 @@ function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPr
|
||||
self.SpawnInitModu = nil -- No special modulation.
|
||||
self.SpawnInitRadio = nil -- No radio comms setting.
|
||||
self.SpawnInitModex = nil
|
||||
self.SpawnInitAirbase = nil
|
||||
self.TweakedTemplate = true -- Check if the user is using self made template.
|
||||
|
||||
self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned.
|
||||
else
|
||||
@@ -446,7 +455,8 @@ function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPr
|
||||
end
|
||||
|
||||
|
||||
--- Limits the Maximum amount of Units that can be alive at the same time, and the maximum amount of groups that can be spawned.
|
||||
--- Stops any more repeat spawns from happening once the UNIT count of Alive units, spawned by the same SPAWN object, exceeds the first parameter. Also can stop spawns from happening once a total GROUP still alive is met.
|
||||
-- Exceptionally powerful when combined with SpawnSchedule for Respawning.
|
||||
-- Note that this method is exceptionally important to balance the performance of the mission. Depending on the machine etc, a mission can only process a maximum amount of units.
|
||||
-- If the time interval must be short, but there should not be more Units or Groups alive than a maximum amount of units, then this method should be used...
|
||||
-- When a @{#SPAWN.New} is executed and the limit of the amount of units alive is reached, then no new spawn will happen of the group, until some of these units of the spawn object will be destroyed.
|
||||
@@ -503,6 +513,24 @@ function SPAWN:InitLateActivated( LateActivated )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set spawns to happen at a particular airbase. Only for aircraft, of course.
|
||||
-- @param #SPAWN self
|
||||
-- @param #string AirbaseName Name of the airbase.
|
||||
-- @param #number Takeoff (Optional) Takeoff type. Can be SPAWN.Takeoff.Hot (default), SPAWN.Takeoff.Cold or SPAWN.Takeoff.Runway.
|
||||
-- @param #number TerminalTyple (Optional) The terminal type.
|
||||
-- @return #SPAWN self
|
||||
function SPAWN:InitAirbase( AirbaseName, Takeoff, TerminalType )
|
||||
self:F( )
|
||||
|
||||
self.SpawnInitAirbase=AIRBASE:FindByName(AirbaseName)
|
||||
|
||||
self.SpawnInitTakeoff=Takeoff or SPAWN.Takeoff.Hot
|
||||
|
||||
self.SpawnInitTerminalType=TerminalType
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Defines the Heading for the new spawned units.
|
||||
-- The heading can be given as one fixed degree, or can be randomized between minimum and maximum degrees.
|
||||
@@ -530,6 +558,41 @@ function SPAWN:InitHeading( HeadingMin, HeadingMax )
|
||||
end
|
||||
|
||||
|
||||
--- Defines the heading of the overall formation of the new spawned group.
|
||||
-- The heading can be given as one fixed degree, or can be randomized between minimum and maximum degrees.
|
||||
-- The Group's formation as laid out in its template will be rotated around the first unit in the group
|
||||
-- Group individual units facings will rotate to match. If InitHeading is also applied to this SPAWN then that will take precedence for individual unit facings.
|
||||
-- Note that InitGroupHeading does *not* rotate the groups route; only its initial facing!
|
||||
-- @param #SPAWN self
|
||||
-- @param #number HeadingMin The minimum or fixed heading in degrees.
|
||||
-- @param #number HeadingMax (optional) The maximum heading in degrees. This there is no maximum heading, then the heading for the group will be HeadingMin.
|
||||
-- @param #number unitVar (optional) Individual units within the group will have their heading randomized by +/- unitVar degrees. Default is zero.
|
||||
-- @return #SPAWN self
|
||||
-- @usage
|
||||
--
|
||||
-- mySpawner = SPAWN:New( ... )
|
||||
--
|
||||
-- -- Spawn the Group with the formation rotated +100 degrees around unit #1, compared to the mission template.
|
||||
-- mySpawner:InitGroupHeading( 100 )
|
||||
--
|
||||
-- Spawn the Group with the formation rotated units between +100 and +150 degrees around unit #1, compared to the mission template, and with individual units varying by +/- 10 degrees from their templated facing.
|
||||
-- mySpawner:InitGroupHeading( 100, 150, 10 )
|
||||
--
|
||||
-- Spawn the Group with the formation rotated -60 degrees around unit #1, compared to the mission template, but with all units facing due north regardless of how they were laid out in the template.
|
||||
-- mySpawner:InitGroupHeading(-60):InitHeading(0)
|
||||
-- or
|
||||
-- mySpawner:InitHeading(0):InitGroupHeading(-60)
|
||||
--
|
||||
function SPAWN:InitGroupHeading( HeadingMin, HeadingMax, unitVar )
|
||||
self:F({HeadingMin=HeadingMin, HeadingMax=HeadingMax, unitVar=unitVar})
|
||||
|
||||
self.SpawnInitGroupHeadingMin = HeadingMin
|
||||
self.SpawnInitGroupHeadingMax = HeadingMax
|
||||
self.SpawnInitGroupUnitVar = unitVar
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Sets the coalition of the spawned group. Note that it might be necessary to also set the country explicitly!
|
||||
-- @param #SPAWN self
|
||||
-- @param DCS#coalition.side Coalition Coalition of the group as number of enumerator:
|
||||
@@ -753,9 +816,9 @@ end
|
||||
-- Spawn_US_Platoon = { 'US Tank Platoon 1', 'US Tank Platoon 2', 'US Tank Platoon 3', 'US Tank Platoon 4', 'US Tank Platoon 5',
|
||||
-- 'US Tank Platoon 6', 'US Tank Platoon 7', 'US Tank Platoon 8', 'US Tank Platoon 9', 'US Tank Platoon 10',
|
||||
-- 'US Tank Platoon 11', 'US Tank Platoon 12', 'US Tank Platoon 13' }
|
||||
-- Spawn_US_Platoon_Left = SPAWN:New( 'US Tank Platoon Left' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplate( Spawn_US_Platoon ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
-- Spawn_US_Platoon_Middle = SPAWN:New( 'US Tank Platoon Middle' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplate( Spawn_US_Platoon ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
-- Spawn_US_Platoon_Right = SPAWN:New( 'US Tank Platoon Right' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplate( Spawn_US_Platoon ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
-- Spawn_US_Platoon_Left = SPAWN:New( 'US Tank Platoon Left' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplate( Spawn_US_Platoon ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
-- Spawn_US_Platoon_Middle = SPAWN:New( 'US Tank Platoon Middle' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplate( Spawn_US_Platoon ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
-- Spawn_US_Platoon_Right = SPAWN:New( 'US Tank Platoon Right' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplate( Spawn_US_Platoon ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
function SPAWN:InitRandomizeTemplate( SpawnTemplatePrefixTable )
|
||||
self:F( { self.SpawnTemplatePrefix, SpawnTemplatePrefixTable } )
|
||||
|
||||
@@ -789,9 +852,9 @@ end
|
||||
-- Spawn_US_PlatoonSet = SET_GROUP:New():FilterPrefixes( "US Tank Platoon Templates" ):FilterOnce()
|
||||
--
|
||||
-- --- Now use the Spawn_US_PlatoonSet to define the templates using InitRandomizeTemplateSet.
|
||||
-- Spawn_US_Platoon_Left = SPAWN:New( 'US Tank Platoon Left' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplateSet( Spawn_US_PlatoonSet ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
-- Spawn_US_Platoon_Middle = SPAWN:New( 'US Tank Platoon Middle' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplateSet( Spawn_US_PlatoonSet ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
-- Spawn_US_Platoon_Right = SPAWN:New( 'US Tank Platoon Right' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplateSet( Spawn_US_PlatoonSet ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
-- Spawn_US_Platoon_Left = SPAWN:New( 'US Tank Platoon Left' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplateSet( Spawn_US_PlatoonSet ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
-- Spawn_US_Platoon_Middle = SPAWN:New( 'US Tank Platoon Middle' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplateSet( Spawn_US_PlatoonSet ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
-- Spawn_US_Platoon_Right = SPAWN:New( 'US Tank Platoon Right' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplateSet( Spawn_US_PlatoonSet ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
function SPAWN:InitRandomizeTemplateSet( SpawnTemplateSet ) -- R2.3
|
||||
self:F( { self.SpawnTemplatePrefix } )
|
||||
|
||||
@@ -822,9 +885,9 @@ end
|
||||
-- -- Each new SPAWN will randomize the route, with a defined time interval of 200 seconds with 40% time variation (randomization) and
|
||||
-- -- with a limit set of maximum 12 Units alive simulteneously and 150 Groups to be spawned during the whole mission.
|
||||
--
|
||||
-- Spawn_US_Platoon_Left = SPAWN:New( 'US Tank Platoon Left' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplatePrefixes( "US Tank Platoon Templates" ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
-- Spawn_US_Platoon_Middle = SPAWN:New( 'US Tank Platoon Middle' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplatePrefixes( "US Tank Platoon Templates" ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
-- Spawn_US_Platoon_Right = SPAWN:New( 'US Tank Platoon Right' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplatePrefixes( "US Tank Platoon Templates" ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
-- Spawn_US_Platoon_Left = SPAWN:New( 'US Tank Platoon Left' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplatePrefixes( "US Tank Platoon Templates" ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
-- Spawn_US_Platoon_Middle = SPAWN:New( 'US Tank Platoon Middle' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplatePrefixes( "US Tank Platoon Templates" ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
-- Spawn_US_Platoon_Right = SPAWN:New( 'US Tank Platoon Right' ):InitLimit( 12, 150 ):SpawnScheduled( 200, 0.4 ):InitRandomizeTemplatePrefixes( "US Tank Platoon Templates" ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
function SPAWN:InitRandomizeTemplatePrefixes( SpawnTemplatePrefixes ) --R2.3
|
||||
self:F( { self.SpawnTemplatePrefix } )
|
||||
|
||||
@@ -916,11 +979,9 @@ end
|
||||
-- -- Re-SPAWN the Group(s) after each landing and Engine Shut-Down automatically.
|
||||
-- SpawnRU_SU34 = SPAWN
|
||||
-- :New( 'Su-34' )
|
||||
-- :Schedule( 2, 3, 1800, 0.4 )
|
||||
-- :SpawnUncontrolled()
|
||||
-- :InitRandomizeRoute( 1, 1, 3000 )
|
||||
-- :InitRepeatOnLanding()
|
||||
--
|
||||
-- :Spawn()
|
||||
function SPAWN:InitRepeatOnLanding()
|
||||
self:F( { self.SpawnTemplatePrefix } )
|
||||
|
||||
@@ -940,11 +1001,10 @@ end
|
||||
-- -- Re-SPAWN the Group(s) after each landing and Engine Shut-Down automatically.
|
||||
-- SpawnRU_SU34 = SPAWN
|
||||
-- :New( 'Su-34' )
|
||||
-- :Schedule( 2, 3, 1800, 0.4 )
|
||||
-- :SpawnUncontrolled()
|
||||
-- :InitRandomizeRoute( 1, 1, 3000 )
|
||||
-- :InitRepeatOnEngineShutDown()
|
||||
--
|
||||
-- :Spawn()
|
||||
function SPAWN:InitRepeatOnEngineShutDown()
|
||||
self:F( { self.SpawnTemplatePrefix } )
|
||||
|
||||
@@ -956,7 +1016,8 @@ function SPAWN:InitRepeatOnEngineShutDown()
|
||||
end
|
||||
|
||||
|
||||
--- CleanUp groups when they are still alive, but inactive.
|
||||
--- Delete groups that have not moved for X seconds - AIR ONLY!!!
|
||||
-- DO NOT USE ON GROUPS THAT DO NOT MOVE OR YOUR SERVER WILL BURN IN HELL (Pikes - April 2020)
|
||||
-- When groups are still alive and have become inactive due to damage and are unable to contribute anything, then this group will be removed at defined intervals in seconds.
|
||||
-- @param #SPAWN self
|
||||
-- @param #string SpawnCleanUpInterval The interval to check for inactive groups within seconds.
|
||||
@@ -981,6 +1042,7 @@ end
|
||||
|
||||
--- Makes the groups visible before start (like a batallion).
|
||||
-- The method will take the position of the group as the first position in the array.
|
||||
-- CAUTION: this directive will NOT work with OnSpawnGroup function.
|
||||
-- @param #SPAWN self
|
||||
-- @param #number SpawnAngle The angle in degrees how the groups and each unit of the group will be positioned.
|
||||
-- @param #number SpawnWidth The amount of Groups that will be positioned on the X axis.
|
||||
@@ -1115,7 +1177,12 @@ end -- Delay methods
|
||||
function SPAWN:Spawn()
|
||||
self:F( { self.SpawnTemplatePrefix, self.SpawnIndex, self.AliveUnits } )
|
||||
|
||||
return self:SpawnWithIndex( self.SpawnIndex + 1 )
|
||||
if self.SpawnInitAirbase then
|
||||
return self:SpawnAtAirbase(self.SpawnInitAirbase, self.SpawnInitTakeoff, nil, self.SpawnInitTerminalType)
|
||||
else
|
||||
return self:SpawnWithIndex( self.SpawnIndex + 1 )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Will re-spawn a group based on a given index.
|
||||
@@ -1130,7 +1197,7 @@ function SPAWN:ReSpawn( SpawnIndex )
|
||||
SpawnIndex = 1
|
||||
end
|
||||
|
||||
-- TODO: This logic makes DCS crash and i don't know why (yet).
|
||||
-- TODO: This logic makes DCS crash and i don't know why (yet). -- ED (Pikes -- not in the least bit scary to see this, right?)
|
||||
local SpawnGroup = self:GetGroupFromIndex( SpawnIndex )
|
||||
local WayPoints = SpawnGroup and SpawnGroup.WayPoints or nil
|
||||
if SpawnGroup then
|
||||
@@ -1214,21 +1281,72 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
|
||||
end
|
||||
end
|
||||
|
||||
-- Get correct heading.
|
||||
local function _Heading(course)
|
||||
-- Get correct heading in Radians.
|
||||
local function _Heading(courseDeg)
|
||||
local h
|
||||
if course<=180 then
|
||||
h=math.rad(course)
|
||||
if courseDeg<=180 then
|
||||
h=math.rad(courseDeg)
|
||||
else
|
||||
h=-math.rad(360-course)
|
||||
h=-math.rad(360-courseDeg)
|
||||
end
|
||||
return h
|
||||
end
|
||||
|
||||
local Rad180 = math.rad(180)
|
||||
local function _HeadingRad(courseRad)
|
||||
if courseRad<=Rad180 then
|
||||
return courseRad
|
||||
else
|
||||
return -((2*Rad180)-courseRad)
|
||||
end
|
||||
end
|
||||
|
||||
-- Generate a random value somewhere between two floating point values.
|
||||
local function _RandomInRange ( min, max )
|
||||
if min and max then
|
||||
return min + ( math.random()*(max-min) )
|
||||
else
|
||||
return min
|
||||
end
|
||||
end
|
||||
|
||||
-- Apply InitGroupHeading rotation if requested.
|
||||
-- We do this before InitHeading unit rotation so that can take precedence
|
||||
-- NOTE: Does *not* rotate the groups route; only its initial facing.
|
||||
if self.SpawnInitGroupHeadingMin and #SpawnTemplate.units > 0 then
|
||||
|
||||
local pivotX = SpawnTemplate.units[1].x -- unit #1 is the pivot point
|
||||
local pivotY = SpawnTemplate.units[1].y
|
||||
|
||||
local headingRad = math.rad(_RandomInRange(self.SpawnInitGroupHeadingMin or 0,self.SpawnInitGroupHeadingMax))
|
||||
local cosHeading = math.cos(headingRad)
|
||||
local sinHeading = math.sin(headingRad)
|
||||
|
||||
local unitVarRad = math.rad(self.SpawnInitGroupUnitVar or 0)
|
||||
|
||||
for UnitID = 1, #SpawnTemplate.units do
|
||||
|
||||
if UnitID > 1 then -- don't rotate position of unit #1
|
||||
local unitXOff = SpawnTemplate.units[UnitID].x - pivotX -- rotate position offset around unit #1
|
||||
local unitYOff = SpawnTemplate.units[UnitID].y - pivotY
|
||||
|
||||
SpawnTemplate.units[UnitID].x = pivotX + (unitXOff*cosHeading) - (unitYOff*sinHeading)
|
||||
SpawnTemplate.units[UnitID].y = pivotY + (unitYOff*cosHeading) + (unitXOff*sinHeading)
|
||||
end
|
||||
|
||||
-- adjust heading of all units, including unit #1
|
||||
local unitHeading = SpawnTemplate.units[UnitID].heading + headingRad -- add group rotation to units default rotation
|
||||
SpawnTemplate.units[UnitID].heading = _HeadingRad(_RandomInRange(unitHeading-unitVarRad, unitHeading+unitVarRad))
|
||||
SpawnTemplate.units[UnitID].psi = -SpawnTemplate.units[UnitID].heading
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- If Heading is given, point all the units towards the given Heading.
|
||||
-- If Heading is given, point all the units towards the given Heading. Overrides any heading set in InitGroupHeading above.
|
||||
if self.SpawnInitHeadingMin then
|
||||
for UnitID = 1, #SpawnTemplate.units do
|
||||
SpawnTemplate.units[UnitID].heading = _Heading(self.SpawnInitHeadingMax and math.random( self.SpawnInitHeadingMin, self.SpawnInitHeadingMax ) or self.SpawnInitHeadingMin)
|
||||
SpawnTemplate.units[UnitID].heading = _Heading(_RandomInRange(self.SpawnInitHeadingMin, self.SpawnInitHeadingMax))
|
||||
SpawnTemplate.units[UnitID].psi = -SpawnTemplate.units[UnitID].heading
|
||||
end
|
||||
end
|
||||
@@ -1319,6 +1437,7 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
|
||||
--end
|
||||
end
|
||||
|
||||
|
||||
self.SpawnGroups[self.SpawnIndex].Spawned = true
|
||||
return self.SpawnGroups[self.SpawnIndex].Group
|
||||
else
|
||||
@@ -1343,7 +1462,7 @@ end
|
||||
-- -- Low limit: 600 * ( 1 - 0.5 / 2 ) = 450
|
||||
-- -- High limit: 600 * ( 1 + 0.5 / 2 ) = 750
|
||||
-- -- Between these two values, a random amount of seconds will be choosen for each new spawn of the helicopters.
|
||||
-- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):Schedule( 600, 0.5 )
|
||||
-- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):SpawnScheduled( 600, 0.5 )
|
||||
function SPAWN:SpawnScheduled( SpawnTime, SpawnTimeVariation )
|
||||
self:F( { SpawnTime, SpawnTimeVariation } )
|
||||
|
||||
@@ -1520,7 +1639,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
|
||||
-- Get airbase ID and category.
|
||||
local AirbaseID = SpawnAirbase:GetID()
|
||||
local AirbaseCategory = SpawnAirbase:GetDesc().category
|
||||
local AirbaseCategory = SpawnAirbase:GetAirbaseCategory()
|
||||
self:F( { AirbaseCategory = AirbaseCategory } )
|
||||
|
||||
-- Set airdromeId.
|
||||
@@ -1958,7 +2077,7 @@ function SPAWN:ParkAircraft( SpawnAirbase, TerminalType, Parkingdata, SpawnIndex
|
||||
|
||||
-- Get airbase ID and category.
|
||||
local AirbaseID = SpawnAirbase:GetID()
|
||||
local AirbaseCategory = SpawnAirbase:GetDesc().category
|
||||
local AirbaseCategory = SpawnAirbase:GetAirbaseCategory()
|
||||
self:F( { AirbaseCategory = AirbaseCategory } )
|
||||
|
||||
-- Set airdromeId.
|
||||
@@ -2207,7 +2326,7 @@ function SPAWN:ParkAircraft( SpawnAirbase, TerminalType, Parkingdata, SpawnIndex
|
||||
end
|
||||
end
|
||||
|
||||
-- Set gereral spawnpoint position.
|
||||
-- Set general spawnpoint position.
|
||||
SpawnPoint.x = PointVec3.x
|
||||
SpawnPoint.y = PointVec3.z
|
||||
SpawnPoint.alt = PointVec3.y
|
||||
@@ -2866,9 +2985,15 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) --R2.2
|
||||
-- self.SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix )
|
||||
-- end
|
||||
|
||||
local SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix )
|
||||
--local SpawnTemplate = self.SpawnTemplate
|
||||
SpawnTemplate.name = self:SpawnGroupName( SpawnIndex )
|
||||
local SpawnTemplate
|
||||
if self.TweakedTemplate ~= nil and self.TweakedTemplate == true then
|
||||
BASE:I("WARNING: You are using a tweaked template.")
|
||||
SpawnTemplate = self.SpawnTemplate
|
||||
else
|
||||
SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix )
|
||||
SpawnTemplate.name = self:SpawnGroupName( SpawnIndex )
|
||||
end
|
||||
|
||||
|
||||
SpawnTemplate.groupId = nil
|
||||
--SpawnTemplate.lateActivation = false
|
||||
@@ -3239,6 +3364,7 @@ end
|
||||
|
||||
--- This function is called automatically by the Spawning scheduler.
|
||||
-- It is the internal worker method SPAWNing new Groups on the defined time intervals.
|
||||
-- @param #SPAWN self
|
||||
function SPAWN:_Scheduler()
|
||||
self:F2( { "_Scheduler", self.SpawnTemplatePrefix, self.SpawnAliasPrefix, self.SpawnIndex, self.SpawnMaxGroups, self.SpawnMaxUnitsAlive } )
|
||||
|
||||
|
||||
@@ -240,23 +240,31 @@ end
|
||||
|
||||
--- Respawns the original @{Static}.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #number delay Delay before respawn in seconds.
|
||||
-- @return #SPAWNSTATIC
|
||||
function SPAWNSTATIC:ReSpawn()
|
||||
function SPAWNSTATIC:ReSpawn(delay)
|
||||
|
||||
local StaticTemplate, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( self.SpawnTemplatePrefix )
|
||||
if delay and delay>0 then
|
||||
self:ScheduleOnce(delay, SPAWNSTATIC.ReSpawn, self)
|
||||
else
|
||||
|
||||
if StaticTemplate then
|
||||
|
||||
local StaticUnitTemplate = StaticTemplate.units[1]
|
||||
StaticTemplate.route = nil
|
||||
StaticTemplate.groupId = nil
|
||||
local StaticTemplate, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( self.SpawnTemplatePrefix )
|
||||
|
||||
local Static = coalition.addStaticObject( self.CountryID or CountryID, StaticTemplate.units[1] )
|
||||
if StaticTemplate then
|
||||
|
||||
local StaticUnitTemplate = StaticTemplate.units[1]
|
||||
StaticTemplate.route = nil
|
||||
StaticTemplate.groupId = nil
|
||||
|
||||
local Static = coalition.addStaticObject( self.CountryID or CountryID, StaticTemplate.units[1] )
|
||||
|
||||
return _DATABASE:FindStatic(Static:getName())
|
||||
end
|
||||
|
||||
return _DATABASE:FindStatic(Static:getName())
|
||||
return nil
|
||||
end
|
||||
|
||||
return nil
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
do -- UserFlag
|
||||
|
||||
--- @type USERFLAG
|
||||
-- @field #string ClassName Name of the class
|
||||
-- @field #string UserFlagName Name of the flag.
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
@@ -30,7 +32,8 @@ do -- UserFlag
|
||||
--
|
||||
-- @field #USERFLAG
|
||||
USERFLAG = {
|
||||
ClassName = "USERFLAG",
|
||||
ClassName = "USERFLAG",
|
||||
UserFlagName = nil,
|
||||
}
|
||||
|
||||
--- USERFLAG Constructor.
|
||||
@@ -46,18 +49,30 @@ do -- UserFlag
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the userflag name.
|
||||
-- @param #USERFLAG self
|
||||
-- @return #string Name of the user flag.
|
||||
function USERFLAG:GetName()
|
||||
return self.UserFlagName
|
||||
end
|
||||
|
||||
--- Set the userflag to a given Number.
|
||||
-- @param #USERFLAG self
|
||||
-- @param #number Number The number value to be checked if it is the same as the userflag.
|
||||
-- @param #number Delay Delay in seconds, before the flag is set.
|
||||
-- @return #USERFLAG The userflag instance.
|
||||
-- @usage
|
||||
-- local BlueVictory = USERFLAG:New( "VictoryBlue" )
|
||||
-- BlueVictory:Set( 100 ) -- Set the UserFlag VictoryBlue to 100.
|
||||
--
|
||||
function USERFLAG:Set( Number ) --R2.3
|
||||
function USERFLAG:Set( Number, Delay ) --R2.3
|
||||
|
||||
trigger.action.setUserFlag( self.UserFlagName, Number )
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, USERFLAG.Set, self, Number)
|
||||
else
|
||||
--env.info(string.format("Setting flag \"%s\" to %d at T=%.1f", self.UserFlagName, Number, timer.getTime()))
|
||||
trigger.action.setUserFlag( self.UserFlagName, Number )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
--- @type ZONE_BASE
|
||||
-- @field #string ZoneName Name of the zone.
|
||||
-- @field #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability.
|
||||
-- @extends Core.Base#BASE
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
|
||||
--- This class is an abstract BASE class for derived classes, and is not meant to be instantiated.
|
||||
@@ -685,30 +685,48 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories )
|
||||
|
||||
local function EvaluateZone( ZoneObject )
|
||||
--if ZoneObject:isExist() then --FF: isExist always returns false for SCENERY objects since DCS 2.2 and still in DCS 2.5
|
||||
if ZoneObject then
|
||||
if ZoneObject then
|
||||
|
||||
local ObjectCategory = ZoneObject:getCategory()
|
||||
if ( ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive() ) or
|
||||
(ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then
|
||||
|
||||
--local name=ZoneObject:getName()
|
||||
--env.info(string.format("Zone object %s", tostring(name)))
|
||||
--self:E(ZoneObject)
|
||||
|
||||
if ( ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive() ) or (ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then
|
||||
|
||||
local CoalitionDCSUnit = ZoneObject:getCoalition()
|
||||
|
||||
local Include = false
|
||||
if not UnitCategories then
|
||||
-- Anythink found is included.
|
||||
Include = true
|
||||
else
|
||||
-- Check if found object is in specified categories.
|
||||
local CategoryDCSUnit = ZoneObject:getDesc().category
|
||||
|
||||
for UnitCategoryID, UnitCategory in pairs( UnitCategories ) do
|
||||
if UnitCategory == CategoryDCSUnit then
|
||||
Include = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if Include then
|
||||
|
||||
local CoalitionDCSUnit = ZoneObject:getCoalition()
|
||||
|
||||
-- This coalition is inside the zone.
|
||||
self.ScanData.Coalitions[CoalitionDCSUnit] = true
|
||||
|
||||
self.ScanData.Units[ZoneObject] = ZoneObject
|
||||
|
||||
self:F2( { Name = ZoneObject:getName(), Coalition = CoalitionDCSUnit } )
|
||||
end
|
||||
end
|
||||
|
||||
if ObjectCategory == Object.Category.SCENERY then
|
||||
local SceneryType = ZoneObject:getTypeName()
|
||||
local SceneryName = ZoneObject:getName()
|
||||
@@ -716,21 +734,29 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories )
|
||||
self.ScanData.Scenery[SceneryType][SceneryName] = SCENERY:Register( SceneryName, ZoneObject )
|
||||
self:F2( { SCENERY = self.ScanData.Scenery[SceneryType][SceneryName] } )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Search objects.
|
||||
world.searchObjects( ObjectCategories, SphereSearch, EvaluateZone )
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Count the number of different coalitions inside the zone.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @return #table Table of DCS units and DCS statics inside the zone.
|
||||
function ZONE_RADIUS:GetScannedUnits()
|
||||
|
||||
return self.ScanData.Units
|
||||
end
|
||||
|
||||
|
||||
--- Get a set of scanned units.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @return Core.Set#SET_UNIT Set of units and statics inside the zone.
|
||||
function ZONE_RADIUS:GetScannedSetUnit()
|
||||
|
||||
local SetUnit = SET_UNIT:New()
|
||||
@@ -756,6 +782,9 @@ function ZONE_RADIUS:GetScannedSetUnit()
|
||||
end
|
||||
|
||||
|
||||
--- Count the number of different coalitions inside the zone.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @return #number Counted coalitions.
|
||||
function ZONE_RADIUS:CountScannedCoalitions()
|
||||
|
||||
local Count = 0
|
||||
@@ -763,14 +792,25 @@ function ZONE_RADIUS:CountScannedCoalitions()
|
||||
for CoalitionID, Coalition in pairs( self.ScanData.Coalitions ) do
|
||||
Count = Count + 1
|
||||
end
|
||||
|
||||
return Count
|
||||
end
|
||||
|
||||
--- Check if a certain coalition is inside a scanned zone.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param #number Coalition The coalition id, e.g. coalition.side.BLUE.
|
||||
-- @return #boolean If true, the coalition is inside the zone.
|
||||
function ZONE_RADIUS:CheckScannedCoalition( Coalition )
|
||||
if Coalition then
|
||||
return self.ScanData.Coalitions[Coalition]
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Get Coalitions of the units in the Zone, or Check if there are units of the given Coalition in the Zone.
|
||||
-- Returns nil if there are none ot two Coalitions in the zone!
|
||||
-- Returns nil if there are none to two Coalitions in the zone!
|
||||
-- Returns one Coalition if there are only Units of one Coalition in the Zone.
|
||||
-- Returns the Coalition for the given Coalition if there are units of the Coalition in the Zone
|
||||
-- Returns the Coalition for the given Coalition if there are units of the Coalition in the Zone.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @return #table
|
||||
function ZONE_RADIUS:GetScannedCoalition( Coalition )
|
||||
@@ -795,20 +835,27 @@ function ZONE_RADIUS:GetScannedCoalition( Coalition )
|
||||
end
|
||||
|
||||
|
||||
--- Get scanned scenery type
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @return #table Table of DCS scenery type objects.
|
||||
function ZONE_RADIUS:GetScannedSceneryType( SceneryType )
|
||||
return self.ScanData.Scenery[SceneryType]
|
||||
end
|
||||
|
||||
|
||||
--- Get scanned scenery table
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @return #table Table of DCS scenery objects.
|
||||
function ZONE_RADIUS:GetScannedScenery()
|
||||
return self.ScanData.Scenery
|
||||
end
|
||||
|
||||
|
||||
--- Is All in Zone of Coalition?
|
||||
-- Check if only the specifed coalition is inside the zone and noone else.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param Coalition
|
||||
-- @return #boolean
|
||||
-- @param #number Coalition Coalition ID of the coalition which is checked to be the only one in the zone.
|
||||
-- @return #boolean True, if **only** that coalition is inside the zone and no one else.
|
||||
-- @usage
|
||||
-- self.Zone:Scan()
|
||||
-- local IsGuarded = self.Zone:IsAllInZoneOfCoalition( self.Coalition )
|
||||
@@ -820,11 +867,12 @@ end
|
||||
|
||||
|
||||
--- Is All in Zone of Other Coalition?
|
||||
-- Check if only one coalition is inside the zone and the specified coalition is not the one.
|
||||
-- You first need to use the @{#ZONE_RADIUS.Scan} method to scan the zone before it can be evaluated!
|
||||
-- Note that once a zone has been scanned, multiple evaluations can be done on the scan result set.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param Coalition
|
||||
-- @return #boolean
|
||||
-- @param #number Coalition Coalition ID of the coalition which is not supposed to be in the zone.
|
||||
-- @return #boolean True, if and only if only one coalition is inside the zone and the specified coalition is not it.
|
||||
-- @usage
|
||||
-- self.Zone:Scan()
|
||||
-- local IsCaptured = self.Zone:IsAllInZoneOfOtherCoalition( self.Coalition )
|
||||
@@ -836,11 +884,12 @@ end
|
||||
|
||||
|
||||
--- Is Some in Zone of Coalition?
|
||||
-- Check if more than one coaltion is inside the zone and the specifed coalition is one of them.
|
||||
-- You first need to use the @{#ZONE_RADIUS.Scan} method to scan the zone before it can be evaluated!
|
||||
-- Note that once a zone has been scanned, multiple evaluations can be done on the scan result set.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param Coalition
|
||||
-- @return #boolean
|
||||
-- @param #number Coalition ID of the coaliton which is checked to be inside the zone.
|
||||
-- @return #boolean True if more than one coalition is inside the zone and the specified coalition is one of them.
|
||||
-- @usage
|
||||
-- self.Zone:Scan()
|
||||
-- local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition )
|
||||
@@ -904,7 +953,6 @@ function ZONE_RADIUS:SearchZone( EvaluateFunction, ObjectCategories )
|
||||
|
||||
local function EvaluateZone( ZoneDCSUnit )
|
||||
|
||||
env.info( ZoneDCSUnit:getName() )
|
||||
|
||||
local ZoneUnit = UNIT:Find( ZoneDCSUnit )
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
--- DCS API prototypes
|
||||
-- See [https://wiki.hoggitworld.com/view/Simulator_Scripting_Engine_Documentation](https://wiki.hoggitworld.com/view/Simulator_Scripting_Engine_Documentation)
|
||||
-- for further explanation and examples.
|
||||
--- **DCS API** Prototypes
|
||||
--
|
||||
-- See the [Simulator Scripting Engine Documentation](https://wiki.hoggitworld.com/view/Simulator_Scripting_Engine_Documentation) on Hoggit for further explanation and examples.
|
||||
--
|
||||
-- @module DCS
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
@@ -46,9 +47,13 @@ do -- world
|
||||
-- @field S_EVENT_PLAYER_COMMENT
|
||||
-- @field S_EVENT_SHOOTING_START [https://wiki.hoggitworld.com/view/DCS_event_shooting_start](https://wiki.hoggitworld.com/view/DCS_event_shooting_start)
|
||||
-- @field S_EVENT_SHOOTING_END [https://wiki.hoggitworld.com/view/DCS_event_shooting_end](https://wiki.hoggitworld.com/view/DCS_event_shooting_end)
|
||||
-- @field S_EVENT_MARK ADDED [https://wiki.hoggitworld.com/view/DCS_event_mark_added](https://wiki.hoggitworld.com/view/DCS_event_mark_added)
|
||||
-- @field S_EVENT_MARK CHANGE [https://wiki.hoggitworld.com/view/DCS_event_mark_change](https://wiki.hoggitworld.com/view/DCS_event_mark_change)
|
||||
-- @field S_EVENT_MARK REMOVE [https://wiki.hoggitworld.com/view/DCS_event_mark_remove](https://wiki.hoggitworld.com/view/DCS_event_mark_remove)
|
||||
-- @field S_EVENT_MARK ADDED [https://wiki.hoggitworld.com/view/DCS_event_mark_added](https://wiki.hoggitworld.com/view/DCS_event_mark_added) DCS>=2.5.1
|
||||
-- @field S_EVENT_MARK CHANGE [https://wiki.hoggitworld.com/view/DCS_event_mark_change](https://wiki.hoggitworld.com/view/DCS_event_mark_change) DCS>=2.5.1
|
||||
-- @field S_EVENT_MARK REMOVE [https://wiki.hoggitworld.com/view/DCS_event_mark_remove](https://wiki.hoggitworld.com/view/DCS_event_mark_remove) DCS>=2.5.1
|
||||
-- @field S_EVENT_KILL [https://wiki.hoggitworld.com/view/DCS_event_kill](https://wiki.hoggitworld.com/view/DCS_event_kill) DCS>=2.5.6
|
||||
-- @field S_EVENT_SCORE [https://wiki.hoggitworld.com/view/DCS_event_score](https://wiki.hoggitworld.com/view/DCS_event_score) DCS>=2.5.6
|
||||
-- @field S_EVENT_UNIT_LOST [https://wiki.hoggitworld.com/view/DCS_event_unit_lost](https://wiki.hoggitworld.com/view/DCS_event_unit_lost) DCS>=2.5.6
|
||||
-- @field S_EVENT_LANDING_AFTER_EJECTION [https://wiki.hoggitworld.com/view/DCS_event_landing_after_ejection](https://wiki.hoggitworld.com/view/DCS_event_landing_after_ejection) DCS>=2.5.6
|
||||
-- @field S_EVENT_MAX
|
||||
|
||||
--- The birthplace enumerator is used to define where an aircraft or helicopter has spawned in association with birth events.
|
||||
@@ -325,9 +330,27 @@ end -- coalition
|
||||
do -- Types
|
||||
|
||||
--- @type Desc
|
||||
-- @field #TypeName typeName type name
|
||||
-- @field #string displayName localized display name
|
||||
-- @field #table attributes object type attributes
|
||||
-- @field #number speedMax0 Max speed in meters/second at zero altitude.
|
||||
-- @field #number massEmpty Empty mass in kg.
|
||||
-- @field #number tankerType Type of refueling system: 0=boom, 1=probe.
|
||||
-- @field #number range Range in km(?).
|
||||
-- @field #table box Bounding box.
|
||||
-- @field #number Hmax Max height in meters.
|
||||
-- @field #number Kmax ?
|
||||
-- @field #number speedMax10K Max speed in meters/second at 10k altitude.
|
||||
-- @field #number NyMin ?
|
||||
-- @field #number NyMax ?
|
||||
-- @field #number fuelMassMax Max fuel mass in kg.
|
||||
-- @field #number speedMax10K Max speed in meters/second.
|
||||
-- @field #number massMax Max mass of unit.
|
||||
-- @field #number RCS ?
|
||||
-- @field #number life Life points.
|
||||
-- @field #number VyMax Max vertical velocity in m/s.
|
||||
-- @field #number Kab ?
|
||||
-- @field #table attributes Table of attributes.
|
||||
-- @field #TypeName typeName Type Name.
|
||||
-- @field #string displayName Localized display name.
|
||||
-- @field #number category Unit category.
|
||||
|
||||
--- A distance type
|
||||
-- @type Distance
|
||||
@@ -427,6 +450,10 @@ do -- Types
|
||||
-- @type TaskArray
|
||||
-- @list <#Task>
|
||||
|
||||
---
|
||||
--@type WaypointAir
|
||||
--@field #boolean lateActivated
|
||||
--@field #boolean uncontrolled
|
||||
|
||||
end --
|
||||
|
||||
@@ -1204,6 +1231,7 @@ do -- AI
|
||||
-- @field TAKEOFF
|
||||
-- @field TAKEOFF_PARKING
|
||||
-- @field TURNING_POINT
|
||||
-- @field TAKEOFF_PARKING_HOT
|
||||
-- @field LAND
|
||||
|
||||
--- @type AI.Task.TurnMethod
|
||||
@@ -1240,8 +1268,8 @@ do -- AI
|
||||
--- @type AI.Option.Naval
|
||||
-- @field #AI.Option.Naval.id id
|
||||
-- @field #AI.Option.Naval.val val
|
||||
|
||||
--TODO: work on formation
|
||||
|
||||
|
||||
--- @type AI.Option.Air.id
|
||||
-- @field NO_OPTION
|
||||
-- @field ROE
|
||||
@@ -1250,7 +1278,34 @@ do -- AI
|
||||
-- @field FLARE_USING
|
||||
-- @field FORMATION
|
||||
-- @field RTB_ON_BINGO
|
||||
-- @field SILENCE
|
||||
-- @field SILENCE
|
||||
-- @field RTB_ON_OUT_OF_AMMO
|
||||
-- @field ECM_USING
|
||||
-- @field PROHIBIT_AA
|
||||
-- @field PROHIBIT_JETT
|
||||
-- @field PROHIBIT_AB
|
||||
-- @field PROHIBIT_AG
|
||||
-- @field MISSILE_ATTACK
|
||||
-- @field PROHIBIT_WP_PASS_REPORT
|
||||
|
||||
--- @type AI.Option.Air.id.FORMATION
|
||||
-- @field LINE_ABREAST
|
||||
-- @field TRAIL
|
||||
-- @field WEDGE
|
||||
-- @field ECHELON_RIGHT
|
||||
-- @field ECHELON_LEFT
|
||||
-- @field FINGER_FOUR
|
||||
-- @field SPREAD_FOUR
|
||||
-- @field WW2_BOMBER_ELEMENT
|
||||
-- @field WW2_BOMBER_ELEMENT_HEIGHT
|
||||
-- @field WW2_FIGHTER_VIC
|
||||
-- @field HEL_WEDGE
|
||||
-- @field HEL_ECHELON
|
||||
-- @field HEL_FRONT
|
||||
-- @field HEL_COLUMN
|
||||
-- @field COMBAT_BOX
|
||||
-- @field JAVELIN_DOWN
|
||||
|
||||
|
||||
--- @type AI.Option.Air.val
|
||||
-- @field #AI.Option.Air.val.ROE ROE
|
||||
@@ -1283,12 +1338,27 @@ do -- AI
|
||||
-- @field AGAINST_FIRED_MISSILE
|
||||
-- @field WHEN_FLYING_IN_SAM_WEZ
|
||||
-- @field WHEN_FLYING_NEAR_ENEMIES
|
||||
|
||||
--- @type AI.Option.Air.val.ECM_USING
|
||||
-- @field NEVER_USE
|
||||
-- @field USE_IF_ONLY_LOCK_BY_RADAR
|
||||
-- @field USE_IF_DETECTED_LOCK_BY_RADAR
|
||||
-- @field ALWAYS_USE
|
||||
|
||||
--- @type AI.Option.Air.val.MISSILE_ATTACK
|
||||
-- @field MAX_RANGE
|
||||
-- @field NEZ_RANGE
|
||||
-- @field HALF_WAY_RMAX_NEZ
|
||||
-- @field TARGET_THREAT_EST
|
||||
-- @field RANDOM_RANGE
|
||||
|
||||
|
||||
--- @type AI.Option.Ground.id
|
||||
-- @field NO_OPTION
|
||||
-- @field ROE @{#AI.Option.Ground.val.ROE}
|
||||
-- @field DISPERSE_ON_ATTACK true or false
|
||||
-- @field ALARM_STATE @{#AI.Option.Ground.val.ALARM_STATE}
|
||||
-- @field ENGAGE_AIR_WEAPONS
|
||||
|
||||
--- @type AI.Option.Ground.val
|
||||
-- @field #AI.Option.Ground.val.ROE ROE
|
||||
|
||||
@@ -1052,7 +1052,6 @@ end
|
||||
-- * `AIRBASE.Nevada.Laughlin_Airport`
|
||||
-- * `AIRBASE.Nevada.Lincoln_County`
|
||||
-- * `AIRBASE.Nevada.McCarran_International_Airport`
|
||||
-- * `AIRBASE.Nevada.Mellan_Airstrip`
|
||||
-- * `AIRBASE.Nevada.Mesquite`
|
||||
-- * `AIRBASE.Nevada.Mina_Airport_3Q0`
|
||||
-- * `AIRBASE.Nevada.Nellis_AFB`
|
||||
@@ -1096,8 +1095,7 @@ end
|
||||
--
|
||||
-- -- Monitor specific airbases.
|
||||
-- ATC_Ground = ATC_GROUND_NEVADA:New(
|
||||
-- { AIRBASE.Nevada.Laughlin_Airport,
|
||||
-- AIRBASE.Nevada.Mellan_Airstrip,
|
||||
-- { AIRBASE.Nevada.Laughlin_Airport,
|
||||
-- AIRBASE.Nevada.Lincoln_County,
|
||||
-- AIRBASE.Nevada.North_Las_Vegas,
|
||||
-- AIRBASE.Nevada.McCarran_International_Airport
|
||||
@@ -1621,6 +1619,13 @@ end
|
||||
-- * `AIRBASE.Normandy.Sainte_Laurent_sur_Mer`
|
||||
-- * `AIRBASE.Normandy.Sommervieu`
|
||||
-- * `AIRBASE.Normandy.Tangmere`
|
||||
-- * `AIRBASE.Normandy.Argentan`
|
||||
-- * `AIRBASE.Normandy.Goulet`
|
||||
-- * `AIRBASE.Normandy.Essay`
|
||||
-- * `AIRBASE.Normandy.Hauterive`
|
||||
-- * `AIRBASE.Normandy.Barville`
|
||||
-- * `AIRBASE.Normandy.Conches`
|
||||
-- * `AIRBASE.Normandy.Vrigny`
|
||||
--
|
||||
-- # Installation
|
||||
--
|
||||
@@ -1834,7 +1839,7 @@ ATC_GROUND_NORMANDY = {
|
||||
},
|
||||
},
|
||||
},
|
||||
[AIRBASE.Normandy.Ford] = {
|
||||
[AIRBASE.Normandy.Ford_AF] = {
|
||||
PointsRunways = {
|
||||
[1] = {
|
||||
[1]={["y"]=-26506.13971428,["x"]=147514.39971429,},
|
||||
@@ -2036,7 +2041,83 @@ ATC_GROUND_NORMANDY = {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
[AIRBASE.Normandy.Argentan] = {
|
||||
PointsRunways = {
|
||||
[1] = {
|
||||
[1]={["y"]=22322.280338032,["x"]=-78607.309765269,},
|
||||
[2]={["y"]=23032.778713963,["x"]=-78967.17709893,},
|
||||
[3]={["y"]=23015.27074041,["x"]=-79008.02903722,},
|
||||
[4]={["y"]=22299.944963827,["x"]=-78650.366148928,},
|
||||
},
|
||||
},
|
||||
},
|
||||
[AIRBASE.Normandy.Goulet] = {
|
||||
PointsRunways = {
|
||||
[1] = {
|
||||
[1]={["y"]=24901.788373185,["x"]=-89139.367511763,},
|
||||
[2]={["y"]=25459.965967043,["x"]=-89709.67940114,},
|
||||
[3]={["y"]=25422.459962713,["x"]=-89741.669816598,},
|
||||
[4]={["y"]=24857.663662208,["x"]=-89173.56416277,},
|
||||
},
|
||||
},
|
||||
},
|
||||
[AIRBASE.Normandy.Essay] = {
|
||||
PointsRunways = {
|
||||
[1] = {
|
||||
[1]={["y"]=44610.072022849,["x"]=-105469.21149064,},
|
||||
[2]={["y"]=45417.939023956,["x"]=-105536.08535277,},
|
||||
[3]={["y"]=45412.558368383,["x"]=-105585.27991801,},
|
||||
[4]={["y"]=44602.38537203,["x"]=-105516.10006064,},
|
||||
},
|
||||
},
|
||||
},
|
||||
[AIRBASE.Normandy.Hauterive] = {
|
||||
PointsRunways = {
|
||||
[1] = {
|
||||
[1]={["y"]=40617.185360953,["x"]=-107657.10147517,},
|
||||
[2]={["y"]=41114.628372034,["x"]=-108298.77015609,},
|
||||
[3]={["y"]=41080.006684855,["x"]=-108319.06562788,},
|
||||
[4]={["y"]=40584.558402807,["x"]=-107692.29370481,},
|
||||
},
|
||||
},
|
||||
},
|
||||
[AIRBASE.Normandy.Vrigny] = {
|
||||
PointsRunways = {
|
||||
[1] = {
|
||||
[1]={["y"]=24892.131051827,["x"]=-89131.628297486,},
|
||||
[2]={["y"]=25469.738000575,["x"]=-89709.235246234,},
|
||||
[3]={["y"]=25418.869206793,["x"]=-89738.771965204,},
|
||||
[4]={["y"]=24859.312475193,["x"]=-89171.010589446,},
|
||||
},
|
||||
},
|
||||
},
|
||||
[AIRBASE.Normandy.Barville] = {
|
||||
PointsRunways = {
|
||||
[1] = {
|
||||
[1]={["y"]=49027.850333166,["x"]=-109217.05049066,},
|
||||
[2]={["y"]=49755.022185805,["x"]=-110346.63783457,},
|
||||
[3]={["y"]=49682.657996586,["x"]=-110401.35222154,},
|
||||
[4]={["y"]=48921.951519675,["x"]=-109285.88471943,},
|
||||
},
|
||||
[2] = {
|
||||
[1]={["y"]=48429.522036941,["x"]=-109818.90874734,},
|
||||
[2]={["y"]=49746.197284681,["x"]=-109954.81222465,},
|
||||
[3]={["y"]=49735.607403332,["x"]=-110032.47135455,},
|
||||
[4]={["y"]=48420.697135816,["x"]=-109900.09783768,},
|
||||
},
|
||||
},
|
||||
},
|
||||
[AIRBASE.Normandy.Conches] = {
|
||||
PointsRunways = {
|
||||
[1] = {
|
||||
[1]={["y"]=95099.187473266,["x"]=-56389.619005858,},
|
||||
[2]={["y"]=95181.545025963,["x"]=-56465.440244849,},
|
||||
[3]={["y"]=94071.678958666,["x"]=-57627.596821795,},
|
||||
[4]={["y"]=94005.008558864,["x"]=-57558.31189651,},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -2477,7 +2558,7 @@ ATC_GROUND_PERSIANGULF = {
|
||||
},
|
||||
},
|
||||
},
|
||||
[AIRBASE.PersianGulf.Abu_Musa_Island_Airport] = {
|
||||
[AIRBASE.PersianGulf.Al_Dhafra_AB] = {
|
||||
PointsRunways = {
|
||||
[1] = {
|
||||
[1]={["y"]=-174672.06004916,["x"]=-209880.97145616,},
|
||||
@@ -3183,4 +3264,4 @@ end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -693,7 +693,7 @@ ARTY.db={
|
||||
|
||||
--- Arty script version.
|
||||
-- @field #string version
|
||||
ARTY.version="1.1.5"
|
||||
ARTY.version="1.1.7"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -2555,6 +2555,13 @@ function ARTY:_OnEventMarkChange(Event)
|
||||
if not _assigned then
|
||||
self:T3(self.lid..string.format("INFO: ARTY group %s was not addressed! Mark text:\n%s", self.groupname, Event.text))
|
||||
return
|
||||
else
|
||||
if self.Controllable and self.Controllable:IsAlive() then
|
||||
|
||||
else
|
||||
self:T3(self.lid..string.format("INFO: ARTY group %s was addressed but is NOT alive! Mark text:\n%s", self.groupname, Event.text))
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- Coordinate was given in text, e.g. as lat, long.
|
||||
@@ -3841,26 +3848,23 @@ end
|
||||
-- @param #boolean final True if it is the final waypoint.
|
||||
function ARTY._PassingWaypoint(group, arty, i, final)
|
||||
|
||||
-- Debug message.
|
||||
local text=string.format("%s, passing waypoint %d.", group:GetName(), i)
|
||||
if final then
|
||||
text=string.format("%s, arrived at destination.", group:GetName())
|
||||
end
|
||||
arty:T(self.lid..text)
|
||||
if group and group:IsAlive() then
|
||||
|
||||
local groupname=tostring(group:GetName())
|
||||
|
||||
--[[
|
||||
if final then
|
||||
MESSAGE:New(text, 10):ToCoalitionIf(group:GetCoalition(), arty.Debug or arty.report)
|
||||
else
|
||||
MESSAGE:New(text, 10):ToAllIf(arty.Debug)
|
||||
-- Debug message.
|
||||
local text=string.format("%s, passing waypoint %d.", groupname, i)
|
||||
if final then
|
||||
text=string.format("%s, arrived at destination.", groupname)
|
||||
end
|
||||
arty:T(arty.lid..text)
|
||||
|
||||
-- Arrived event.
|
||||
if final and arty.groupname==groupname then
|
||||
arty:Arrived()
|
||||
end
|
||||
|
||||
end
|
||||
]]
|
||||
|
||||
-- Arrived event.
|
||||
if final and arty.groupname==group:GetName() then
|
||||
arty:Arrived()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Relocate to another position, e.g. after an engagement to avoid couter strikes.
|
||||
|
||||
@@ -1,54 +1,54 @@
|
||||
--- **Functional** -- Keep airbases clean of crashing or colliding airplanes, and kill missiles when being fired at airbases.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
--
|
||||
--
|
||||
--
|
||||
-- * Try to keep the airbase clean and operational.
|
||||
-- * Prevent airplanes from crashing.
|
||||
-- * Clean up obstructing airplanes from the runway that are standing still for a period of time.
|
||||
-- * Prevent airplanes firing missiles within the airbase zone.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ## Missions:
|
||||
--
|
||||
--
|
||||
-- [CLA - CleanUp Airbase](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CLA%20-%20CleanUp%20Airbase)
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- Specific airbases need to be provided that need to be guarded. Each airbase registered, will be guarded within a zone of 8 km around the airbase.
|
||||
-- Any unit that fires a missile, or shoots within the zone of an airbase, will be monitored by CLEANUP_AIRBASE.
|
||||
-- Within the 8km zone, units cannot fire any missile, which prevents the airbase runway to receive missile or bomb hits.
|
||||
-- Within the 8km zone, units cannot fire any missile, which prevents the airbase runway to receive missile or bomb hits.
|
||||
-- Any airborne or ground unit that is on the runway below 30 meters (default value) will be automatically removed if it is damaged.
|
||||
--
|
||||
--
|
||||
-- This is not a full 100% secure implementation. It is still possible that CLEANUP_AIRBASE cannot prevent (in-time) to keep the airbase clean.
|
||||
-- The following situations may happen that will still stop the runway of an airbase:
|
||||
--
|
||||
--
|
||||
-- * A damaged unit is not removed on time when above the runway, and crashes on the runway.
|
||||
-- * A bomb or missile is still able to dropped on the runway.
|
||||
-- * Units collide on the airbase, and could not be removed on time.
|
||||
--
|
||||
--
|
||||
-- When a unit is within the airbase zone and needs to be monitored,
|
||||
-- its status will be checked every 0.25 seconds! This is required to ensure that the airbase is kept clean.
|
||||
-- But as a result, there is more CPU overload.
|
||||
--
|
||||
--
|
||||
-- So as an advise, I suggest you use the CLEANUP_AIRBASE class with care:
|
||||
--
|
||||
--
|
||||
-- * Only monitor airbases that really need to be monitored!
|
||||
-- * Try not to monitor airbases that are likely to be invaded by enemy troops.
|
||||
-- For these airbases, there is little use to keep them clean, as they will be invaded anyway...
|
||||
--
|
||||
--
|
||||
-- By following the above guidelines, you can add airbase cleanup with acceptable CPU overhead.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- @module Functional.CleanUp
|
||||
-- @image CleanUp_Airbases.JPG
|
||||
|
||||
@@ -60,29 +60,29 @@
|
||||
-- @extends #CLEANUP_AIRBASE.__
|
||||
|
||||
--- Keeps airbases clean, and tries to guarantee continuous airbase operations, even under combat.
|
||||
--
|
||||
--
|
||||
-- # 1. CLEANUP_AIRBASE Constructor
|
||||
--
|
||||
--
|
||||
-- Creates the main object which is preventing the airbase to get polluted with debris on the runway, which halts the airbase.
|
||||
--
|
||||
--
|
||||
-- -- Clean these Zones.
|
||||
-- CleanUpAirports = CLEANUP_AIRBASE:New( { AIRBASE.Caucasus.Tbilisi, AIRBASE.Caucasus.Kutaisi )
|
||||
--
|
||||
-- CleanUpAirports = CLEANUP_AIRBASE:New( { AIRBASE.Caucasus.Tbilisi, AIRBASE.Caucasus.Kutaisi } )
|
||||
--
|
||||
-- -- or
|
||||
-- CleanUpTbilisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Tbilisi )
|
||||
-- CleanUpKutaisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Kutaisi )
|
||||
--
|
||||
--
|
||||
-- # 2. Add or Remove airbases
|
||||
--
|
||||
--
|
||||
-- The method @{#CLEANUP_AIRBASE.AddAirbase}() to add an airbase to the cleanup validation process.
|
||||
-- The method @{#CLEANUP_AIRBASE.RemoveAirbase}() removes an airbase from the cleanup validation process.
|
||||
--
|
||||
--
|
||||
-- # 3. Clean missiles and bombs within the airbase zone.
|
||||
--
|
||||
--
|
||||
-- When missiles or bombs hit the runway, the airbase operations stop.
|
||||
-- Use the method @{#CLEANUP_AIRBASE.SetCleanMissiles}() to control the cleaning of missiles, which will prevent airbases to stop.
|
||||
-- Note that this method will not allow anymore airbases to be attacked, so there is a trade-off here to do.
|
||||
--
|
||||
--
|
||||
-- @field #CLEANUP_AIRBASE
|
||||
CLEANUP_AIRBASE = {
|
||||
ClassName = "CLEANUP_AIRBASE",
|
||||
@@ -106,11 +106,11 @@ CLEANUP_AIRBASE.__.Airbases = {}
|
||||
-- or
|
||||
-- CleanUpTbilisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Tbilisi )
|
||||
-- CleanUpKutaisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Kutaisi )
|
||||
function CLEANUP_AIRBASE:New( AirbaseNames )
|
||||
function CLEANUP_AIRBASE:New( AirbaseNames )
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #CLEANUP_AIRBASE
|
||||
self:F( { AirbaseNames } )
|
||||
|
||||
|
||||
if type( AirbaseNames ) == 'table' then
|
||||
for AirbaseID, AirbaseName in pairs( AirbaseNames ) do
|
||||
self:AddAirbase( AirbaseName )
|
||||
@@ -119,9 +119,9 @@ function CLEANUP_AIRBASE:New( AirbaseNames )
|
||||
local AirbaseName = AirbaseNames
|
||||
self:AddAirbase( AirbaseName )
|
||||
end
|
||||
|
||||
|
||||
self:HandleEvent( EVENTS.Birth, self.__.OnEventBirth )
|
||||
|
||||
|
||||
self.__.CleanUpScheduler = SCHEDULER:New( self, self.__.CleanUpSchedule, {}, 1, self.TimeInterval )
|
||||
|
||||
self:HandleEvent( EVENTS.EngineShutdown , self.__.EventAddForCleanUp )
|
||||
@@ -130,7 +130,7 @@ function CLEANUP_AIRBASE:New( AirbaseNames )
|
||||
self:HandleEvent( EVENTS.PilotDead, self.__.OnEventCrash )
|
||||
self:HandleEvent( EVENTS.Dead, self.__.OnEventCrash )
|
||||
self:HandleEvent( EVENTS.Crash, self.__.OnEventCrash )
|
||||
|
||||
|
||||
for UnitName, Unit in pairs( _DATABASE.UNITS ) do
|
||||
local Unit = Unit -- Wrapper.Unit#UNIT
|
||||
if Unit:IsAlive() ~= nil then
|
||||
@@ -144,7 +144,7 @@ function CLEANUP_AIRBASE:New( AirbaseNames )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -155,7 +155,7 @@ end
|
||||
function CLEANUP_AIRBASE:AddAirbase( AirbaseName )
|
||||
self.__.Airbases[AirbaseName] = AIRBASE:FindByName( AirbaseName )
|
||||
self:F({"Airbase:", AirbaseName, self.__.Airbases[AirbaseName]:GetDesc()})
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -197,7 +197,7 @@ function CLEANUP_AIRBASE.__:IsInAirbase( Vec2 )
|
||||
break;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return InAirbase
|
||||
end
|
||||
|
||||
@@ -233,7 +233,7 @@ end
|
||||
-- @param DCS#Weapon MissileObject
|
||||
function CLEANUP_AIRBASE.__:DestroyMissile( MissileObject )
|
||||
self:F( { MissileObject } )
|
||||
|
||||
|
||||
if MissileObject and MissileObject:isExist() then
|
||||
MissileObject:destroy()
|
||||
self:T( "MissileObject Destroyed")
|
||||
@@ -245,7 +245,7 @@ end
|
||||
function CLEANUP_AIRBASE.__:OnEventBirth( EventData )
|
||||
self:F( { EventData } )
|
||||
|
||||
if EventData.IniUnit:IsAlive() ~= nil then
|
||||
if EventData and EventData.IniUnit and EventData.IniUnit:IsAlive() ~= nil then
|
||||
if self:IsInAirbase( EventData.IniUnit:GetVec2() ) then
|
||||
self.CleanUpList[EventData.IniDCSUnitName] = {}
|
||||
self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnit = EventData.IniUnit
|
||||
@@ -267,7 +267,7 @@ function CLEANUP_AIRBASE.__:OnEventCrash( Event )
|
||||
|
||||
--TODO: DCS BUG - This stuff is not working due to a DCS bug. Burning units cannot be destroyed.
|
||||
-- self:T("before getGroup")
|
||||
-- local _grp = Unit.getGroup(event.initiator)-- Identify the group that fired
|
||||
-- local _grp = Unit.getGroup(event.initiator)-- Identify the group that fired
|
||||
-- self:T("after getGroup")
|
||||
-- _grp:destroy()
|
||||
-- self:T("after deactivateGroup")
|
||||
@@ -280,7 +280,7 @@ function CLEANUP_AIRBASE.__:OnEventCrash( Event )
|
||||
self.CleanUpList[Event.IniDCSUnitName].CleanUpGroupName = Event.IniDCSGroupName
|
||||
self.CleanUpList[Event.IniDCSUnitName].CleanUpUnitName = Event.IniDCSUnitName
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Detects if a unit shoots a missile.
|
||||
@@ -334,16 +334,16 @@ function CLEANUP_AIRBASE.__:AddForCleanUp( CleanUpUnit, CleanUpUnitName )
|
||||
self.CleanUpList[CleanUpUnitName] = {}
|
||||
self.CleanUpList[CleanUpUnitName].CleanUpUnit = CleanUpUnit
|
||||
self.CleanUpList[CleanUpUnitName].CleanUpUnitName = CleanUpUnitName
|
||||
|
||||
|
||||
local CleanUpGroup = CleanUpUnit:GetGroup()
|
||||
|
||||
|
||||
self.CleanUpList[CleanUpUnitName].CleanUpGroup = CleanUpGroup
|
||||
self.CleanUpList[CleanUpUnitName].CleanUpGroupName = CleanUpGroup:GetName()
|
||||
self.CleanUpList[CleanUpUnitName].CleanUpTime = timer.getTime()
|
||||
self.CleanUpList[CleanUpUnitName].CleanUpMoved = false
|
||||
|
||||
self:T( { "CleanUp: Add to CleanUpList: ", CleanUpGroup:GetName(), CleanUpUnitName } )
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Detects if the Unit has an S_EVENT_ENGINE_SHUTDOWN or an S_EVENT_HIT within the given AirbaseNames. If this is the case, add the Group to the CLEANUP_AIRBASE List.
|
||||
@@ -369,7 +369,7 @@ function CLEANUP_AIRBASE.__:EventAddForCleanUp( Event )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -380,26 +380,26 @@ function CLEANUP_AIRBASE.__:CleanUpSchedule()
|
||||
local CleanUpCount = 0
|
||||
for CleanUpUnitName, CleanUpListData in pairs( self.CleanUpList ) do
|
||||
CleanUpCount = CleanUpCount + 1
|
||||
|
||||
|
||||
local CleanUpUnit = CleanUpListData.CleanUpUnit -- Wrapper.Unit#UNIT
|
||||
local CleanUpGroupName = CleanUpListData.CleanUpGroupName
|
||||
|
||||
if CleanUpUnit:IsAlive() ~= nil then
|
||||
|
||||
|
||||
if self:IsInAirbase( CleanUpUnit:GetVec2() ) then
|
||||
|
||||
if _DATABASE:GetStatusGroup( CleanUpGroupName ) ~= "ReSpawn" then
|
||||
|
||||
|
||||
local CleanUpCoordinate = CleanUpUnit:GetCoordinate()
|
||||
|
||||
|
||||
self:T( { "CleanUp Scheduler", CleanUpUnitName } )
|
||||
if CleanUpUnit:GetLife() <= CleanUpUnit:GetLife0() * 0.95 then
|
||||
if CleanUpUnit:IsAboveRunway() then
|
||||
if CleanUpUnit:InAir() then
|
||||
|
||||
|
||||
local CleanUpLandHeight = CleanUpCoordinate:GetLandHeight()
|
||||
local CleanUpUnitHeight = CleanUpCoordinate.y - CleanUpLandHeight
|
||||
|
||||
|
||||
if CleanUpUnitHeight < 100 then
|
||||
self:T( { "CleanUp Scheduler", "Destroy " .. CleanUpUnitName .. " because below safe height and damaged." } )
|
||||
self:DestroyUnit( CleanUpUnit )
|
||||
@@ -439,7 +439,6 @@ function CLEANUP_AIRBASE.__:CleanUpSchedule()
|
||||
end
|
||||
end
|
||||
self:T(CleanUpCount)
|
||||
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
@@ -1438,27 +1438,31 @@ do -- DETECTION_BASE
|
||||
function( PlayerUnitName )
|
||||
local PlayerUnit = UNIT:FindByName( PlayerUnitName )
|
||||
|
||||
if PlayerUnit and PlayerUnit:GetCoordinate():IsInRadius( DetectedUnitCoord, self.FriendliesRange ) then
|
||||
--if PlayerUnit and PlayerUnit:IsInZone(DetectionZone) then
|
||||
-- Fix for issue https://github.com/FlightControl-Master/MOOSE/issues/1225
|
||||
if PlayerUnit and PlayerUnit:IsAlive() then
|
||||
local coord=PlayerUnit:GetCoordinate()
|
||||
|
||||
if coord and coord:IsInRadius( DetectedUnitCoord, self.FriendliesRange ) then
|
||||
|
||||
local PlayerUnitCategory = PlayerUnit:GetDesc().category
|
||||
|
||||
if ( not self.FriendliesCategory ) or ( self.FriendliesCategory and ( self.FriendliesCategory == PlayerUnitCategory ) ) then
|
||||
|
||||
local PlayerUnitName = PlayerUnit:GetName()
|
||||
local PlayerUnitCategory = PlayerUnit:GetDesc().category
|
||||
|
||||
DetectedItem.PlayersNearBy = DetectedItem.PlayersNearBy or {}
|
||||
DetectedItem.PlayersNearBy[PlayerUnitName] = PlayerUnit
|
||||
|
||||
-- Friendlies are sorted per unit category.
|
||||
DetectedItem.FriendliesNearBy = DetectedItem.FriendliesNearBy or {}
|
||||
DetectedItem.FriendliesNearBy[PlayerUnitCategory] = DetectedItem.FriendliesNearBy[PlayerUnitCategory] or {}
|
||||
DetectedItem.FriendliesNearBy[PlayerUnitCategory][PlayerUnitName] = PlayerUnit
|
||||
|
||||
local Distance = DetectedUnitCoord:Get2DDistance( PlayerUnit:GetCoordinate() )
|
||||
DetectedItem.FriendliesDistance = DetectedItem.FriendliesDistance or {}
|
||||
DetectedItem.FriendliesDistance[Distance] = PlayerUnit
|
||||
|
||||
if ( not self.FriendliesCategory ) or ( self.FriendliesCategory and ( self.FriendliesCategory == PlayerUnitCategory ) ) then
|
||||
|
||||
local PlayerUnitName = PlayerUnit:GetName()
|
||||
|
||||
DetectedItem.PlayersNearBy = DetectedItem.PlayersNearBy or {}
|
||||
DetectedItem.PlayersNearBy[PlayerUnitName] = PlayerUnit
|
||||
|
||||
-- Friendlies are sorted per unit category.
|
||||
DetectedItem.FriendliesNearBy = DetectedItem.FriendliesNearBy or {}
|
||||
DetectedItem.FriendliesNearBy[PlayerUnitCategory] = DetectedItem.FriendliesNearBy[PlayerUnitCategory] or {}
|
||||
DetectedItem.FriendliesNearBy[PlayerUnitCategory][PlayerUnitName] = PlayerUnit
|
||||
|
||||
local Distance = DetectedUnitCoord:Get2DDistance( PlayerUnit:GetCoordinate() )
|
||||
DetectedItem.FriendliesDistance = DetectedItem.FriendliesDistance or {}
|
||||
DetectedItem.FriendliesDistance[Distance] = PlayerUnit
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -198,7 +198,7 @@ FOX.MenuF10Root=nil
|
||||
|
||||
--- FOX class version.
|
||||
-- @field #string version
|
||||
FOX.version="0.6.0"
|
||||
FOX.version="0.6.1"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- ToDo list
|
||||
@@ -243,6 +243,7 @@ function FOX:New()
|
||||
self:AddTransition("*", "MissileDestroyed", "*") -- Missile was destroyed before impact.
|
||||
self:AddTransition("*", "EnterSafeZone", "*") -- Player enters a safe zone.
|
||||
self:AddTransition("*", "ExitSafeZone", "*") -- Player exists a safe zone.
|
||||
self:AddTransition("Running", "Stop", "Stopped") -- Stop FOX script.
|
||||
|
||||
------------------------
|
||||
--- Pseudo Functions ---
|
||||
@@ -960,7 +961,7 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
|
||||
end
|
||||
|
||||
if target then
|
||||
self:I(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)))
|
||||
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
|
||||
@@ -1065,7 +1066,7 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
|
||||
else
|
||||
|
||||
-- Destroy missile.
|
||||
self:I(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))
|
||||
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.
|
||||
|
||||
@@ -242,7 +242,7 @@
|
||||
-- * AIRBASE.TerminalType.OpenMed: Open/Shelter air airplane only.
|
||||
-- * AIRBASE.TerminalType.OpenBig: Open air spawn points. Generally larger but does not guarantee large aircraft are capable of spawning there.
|
||||
-- * AIRBASE.TerminalType.OpenMedOrBig: Combines OpenMed and OpenBig spots.
|
||||
-- * AIRBASE.TerminalType.HelicopterUnsable: Combines HelicopterOnly, OpenMed and OpenBig.
|
||||
-- * AIRBASE.TerminalType.HelicopterUsable: Combines HelicopterOnly, OpenMed and OpenBig.
|
||||
-- * AIRBASE.TerminalType.FighterAircraft: Combines Shelter, OpenMed and OpenBig spots. So effectively all spots usable by fixed wing aircraft.
|
||||
--
|
||||
-- So for example
|
||||
@@ -550,7 +550,7 @@ RAT.id="RAT | "
|
||||
--- RAT version.
|
||||
-- @list version
|
||||
RAT.version={
|
||||
version = "2.3.8",
|
||||
version = "2.3.9",
|
||||
print = true,
|
||||
}
|
||||
|
||||
@@ -3416,7 +3416,7 @@ function RAT:_GetAirportsOfCoalition()
|
||||
for _,coalition in pairs(self.ctable) do
|
||||
for _,_airport in pairs(self.airports_map) do
|
||||
local airport=_airport --Wrapper.Airbase#AIRBASE
|
||||
local category=airport:GetDesc().category
|
||||
local category=airport:GetAirbaseCategory()
|
||||
if airport:GetCoalition()==coalition then
|
||||
-- Planes cannot land on FARPs.
|
||||
--local condition1=self.category==RAT.cat.plane and airport:GetTypeName()=="FARP"
|
||||
@@ -3847,7 +3847,7 @@ function RAT:_OnBirth(EventData)
|
||||
|
||||
-- Check if any unit of the group was spawned on top of another unit in the MOOSE data base.
|
||||
local ontop=false
|
||||
if self.checkontop and (_airbase and _airbase:GetDesc().category==Airbase.Category.AIRDROME) then
|
||||
if self.checkontop and (_airbase and _airbase:GetAirbaseCategory()==Airbase.Category.AIRDROME) then
|
||||
ontop=self:_CheckOnTop(SpawnGroup, self.ontopradius)
|
||||
end
|
||||
|
||||
@@ -4457,7 +4457,7 @@ function RAT:_Waypoint(index, description, Type, Coord, Speed, Altitude, Airport
|
||||
|
||||
if (Airport~=nil) and (Type~=RAT.wp.air) then
|
||||
local AirbaseID = Airport:GetID()
|
||||
local AirbaseCategory = Airport:GetDesc().category
|
||||
local AirbaseCategory = Airport:GetAirbaseCategory()
|
||||
if AirbaseCategory == Airbase.Category.SHIP then
|
||||
RoutePoint.linkUnit = AirbaseID
|
||||
RoutePoint.helipadId = AirbaseID
|
||||
@@ -5141,7 +5141,7 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take
|
||||
local spawnonrunway=false
|
||||
local spawnonairport=false
|
||||
if spawnonground then
|
||||
local AirbaseCategory = departure:GetDesc().category
|
||||
local AirbaseCategory = departure:GetAirbaseCategory()
|
||||
if AirbaseCategory == Airbase.Category.SHIP then
|
||||
spawnonship=true
|
||||
elseif AirbaseCategory == Airbase.Category.HELIPAD then
|
||||
@@ -5173,6 +5173,7 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take
|
||||
if self.uncontrolled then
|
||||
-- This is used in the SPAWN:SpawnWithIndex() function. Some values are overwritten there!
|
||||
self.SpawnUnControlled=true
|
||||
SpawnTemplate.uncontrolled=true
|
||||
end
|
||||
|
||||
-- Number of units in the group. With grouping this can actually differ from the template group size!
|
||||
|
||||
@@ -516,7 +516,7 @@ RANGE.MenuF10Root=nil
|
||||
|
||||
--- Range script version.
|
||||
-- @field #string version
|
||||
RANGE.version="2.2.0"
|
||||
RANGE.version="2.2.2"
|
||||
|
||||
--TODO list:
|
||||
--TODO: Verbosity level for messages.
|
||||
@@ -754,7 +754,7 @@ function RANGE:onafterStart()
|
||||
if self.rangecontrolfreq then
|
||||
|
||||
-- Radio queue.
|
||||
self.rangecontrol=RADIOQUEUE:New(self.rangecontrolfreq)
|
||||
self.rangecontrol=RADIOQUEUE:New(self.rangecontrolfreq, nil, self.rangename)
|
||||
|
||||
-- Init numbers.
|
||||
self.rangecontrol:SetDigit(0, RANGE.Sound.RC0.filename, RANGE.Sound.RC0.duration, self.soundpath)
|
||||
@@ -778,7 +778,7 @@ function RANGE:onafterStart()
|
||||
if self.instructorfreq then
|
||||
|
||||
-- Radio queue.
|
||||
self.instructor=RADIOQUEUE:New(self.instructorfreq)
|
||||
self.instructor=RADIOQUEUE:New(self.instructorfreq, nil, self.rangename)
|
||||
|
||||
-- Init numbers.
|
||||
self.instructor:SetDigit(0, RANGE.Sound.IR0.filename, RANGE.Sound.IR0.duration, self.soundpath)
|
||||
@@ -1088,7 +1088,7 @@ end
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetSoundfilesPath(path)
|
||||
self.soundpath=tostring(path or "Range Soundfiles/")
|
||||
self:I(self.lid..string.format("Setting sound files path to %s", self.soundpath))
|
||||
self:I(self.id..string.format("Setting sound files path to %s", self.soundpath))
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1341,9 +1341,9 @@ function RANGE:AddBombingTargetUnit(unit, goodhitrange, randommove)
|
||||
|
||||
-- Debug or error output.
|
||||
if _isstatic==true then
|
||||
self:T(self.id..string.format("Adding STATIC bombing target %s with good hit range %d. Random move = %s.", name, goodhitrange, tostring(randommove)))
|
||||
self:I(self.id..string.format("Adding STATIC bombing target %s with good hit range %d. Random move = %s.", name, goodhitrange, tostring(randommove)))
|
||||
elseif _isstatic==false then
|
||||
self:T(self.id..string.format("Adding UNIT bombing target %s with good hit range %d. Random move = %s.", name, goodhitrange, tostring(randommove)))
|
||||
self:I(self.id..string.format("Adding UNIT bombing target %s with good hit range %d. Random move = %s.", name, goodhitrange, tostring(randommove)))
|
||||
else
|
||||
self:E(self.id..string.format("ERROR! No bombing target with name %s could be found. Carefully check all UNIT and STATIC names defined in the mission editor!", name))
|
||||
end
|
||||
@@ -2521,10 +2521,17 @@ function RANGE:_DisplayBombTargets(_unitname)
|
||||
local coord=self:_GetBombTargetCoordinate(bombtarget)
|
||||
|
||||
if coord then
|
||||
|
||||
-- Get elevation
|
||||
local elevation=coord:GetLandHeight()
|
||||
local eltxt=string.format("%d m", elevation)
|
||||
if _settings:IsImperial() then
|
||||
elevation=UTILS.MetersToFeet(elevation)
|
||||
eltxt=string.format("%d ft", elevation)
|
||||
end
|
||||
|
||||
local ca2g=coord:ToStringA2G(_unit,_settings)
|
||||
--local lldms=coord:ToStringLLDMS(_settings)
|
||||
_text=_text..string.format("\n- %s:\n%s", bombtarget.name or "unknown", ca2g)
|
||||
_text=_text..string.format("\n- %s:\n%s @ %s", bombtarget.name or "unknown", ca2g, eltxt)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2960,10 +2967,10 @@ function RANGE:_AddF10Commands(_unitName)
|
||||
missionCommands.addCommandForGroup(_gid, "Strafe Pits", _infoPath, self._DisplayStrafePits, self, _unitName)
|
||||
end
|
||||
else
|
||||
self:T(self.id.."Could not find group or group ID in AddF10Menu() function. Unit name: ".._unitName)
|
||||
self:E(self.id.."Could not find group or group ID in AddF10Menu() function. Unit name: ".._unitName)
|
||||
end
|
||||
else
|
||||
self:T(self.id.."Player unit does not exist in AddF10Menu() function. Unit name: ".._unitName)
|
||||
self:E(self.id.."Player unit does not exist in AddF10Menu() function. Unit name: ".._unitName)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -39,7 +39,7 @@
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions: **Millertime** - Concept
|
||||
-- ### Contributions: **Millertime** - Concept, **funkyfranky**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -49,6 +49,15 @@
|
||||
do -- ZONE_CAPTURE_COALITION
|
||||
|
||||
--- @type ZONE_CAPTURE_COALITION
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #number MarkBlue ID of blue F10 mark.
|
||||
-- @field #number MarkRed ID of red F10 mark.
|
||||
-- @field #number StartInterval Time in seconds after the status monitor is started.
|
||||
-- @field #number RepeatInterval Time in seconds after which the zone status is updated.
|
||||
-- @field #boolean HitsOn If true, hit events are monitored and trigger the "Attack" event when a defending unit is hit.
|
||||
-- @field #number HitTimeLast Time stamp in seconds when the last unit inside the zone was hit.
|
||||
-- @field #number HitTimeAttackOver Time interval in seconds before the zone goes from "Attacked" to "Guarded" state after the last hit.
|
||||
-- @field #boolean MarkOn If true, create marks of zone status on F10 map.
|
||||
-- @extends Functional.ZoneGoalCoalition#ZONE_GOAL_COALITION
|
||||
|
||||
|
||||
@@ -197,8 +206,7 @@ do -- ZONE_CAPTURE_COALITION
|
||||
--
|
||||
-- ### IMPORTANT
|
||||
--
|
||||
-- **Each capture zone object must have the monitoring process started specifically.
|
||||
-- The monitoring process is NOT started by default!!!**
|
||||
-- **Each capture zone object must have the monitoring process started specifically. The monitoring process is NOT started by default!**
|
||||
--
|
||||
--
|
||||
-- # Full Example
|
||||
@@ -338,29 +346,48 @@ do -- ZONE_CAPTURE_COALITION
|
||||
--
|
||||
-- @field #ZONE_CAPTURE_COALITION
|
||||
ZONE_CAPTURE_COALITION = {
|
||||
ClassName = "ZONE_CAPTURE_COALITION",
|
||||
ClassName = "ZONE_CAPTURE_COALITION",
|
||||
MarkBlue = nil,
|
||||
MarkRed = nil,
|
||||
StartInterval = nil,
|
||||
RepeatInterval = nil,
|
||||
HitsOn = nil,
|
||||
HitTimeLast = nil,
|
||||
HitTimeAttackOver = nil,
|
||||
MarkOn = nil,
|
||||
}
|
||||
|
||||
--- @field #table ZONE_CAPTURE_COALITION.States
|
||||
ZONE_CAPTURE_COALITION.States = {}
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Constructor and Start/Stop Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- ZONE_CAPTURE_COALITION Constructor.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param Core.Zone#ZONE Zone A @{Zone} object with the goal to be achieved.
|
||||
-- @param DCSCoalition.DCSCoalition#coalition Coalition The initial coalition owning the zone.
|
||||
-- @param #table UnitCategories Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}.
|
||||
-- @param #table ObjectCategories Table of unit categories. See [DCS Class Object](https://wiki.hoggitworld.com/view/DCS_Class_Object). Default {Object.Category.UNIT, Object.Category.STATIC}, i.e. all UNITS and STATICS.
|
||||
-- @return #ZONE_CAPTURE_COALITION
|
||||
-- @usage
|
||||
--
|
||||
-- AttackZone = ZONE:New( "AttackZone" )
|
||||
--
|
||||
-- ZoneCaptureCoalition = ZONE_CAPTURE_COALITION:New( AttackZone, coalition.side.RED ) -- Create a new ZONE_CAPTURE_COALITION object of zone AttackZone with ownership RED coalition.
|
||||
-- ZoneCaptureCoalition = ZONE_CAPTURE_COALITION:New( AttackZone, coalition.side.RED, {UNITS ) -- Create a new ZONE_CAPTURE_COALITION object of zone AttackZone with ownership RED coalition.
|
||||
-- ZoneCaptureCoalition:__Guard( 1 ) -- Start the Guarding of the AttackZone.
|
||||
--
|
||||
function ZONE_CAPTURE_COALITION:New( Zone, Coalition, UnitCategories )
|
||||
function ZONE_CAPTURE_COALITION:New( Zone, Coalition, UnitCategories, ObjectCategories )
|
||||
|
||||
local self = BASE:Inherit( self, ZONE_GOAL_COALITION:New( Zone, Coalition, UnitCategories ) ) -- #ZONE_CAPTURE_COALITION
|
||||
|
||||
self:F( { Zone = Zone, Coalition = Coalition, UnitCategories = UnitCategories } )
|
||||
self:F( { Zone = Zone, Coalition = Coalition, UnitCategories = UnitCategories, ObjectCategories = ObjectCategories } )
|
||||
|
||||
self:SetObjectCategories(ObjectCategories)
|
||||
|
||||
-- Default is no smoke.
|
||||
self:SetSmokeZone(false)
|
||||
-- Default is F10 marks ON.
|
||||
self:SetMarkZone(true)
|
||||
-- Start in state "Empty".
|
||||
self:SetStartState("Empty")
|
||||
|
||||
do
|
||||
|
||||
@@ -545,184 +572,12 @@ do -- ZONE_CAPTURE_COALITION
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #number Delay
|
||||
|
||||
-- We check if a unit within the zone is hit.
|
||||
-- If it is, then we must move the zone to attack state.
|
||||
self:HandleEvent( EVENTS.Hit, self.OnEventHit )
|
||||
|
||||
-- ZoneGoal objects are added to the _DATABASE.ZONES_GOAL and SET_ZONE_GOAL sets.
|
||||
_EVENTDISPATCHER:CreateEventNewZoneGoal( self )
|
||||
_EVENTDISPATCHER:CreateEventNewZoneGoal(self)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:onenterCaptured()
|
||||
|
||||
self:F({"hello"})
|
||||
|
||||
self:GetParent( self, ZONE_CAPTURE_COALITION ).onenterCaptured( self )
|
||||
|
||||
self.Goal:Achieved()
|
||||
end
|
||||
|
||||
|
||||
function ZONE_CAPTURE_COALITION:IsGuarded()
|
||||
|
||||
local IsGuarded = self:IsAllInZoneOfCoalition( self.Coalition )
|
||||
self:F( { IsGuarded = IsGuarded } )
|
||||
return IsGuarded
|
||||
end
|
||||
|
||||
|
||||
function ZONE_CAPTURE_COALITION:IsEmpty()
|
||||
|
||||
local IsEmpty = self:IsNoneInZone()
|
||||
self:F( { IsEmpty = IsEmpty } )
|
||||
return IsEmpty
|
||||
end
|
||||
|
||||
|
||||
function ZONE_CAPTURE_COALITION:IsCaptured()
|
||||
|
||||
local IsCaptured = self:IsAllInZoneOfOtherCoalition( self.Coalition )
|
||||
self:F( { IsCaptured = IsCaptured } )
|
||||
return IsCaptured
|
||||
end
|
||||
|
||||
|
||||
function ZONE_CAPTURE_COALITION:IsAttacked()
|
||||
|
||||
local IsAttacked = self:IsSomeInZoneOfCoalition( self.Coalition )
|
||||
self:F( { IsAttacked = IsAttacked } )
|
||||
return IsAttacked
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Mark.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:Mark()
|
||||
|
||||
local Coord = self:GetCoordinate()
|
||||
local ZoneName = self:GetZoneName()
|
||||
local State = self:GetState()
|
||||
|
||||
if self.MarkRed and self.MarkBlue then
|
||||
self:F( { MarkRed = self.MarkRed, MarkBlue = self.MarkBlue } )
|
||||
Coord:RemoveMark( self.MarkRed )
|
||||
Coord:RemoveMark( self.MarkBlue )
|
||||
end
|
||||
|
||||
if self.Coalition == coalition.side.BLUE then
|
||||
self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Blue\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Blue\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
else
|
||||
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Red\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Red\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
end
|
||||
end
|
||||
|
||||
--- Bound.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:onenterGuarded()
|
||||
|
||||
--self:GetParent( self ):onenterGuarded()
|
||||
|
||||
if self.Coalition == coalition.side.BLUE then
|
||||
--elf.ProtectZone:BoundZone( 12, country.id.USA )
|
||||
else
|
||||
--self.ProtectZone:BoundZone( 12, country.id.RUSSIA )
|
||||
end
|
||||
|
||||
self:Mark()
|
||||
|
||||
end
|
||||
|
||||
function ZONE_CAPTURE_COALITION:onenterCaptured()
|
||||
|
||||
--self:GetParent( self ):onenterCaptured()
|
||||
|
||||
local NewCoalition = self:GetScannedCoalition()
|
||||
self:F( { NewCoalition = NewCoalition } )
|
||||
self:SetCoalition( NewCoalition )
|
||||
|
||||
self:Mark()
|
||||
self.Goal:Achieved()
|
||||
end
|
||||
|
||||
|
||||
function ZONE_CAPTURE_COALITION:onenterEmpty()
|
||||
|
||||
--self:GetParent( self ):onenterEmpty()
|
||||
|
||||
self:Mark()
|
||||
end
|
||||
|
||||
|
||||
function ZONE_CAPTURE_COALITION:onenterAttacked()
|
||||
|
||||
--self:GetParent( self ):onenterAttacked()
|
||||
|
||||
self:Mark()
|
||||
end
|
||||
|
||||
|
||||
--- When started, check the Coalition status.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:onafterGuard()
|
||||
|
||||
--self:F({BASE:GetParent( self )})
|
||||
--BASE:GetParent( self ).onafterGuard( self )
|
||||
|
||||
if not self.SmokeScheduler then
|
||||
self.SmokeScheduler = self:ScheduleRepeat( self.StartInterval, self.RepeatInterval, 0.1, nil, self.StatusSmoke, self )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function ZONE_CAPTURE_COALITION:IsCaptured()
|
||||
|
||||
local IsCaptured = self:IsAllInZoneOfOtherCoalition( self.Coalition )
|
||||
self:F( { IsCaptured = IsCaptured } )
|
||||
return IsCaptured
|
||||
end
|
||||
|
||||
|
||||
function ZONE_CAPTURE_COALITION:IsAttacked()
|
||||
|
||||
local IsAttacked = self:IsSomeInZoneOfCoalition( self.Coalition )
|
||||
self:F( { IsAttacked = IsAttacked } )
|
||||
return IsAttacked
|
||||
end
|
||||
|
||||
|
||||
--- Check status Coalition ownership.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:StatusZone()
|
||||
|
||||
local State = self:GetState()
|
||||
self:F( { State = self:GetState() } )
|
||||
|
||||
self:GetParent( self, ZONE_CAPTURE_COALITION ).StatusZone( self )
|
||||
|
||||
if State ~= "Guarded" and self:IsGuarded() then
|
||||
self:Guard()
|
||||
end
|
||||
|
||||
if State ~= "Empty" and self:IsEmpty() then
|
||||
self:Empty()
|
||||
end
|
||||
|
||||
if State ~= "Attacked" and self:IsAttacked() then
|
||||
self:Attack()
|
||||
end
|
||||
|
||||
if State ~= "Captured" and self:IsCaptured() then
|
||||
self:Capture()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Starts the zone capturing monitoring process.
|
||||
-- This process can be CPU intensive, ensure that you specify reasonable time intervals for the monitoring process.
|
||||
@@ -733,6 +588,7 @@ do -- ZONE_CAPTURE_COALITION
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #number StartInterval (optional) Specifies the start time interval in seconds when the zone state will be checked for the first time.
|
||||
-- @param #number RepeatInterval (optional) Specifies the repeat time interval in seconds when the zone state will be checked repeatedly.
|
||||
-- @return #ZONE_CAPTURE_COALITION self
|
||||
-- @usage
|
||||
--
|
||||
-- -- Setup the zone.
|
||||
@@ -747,13 +603,23 @@ do -- ZONE_CAPTURE_COALITION
|
||||
--
|
||||
function ZONE_CAPTURE_COALITION:Start( StartInterval, RepeatInterval )
|
||||
|
||||
self.StartInterval = StartInterval or 15
|
||||
self.StartInterval = StartInterval or 1
|
||||
self.RepeatInterval = RepeatInterval or 15
|
||||
|
||||
if self.ScheduleStatusZone then
|
||||
self:ScheduleStop( self.ScheduleStatusZone )
|
||||
end
|
||||
self.ScheduleStatusZone = self:ScheduleRepeat( self.StartInterval, self.RepeatInterval, 1.5, nil, self.StatusZone, self )
|
||||
|
||||
-- Start Status scheduler.
|
||||
self.ScheduleStatusZone = self:ScheduleRepeat( self.StartInterval, self.RepeatInterval, 0.1, nil, self.StatusZone, self )
|
||||
|
||||
-- We check if a unit within the zone is hit. If it is, then we must move the zone to attack state.
|
||||
self:HandleEvent(EVENTS.Hit, self.OnEventHit)
|
||||
|
||||
-- Create mark on F10 map.
|
||||
self:Mark()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@@ -795,24 +661,281 @@ do -- ZONE_CAPTURE_COALITION
|
||||
function ZONE_CAPTURE_COALITION:Stop()
|
||||
|
||||
if self.ScheduleStatusZone then
|
||||
self:ScheduleStop( self.ScheduleStatusZone )
|
||||
self:ScheduleStop(self.ScheduleStatusZone)
|
||||
end
|
||||
|
||||
if self.SmokeScheduler then
|
||||
self:ScheduleStop(self.SmokeScheduler)
|
||||
end
|
||||
|
||||
self:UnHandleEvent(EVENTS.Hit)
|
||||
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- User API Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Set whether hit events of defending units are monitored and trigger "Attack" events.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #boolean Switch If *true*, hit events are monitored. If *false* or *nil*, hit events are not monitored.
|
||||
-- @param #number TimeAttackOver (Optional) Time in seconds after an attack is over after the last hit and the zone state goes to "Guarded". Default is 300 sec = 5 min.
|
||||
-- @return #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:SetMonitorHits(Switch, TimeAttackOver)
|
||||
self.HitsOn=Switch
|
||||
self.HitTimeAttackOver=TimeAttackOver or 5*60
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set whether marks on the F10 map are shown, which display the current zone status.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #boolean Switch If *true* or *nil*, marks are shown. If *false*, marks are not displayed.
|
||||
-- @return #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:SetMarkZone(Switch)
|
||||
if Switch==nil or Switch==true then
|
||||
self.MarkOn=true
|
||||
else
|
||||
self.MarkOn=false
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- DCS Event Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
--- @param #ZONE_CAPTURE_COALITION self
|
||||
--- Monitor hit events.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param Core.Event#EVENTDATA EventData The event data.
|
||||
function ZONE_CAPTURE_COALITION:OnEventHit( EventData )
|
||||
|
||||
local UnitHit = EventData.TgtUnit
|
||||
|
||||
if UnitHit then
|
||||
if UnitHit:IsInZone( self ) then
|
||||
self:Attack()
|
||||
if self.HitsOn then
|
||||
|
||||
local UnitHit = EventData.TgtUnit
|
||||
|
||||
-- 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
|
||||
|
||||
-- Update last hit time.
|
||||
self.HitTimeLast=timer.getTime()
|
||||
|
||||
-- Only trigger attacked event if not already in state "Attacked".
|
||||
if self:GetState()~="Attacked" then
|
||||
self:F2("Hit ==> Attack")
|
||||
self:Attack()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- FSM Event Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- On after "Guard" event.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:onafterGuard()
|
||||
self:F2("After Guard")
|
||||
|
||||
if self.SmokeZone and not self.SmokeScheduler then
|
||||
self.SmokeScheduler = self:ScheduleRepeat( self.StartInterval, self.RepeatInterval, 0.1, nil, self.StatusSmoke, self )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- On enter "Guarded" state.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:onenterGuarded()
|
||||
self:F2("Enter Guarded")
|
||||
self:Mark()
|
||||
end
|
||||
|
||||
--- On enter "Captured" state.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:onenterCaptured()
|
||||
self:F2("Enter Captured")
|
||||
|
||||
-- Get new coalition.
|
||||
local NewCoalition = self:GetScannedCoalition()
|
||||
self:F( { NewCoalition = NewCoalition } )
|
||||
|
||||
-- Set new owner of zone.
|
||||
self:SetCoalition(NewCoalition)
|
||||
|
||||
-- Update mark.
|
||||
self:Mark()
|
||||
|
||||
-- Goal achieved.
|
||||
self.Goal:Achieved()
|
||||
end
|
||||
|
||||
--- On enter "Empty" state.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:onenterEmpty()
|
||||
self:F2("Enter Empty")
|
||||
self:Mark()
|
||||
end
|
||||
|
||||
--- On enter "Attacked" state.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:onenterAttacked()
|
||||
self:F2("Enter Attacked")
|
||||
self:Mark()
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Status Check Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Check if zone is "Empty".
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @return #boolean self:IsNoneInZone()
|
||||
function ZONE_CAPTURE_COALITION:IsEmpty()
|
||||
|
||||
local IsEmpty = self:IsNoneInZone()
|
||||
self:F( { IsEmpty = IsEmpty } )
|
||||
|
||||
return IsEmpty
|
||||
end
|
||||
|
||||
--- Check if zone is "Guarded", i.e. only one (the defending) coaliton is present inside the zone.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @return #boolean self:IsAllInZoneOfCoalition( self.Coalition )
|
||||
function ZONE_CAPTURE_COALITION:IsGuarded()
|
||||
|
||||
local IsGuarded = self:IsAllInZoneOfCoalition( self.Coalition )
|
||||
self:F( { IsGuarded = IsGuarded } )
|
||||
|
||||
return IsGuarded
|
||||
end
|
||||
|
||||
--- Check if zone is "Captured", i.e. another coalition took control over the zone and is the only one present.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @return #boolean self:IsAllInZoneOfOtherCoalition( self.Coalition )
|
||||
function ZONE_CAPTURE_COALITION:IsCaptured()
|
||||
|
||||
local IsCaptured = self:IsAllInZoneOfOtherCoalition( self.Coalition )
|
||||
self:F( { IsCaptured = IsCaptured } )
|
||||
|
||||
return IsCaptured
|
||||
end
|
||||
|
||||
--- Check if zone is "Attacked", i.e. another coaliton entered the zone.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @return #boolean self:IsSomeInZoneOfCoalition( self.Coalition )
|
||||
function ZONE_CAPTURE_COALITION:IsAttacked()
|
||||
|
||||
local IsAttacked = self:IsSomeInZoneOfCoalition( self.Coalition )
|
||||
self:F( { IsAttacked = IsAttacked } )
|
||||
|
||||
return IsAttacked
|
||||
end
|
||||
|
||||
|
||||
--- Check status Coalition ownership.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:StatusZone()
|
||||
|
||||
-- Get FSM state.
|
||||
local State = self:GetState()
|
||||
|
||||
-- Scan zone in parent class ZONE_GOAL_COALITION
|
||||
self:GetParent( self, ZONE_CAPTURE_COALITION ).StatusZone( self )
|
||||
|
||||
local Tnow=timer.getTime()
|
||||
|
||||
-- Check if zone is guarded.
|
||||
if State ~= "Guarded" and self:IsGuarded() then
|
||||
|
||||
-- Check that there was a sufficient amount of time after the last hit before going back to "Guarded".
|
||||
if self.HitTimeLast==nil or Tnow>=self.HitTimeLast+self.HitTimeAttackOver then
|
||||
self:Guard()
|
||||
self.HitTimeLast=nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if zone is empty.
|
||||
if State ~= "Empty" and self:IsEmpty() then
|
||||
self:Empty()
|
||||
end
|
||||
|
||||
-- Check if zone is attacked.
|
||||
if State ~= "Attacked" and self:IsAttacked() then
|
||||
self:Attack()
|
||||
end
|
||||
|
||||
-- Check if zone is captured.
|
||||
if State ~= "Captured" and self:IsCaptured() then
|
||||
self:Capture()
|
||||
end
|
||||
|
||||
-- Count stuff in zone.
|
||||
local unitset=self:GetScannedSetUnit()
|
||||
local nRed=0
|
||||
local nBlue=0
|
||||
for _,object in pairs(unitset:GetSet()) do
|
||||
local coal=object:GetCoalition()
|
||||
if object:IsAlive() then
|
||||
if coal==coalition.side.RED then
|
||||
nRed=nRed+1
|
||||
elseif coal==coalition.side.BLUE then
|
||||
nBlue=nBlue+1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Status text.
|
||||
local text=string.format("CAPTURE ZONE %s: Owner=%s (Previous=%s): #blue=%d, #red=%d, Status %s", self:GetZoneName(), self:GetCoalitionName(), UTILS.GetCoalitionName(self:GetPreviousCoalition()), nBlue, nRed, State)
|
||||
local NewState = self:GetState()
|
||||
if NewState~=State then
|
||||
text=text..string.format(" --> %s", NewState)
|
||||
end
|
||||
self:I(text)
|
||||
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Misc Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
--- Update Mark on F10 map.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:Mark()
|
||||
|
||||
if self.MarkOn then
|
||||
|
||||
local Coord = self:GetCoordinate()
|
||||
local ZoneName = self:GetZoneName()
|
||||
local State = self:GetState()
|
||||
|
||||
-- Remove marks.
|
||||
if self.MarkRed then
|
||||
Coord:RemoveMark(self.MarkRed)
|
||||
end
|
||||
if self.MarkBlue then
|
||||
Coord:RemoveMark(self.MarkBlue)
|
||||
end
|
||||
|
||||
-- Create new marks for each coaliton.
|
||||
if self.Coalition == coalition.side.BLUE then
|
||||
self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Blue\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Blue\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
elseif self.Coalition == coalition.side.RED then
|
||||
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Red\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Red\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
else
|
||||
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Neutral\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Neutral\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions: **funkyfranky**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -17,10 +18,16 @@
|
||||
do -- Zone
|
||||
|
||||
--- @type ZONE_GOAL
|
||||
-- @extends Core.Fsm#FSM
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field Core.Goal#GOAL Goal The goal object.
|
||||
-- @field #number SmokeTime Time stamp in seconds when the last smoke of the zone was triggered.
|
||||
-- @field Core.Scheduler#SCHEDULER SmokeScheduler Scheduler responsible for smoking the zone.
|
||||
-- @field #number SmokeColor Color of the smoke.
|
||||
-- @field #boolean SmokeZone If true, smoke zone.
|
||||
-- @extends Core.Zone#ZONE_RADIUS
|
||||
|
||||
|
||||
-- Models processes that have a Goal with a defined achievement involving a Zone.
|
||||
--- Models processes that have a Goal with a defined achievement involving a Zone.
|
||||
-- Derived classes implement the ways how the achievements can be realized.
|
||||
--
|
||||
-- ## 1. ZONE_GOAL constructor
|
||||
@@ -39,7 +46,12 @@ do -- Zone
|
||||
--
|
||||
-- @field #ZONE_GOAL
|
||||
ZONE_GOAL = {
|
||||
ClassName = "ZONE_GOAL",
|
||||
ClassName = "ZONE_GOAL",
|
||||
Goal = nil,
|
||||
SmokeTime = nil,
|
||||
SmokeScheduler = nil,
|
||||
SmokeColor = nil,
|
||||
SmokeZone = nil,
|
||||
}
|
||||
|
||||
--- ZONE_GOAL Constructor.
|
||||
@@ -51,11 +63,24 @@ do -- Zone
|
||||
local self = BASE:Inherit( self, ZONE_RADIUS:New( Zone:GetName(), Zone:GetVec2(), Zone:GetRadius() ) ) -- #ZONE_GOAL
|
||||
self:F( { Zone = Zone } )
|
||||
|
||||
-- Goal object.
|
||||
self.Goal = GOAL:New()
|
||||
|
||||
self.SmokeTime = nil
|
||||
|
||||
-- Set smoke ON.
|
||||
self:SetSmokeZone(true)
|
||||
|
||||
self:AddTransition( "*", "DestroyedUnit", "*" )
|
||||
|
||||
--- DestroyedUnit event.
|
||||
-- @function [parent=#ZONE_GOAL] DestroyedUnit
|
||||
-- @param #ZONE_GOAL self
|
||||
|
||||
--- DestroyedUnit delayed event
|
||||
-- @function [parent=#ZONE_GOAL] __DestroyedUnit
|
||||
-- @param #ZONE_GOAL self
|
||||
-- @param #number delay Delay in seconds.
|
||||
|
||||
--- DestroyedUnit Handler OnAfter for ZONE_GOAL
|
||||
-- @function [parent=#ZONE_GOAL] OnAfterDestroyedUnit
|
||||
@@ -66,19 +91,18 @@ do -- Zone
|
||||
-- @param Wrapper.Unit#UNIT DestroyedUnit The destroyed unit.
|
||||
-- @param #string PlayerName The name of the player.
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the Zone
|
||||
--- Get the Zone.
|
||||
-- @param #ZONE_GOAL self
|
||||
-- @return Core.Zone#ZONE_BASE
|
||||
-- @return #ZONE_GOAL
|
||||
function ZONE_GOAL:GetZone()
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Get the name of the ProtectZone
|
||||
--- Get the name of the Zone.
|
||||
-- @param #ZONE_GOAL self
|
||||
-- @return #string
|
||||
function ZONE_GOAL:GetZoneName()
|
||||
@@ -86,36 +110,48 @@ do -- Zone
|
||||
end
|
||||
|
||||
|
||||
--- Smoke the center of theh zone.
|
||||
--- Activate smoking of zone with the color or the current owner.
|
||||
-- @param #ZONE_GOAL self
|
||||
-- @param #SMOKECOLOR.Color SmokeColor
|
||||
function ZONE_GOAL:Smoke( SmokeColor )
|
||||
|
||||
-- @param #boolean switch If *true* or *nil* activate smoke. If *false* or *nil*, no smoke.
|
||||
-- @return #ZONE_GOAL
|
||||
function ZONE_GOAL:SetSmokeZone(switch)
|
||||
self.SmokeZone=switch
|
||||
--[[
|
||||
if switch==nil or switch==true then
|
||||
self.SmokeZone=true
|
||||
else
|
||||
self.SmokeZone=false
|
||||
end
|
||||
]]
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the smoke color.
|
||||
-- @param #ZONE_GOAL self
|
||||
-- @param DCS#SMOKECOLOR.Color SmokeColor
|
||||
function ZONE_GOAL:Smoke( SmokeColor )
|
||||
self:F( { SmokeColor = SmokeColor} )
|
||||
|
||||
self.SmokeColor = SmokeColor
|
||||
end
|
||||
|
||||
|
||||
--- Flare the center of the zone.
|
||||
--- Flare the zone boundary.
|
||||
-- @param #ZONE_GOAL self
|
||||
-- @param #SMOKECOLOR.Color FlareColor
|
||||
-- @param DCS#SMOKECOLOR.Color FlareColor
|
||||
function ZONE_GOAL:Flare( FlareColor )
|
||||
self:FlareZone( FlareColor, math.random( 1, 360 ) )
|
||||
self:FlareZone( FlareColor, 30)
|
||||
end
|
||||
|
||||
|
||||
--- When started, check the Smoke and the Zone status.
|
||||
-- @param #ZONE_GOAL self
|
||||
function ZONE_GOAL:onafterGuard()
|
||||
|
||||
--self:GetParent( self ):onafterStart()
|
||||
|
||||
self:F("Guard")
|
||||
|
||||
--self:ScheduleRepeat( 15, 15, 0.1, nil, self.StatusZone, self )
|
||||
if not self.SmokeScheduler then
|
||||
self.SmokeScheduler = self:ScheduleRepeat( 1, 1, 0.1, nil, self.StatusSmoke, self )
|
||||
|
||||
-- Start smoke
|
||||
if self.SmokeZone and not self.SmokeScheduler then
|
||||
self.SmokeScheduler = self:ScheduleRepeat(1, 1, 0.1, nil, self.StatusSmoke, self)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -123,42 +159,54 @@ do -- Zone
|
||||
--- Check status Smoke.
|
||||
-- @param #ZONE_GOAL self
|
||||
function ZONE_GOAL:StatusSmoke()
|
||||
|
||||
self:F({self.SmokeTime, self.SmokeColor})
|
||||
|
||||
local CurrentTime = timer.getTime()
|
||||
|
||||
if self.SmokeTime == nil or self.SmokeTime + 300 <= CurrentTime then
|
||||
if self.SmokeColor then
|
||||
self:GetCoordinate():Smoke( self.SmokeColor )
|
||||
--self.SmokeColor = nil
|
||||
self.SmokeTime = CurrentTime
|
||||
if self.SmokeZone then
|
||||
|
||||
-- Current time.
|
||||
local CurrentTime = timer.getTime()
|
||||
|
||||
-- Restart smoke every 5 min.
|
||||
if self.SmokeTime == nil or self.SmokeTime + 300 <= CurrentTime then
|
||||
if self.SmokeColor then
|
||||
self:GetCoordinate():Smoke( self.SmokeColor )
|
||||
self.SmokeTime = CurrentTime
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- @param #ZONE_GOAL self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
-- @param Core.Event#EVENTDATA EventData Event data table.
|
||||
function ZONE_GOAL:__Destroyed( EventData )
|
||||
self:F( { "EventDead", EventData } )
|
||||
|
||||
self:F( { EventData.IniUnit } )
|
||||
|
||||
local Vec3 = EventData.IniDCSUnit:getPosition().p
|
||||
self:F( { Vec3 = Vec3 } )
|
||||
|
||||
if EventData.IniDCSUnit then
|
||||
if self:IsVec3InZone(Vec3) then
|
||||
|
||||
local Vec3 = EventData.IniDCSUnit:getPosition().p
|
||||
self:F( { Vec3 = Vec3 } )
|
||||
|
||||
if Vec3 and self:IsVec3InZone(Vec3) then
|
||||
|
||||
local PlayerHits = _DATABASE.HITS[EventData.IniUnitName]
|
||||
|
||||
if PlayerHits then
|
||||
|
||||
for PlayerName, PlayerHit in pairs( PlayerHits.Players or {} ) do
|
||||
self.Goal:AddPlayerContribution( PlayerName )
|
||||
self:DestroyedUnit( EventData.IniUnitName, PlayerName )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -17,6 +17,11 @@
|
||||
do -- ZoneGoal
|
||||
|
||||
--- @type ZONE_GOAL_COALITION
|
||||
-- @field #string ClassName Name of the Class.
|
||||
-- @field #number Coalition The current coalition ID of the zone owner.
|
||||
-- @field #number PreviousCoalition The previous owner of the zone.
|
||||
-- @field #table UnitCategories Table of unit categories that are able to capture and hold the zone. Default is only GROUND units.
|
||||
-- @field #table ObjectCategories Table of object categories that are able to hold a zone. Default is UNITS and STATICS.
|
||||
-- @extends Functional.ZoneGoal#ZONE_GOAL
|
||||
|
||||
|
||||
@@ -37,7 +42,11 @@ do -- ZoneGoal
|
||||
--
|
||||
-- @field #ZONE_GOAL_COALITION
|
||||
ZONE_GOAL_COALITION = {
|
||||
ClassName = "ZONE_GOAL_COALITION",
|
||||
ClassName = "ZONE_GOAL_COALITION",
|
||||
Coalition = nil,
|
||||
PreviousCoaliton = nil,
|
||||
UnitCategories = nil,
|
||||
ObjectCategories = nil,
|
||||
}
|
||||
|
||||
--- @field #table ZONE_GOAL_COALITION.States
|
||||
@@ -46,27 +55,70 @@ do -- ZoneGoal
|
||||
--- ZONE_GOAL_COALITION Constructor.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @param Core.Zone#ZONE Zone A @{Zone} object with the goal to be achieved.
|
||||
-- @param DCSCoalition.DCSCoalition#coalition Coalition The initial coalition owning the zone.
|
||||
-- @param DCSCoalition.DCSCoalition#coalition Coalition The initial coalition owning the zone. Default coalition.side.NEUTRAL.
|
||||
-- @param #table UnitCategories Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}.
|
||||
-- @return #ZONE_GOAL_COALITION
|
||||
function ZONE_GOAL_COALITION:New( Zone, Coalition )
|
||||
function ZONE_GOAL_COALITION:New( Zone, Coalition, UnitCategories )
|
||||
|
||||
if not Zone then
|
||||
BASE:E("ERROR: No Zone specified in ZONE_GOAL_COALITON!")
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Inherit ZONE_GOAL.
|
||||
local self = BASE:Inherit( self, ZONE_GOAL:New( Zone ) ) -- #ZONE_GOAL_COALITION
|
||||
self:F( { Zone = Zone, Coalition = Coalition } )
|
||||
|
||||
self:SetCoalition( Coalition )
|
||||
|
||||
|
||||
-- Set initial owner.
|
||||
self:SetCoalition( Coalition or coalition.side.NEUTRAL)
|
||||
|
||||
-- Set default unit and object categories for the zone scan.
|
||||
self:SetUnitCategories(UnitCategories)
|
||||
self:SetObjectCategories()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set the owning coalition of the zone.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @param DCSCoalition.DCSCoalition#coalition Coalition
|
||||
-- @param DCSCoalition.DCSCoalition#coalition Coalition The coalition ID, e.g. *coalition.side.RED*.
|
||||
-- @return #ZONE_GOAL_COALITION
|
||||
function ZONE_GOAL_COALITION:SetCoalition( Coalition )
|
||||
self.PreviousCoalition=self.Coalition or Coalition
|
||||
self.Coalition = Coalition
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the owning coalition of the zone.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @param #table UnitCategories Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}.
|
||||
-- @return #ZONE_GOAL_COALITION
|
||||
function ZONE_GOAL_COALITION:SetUnitCategories( UnitCategories )
|
||||
|
||||
if UnitCategories and type(UnitCategories)~="table" then
|
||||
UnitCategories={UnitCategories}
|
||||
end
|
||||
|
||||
self.UnitCategories=UnitCategories or {Unit.Category.GROUND_UNIT}
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the owning coalition of the zone.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @param #table ObjectCategories Table of unit categories. See [DCS Class Object](https://wiki.hoggitworld.com/view/DCS_Class_Object). Default {Object.Category.UNIT, Object.Category.STATIC}, i.e. all UNITS and STATICS.
|
||||
-- @return #ZONE_GOAL_COALITION
|
||||
function ZONE_GOAL_COALITION:SetObjectCategories( ObjectCategories )
|
||||
|
||||
if ObjectCategories and type(ObjectCategories)~="table" then
|
||||
ObjectCategories={ObjectCategories}
|
||||
end
|
||||
|
||||
self.ObjectCategories=ObjectCategories or {Object.Category.UNIT, Object.Category.STATIC}
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the owning coalition of the zone.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
@@ -75,37 +127,38 @@ do -- ZoneGoal
|
||||
return self.Coalition
|
||||
end
|
||||
|
||||
--- Get the previous coaliton, i.e. the one owning the zone before the current one.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @return DCSCoalition.DCSCoalition#coalition Coalition.
|
||||
function ZONE_GOAL_COALITION:GetPreviousCoalition()
|
||||
return self.PreviousCoalition
|
||||
end
|
||||
|
||||
|
||||
--- Get the owning coalition name of the zone.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @return #string Coalition name.
|
||||
function ZONE_GOAL_COALITION:GetCoalitionName()
|
||||
|
||||
if self.Coalition == coalition.side.BLUE then
|
||||
return "Blue"
|
||||
end
|
||||
|
||||
if self.Coalition == coalition.side.RED then
|
||||
return "Red"
|
||||
end
|
||||
|
||||
if self.Coalition == coalition.side.NEUTRAL then
|
||||
return "Neutral"
|
||||
end
|
||||
|
||||
return ""
|
||||
return UTILS.GetCoalitionName(self.Coalition)
|
||||
end
|
||||
|
||||
|
||||
--- Check status Coalition ownership.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @return #ZONE_GOAL_COALITION
|
||||
function ZONE_GOAL_COALITION:StatusZone()
|
||||
|
||||
-- Get current state.
|
||||
local State = self:GetState()
|
||||
self:F( { State = self:GetState() } )
|
||||
|
||||
self:Scan( { Object.Category.UNIT, Object.Category.STATIC } )
|
||||
-- Debug text.
|
||||
local text=string.format("Zone state=%s, Owner=%s, Scanning...", State, self:GetCoalitionName())
|
||||
self:F(text)
|
||||
|
||||
-- Scan zone.
|
||||
self:Scan( self.ObjectCategories, self.UnitCategories )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
_EVENTDISPATCHER = EVENT:New() -- Core.Event#EVENT
|
||||
|
||||
--- Declare the timer dispatcher based on the SCHEDULEDISPATCHER class
|
||||
_SCHEDULEDISPATCHER = SCHEDULEDISPATCHER:New() -- Core.Timer#SCHEDULEDISPATCHER
|
||||
_SCHEDULEDISPATCHER = SCHEDULEDISPATCHER:New() -- Core.ScheduleDispatcher#SCHEDULEDISPATCHER
|
||||
|
||||
--- Declare the main database object, which is used internally by the MOOSE classes.
|
||||
_DATABASE = DATABASE:New() -- Core.Database#DATABASE
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
--- **Ops** - (R2.5) - Automatic Terminal Information Service (ATIS).
|
||||
--- **Ops** - Automatic Terminal Information Service (ATIS).
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- **Main Features:**
|
||||
--
|
||||
-- * Wind direction and speed,
|
||||
-- * Visibility,
|
||||
-- * Cloud coverage, base and ceiling,
|
||||
-- * Temprature,
|
||||
-- * Pressure QNH/QFE,
|
||||
-- * Weather phenomena: rain, thunderstorm, fog, dust,
|
||||
-- * Active runway based on wind direction,
|
||||
-- * Tower frequencies,
|
||||
-- * More than 180 voice overs,
|
||||
-- * Airbase names pronounced in locale accent (russian, US, french, arabic),
|
||||
-- * Option to present information in imperial or metric units,
|
||||
-- * Runway length and airfield elevation (optional),
|
||||
-- * Frequencies/channels of nav aids (ILS, VOR, NDB, TACAN, PRMG, RSBN) (optional).
|
||||
-- * Wind direction and speed
|
||||
-- * Visibility
|
||||
-- * Cloud coverage, base and ceiling
|
||||
-- * Temprature
|
||||
-- * Dew point (approximate as there is no relative humidity in DCS yet)
|
||||
-- * Pressure QNH/QFE
|
||||
-- * Weather phenomena: rain, thunderstorm, fog, dust
|
||||
-- * Active runway based on wind direction
|
||||
-- * Tower frequencies
|
||||
-- * More than 180 voice overs
|
||||
-- * Airbase names pronounced in locale accent (russian, US, french, arabic)
|
||||
-- * Option to present information in imperial or metric units
|
||||
-- * Runway length and airfield elevation (optional)
|
||||
-- * Frequencies/channels of nav aids (ILS, VOR, NDB, TACAN, PRMG, RSBN) (optional)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -81,6 +82,10 @@
|
||||
-- @field #table runwaymag Table of magnetic runway headings.
|
||||
-- @field #number runwaym2t Optional correction for magnetic to true runway heading conversion (and vice versa) in degrees.
|
||||
-- @field #boolean windtrue Report true (from) heading of wind. Default is magnetic.
|
||||
-- @field #boolean altimeterQNH Report altimeter QNH.
|
||||
-- @field #boolean usemarker Use mark on the F10 map.
|
||||
-- @field #number markerid Numerical ID of the F10 map mark point.
|
||||
-- @field #number relHumidity Relative humidity (used to approximately calculate the dew point).
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
--- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde
|
||||
@@ -127,6 +132,7 @@
|
||||
-- **Note** that you should use a different relay unit for each ATIS!
|
||||
--
|
||||
-- By default, subtitles are displayed for 10 seconds. This can be changed using @{#ATIS.SetSubtitleDuration}(*duration*) with *duration* being the duration in seconds.
|
||||
-- Setting a *duration* of 0 will completely disable all subtitles.
|
||||
--
|
||||
-- ## Active Runway
|
||||
--
|
||||
@@ -241,6 +247,10 @@
|
||||
--
|
||||
-- **Note** that the default folder name is *ATIS Soundfiles/*. If you want to change it, you can use the @{#ATIS.SetSoundfilesPath}(*path*), where *path* is the path of the directory. This must end with a slash "/"!
|
||||
--
|
||||
-- # Marks on the F10 Map
|
||||
--
|
||||
-- You can place marks on the F10 map via the @{#ATIS.SetMapMarks}() function. These will contain info about the ATIS frequency, the currently active runway and some basic info about the weather (wind, pressure and temperature).
|
||||
--
|
||||
-- # Examples
|
||||
--
|
||||
-- ## Caucasus: Batumi
|
||||
@@ -307,6 +317,10 @@ ATIS = {
|
||||
runwaymag = {},
|
||||
runwaym2t = nil,
|
||||
windtrue = nil,
|
||||
altimeterQNH = nil,
|
||||
usemarker = nil,
|
||||
markerid = nil,
|
||||
relHumidity = nil,
|
||||
}
|
||||
|
||||
--- NATO alphabet.
|
||||
@@ -344,10 +358,10 @@ ATIS.Alphabet = {
|
||||
|
||||
--- Runway correction for converting true to magnetic heading.
|
||||
-- @type ATIS.RunwayM2T
|
||||
-- @field #number Caucasus 0° (East).
|
||||
-- @field #number Nevada +12° (East).
|
||||
-- @field #number Normandy -10° (West).
|
||||
-- @field #number PersianGulf +2° (East).
|
||||
-- @field #number Caucasus 0° (East).
|
||||
-- @field #number Nevada +12° (East).
|
||||
-- @field #number Normandy -10° (West).
|
||||
-- @field #number PersianGulf +2° (East).
|
||||
ATIS.RunwayM2T={
|
||||
Caucasus=0,
|
||||
Nevada=12,
|
||||
@@ -369,6 +383,7 @@ ATIS.RunwayM2T={
|
||||
--- Sound files.
|
||||
-- @type ATIS.Sound
|
||||
-- @field #ATIS.Soundfile ActiveRunway
|
||||
-- @field #ATIS.Soundfile AdviceOnInitial
|
||||
-- @field #ATIS.Soundfile Airport
|
||||
-- @field #ATIS.Soundfile Altimeter
|
||||
-- @field #ATIS.Soundfile At
|
||||
@@ -383,6 +398,7 @@ ATIS.RunwayM2T={
|
||||
-- @field #ATIS.Soundfile Decimal
|
||||
-- @field #ATIS.Soundfile DegreesCelsius
|
||||
-- @field #ATIS.Soundfile DegreesFahrenheit
|
||||
-- @field #ATIS.Soundfile DewPoint
|
||||
-- @field #ATIS.Soundfile Dust
|
||||
-- @field #ATIS.Soundfile Elevation
|
||||
-- @field #ATIS.Soundfile EndOfInformation
|
||||
@@ -437,6 +453,7 @@ ATIS.RunwayM2T={
|
||||
-- @field #ATIS.Soundfile VORFrequency
|
||||
ATIS.Sound = {
|
||||
ActiveRunway={filename="ActiveRunway.ogg", duration=0.99},
|
||||
AdviceOnInitial={filename="AdviceOnInitial.ogg", duration=3.00},
|
||||
Airport={filename="Airport.ogg", duration=0.66},
|
||||
Altimeter={filename="Altimeter.ogg", duration=0.68},
|
||||
At={filename="At.ogg", duration=0.41},
|
||||
@@ -451,6 +468,7 @@ ATIS.Sound = {
|
||||
Decimal={filename="Decimal.ogg", duration=0.54},
|
||||
DegreesCelsius={filename="DegreesCelsius.ogg", duration=1.27},
|
||||
DegreesFahrenheit={filename="DegreesFahrenheit.ogg", duration=1.23},
|
||||
DewPoint={filename="DewPoint.ogg", duration=0.65},
|
||||
Dust={filename="Dust.ogg", duration=0.54},
|
||||
Elevation={filename="Elevation.ogg", duration=0.78},
|
||||
EndOfInformation={filename="EndOfInformation.ogg", duration=1.15},
|
||||
@@ -513,15 +531,19 @@ _ATIS={}
|
||||
|
||||
--- ATIS class version.
|
||||
-- @field #string version
|
||||
ATIS.version="0.5.0"
|
||||
ATIS.version="0.7.1"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: Add stop/pause FMS functions.
|
||||
-- TODO: Add new Normany airfields.
|
||||
-- TODO: Zulu time --> Zulu in output.
|
||||
-- TODO: Correct fog for elevation.
|
||||
-- TODO: Use local time.
|
||||
-- DONE: Add text report for output.
|
||||
-- DONE: Add stop FMS functions.
|
||||
-- NOGO: Use local time. Not realisitc!
|
||||
-- DONE: Dew point. Approx. done.
|
||||
-- DONE: Metric units.
|
||||
-- DONE: Set UTC correction.
|
||||
-- DONE: Set magnetic variation.
|
||||
@@ -534,7 +556,7 @@ ATIS.version="0.5.0"
|
||||
-- @param #ATIS self
|
||||
-- @param #string airbasename Name of the airbase.
|
||||
-- @param #number frequency Radio frequency in MHz. Default 143.00 MHz.
|
||||
-- @param #number modulation 0=AM, 1=FM. Default 0=AM.
|
||||
-- @param #number modulation Radio modulation: 0=AM, 1=FM. Default 0=AM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators
|
||||
-- @return #ATIS self
|
||||
function ATIS:New(airbasename, frequency, modulation)
|
||||
|
||||
@@ -546,6 +568,7 @@ function ATIS:New(airbasename, frequency, modulation)
|
||||
|
||||
if self.airbase==nil then
|
||||
self:E("ERROR: Airbase %s for ATIS could not be found!", tostring(airbasename))
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Default freq and modulation.
|
||||
@@ -557,7 +580,7 @@ function ATIS:New(airbasename, frequency, modulation)
|
||||
|
||||
-- Set some string id for output to DCS.log file.
|
||||
self.lid=string.format("ATIS %s | ", self.airbasename)
|
||||
|
||||
|
||||
-- This is just to hinder the garbage collector deallocating the ATIS object.
|
||||
_ATIS[#_ATIS+1]=self
|
||||
|
||||
@@ -567,6 +590,9 @@ function ATIS:New(airbasename, frequency, modulation)
|
||||
self:SetMagneticDeclination()
|
||||
self:SetRunwayCorrectionMagnetic2True()
|
||||
self:SetRadioPower()
|
||||
self:SetAltimeterQNH(true)
|
||||
self:SetMapMarks(false)
|
||||
self:SetRelativeHumidity()
|
||||
|
||||
-- Start State.
|
||||
self:SetStartState("Stopped")
|
||||
@@ -574,9 +600,11 @@ function ATIS:New(airbasename, frequency, modulation)
|
||||
-- Add FSM transitions.
|
||||
-- From State --> Event --> To State
|
||||
self:AddTransition("Stopped", "Start", "Running") -- Start FSM.
|
||||
self:AddTransition("*", "Status", "*") -- Update status.
|
||||
self:AddTransition("*", "Broadcast", "*") -- Broadcast ATIS message.
|
||||
self:AddTransition("*", "CheckQueue", "*") -- Check if radio queue is empty.
|
||||
self:AddTransition("*", "Status", "*") -- Update status.
|
||||
self:AddTransition("*", "Broadcast", "*") -- Broadcast ATIS message.
|
||||
self:AddTransition("*", "CheckQueue", "*") -- Check if radio queue is empty.
|
||||
self:AddTransition("*", "Report", "*") -- Report ATIS text.
|
||||
self:AddTransition("*", "Stop", "Stopped") -- Stop.
|
||||
|
||||
------------------------
|
||||
--- Pseudo Functions ---
|
||||
@@ -591,7 +619,9 @@ function ATIS:New(airbasename, frequency, modulation)
|
||||
-- @param #ATIS self
|
||||
-- @param #number delay Delay in seconds.
|
||||
|
||||
|
||||
--- Triggers the FSM event "Stop". Stops the ATIS.
|
||||
-- @function [parent=#ATIS] Stop
|
||||
-- @param #ATIS self
|
||||
|
||||
--- Triggers the FSM event "Stop" after a delay.
|
||||
@@ -599,6 +629,57 @@ function ATIS:New(airbasename, frequency, modulation)
|
||||
-- @param #ATIS self
|
||||
-- @param #number delay Delay in seconds.
|
||||
|
||||
|
||||
--- Triggers the FSM event "Status".
|
||||
-- @function [parent=#ATIS] Status
|
||||
-- @param #ATIS self
|
||||
|
||||
--- Triggers the FSM event "Status" after a delay.
|
||||
-- @function [parent=#ATIS] __Status
|
||||
-- @param #ATIS self
|
||||
-- @param #number delay Delay in seconds.
|
||||
|
||||
|
||||
--- Triggers the FSM event "Broadcast".
|
||||
-- @function [parent=#ATIS] Broadcast
|
||||
-- @param #ATIS self
|
||||
|
||||
--- Triggers the FSM event "Broadcast" after a delay.
|
||||
-- @function [parent=#ATIS] __Broadcast
|
||||
-- @param #ATIS self
|
||||
-- @param #number delay Delay in seconds.
|
||||
|
||||
|
||||
--- Triggers the FSM event "CheckQueue".
|
||||
-- @function [parent=#ATIS] CheckQueue
|
||||
-- @param #ATIS self
|
||||
|
||||
--- Triggers the FSM event "CheckQueue" after a delay.
|
||||
-- @function [parent=#ATIS] __CheckQueue
|
||||
-- @param #ATIS self
|
||||
-- @param #number delay Delay in seconds.
|
||||
|
||||
|
||||
--- Triggers the FSM event "Report".
|
||||
-- @function [parent=#ATIS] Report
|
||||
-- @param #ATIS self
|
||||
-- @param #string Text Report text.
|
||||
|
||||
--- Triggers the FSM event "Report" after a delay.
|
||||
-- @function [parent=#ATIS] __Report
|
||||
-- @param #ATIS self
|
||||
-- @param #number delay Delay in seconds.
|
||||
-- @param #string Text Report text.
|
||||
|
||||
--- On after "Report" event user function.
|
||||
-- @function [parent=#ATIS] OnAfterReport
|
||||
-- @param #ATIS self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param #string Text Report text.
|
||||
|
||||
|
||||
-- Debug trace.
|
||||
if false then
|
||||
self.Debug=true
|
||||
@@ -684,9 +765,22 @@ function ATIS:SetRadioPower(power)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set magnetic runway headings as depicted on the runway, *e.g.* "13" for 130° or "25L" for the left runway with magnetic heading 250°.
|
||||
--- Use F10 map mark points.
|
||||
-- @param #ATIS self
|
||||
-- @param #table headings Magnetic headings. Inverse (-180°) headings are added automatically. You only need to specify one heading per runway direction. "L"eft and "R" right can also be appended.
|
||||
-- @param #boolean switch If *true* or *nil*, marks are placed on F10 map. If *false* this feature is set to off (default).
|
||||
-- @return #ATIS self
|
||||
function ATIS:SetMapMarks(switch)
|
||||
if switch==nil or switch==true then
|
||||
self.usemarker=true
|
||||
else
|
||||
self.usemarker=false
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set magnetic runway headings as depicted on the runway, *e.g.* "13" for 130° or "25L" for the left runway with magnetic heading 250°.
|
||||
-- @param #ATIS self
|
||||
-- @param #table headings Magnetic headings. Inverse (-180°) headings are added automatically. You only need to specify one heading per runway direction. "L"eft and "R" right can also be appended.
|
||||
-- @return #ATIS self
|
||||
function ATIS:SetRunwayHeadingsMagnetic(headings)
|
||||
|
||||
@@ -698,25 +792,25 @@ function ATIS:SetRunwayHeadingsMagnetic(headings)
|
||||
end
|
||||
|
||||
for _,heading in pairs(headings) do
|
||||
|
||||
|
||||
if type(heading)=="number" then
|
||||
heading=string.format("%02d", heading)
|
||||
end
|
||||
|
||||
|
||||
-- Add runway heading to table.
|
||||
self:I(self.lid..string.format("Adding user specified magnetic runway heading %s", heading))
|
||||
table.insert(self.runwaymag, heading)
|
||||
|
||||
local h=self:GetRunwayWithoutLR(heading)
|
||||
|
||||
local head2=tonumber(h)-18
|
||||
|
||||
local head2=tonumber(h)-18
|
||||
if head2<0 then
|
||||
head2=head2+36
|
||||
end
|
||||
|
||||
|
||||
-- Convert to string.
|
||||
head2=string.format("%02d", head2)
|
||||
|
||||
|
||||
-- Append "L" or "R" if necessary.
|
||||
local left=self:GetRunwayLR(heading)
|
||||
if left==true then
|
||||
@@ -775,6 +869,31 @@ function ATIS:SetTemperatureFahrenheit()
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set relative humidity. This is used to approximately calculate the dew point.
|
||||
-- Note that the dew point is only an artificial information as DCS does not have an atmospheric model that includes humidity (yet).
|
||||
-- @param #ATIS self
|
||||
-- @param #number Humidity Relative Humidity, i.e. a number between 0 and 100 %. Default is 50 %.
|
||||
-- @return #ATIS self
|
||||
function ATIS:SetRelativeHumidity(Humidity)
|
||||
self.relHumidity=Humidity or 50
|
||||
return self
|
||||
end
|
||||
|
||||
--- Report altimeter QNH.
|
||||
-- @param #ATIS self
|
||||
-- @param #boolean switch If true or nil, report altimeter QHN. If false, report QFF.
|
||||
-- @return #ATIS self
|
||||
function ATIS:SetAltimeterQNH(switch)
|
||||
|
||||
if switch==true or switch==nil then
|
||||
self.altimeterQNH=true
|
||||
else
|
||||
self.altimeterQNH=false
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set magnetic declination/variation at the airport.
|
||||
--
|
||||
-- Default is per map:
|
||||
@@ -786,12 +905,12 @@ end
|
||||
--
|
||||
-- To get *true* from *magnetic* heading one has to add easterly or substract westerly variation, e.g
|
||||
--
|
||||
-- A magnetic heading of 180° corresponds to a true heading of
|
||||
-- A magnetic heading of 180° corresponds to a true heading of
|
||||
--
|
||||
-- * 186° on the Caucaus map
|
||||
-- * 192° on the Nevada map
|
||||
-- * 170° on the Normany map
|
||||
-- * 182° on the Persian Gulf map
|
||||
-- * 186° on the Caucaus map
|
||||
-- * 192° on the Nevada map
|
||||
-- * 170° on the Normany map
|
||||
-- * 182° on the Persian Gulf map
|
||||
--
|
||||
-- Likewise, to convert *magnetic* into *true* heading, one has to substract easterly and add westerly variation.
|
||||
--
|
||||
@@ -935,8 +1054,17 @@ end
|
||||
|
||||
--- Start ATIS FSM.
|
||||
-- @param #ATIS self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
function ATIS:onafterStart(From, Event, To)
|
||||
|
||||
-- Check that this is an airdrome.
|
||||
if self.airbase:GetAirbaseCategory()~=Airbase.Category.AIRDROME then
|
||||
self:E(self.lid..string.format("ERROR: Cannot start ATIS for airbase %s! Only AIRDROMES are supported but NOT FARPS or SHIPS.", self.airbasename))
|
||||
return
|
||||
end
|
||||
|
||||
-- Info.
|
||||
self:I(self.lid..string.format("Starting ATIS v%s for airbase %s on %.3f MHz Modulation=%d", ATIS.version, self.airbasename, self.frequency, self.modulation))
|
||||
|
||||
@@ -948,7 +1076,7 @@ function ATIS:onafterStart(From, Event, To)
|
||||
|
||||
-- Set relay unit if we have one.
|
||||
self.radioqueue:SetSenderUnitName(self.relayunitname)
|
||||
|
||||
|
||||
-- Set radio power.
|
||||
self.radioqueue:SetRadioPower(self.power)
|
||||
|
||||
@@ -974,13 +1102,24 @@ end
|
||||
|
||||
--- Update status.
|
||||
-- @param #ATIS self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
function ATIS:onafterStatus(From, Event, To)
|
||||
|
||||
-- Get FSM state.
|
||||
local fsmstate=self:GetState()
|
||||
|
||||
local relayunitstatus="N/A"
|
||||
if self.relayunitname then
|
||||
local ru=UNIT:FindByName(self.relayunitname)
|
||||
if ru then
|
||||
relayunitstatus=tostring(ru:IsAlive())
|
||||
end
|
||||
end
|
||||
|
||||
-- Info text.
|
||||
local text=string.format("State %s", fsmstate)
|
||||
local text=string.format("State %s: Freq=%.3f MHz %s, Relay unit=%s (alive=%s)", fsmstate, self.frequency, UTILS.GetModulationName(self.modulation), tostring(self.relayunitname), relayunitstatus)
|
||||
self:I(self.lid..text)
|
||||
|
||||
self:__Status(-60)
|
||||
@@ -992,6 +1131,9 @@ end
|
||||
|
||||
--- Check if radio queue is empty. If so, start broadcasting the message again.
|
||||
-- @param #ATIS self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
function ATIS:onafterCheckQueue(From, Event, To)
|
||||
|
||||
if #self.radioqueue.queue==0 then
|
||||
@@ -1007,13 +1149,16 @@ end
|
||||
|
||||
--- Broadcast ATIS radio message.
|
||||
-- @param #ATIS self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
function ATIS:onafterBroadcast(From, Event, To)
|
||||
|
||||
-- Get current coordinate.
|
||||
local coord=self.airbase:GetCoordinate()
|
||||
|
||||
-- Get elevation.
|
||||
local height=coord:GetLandHeight()+10
|
||||
local height=coord:GetLandHeight()
|
||||
|
||||
----------------
|
||||
--- Pressure ---
|
||||
@@ -1023,6 +1168,32 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
local qfe=coord:GetPressure(height)
|
||||
local qnh=coord:GetPressure(0)
|
||||
|
||||
if self.altimeterQNH then
|
||||
|
||||
-- Some constants.
|
||||
local L=-0.0065 --[K/m]
|
||||
local R= 8.31446 --[J/mol/K]
|
||||
local g= 9.80665 --[m/s^2]
|
||||
local M= 0.0289644 --[kg/mol]
|
||||
local T0=coord:GetTemperature(0)+273.15 --[K] Temp at sea level.
|
||||
local TS=288.15 -- Standard Temperature assumed by Altimeter is 15°C
|
||||
local q=qnh*100
|
||||
|
||||
-- Calculate Pressure.
|
||||
local P=q*(1+L*height/T0)^(-g*M/(R*L)) -- Pressure at sea level
|
||||
local Q=P/(1+L*height/TS)^(-g*M/(R*L)) -- Altimeter QNH
|
||||
local A=(T0/L)*((P/q)^(((-R*L)/(g*M)))-1) -- Altitude check
|
||||
|
||||
|
||||
-- Debug aoutput
|
||||
self:T2(self.lid..string.format("height=%.1f, A=%.1f, T0=%.1f, QFE=%.1f, QNH=%.1f, P=%.1f, Q=%.1f hPa = %.2f", height, A, T0-273.15, qfe, qnh, P/100, Q/100, UTILS.hPa2inHg(Q/100)))
|
||||
|
||||
-- Set QNH value in hPa.
|
||||
qnh=Q/100
|
||||
|
||||
end
|
||||
|
||||
|
||||
-- Convert to inHg.
|
||||
if self.PmmHg then
|
||||
qfe=UTILS.hPa2mmHg(qfe)
|
||||
@@ -1052,16 +1223,29 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
------------
|
||||
|
||||
-- Get wind direction and speed in m/s.
|
||||
local windFrom, windSpeed=coord:GetWind(height)
|
||||
local windFrom, windSpeed=coord:GetWind(height+10)
|
||||
|
||||
-- Wind in magnetic or true.
|
||||
local magvar=self.magvar
|
||||
if self.windtrue then
|
||||
magvar=0
|
||||
end
|
||||
windFrom=windFrom-magvar
|
||||
|
||||
-- Correct negative values.
|
||||
if windFrom<0 then
|
||||
windFrom=windFrom+360
|
||||
end
|
||||
|
||||
local WINDFROM=string.format("%03d", windFrom-magvar)
|
||||
local WINDFROM=string.format("%03d", windFrom)
|
||||
local WINDSPEED=string.format("%d", UTILS.MpsToKnots(windSpeed))
|
||||
|
||||
-- Report North as 0.
|
||||
if WINDFROM=="000" then
|
||||
WINDFROM="360"
|
||||
end
|
||||
|
||||
env.info(string.format("FF WINDFROM = %s", tostring(WINDFROM)))
|
||||
|
||||
if self.metric then
|
||||
WINDSPEED=string.format("%d", windSpeed)
|
||||
@@ -1071,27 +1255,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
--- Runway ---
|
||||
--------------
|
||||
|
||||
-- Get active runway data based on wind direction.
|
||||
local runact=self.airbase:GetActiveRunway(self.runwaym2t)
|
||||
|
||||
-- Active runway "31".
|
||||
local runway=self:GetMagneticRunway(windFrom) or runact.idx
|
||||
|
||||
-- Left or right in case there are two runways with the same heading.
|
||||
local rwyLeft=nil
|
||||
|
||||
-- Check if user explicitly specified a runway.
|
||||
if self.activerunway then
|
||||
|
||||
-- Get explicit runway heading if specified.
|
||||
local runwayno=self:GetRunwayWithoutLR(self.activerunway)
|
||||
if runwayno~="" then
|
||||
runway=runwayno
|
||||
end
|
||||
|
||||
-- Was "L"eft or "R"ight given?
|
||||
rwyLeft=self:GetRunwayLR(self.activerunway)
|
||||
end
|
||||
local runway, rwyLeft=self:GetActiveRunway()
|
||||
|
||||
------------
|
||||
--- Time ---
|
||||
@@ -1129,19 +1293,24 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
self:T3(string.format("ZULU =%s", tostring(ZULU)))
|
||||
self:T3(string.format("NATO =%s", tostring(NATO)))
|
||||
|
||||
-------------------
|
||||
--- Temperature ---
|
||||
-------------------
|
||||
---------------------------------
|
||||
--- Temperature and Dew Point ---
|
||||
---------------------------------
|
||||
|
||||
-- Temperature in °C (or °F).
|
||||
local temperature=coord:GetTemperature(height)
|
||||
-- Temperature in °C.
|
||||
local temperature=coord:GetTemperature(height+5)
|
||||
|
||||
-- Dew point in °C.
|
||||
local dewpoint=temperature-(100-self.relHumidity)/5
|
||||
|
||||
-- Convert to °F.
|
||||
-- Convert to °F.
|
||||
if self.TDegF then
|
||||
temperature=UTILS.CelciusToFarenheit(temperature)
|
||||
dewpoint=UTILS.CelciusToFarenheit(dewpoint)
|
||||
end
|
||||
|
||||
local TEMPERATURE=string.format("%d", math.abs(temperature))
|
||||
local DEWPOINT=string.format("%d", math.abs(dewpoint))
|
||||
|
||||
---------------
|
||||
--- Weather ---
|
||||
@@ -1151,12 +1320,12 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
local clouds, visibility, turbulence, fog, dust, static=self:GetMissionWeather()
|
||||
|
||||
-- Check that fog is actually "thick" enough to reach the airport. If an airport is in the mountains, fog might not affect it as it is measured from sea level.
|
||||
if fog and fog.thickness<height then
|
||||
if fog and fog.thickness<height+25 then
|
||||
fog=nil
|
||||
end
|
||||
|
||||
-- Dust only up to 1500 ft = 457 m ASL.
|
||||
if dust and height>UTILS.FeetToMeters(1500) then
|
||||
if dust and height+25>UTILS.FeetToMeters(1500) then
|
||||
dust=nil
|
||||
end
|
||||
|
||||
@@ -1260,16 +1429,20 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
subtitle=subtitle.." Airport"
|
||||
end
|
||||
self.radioqueue:NewTransmission(string.format("%s/%s.ogg", self.theatre, self.airbasename), 3.0, self.soundpath, nil, nil, subtitle, self.subduration)
|
||||
local alltext=subtitle
|
||||
|
||||
-- Information tag
|
||||
subtitle=string.format("Information %s", NATO)
|
||||
local _INFORMATION=subtitle
|
||||
self:Transmission(ATIS.Sound.Information, 0.5, subtitle)
|
||||
self.radioqueue:NewTransmission(string.format("NATO Alphabet/%s.ogg", NATO), 0.75, self.soundpath)
|
||||
alltext=alltext..";\n"..subtitle
|
||||
|
||||
-- Zulu Time
|
||||
subtitle=string.format("%s Zulu Time", ZULU)
|
||||
subtitle=string.format("%s Zulu", ZULU)
|
||||
self.radioqueue:Number2Transmission(ZULU, nil, 0.5)
|
||||
self:Transmission(ATIS.Sound.TimeZulu, 0.2, subtitle)
|
||||
alltext=alltext..";\n"..subtitle
|
||||
|
||||
-- Visibility
|
||||
if self.metric then
|
||||
@@ -1284,6 +1457,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
else
|
||||
self:Transmission(ATIS.Sound.NauticalMiles, 0.2)
|
||||
end
|
||||
alltext=alltext..";\n"..subtitle
|
||||
|
||||
-- Cloud base
|
||||
self:Transmission(CloudCover, 1.0, CLOUDSsub)
|
||||
@@ -1319,6 +1493,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
self:Transmission(ATIS.Sound.Feet, 0.1)
|
||||
end
|
||||
end
|
||||
alltext=alltext..";\n"..subtitle
|
||||
|
||||
-- Weather phenomena
|
||||
local wp=false
|
||||
@@ -1372,6 +1547,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
if dust then
|
||||
self:Transmission(ATIS.Sound.Dust, 0.5)
|
||||
end
|
||||
alltext=alltext..";\n"..subtitle
|
||||
end
|
||||
|
||||
-- Altimeter QNH/QFE.
|
||||
@@ -1384,6 +1560,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
subtitle=string.format("Altimeter QNH %s.%s, QFE %s.%s inHg", QNH[1], QNH[2], QFE[1], QFE[2])
|
||||
end
|
||||
end
|
||||
local _ALTIMETER=subtitle
|
||||
self:Transmission(ATIS.Sound.Altimeter, 1.0, subtitle)
|
||||
self:Transmission(ATIS.Sound.QNH, 0.5)
|
||||
self.radioqueue:Number2Transmission(QNH[1])
|
||||
@@ -1402,6 +1579,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
self:Transmission(ATIS.Sound.InchesOfMercury, 0.1)
|
||||
end
|
||||
end
|
||||
alltext=alltext..";\n"..subtitle
|
||||
|
||||
-- Temperature
|
||||
if self.TDegF then
|
||||
@@ -1417,6 +1595,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
subtitle=string.format("Temperature %s °C", TEMPERATURE)
|
||||
end
|
||||
end
|
||||
local _TEMPERATURE=subtitle
|
||||
self:Transmission(ATIS.Sound.Temperature, 1.0, subtitle)
|
||||
if temperature<0 then
|
||||
self:Transmission(ATIS.Sound.Minus, 0.2)
|
||||
@@ -1427,6 +1606,34 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
else
|
||||
self:Transmission(ATIS.Sound.DegreesCelsius, 0.2)
|
||||
end
|
||||
alltext=alltext..";\n"..subtitle
|
||||
|
||||
-- Dew point
|
||||
if self.TDegF then
|
||||
if dewpoint<0 then
|
||||
subtitle=string.format("Dew point -%s °F", DEWPOINT)
|
||||
else
|
||||
subtitle=string.format("Dew point %s °F", DEWPOINT)
|
||||
end
|
||||
else
|
||||
if dewpoint<0 then
|
||||
subtitle=string.format("Dew point -%s °C", DEWPOINT)
|
||||
else
|
||||
subtitle=string.format("Dew point %s °C", DEWPOINT)
|
||||
end
|
||||
end
|
||||
local _DEWPOINT=subtitle
|
||||
self:Transmission(ATIS.Sound.DewPoint, 1.0, subtitle)
|
||||
if dewpoint<0 then
|
||||
self:Transmission(ATIS.Sound.Minus, 0.2)
|
||||
end
|
||||
self.radioqueue:Number2Transmission(DEWPOINT)
|
||||
if self.TDegF then
|
||||
self:Transmission(ATIS.Sound.DegreesFahrenheit, 0.2)
|
||||
else
|
||||
self:Transmission(ATIS.Sound.DegreesCelsius, 0.2)
|
||||
end
|
||||
alltext=alltext..";\n"..subtitle
|
||||
|
||||
-- Wind
|
||||
if self.metric then
|
||||
@@ -1437,6 +1644,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
if turbulence>0 then
|
||||
subtitle=subtitle..", gusting"
|
||||
end
|
||||
local _WIND=subtitle
|
||||
self:Transmission(ATIS.Sound.WindFrom, 1.0, subtitle)
|
||||
self.radioqueue:Number2Transmission(WINDFROM)
|
||||
self:Transmission(ATIS.Sound.At, 0.2)
|
||||
@@ -1449,6 +1657,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
if turbulence>0 then
|
||||
self:Transmission(ATIS.Sound.Gusting, 0.2)
|
||||
end
|
||||
alltext=alltext..";\n"..subtitle
|
||||
|
||||
-- Active runway.
|
||||
local subtitle=string.format("Active runway %s", runway)
|
||||
@@ -1457,6 +1666,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
elseif rwyLeft==false then
|
||||
subtitle=subtitle.." Right"
|
||||
end
|
||||
local _RUNACT=subtitle
|
||||
self:Transmission(ATIS.Sound.ActiveRunway, 1.0, subtitle)
|
||||
self.radioqueue:Number2Transmission(runway)
|
||||
if rwyLeft==true then
|
||||
@@ -1464,10 +1674,12 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
elseif rwyLeft==false then
|
||||
self:Transmission(ATIS.Sound.Right, 0.2)
|
||||
end
|
||||
alltext=alltext..";\n"..subtitle
|
||||
|
||||
-- Runway length.
|
||||
if self.rwylength then
|
||||
|
||||
local runact=self.airbase:GetActiveRunway(self.runwaym2t)
|
||||
local length=runact.length
|
||||
if not self.metric then
|
||||
length=UTILS.MetersToFeet(length)
|
||||
@@ -1484,7 +1696,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
subtitle=subtitle.." feet"
|
||||
end
|
||||
|
||||
-- Transmitt.
|
||||
-- Transmit.
|
||||
self:Transmission(ATIS.Sound.RunwayLength, 1.0, subtitle)
|
||||
if tonumber(L1000)>0 then
|
||||
self.radioqueue:Number2Transmission(L1000)
|
||||
@@ -1500,8 +1712,9 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
self:Transmission(ATIS.Sound.Feet, 0.1)
|
||||
end
|
||||
|
||||
alltext=alltext..";\n"..subtitle
|
||||
end
|
||||
|
||||
|
||||
-- Airfield elevation
|
||||
if self.elevation then
|
||||
|
||||
@@ -1537,6 +1750,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
self:Transmission(ATIS.Sound.Feet, 0.1)
|
||||
end
|
||||
|
||||
alltext=alltext..";\n"..subtitle
|
||||
end
|
||||
|
||||
-- Tower frequency.
|
||||
@@ -1560,6 +1774,8 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
end
|
||||
self:Transmission(ATIS.Sound.MegaHertz, 0.2)
|
||||
end
|
||||
|
||||
alltext=alltext..";\n"..subtitle
|
||||
end
|
||||
|
||||
-- ILS
|
||||
@@ -1575,6 +1791,8 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
self.radioqueue:Number2Transmission(f[2])
|
||||
end
|
||||
self:Transmission(ATIS.Sound.MegaHertz, 0.2)
|
||||
|
||||
alltext=alltext..";\n"..subtitle
|
||||
end
|
||||
|
||||
-- Outer NDB
|
||||
@@ -1590,6 +1808,8 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
self.radioqueue:Number2Transmission(f[2])
|
||||
end
|
||||
self:Transmission(ATIS.Sound.MegaHertz, 0.2)
|
||||
|
||||
alltext=alltext..";\n"..subtitle
|
||||
end
|
||||
|
||||
-- Inner NDB
|
||||
@@ -1605,6 +1825,8 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
self.radioqueue:Number2Transmission(f[2])
|
||||
end
|
||||
self:Transmission(ATIS.Sound.MegaHertz, 0.2)
|
||||
|
||||
alltext=alltext..";\n"..subtitle
|
||||
end
|
||||
|
||||
-- VOR
|
||||
@@ -1619,6 +1841,8 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
self.radioqueue:Number2Transmission(f[2])
|
||||
end
|
||||
self:Transmission(ATIS.Sound.MegaHertz, 0.2)
|
||||
|
||||
alltext=alltext..";\n"..subtitle
|
||||
end
|
||||
|
||||
-- TACAN
|
||||
@@ -1627,6 +1851,8 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
self:Transmission(ATIS.Sound.TACANChannel, 1.0, subtitle)
|
||||
self.radioqueue:Number2Transmission(tostring(self.tacan), nil, 0.2)
|
||||
self.radioqueue:NewTransmission("NATO Alphabet/Xray.ogg", 0.75, self.soundpath, nil, 0.2)
|
||||
|
||||
alltext=alltext..";\n"..subtitle
|
||||
end
|
||||
|
||||
-- RSBN
|
||||
@@ -1634,6 +1860,8 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
subtitle=string.format("RSBN channel %d", self.rsbn)
|
||||
self:Transmission(ATIS.Sound.RSBNChannel, 1.0, subtitle)
|
||||
self.radioqueue:Number2Transmission(tostring(self.rsbn), nil, 0.2)
|
||||
|
||||
alltext=alltext..";\n"..subtitle
|
||||
end
|
||||
|
||||
-- PRMG
|
||||
@@ -1642,29 +1870,122 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
subtitle=string.format("PRMG channel %d", ndb.frequency)
|
||||
self:Transmission(ATIS.Sound.PRMGChannel, 1.0, subtitle)
|
||||
self.radioqueue:Number2Transmission(tostring(ndb.frequency), nil, 0.5)
|
||||
|
||||
alltext=alltext..";\n"..subtitle
|
||||
end
|
||||
|
||||
-- End of Information Alpha, Bravo, ...
|
||||
|
||||
--[[
|
||||
-- End of Information Alpha, Bravo, ...
|
||||
subtitle=string.format("End of information %s", NATO)
|
||||
self:Transmission(ATIS.Sound.EndOfInformation, 0.5, subtitle)
|
||||
self.radioqueue:NewTransmission(string.format("NATO Alphabet/%s.ogg", NATO), 0.75, self.soundpath)
|
||||
--]]
|
||||
|
||||
-- Advice on initial...
|
||||
subtitle=string.format("Advise on initial contact, you have information %s", NATO)
|
||||
self:Transmission(ATIS.Sound.AdviceOnInitial, 0.5, subtitle)
|
||||
self.radioqueue:NewTransmission(string.format("NATO Alphabet/%s.ogg", NATO), 0.75, self.soundpath)
|
||||
|
||||
alltext=alltext..";\n"..subtitle
|
||||
|
||||
-- Report ATIS text.
|
||||
self:Report(alltext)
|
||||
|
||||
-- Update F10 marker.
|
||||
if self.usemarker then
|
||||
self:UpdateMarker(_INFORMATION, _RUNACT, _WIND, _ALTIMETER, _TEMPERATURE)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Text report of ATIS information. Information delimitor is a semicolon ";" and a line break "\n".
|
||||
-- @param #ATIS self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param #string Text Report text.
|
||||
function ATIS:onafterReport(From, Event, To, Text)
|
||||
self:T(self.lid..string.format("Report:\n%s", Text))
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Misc Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Update F10 map marker.
|
||||
-- @param #ATIS self
|
||||
-- @param #string information Information tag text.
|
||||
-- @param #string runact Active runway text.
|
||||
-- @param #string wind Wind text.
|
||||
-- @param #string altimeter Altimeter text.
|
||||
-- @param #string temperature Temperature text.
|
||||
-- @return #number Marker ID.
|
||||
function ATIS:UpdateMarker(information, runact, wind, altimeter, temperature)
|
||||
|
||||
if self.markerid then
|
||||
self.airbase:GetCoordinate():RemoveMark(self.markerid)
|
||||
end
|
||||
|
||||
local text=string.format("ATIS on %.3f %s, %s:\n", self.frequency, UTILS.GetModulationName(self.modulation), tostring(information))
|
||||
text=text..string.format("%s\n", tostring(runact))
|
||||
text=text..string.format("%s\n", tostring(wind))
|
||||
text=text..string.format("%s\n", tostring(altimeter))
|
||||
text=text..string.format("%s", tostring(temperature))
|
||||
-- More info is not displayed on the marker!
|
||||
|
||||
-- Place new mark
|
||||
self.markerid=self.airbase:GetCoordinate():MarkToAll(text, true)
|
||||
|
||||
return self.markerid
|
||||
end
|
||||
|
||||
--- Get active runway runway.
|
||||
-- @param #ATIS self
|
||||
-- @return #string Active runway, e.g. "31" for 310 deg.
|
||||
-- @return #boolean Use Left=true, Right=false, or nil.
|
||||
function ATIS:GetActiveRunway()
|
||||
|
||||
local coord=self.airbase:GetCoordinate()
|
||||
local height=coord:GetLandHeight()
|
||||
|
||||
-- Get wind direction and speed in m/s.
|
||||
local windFrom, windSpeed=coord:GetWind(height+10)
|
||||
|
||||
-- Get active runway data based on wind direction.
|
||||
local runact=self.airbase:GetActiveRunway(self.runwaym2t)
|
||||
|
||||
-- Active runway "31".
|
||||
local runway=self:GetMagneticRunway(windFrom) or runact.idx
|
||||
|
||||
-- Left or right in case there are two runways with the same heading.
|
||||
local rwyLeft=nil
|
||||
|
||||
-- Check if user explicitly specified a runway.
|
||||
if self.activerunway then
|
||||
|
||||
-- Get explicit runway heading if specified.
|
||||
local runwayno=self:GetRunwayWithoutLR(self.activerunway)
|
||||
if runwayno~="" then
|
||||
runway=runwayno
|
||||
end
|
||||
|
||||
-- Was "L"eft or "R"ight given?
|
||||
rwyLeft=self:GetRunwayLR(self.activerunway)
|
||||
end
|
||||
|
||||
return runway, rwyLeft
|
||||
end
|
||||
|
||||
--- Get runway from user supplied magnetic heading.
|
||||
-- @param #ATIS self
|
||||
-- @param #number windfrom Wind direction (from) in degrees.
|
||||
-- @return #string Runway magnetic heading divided by ten (and rounded). Eg, "13" for 130°.
|
||||
-- @return #string Runway magnetic heading divided by ten (and rounded). Eg, "13" for 130°.
|
||||
function ATIS:GetMagneticRunway(windfrom)
|
||||
|
||||
local diffmin=nil
|
||||
local runway=nil
|
||||
for _,heading in pairs(self.runwaymag) do
|
||||
|
||||
|
||||
local hdg=self:GetRunwayWithoutLR(heading)
|
||||
|
||||
local diff=UTILS.HdgDiff(windfrom, tonumber(hdg)*10)
|
||||
@@ -1689,26 +2010,26 @@ function ATIS:GetNavPoint(navpoints, runway, left)
|
||||
-- Loop over all defined nav aids.
|
||||
for _,_nav in pairs(navpoints or {}) do
|
||||
local nav=_nav --#ATIS.NavPoint
|
||||
|
||||
|
||||
if nav.runway==nil then
|
||||
-- No explicit runway data specified ==> data is valid for all runways.
|
||||
return nav
|
||||
else
|
||||
|
||||
|
||||
local navy=tonumber(self:GetRunwayWithoutLR(nav.runway))*10
|
||||
local rwyy=tonumber(self:GetRunwayWithoutLR(runway))*10
|
||||
|
||||
|
||||
local navL=self:GetRunwayLR(nav.runway)
|
||||
local hdgD=UTILS.HdgDiff(navy,rwyy)
|
||||
|
||||
if hdgD<=15 then --We allow an error of +-15° here.
|
||||
|
||||
if hdgD<=15 then --We allow an error of +-15° here.
|
||||
if navL==nil or (navL==true and left==true) or (navL==false and left==false) then
|
||||
return nav
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -1731,7 +2052,7 @@ function ATIS:GetRunwayLR(runway)
|
||||
-- Get left/right if specified.
|
||||
local rwyL=runway:lower():find("l")
|
||||
local rwyR=runway:lower():find("r")
|
||||
|
||||
|
||||
if rwyL then
|
||||
return true
|
||||
elseif rwyR then
|
||||
@@ -1739,7 +2060,7 @@ function ATIS:GetRunwayLR(runway)
|
||||
else
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Transmission via RADIOQUEUE.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- **Ops** - (R2.5) - Manages aircraft CASE X recoveries for carrier operations (X=I, II, III).
|
||||
--- **Ops** - Manages aircraft CASE X recoveries for carrier operations (X=I, II, III).
|
||||
--
|
||||
-- The AIRBOSS class manages recoveries of human pilots and AI aircraft on aircraft carriers.
|
||||
--
|
||||
@@ -39,6 +39,7 @@
|
||||
-- * F-14A Tomcat (AI)
|
||||
-- * E-2D Hawkeye (AI)
|
||||
-- * S-3B Viking & tanker version (AI)
|
||||
-- * [C-2A Greyhound](https://forums.eagle.ru/showthread.php?t=255641) (AI)
|
||||
--
|
||||
-- At the moment, optimized parameters are available for the F/A-18C Hornet (Lot 20) and A-4E community mod as aircraft and the USS John C. Stennis as carrier.
|
||||
--
|
||||
@@ -1255,6 +1256,7 @@ AIRBOSS = {
|
||||
-- @field #string S3B Lockheed S-3B Viking.
|
||||
-- @field #string S3BTANKER Lockheed S-3B Viking tanker.
|
||||
-- @field #string E2D Grumman E-2D Hawkeye AWACS.
|
||||
-- @field #string C2A Grumman C-2A Greyhound from Military Aircraft Mod.
|
||||
AIRBOSS.AircraftCarrier={
|
||||
AV8B="AV8BNA",
|
||||
HORNET="FA-18C_hornet",
|
||||
@@ -1266,15 +1268,22 @@ AIRBOSS.AircraftCarrier={
|
||||
S3B="S-3B",
|
||||
S3BTANKER="S-3B Tanker",
|
||||
E2D="E-2C",
|
||||
C2A="C2A_Greyhound",
|
||||
}
|
||||
|
||||
--- Carrier types.
|
||||
-- @type AIRBOSS.CarrierType
|
||||
-- @field #string ROOSEVELT USS Theodore Roosevelt (CVN-71)
|
||||
-- @field #string LINCOLN USS Abraham Lincoln (CVN-72)
|
||||
-- @field #string WASHINGTON USS George Washington (CVN-73)
|
||||
-- @field #string STENNIS USS John C. Stennis (CVN-74)
|
||||
-- @field #string VINSON USS Carl Vinson (CVN-70)
|
||||
-- @field #string TARAWA USS Tarawa (LHA-1)
|
||||
-- @field #string KUZNETSOV Admiral Kuznetsov (CV 1143.5)
|
||||
AIRBOSS.CarrierType={
|
||||
ROOSEVELT="CVN_71",
|
||||
LINCOLN="CVN_72",
|
||||
WASHINGTON="CVN_73",
|
||||
STENNIS="Stennis",
|
||||
VINSON="VINSON",
|
||||
TARAWA="LHA_Tarawa",
|
||||
@@ -1688,7 +1697,7 @@ AIRBOSS.MenuF10Root=nil
|
||||
|
||||
--- Airboss class version.
|
||||
-- @field #string version
|
||||
AIRBOSS.version="1.0.9"
|
||||
AIRBOSS.version="1.1.3"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -1877,6 +1886,9 @@ function AIRBOSS:New(carriername, alias)
|
||||
-- Default recovery case. This sets self.defaultcase and self.case. Default Case I.
|
||||
self:SetRecoveryCase()
|
||||
|
||||
-- Set time the turn starts before the window opens.
|
||||
self:SetRecoveryTurnTime()
|
||||
|
||||
-- Set holding offset to 0 degrees. This set self.defaultoffset and self.holdingoffset.
|
||||
self:SetHoldingOffsetAngle()
|
||||
|
||||
@@ -1923,6 +1935,12 @@ function AIRBOSS:New(carriername, alias)
|
||||
-- Init carrier parameters.
|
||||
if self.carriertype==AIRBOSS.CarrierType.STENNIS then
|
||||
self:_InitStennis()
|
||||
elseif self.carriertype==AIRBOSS.CarrierType.ROOSEVELT then
|
||||
self:_InitStennis()
|
||||
elseif self.carriertype==AIRBOSS.CarrierType.LINCOLN then
|
||||
self:_InitStennis()
|
||||
elseif self.carriertype==AIRBOSS.CarrierType.WASHINGTON then
|
||||
self:_InitStennis()
|
||||
elseif self.carriertype==AIRBOSS.CarrierType.VINSON then
|
||||
-- TODO: Carl Vinson parameters.
|
||||
self:_InitStennis()
|
||||
@@ -2078,6 +2096,7 @@ function AIRBOSS:New(carriername, alias)
|
||||
self:AddTransition("*", "RecoveryCase", "*") -- Switch to another case recovery.
|
||||
self:AddTransition("*", "PassingWaypoint", "*") -- Carrier is passing a waypoint.
|
||||
self:AddTransition("*", "LSOGrade", "*") -- LSO grade.
|
||||
self:AddTransition("*", "Marshal", "*") -- A flight was send into the marshal stack.
|
||||
self:AddTransition("*", "Save", "*") -- Save player scores to file.
|
||||
self:AddTransition("*", "Stop", "Stopped") -- Stop AIRBOSS FMS.
|
||||
|
||||
@@ -2251,29 +2270,6 @@ function AIRBOSS:New(carriername, alias)
|
||||
-- @param #string filename (Optional) File name. Default is AIRBOSS-*ALIAS*_LSOgrades.csv.
|
||||
|
||||
|
||||
--- Triggers the FSM event "LSOgrade". Called when the LSO grades a player
|
||||
-- @function [parent=#AIRBOSS] LSOgrade
|
||||
-- @param #AIRBOSS self
|
||||
-- @param #AIRBOSS.PlayerData playerData Player Data.
|
||||
-- @param #AIRBOSS.LSOgrade grade LSO grade.
|
||||
|
||||
--- Triggers the FSM event "LSOgrade". Delayed called when the LSO grades a player.
|
||||
-- @function [parent=#AIRBOSS] __LSOgrade
|
||||
-- @param #AIRBOSS self
|
||||
-- @param #number delay Delay in seconds.
|
||||
-- @param #AIRBOSS.PlayerData playerData Player Data.
|
||||
-- @param #AIRBOSS.LSOgrade grade LSO grade.
|
||||
|
||||
--- On after "LSOgrade" user function. Called when the carrier passes a waypoint of its route.
|
||||
-- @function [parent=#AIRBOSS] OnAfterLSOgrade
|
||||
-- @param #AIRBOSS self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param #AIRBOSS.PlayerData playerData Player Data.
|
||||
-- @param #AIRBOSS.LSOgrade grade LSO grade.
|
||||
|
||||
|
||||
--- Triggers the FSM event "LSOGrade". Called when the LSO grades a player
|
||||
-- @function [parent=#AIRBOSS] LSOGrade
|
||||
-- @param #AIRBOSS self
|
||||
@@ -2297,27 +2293,24 @@ function AIRBOSS:New(carriername, alias)
|
||||
-- @param #AIRBOSS.LSOgrade grade LSO grade.
|
||||
|
||||
|
||||
--- Triggers the FSM event "LSOGrade". Called when the LSO grades a player
|
||||
-- @function [parent=#AIRBOSS] LSOGrade
|
||||
--- Triggers the FSM event "Marshal". Called when a flight is send to the Marshal stack.
|
||||
-- @function [parent=#AIRBOSS] Marshal
|
||||
-- @param #AIRBOSS self
|
||||
-- @param #AIRBOSS.PlayerData playerData Player Data.
|
||||
-- @param #AIRBOSS.LSOgrade grade LSO grade.
|
||||
-- @param #AIRBOSS.FlightGroup flight The flight group data.
|
||||
|
||||
--- Triggers the FSM event "LSOGrade". Delayed called when the LSO grades a player.
|
||||
-- @function [parent=#AIRBOSS] __LSOGrade
|
||||
--- Triggers the FSM event "Marshal". Delayed call when a flight is send to the Marshal stack.
|
||||
-- @function [parent=#AIRBOSS] __Marshal
|
||||
-- @param #AIRBOSS self
|
||||
-- @param #number delay Delay in seconds.
|
||||
-- @param #AIRBOSS.PlayerData playerData Player Data.
|
||||
-- @param #AIRBOSS.LSOgrade grade LSO grade.
|
||||
-- @param #AIRBOSS.FlightGroup flight The flight group data.
|
||||
|
||||
--- On after "LSOGrade" user function. Called when the carrier passes a waypoint of its route.
|
||||
-- @function [parent=#AIRBOSS] OnAfterLSOGrade
|
||||
--- On after "Marshal" user function. Called when a flight is send to the Marshal stack.
|
||||
-- @function [parent=#AIRBOSS] OnAfterMarshal
|
||||
-- @param #AIRBOSS self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param #AIRBOSS.PlayerData playerData Player Data.
|
||||
-- @param #AIRBOSS.LSOgrade grade LSO grade.
|
||||
-- @param #AIRBOSS.FlightGroup flight The flight group data.
|
||||
|
||||
|
||||
--- Triggers the FSM event "Stop" that stops the airboss. Event handlers are stopped.
|
||||
@@ -2455,6 +2448,15 @@ function AIRBOSS:AddRecoveryWindow(starttime, stoptime, case, holdingoffset, tur
|
||||
-- Absolute mission time in seconds.
|
||||
local Tnow=timer.getAbsTime()
|
||||
|
||||
if starttime and type(starttime)=="number" then
|
||||
starttime=UTILS.SecondsToClock(Tnow+starttime)
|
||||
end
|
||||
|
||||
if stoptime and type(stoptime)=="number" then
|
||||
stoptime=UTILS.SecondsToClock(Tnow+stoptime)
|
||||
end
|
||||
|
||||
|
||||
-- Input or now.
|
||||
starttime=starttime or UTILS.SecondsToClock(Tnow)
|
||||
|
||||
@@ -2620,6 +2622,15 @@ function AIRBOSS:DeleteRecoveryWindow(window, delay)
|
||||
end
|
||||
end
|
||||
|
||||
--- Set time before carrier turns and recovery window opens.
|
||||
-- @param #AIRBOSS self
|
||||
-- @param #number interval Time interval in seconds. Default 600 sec.
|
||||
-- @return #AIRBOSS self
|
||||
function AIRBOSS:SetRecoveryTurnTime(interval)
|
||||
self.dTturn=interval or 600
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set time interval for updating queues and other stuff.
|
||||
-- @param #AIRBOSS self
|
||||
-- @param #number interval Time interval in seconds. Default 30 sec.
|
||||
@@ -3248,6 +3259,27 @@ function AIRBOSS:SetDebugModeOFF()
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get next time the carrier will start recovering aircraft.
|
||||
-- @param #AIRBOSS self
|
||||
-- @param #boolean InSeconds If true, abs. mission time seconds is returned. Default is a clock #string.
|
||||
-- @return #string Clock start (or start time in abs. seconds).
|
||||
-- @return #string Clock stop (or stop time in abs. seconds).
|
||||
function AIRBOSS:GetNextRecoveryTime(InSeconds)
|
||||
if self.recoverywindow then
|
||||
if InSeconds then
|
||||
return self.recoverywindow.START, self.recoverywindow.STOP
|
||||
else
|
||||
return UTILS.SecondsToClock(self.recoverywindow.START), UTILS.SecondsToClock(self.recoverywindow.STOP)
|
||||
end
|
||||
else
|
||||
if InSeconds then
|
||||
return -1, -1
|
||||
else
|
||||
return "?", "?"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Check if carrier is recovering aircraft.
|
||||
-- @param #AIRBOSS self
|
||||
-- @return #boolean If true, time slot for recovery is open.
|
||||
@@ -3804,7 +3836,7 @@ function AIRBOSS:_CheckRecoveryTimes()
|
||||
self:RecoveryCase(nextwindow.CASE, nextwindow.OFFSET)
|
||||
|
||||
-- Check if time is less than 5 minutes.
|
||||
if nextwindow.WIND and nextwindow.START-time<5*60 and not self.turnintowind then
|
||||
if nextwindow.WIND and nextwindow.START-time<self.dTturn and not self.turnintowind then
|
||||
|
||||
-- Check that wind is blowing from a direction > 5° different from the current heading.
|
||||
local hdg=self:GetHeading()
|
||||
@@ -3827,7 +3859,7 @@ function AIRBOSS:_CheckRecoveryTimes()
|
||||
self:T(self.lid..string.format("Heading=%03d°, Wind=%03d° %.1f kts, Delta=%03d° ==> U-turn=%s", hdg, wind,UTILS.MpsToKnots(vwind), delta, tostring(uturn)))
|
||||
|
||||
-- Time into the wind 1 day or if longer recovery time + the 5 min early.
|
||||
local t=math.max(nextwindow.STOP-nextwindow.START+300, 60*60*24)
|
||||
local t=math.max(nextwindow.STOP-nextwindow.START+self.dTturn, 60*60*24)
|
||||
|
||||
-- Recovery wind on deck in knots.
|
||||
local v=UTILS.KnotsToMps(nextwindow.SPEED)
|
||||
@@ -5986,7 +6018,6 @@ function AIRBOSS:_ScanCarrierZone()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Find new flights that are inside CCA.
|
||||
for groupname,_group in pairs(insideCCA) do
|
||||
local group=_group --Wrapper.Group#GROUP
|
||||
@@ -6092,7 +6123,6 @@ function AIRBOSS:_ScanCarrierZone()
|
||||
|
||||
end
|
||||
|
||||
|
||||
-- Find flights that are not in CCA.
|
||||
local remove={}
|
||||
for _,_flight in pairs(self.flights) do
|
||||
@@ -6184,6 +6214,9 @@ function AIRBOSS:_MarshalPlayer(playerData, stack)
|
||||
|
||||
-- Set stack flag.
|
||||
flight.flag=stack
|
||||
|
||||
-- Trigger Marshal event.
|
||||
self:Marshal(flight)
|
||||
end
|
||||
|
||||
else
|
||||
@@ -6440,6 +6473,9 @@ function AIRBOSS:_MarshalAI(flight, nstack, respawn)
|
||||
|
||||
-- Route group.
|
||||
flight.group:Route(wp, 1)
|
||||
|
||||
-- Trigger Marshal event.
|
||||
self:Marshal(flight)
|
||||
|
||||
end
|
||||
|
||||
@@ -10785,53 +10821,6 @@ function AIRBOSS:_GetZoneCorridor(case, l)
|
||||
local zone=ZONE_POLYGON_BASE:New("CASE II/III Approach Corridor", p)
|
||||
|
||||
return zone
|
||||
|
||||
--[[
|
||||
-- OLD
|
||||
|
||||
-- Angle between radial and offset in rad.
|
||||
local alpha=math.rad(self.holdingoffset)
|
||||
|
||||
-- Some math...
|
||||
local y1=d-w2
|
||||
local x1=y1*math.tan(alpha)
|
||||
local y2=d+w2
|
||||
local x2=y2*math.tan(alpha)
|
||||
local b=w2*(1/math.cos(alpha)-1)
|
||||
|
||||
-- This is what we need.
|
||||
local P=x1+b
|
||||
local Q=x2-b
|
||||
|
||||
-- Debug output.
|
||||
self:T3(string.format("FF case %d radial = %d", case, radial))
|
||||
self:T3(string.format("FF case %d offset = %d", case, offset))
|
||||
self:T3(string.format("FF w = %.1f NM", w))
|
||||
self:T3(string.format("FF l = %.1f NM", l))
|
||||
self:T3(string.format("FF d = %.1f NM", d))
|
||||
self:T3(string.format("FF y1 = %.1f NM", y1))
|
||||
self:T3(string.format("FF x1 = %.1f NM", x1))
|
||||
self:T3(string.format("FF y2 = %.1f NM", y2))
|
||||
self:T3(string.format("FF x2 = %.1f NM", x2))
|
||||
self:T3(string.format("FF b = %.1f NM", b))
|
||||
self:T3(string.format("FF P = %.1f NM", P))
|
||||
self:T3(string.format("FF Q = %.1f NM", Q))
|
||||
|
||||
-- Complicated case with an angle.
|
||||
c[2]=c[1]:Translate( UTILS.NMToMeters(w2), radial-90) -- 1 Right of carrier.
|
||||
c[3]=c[2]:Translate( UTILS.NMToMeters(d+dx+w2), radial) -- 13 "south" @ 1 right
|
||||
c[4]=c[3]:Translate( UTILS.NMToMeters(Q), radial+90) --
|
||||
c[5]=c[4]:Translate( UTILS.NMToMeters(l), offset)
|
||||
c[6]=c[5]:Translate( UTILS.NMToMeters(w), offset+90) -- Back wall (angled)
|
||||
c[9]=c[1]:Translate( UTILS.NMToMeters(w2), radial+90) -- 1 left of carrier.
|
||||
c[8]=c[9]:Translate( UTILS.NMToMeters(d+dx-w2), radial) -- 1 left and 11 behind of carrier.
|
||||
c[7]=c[8]:Translate( UTILS.NMToMeters(P), radial+90)
|
||||
|
||||
-- Translate these points a bit for a smoother turn.
|
||||
--c[4]=c[4]:Translate(UTILS.NMToMeters(2), offset)
|
||||
--c[7]=c[7]:Translate(UTILS.NMToMeters(2), offset)
|
||||
]]
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -11330,20 +11319,6 @@ function AIRBOSS:_Lineup(unit, runway)
|
||||
---
|
||||
local lineup=math.deg(math.atan2(z, x))
|
||||
|
||||
--[[
|
||||
-- Position of the aircraft in the new coordinate system.
|
||||
local a={x=x, y=0, z=z}
|
||||
|
||||
-- Stern position in the new coordinate system, which is simply the origin.
|
||||
local b={x=0, y=0, z=0}
|
||||
|
||||
-- Vector from plane to ref point on the boat.
|
||||
local c=UTILS.VecSubstract(a, b)
|
||||
|
||||
-- Current line up and error wrt to final heading of the runway.
|
||||
local lineup=math.deg(math.atan2(c.z, c.x))
|
||||
]]
|
||||
|
||||
return lineup
|
||||
end
|
||||
|
||||
@@ -11962,9 +11937,13 @@ function AIRBOSS:_LSOgrade(playerData)
|
||||
local nS=count(G, '%(')
|
||||
local nN=N-nS-nL
|
||||
|
||||
-- Groove time 16-18 sec for a unicorn.
|
||||
local Tgroove=playerData.Tgroove
|
||||
local TgrooveUnicorn=Tgroove and (Tgroove>=16.0 and Tgroove<=18.0) or false
|
||||
|
||||
local grade
|
||||
local points
|
||||
if N==0 then
|
||||
if N==0 and TgrooveUnicorn then
|
||||
-- No deviations, should be REALLY RARE!
|
||||
grade="_OK_"
|
||||
points=5.0
|
||||
@@ -12035,7 +12014,7 @@ function AIRBOSS:_LSOgrade(playerData)
|
||||
-----------------
|
||||
grade="OWO"
|
||||
points=2.0
|
||||
if G=="Unicorn" then
|
||||
if N==0 then
|
||||
G="n/a"
|
||||
end
|
||||
elseif playerData.waveoff then
|
||||
@@ -13527,63 +13506,6 @@ function AIRBOSS:_InitWaypoints()
|
||||
return self
|
||||
end
|
||||
|
||||
--[[
|
||||
|
||||
--- Patrol carrier.
|
||||
-- @param #AIRBOSS self
|
||||
-- @param #number n Current waypoint.
|
||||
-- @return #AIRBOSS self
|
||||
function AIRBOSS:_PatrolRoute(n)
|
||||
|
||||
-- Get carrier group.
|
||||
local CarrierGroup=self.carrier:GetGroup()
|
||||
|
||||
-- Waypoints of group.
|
||||
local Waypoints = CarrierGroup:GetTemplateRoutePoints()
|
||||
|
||||
-- Loop over waypoints.
|
||||
for n=1,#Waypoints do
|
||||
|
||||
-- Passing waypoint taskfunction
|
||||
local TaskPassingWP=CarrierGroup:TaskFunction("AIRBOSS._PassingWaypoint", self, n, #Waypoints)
|
||||
|
||||
-- Call task function when carrier arrives at waypoint.
|
||||
CarrierGroup:SetTaskWaypoint(Waypoints[n], TaskPassingWP)
|
||||
end
|
||||
|
||||
-- Init array.
|
||||
self.waypoints={}
|
||||
|
||||
-- Set waypoint table.
|
||||
for i,point in ipairs(Waypoints) do
|
||||
|
||||
-- Coordinate of the waypoint
|
||||
local coord=COORDINATE:New(point.x, point.alt, point.y)
|
||||
|
||||
-- Set velocity of the coordinate.
|
||||
coord:SetVelocity(point.speed)
|
||||
|
||||
-- Add to table.
|
||||
table.insert(self.waypoints, coord)
|
||||
|
||||
-- Debug info.
|
||||
if self.Debug then
|
||||
coord:MarkToAll(string.format("Carrier Waypoint %d, Speed=%.1f knots", i, UTILS.MpsToKnots(point.speed)))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Current waypoint is 1.
|
||||
self.currentwp=n or 1
|
||||
|
||||
-- Route carrier group.
|
||||
CarrierGroup:Route(Waypoints)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
]]
|
||||
|
||||
--- Patrol carrier.
|
||||
-- @param #AIRBOSS self
|
||||
-- @param #number n Next waypoint number.
|
||||
@@ -15544,7 +15466,7 @@ end
|
||||
function AIRBOSS:_MarshalCallRecoveryStart(case)
|
||||
|
||||
-- Marshal radial.
|
||||
local radial=self:GetRadial(case, true, true, true)
|
||||
local radial=self:GetRadial(case, true, true, false)
|
||||
|
||||
-- Debug output.
|
||||
local text=string.format("Starting aircraft recovery Case %d ops.", case)
|
||||
@@ -17013,7 +16935,7 @@ function AIRBOSS:_DisplayCarrierInfo(_unitname)
|
||||
if self.case==1 then
|
||||
text=text..string.format("Case %d recovery ops\n", self.case)
|
||||
else
|
||||
local radial=self:GetRadial(self.case, true, true, true)
|
||||
local radial=self:GetRadial(self.case, true, true, false)
|
||||
text=text..string.format("Case %d recovery ops\nMarshal radial %03d°\n", self.case, radial)
|
||||
end
|
||||
text=text..string.format("BRC %03d° - FB %03d°\n", self:GetBRC(), self:GetFinalBearing(true))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- **Ops** - (R2.5) - Recovery tanker for carrier operations.
|
||||
--- **Ops** - Recovery tanker for carrier operations.
|
||||
--
|
||||
-- Tanker aircraft flying a racetrack pattern overhead an aircraft carrier.
|
||||
--
|
||||
@@ -62,6 +62,7 @@
|
||||
-- @field #string modex Tail number of the tanker.
|
||||
-- @field #boolean eplrs If true, enable data link, e.g. if used as AWACS.
|
||||
-- @field #boolean recovery If true, tanker will recover using the AIRBOSS marshal pattern.
|
||||
-- @field #number terminaltype Terminal type of used parking spots on airbases.
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
--- Recovery Tanker.
|
||||
@@ -298,6 +299,7 @@ RECOVERYTANKER = {
|
||||
modex = nil,
|
||||
eplrs = nil,
|
||||
recovery = nil,
|
||||
terminaltype = nil,
|
||||
}
|
||||
|
||||
--- Unique ID (global).
|
||||
@@ -306,7 +308,7 @@ _RECOVERYTANKERID=0
|
||||
|
||||
--- Class version.
|
||||
-- @field #string version
|
||||
RECOVERYTANKER.version="1.0.8"
|
||||
RECOVERYTANKER.version="1.0.9"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -381,6 +383,7 @@ function RECOVERYTANKER:New(carrierunit, tankergroupname)
|
||||
self:SetPatternUpdateInterval()
|
||||
self:SetAWACS(false)
|
||||
self:SetRecoveryAirboss(false)
|
||||
self.terminaltype=AIRBASE.TerminalType.OpenMedOrBig
|
||||
|
||||
-- Debug trace.
|
||||
if false then
|
||||
@@ -615,8 +618,9 @@ end
|
||||
--- Set home airbase of the tanker. This is the airbase where the tanker will go when it is out of fuel.
|
||||
-- @param #RECOVERYTANKER self
|
||||
-- @param Wrapper.Airbase#AIRBASE airbase The home airbase. Can be the airbase name or a Moose AIRBASE object.
|
||||
-- @param #number terminaltype (Optional) The terminal type of parking spots used for spawning at airbases. Default AIRBASE.TerminalType.OpenMedOrBig.
|
||||
-- @return #RECOVERYTANKER self
|
||||
function RECOVERYTANKER:SetHomeBase(airbase)
|
||||
function RECOVERYTANKER:SetHomeBase(airbase, terminaltype)
|
||||
if type(airbase)=="string" then
|
||||
self.airbase=AIRBASE:FindByName(airbase)
|
||||
else
|
||||
@@ -625,6 +629,9 @@ function RECOVERYTANKER:SetHomeBase(airbase)
|
||||
if not self.airbase then
|
||||
self:E(self.lid.."ERROR: Airbase is nil!")
|
||||
end
|
||||
if terminaltype then
|
||||
self.terminaltype=terminaltype
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -937,7 +944,7 @@ function RECOVERYTANKER:onafterStart(From, Event, To)
|
||||
else
|
||||
|
||||
-- Spawn tanker at airbase.
|
||||
self.tanker=Spawn:SpawnAtAirbase(self.airbase, self.takeoff, nil, AIRBASE.TerminalType.OpenMedOrBig)
|
||||
self.tanker=Spawn:SpawnAtAirbase(self.airbase, self.takeoff, nil, self.terminaltype)
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- **Ops** - (R2.5) - Rescue helicopter for carrier operations.
|
||||
--- **Ops** - Rescue helicopter for carrier operations.
|
||||
--
|
||||
-- Recue helicopter for carrier operations.
|
||||
--
|
||||
@@ -237,7 +237,7 @@ _RESCUEHELOID=0
|
||||
|
||||
--- Class version.
|
||||
-- @field #string version
|
||||
RESCUEHELO.version="1.0.9"
|
||||
RESCUEHELO.version="1.1.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -902,7 +902,7 @@ function RESCUEHELO:onafterStart(From, Event, To)
|
||||
else
|
||||
|
||||
-- Check if an uncontrolled helo group was requested.
|
||||
if self.useuncontrolled then
|
||||
if self.uncontrolledac then
|
||||
|
||||
-- Use an uncontrolled aircraft group.
|
||||
self.helo=GROUP:FindByName(self.helogroupname)
|
||||
|
||||
@@ -292,7 +292,9 @@ do -- TASK_A2A
|
||||
|
||||
--- Return the relative distance to the target vicinity from the player, in order to sort the targets in the reports per distance from the threats.
|
||||
-- @param #TASK_A2A self
|
||||
function TASK_A2A:ReportOrder( ReportGroup )
|
||||
function TASK_A2A:ReportOrder( ReportGroup )
|
||||
self:UpdateTaskInfo( self.DetectedItem )
|
||||
|
||||
local Coordinate = self.TaskInfo:GetData( "Coordinate" )
|
||||
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
|
||||
|
||||
|
||||
@@ -295,6 +295,8 @@ do -- TASK_A2G
|
||||
--- Return the relative distance to the target vicinity from the player, in order to sort the targets in the reports per distance from the threats.
|
||||
-- @param #TASK_A2G self
|
||||
function TASK_A2G:ReportOrder( ReportGroup )
|
||||
self:UpdateTaskInfo( self.DetectedItem )
|
||||
|
||||
local Coordinate = self.TaskInfo:GetData( "Coordinate" )
|
||||
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
-- ===
|
||||
--
|
||||
-- @module Tasking.Task_Zone_Capture_Dispatcher
|
||||
-- @image Task_Zone_Capture_Dispatcher.JPG
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
do -- TASK_CAPTURE_DISPATCHER
|
||||
|
||||
|
||||
@@ -1,15 +1,293 @@
|
||||
--- **Utilities** Enumerators.
|
||||
--
|
||||
-- An enumerator is a variable that holds a constant value. Enumerators are very useful because they make the code easier to read and to change in general.
|
||||
--
|
||||
-- For example, instead of using the same value at multiple different places in your code, you should use a variable set to that value.
|
||||
-- If, for whatever reason, the value needs to be changed, you only have to change the variable once and do not have to search through you code and reset
|
||||
-- every value by hand.
|
||||
--
|
||||
-- Another big advantage is that the LDT intellisense "knows" the enumerators. So you can use the autocompletion feature and do not have to keep all the
|
||||
-- values in your head or look them up in the docs.
|
||||
--
|
||||
-- DCS itself provides a lot of enumerators for various things. See [Enumerators](https://wiki.hoggitworld.com/view/Category:Enumerators) on Hoggit.
|
||||
--
|
||||
-- Other Moose classe also have enumerators. For example, the AIRBASE class has enumerators for airbase names.
|
||||
--
|
||||
-- @module ENUMS
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
--- [DCS Enum world](https://wiki.hoggitworld.com/view/DCS_enum_world)
|
||||
-- @type ENUMS
|
||||
|
||||
--- Because ENUMS are just better practice.
|
||||
--
|
||||
-- The ENUMS class adds some handy variables, which help you to make your code better and more general.
|
||||
--
|
||||
-- @field #ENUMS
|
||||
ENUMS = {}
|
||||
|
||||
--- Rules of Engagement.
|
||||
-- @type ENUMS.ROE
|
||||
-- @field #number WeaponFree AI will engage any enemy group it detects. Target prioritization is based based on the threat of the target.
|
||||
-- @field #number OpenFireWeaponFree AI will engage any enemy group it detects, but will prioritize targets specified in the groups tasking.
|
||||
-- @field #number OpenFire AI will engage only targets specified in its taskings.
|
||||
-- @field #number ReturnFire AI will only engage threats that shoot first.
|
||||
-- @field #number WeaponHold AI will hold fire under all circumstances.
|
||||
ENUMS.ROE = {
|
||||
HoldFire = 1,
|
||||
ReturnFire = 2,
|
||||
OpenFire = 3,
|
||||
WeaponFree = 4
|
||||
WeaponFree=0,
|
||||
OpenFireWeaponFree=1,
|
||||
OpenFire=2,
|
||||
ReturnFire=3,
|
||||
WeaponHold=4,
|
||||
}
|
||||
|
||||
|
||||
--- Reaction On Threat.
|
||||
-- @type ENUMS.ROT
|
||||
-- @field #number NoReaction No defensive actions will take place to counter threats.
|
||||
-- @field #number PassiveDefense AI will use jammers and other countermeasures in an attempt to defeat the threat. AI will not attempt a maneuver to defeat a threat.
|
||||
-- @field #number EvadeFire AI will react by performing defensive maneuvers against incoming threats. AI will also use passive defense.
|
||||
-- @field #number BypassAndEscape AI will attempt to avoid enemy threat zones all together. This includes attempting to fly above or around threats.
|
||||
-- @field #number AllowAbortMission If a threat is deemed severe enough the AI will abort its mission and return to base.
|
||||
ENUMS.ROT = {
|
||||
NoReaction = 1,
|
||||
PassiveDefense = 2,
|
||||
EvadeFire = 3,
|
||||
Vertical = 4
|
||||
}
|
||||
NoReaction=0,
|
||||
PassiveDefense=1,
|
||||
EvadeFire=2,
|
||||
BypassAndEscape=3,
|
||||
AllowAbortMission=4,
|
||||
}
|
||||
|
||||
--- Weapon types. See the [Weapon Flag](https://wiki.hoggitworld.com/view/DCS_enum_weapon_flag) enumerotor on hoggit wiki.
|
||||
-- @type ENUMS.WeaponFlag
|
||||
ENUMS.WeaponFlag={
|
||||
-- Bombs
|
||||
LGB = 2,
|
||||
TvGB = 4,
|
||||
SNSGB = 8,
|
||||
HEBomb = 16,
|
||||
Penetrator = 32,
|
||||
NapalmBomb = 64,
|
||||
FAEBomb = 128,
|
||||
ClusterBomb = 256,
|
||||
Dispencer = 512,
|
||||
CandleBomb = 1024,
|
||||
ParachuteBomb = 2147483648,
|
||||
-- Rockets
|
||||
LightRocket = 2048,
|
||||
MarkerRocket = 4096,
|
||||
CandleRocket = 8192,
|
||||
HeavyRocket = 16384,
|
||||
-- Air-To-Surface Missiles
|
||||
AntiRadarMissile = 32768,
|
||||
AntiShipMissile = 65536,
|
||||
AntiTankMissile = 131072,
|
||||
FireAndForgetASM = 262144,
|
||||
LaserASM = 524288,
|
||||
TeleASM = 1048576,
|
||||
CruiseMissile = 2097152,
|
||||
AntiRadarMissile2 = 1073741824,
|
||||
-- Air-To-Air Missiles
|
||||
SRAM = 4194304,
|
||||
MRAAM = 8388608,
|
||||
LRAAM = 16777216,
|
||||
IR_AAM = 33554432,
|
||||
SAR_AAM = 67108864,
|
||||
AR_AAM = 134217728,
|
||||
--- Guns
|
||||
GunPod = 268435456,
|
||||
BuiltInCannon = 536870912,
|
||||
---
|
||||
-- Combinations
|
||||
--
|
||||
-- Bombs
|
||||
GuidedBomb = 14, -- (LGB + TvGB + SNSGB)
|
||||
AnyUnguidedBomb = 2147485680, -- (HeBomb + Penetrator + NapalmBomb + FAEBomb + ClusterBomb + Dispencer + CandleBomb + ParachuteBomb)
|
||||
AnyBomb = 2147485694, -- (GuidedBomb + AnyUnguidedBomb)
|
||||
--- Rockets
|
||||
AnyRocket = 30720, -- LightRocket + MarkerRocket + CandleRocket + HeavyRocket
|
||||
--- Air-To-Surface Missiles
|
||||
GuidedASM = 1572864, -- (LaserASM + TeleASM)
|
||||
TacticalASM = 1835008, -- (GuidedASM + FireAndForgetASM)
|
||||
AnyASM = 4161536, -- (AntiRadarMissile + AntiShipMissile + AntiTankMissile + FireAndForgetASM + GuidedASM + CruiseMissile)
|
||||
AnyASM2 = 1077903360, -- 4161536+1073741824,
|
||||
--- Air-To-Air Missiles
|
||||
AnyAAM = 264241152, -- IR_AAM + SAR_AAM + AR_AAM + SRAAM + MRAAM + LRAAM
|
||||
AnyAutonomousMissile = 36012032, -- IR_AAM + AntiRadarMissile + AntiShipMissile + FireAndForgetASM + CruiseMissile
|
||||
AnyMissile = 268402688, -- AnyASM + AnyAAM
|
||||
--- Guns
|
||||
Cannons = 805306368, -- GUN_POD + BuiltInCannon
|
||||
---
|
||||
-- Even More Genral
|
||||
Auto = 3221225470, -- Any Weapon (AnyBomb + AnyRocket + AnyMissile + Cannons)
|
||||
AutoDCS = 1073741822, -- Something if often see
|
||||
AnyAG = 2956984318, -- Any Air-To-Ground Weapon
|
||||
AnyAA = 264241152, -- Any Air-To-Air Weapon
|
||||
AnyUnguided = 2952822768, -- Any Unguided Weapon
|
||||
AnyGuided = 268402702, -- Any Guided Weapon
|
||||
}
|
||||
|
||||
--- Mission tasks.
|
||||
-- @type ENUMS.MissionTask
|
||||
-- @field #string NOTHING No special task. Group can perform the minimal tasks: Orbit, Refuelling, Follow and Aerobatics.
|
||||
-- @field #string AFAC Forward Air Controller Air. Can perform the tasks: Attack Group, Attack Unit, FAC assign group, Bombing, Attack Map Object.
|
||||
-- @field #string ANTISHIPSTRIKE Naval ops. Can perform the tasks: Attack Group, Attack Unit.
|
||||
-- @field #string AWACS AWACS.
|
||||
-- @field #string CAP Combat Air Patrol.
|
||||
-- @field #string CAS Close Air Support.
|
||||
-- @field #string ESCORT Escort another group.
|
||||
-- @field #string FIGHTERSWEEP Fighter sweep.
|
||||
-- @field #string GROUNDATTACK Ground attack.
|
||||
-- @field #string INTERCEPT Intercept.
|
||||
-- @field #string PINPOINTSTRIKE Pinpoint strike.
|
||||
-- @field #string RECONNAISSANCE Reconnaissance mission.
|
||||
-- @field #string REFUELING Refueling mission.
|
||||
-- @field #string RUNWAYATTACK Attack the runway of an airdrome.
|
||||
-- @field #string SEAD Suppression of Enemy Air Defenses.
|
||||
-- @field #string TRANSPORT Troop transport.
|
||||
ENUMS.MissionTask={
|
||||
NOTHING="Nothing",
|
||||
AFAC="AFAC",
|
||||
ANTISHIPSTRIKE="Antiship Strike",
|
||||
AWACS="AWACS",
|
||||
CAP="CAP",
|
||||
CAS="CAS",
|
||||
ESCORT="Escort",
|
||||
FIGHTERSWEEP="Fighter Sweep",
|
||||
GROUNDATTACK="Ground Attack",
|
||||
INTERCEPT="Intercept",
|
||||
PINPOINTSTRIKE="Pinpoint Strike",
|
||||
RECONNAISSANCE="Reconnaissance",
|
||||
REFUELING="Refueling",
|
||||
RUNWAYATTACK="Runway Attack",
|
||||
SEAD="SEAD",
|
||||
TRANSPORT="Transport",
|
||||
}
|
||||
|
||||
--- Formations (new). See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on hoggit wiki.
|
||||
-- @type ENUMS.Formation
|
||||
ENUMS.Formation={}
|
||||
ENUMS.Formation.FixedWing={}
|
||||
ENUMS.Formation.FixedWing.LineAbreast={}
|
||||
ENUMS.Formation.FixedWing.LineAbreast.Close = 65537
|
||||
ENUMS.Formation.FixedWing.LineAbreast.Open = 65538
|
||||
ENUMS.Formation.FixedWing.LineAbreast.Group = 65539
|
||||
ENUMS.Formation.FixedWing.Trail={}
|
||||
ENUMS.Formation.FixedWing.Trail.Close = 131073
|
||||
ENUMS.Formation.FixedWing.Trail.Open = 131074
|
||||
ENUMS.Formation.FixedWing.Trail.Group = 131075
|
||||
ENUMS.Formation.FixedWing.Wedge={}
|
||||
ENUMS.Formation.FixedWing.Wedge.Close = 196609
|
||||
ENUMS.Formation.FixedWing.Wedge.Open = 196610
|
||||
ENUMS.Formation.FixedWing.Wedge.Group = 196611
|
||||
ENUMS.Formation.FixedWing.EchelonRight={}
|
||||
ENUMS.Formation.FixedWing.EchelonRight.Close = 262145
|
||||
ENUMS.Formation.FixedWing.EchelonRight.Open = 262146
|
||||
ENUMS.Formation.FixedWing.EchelonRight.Group = 262147
|
||||
ENUMS.Formation.FixedWing.EchelonLeft={}
|
||||
ENUMS.Formation.FixedWing.EchelonLeft.Close = 327681
|
||||
ENUMS.Formation.FixedWing.EchelonLeft.Open = 327682
|
||||
ENUMS.Formation.FixedWing.EchelonLeft.Group = 327683
|
||||
ENUMS.Formation.FixedWing.FingerFour={}
|
||||
ENUMS.Formation.FixedWing.FingerFour.Close = 393217
|
||||
ENUMS.Formation.FixedWing.FingerFour.Open = 393218
|
||||
ENUMS.Formation.FixedWing.FingerFour.Group = 393219
|
||||
ENUMS.Formation.FixedWing.Spread={}
|
||||
ENUMS.Formation.FixedWing.Spread.Close = 458753
|
||||
ENUMS.Formation.FixedWing.Spread.Open = 458754
|
||||
ENUMS.Formation.FixedWing.Spread.Group = 458755
|
||||
ENUMS.Formation.FixedWing.BomberElement={}
|
||||
ENUMS.Formation.FixedWing.BomberElement.Close = 786433
|
||||
ENUMS.Formation.FixedWing.BomberElement.Open = 786434
|
||||
ENUMS.Formation.FixedWing.BomberElement.Group = 786435
|
||||
ENUMS.Formation.FixedWing.BomberElementHeight={}
|
||||
ENUMS.Formation.FixedWing.BomberElementHeight.Close = 851968
|
||||
ENUMS.Formation.FixedWing.FighterVic={}
|
||||
ENUMS.Formation.FixedWing.FighterVic.Close = 917505
|
||||
ENUMS.Formation.FixedWing.FighterVic.Open = 917506
|
||||
ENUMS.Formation.RotaryWing={}
|
||||
ENUMS.Formation.RotaryWing.Column={}
|
||||
ENUMS.Formation.RotaryWing.Column.D70=720896
|
||||
ENUMS.Formation.RotaryWing.Wedge={}
|
||||
ENUMS.Formation.RotaryWing.Wedge.D70=8
|
||||
ENUMS.Formation.RotaryWing.FrontRight={}
|
||||
ENUMS.Formation.RotaryWing.FrontRight.D300=655361
|
||||
ENUMS.Formation.RotaryWing.FrontRight.D600=655362
|
||||
ENUMS.Formation.RotaryWing.FrontLeft={}
|
||||
ENUMS.Formation.RotaryWing.FrontLeft.D300=655617
|
||||
ENUMS.Formation.RotaryWing.FrontLeft.D600=655618
|
||||
ENUMS.Formation.RotaryWing.EchelonRight={}
|
||||
ENUMS.Formation.RotaryWing.EchelonRight.D70 =589825
|
||||
ENUMS.Formation.RotaryWing.EchelonRight.D300=589826
|
||||
ENUMS.Formation.RotaryWing.EchelonRight.D600=589827
|
||||
ENUMS.Formation.RotaryWing.EchelonLeft={}
|
||||
ENUMS.Formation.RotaryWing.EchelonLeft.D70 =590081
|
||||
ENUMS.Formation.RotaryWing.EchelonLeft.D300=590082
|
||||
ENUMS.Formation.RotaryWing.EchelonLeft.D600=590083
|
||||
|
||||
--- Formations (old). The old format is a simplified version of the new formation enums, which allow more sophisticated settings.
|
||||
-- See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on hoggit wiki.
|
||||
-- @type ENUMS.FormationOld
|
||||
ENUMS.FormationOld={}
|
||||
ENUMS.FormationOld.FixedWing={}
|
||||
ENUMS.FormationOld.FixedWing.LineAbreast=1
|
||||
ENUMS.FormationOld.FixedWing.Trail=2
|
||||
ENUMS.FormationOld.FixedWing.Wedge=3
|
||||
ENUMS.FormationOld.FixedWing.EchelonRight=4
|
||||
ENUMS.FormationOld.FixedWing.EchelonLeft=5
|
||||
ENUMS.FormationOld.FixedWing.FingerFour=6
|
||||
ENUMS.FormationOld.FixedWing.SpreadFour=7
|
||||
ENUMS.FormationOld.FixedWing.BomberElement=12
|
||||
ENUMS.FormationOld.FixedWing.BomberElementHeight=13
|
||||
ENUMS.FormationOld.FixedWing.FighterVic=14
|
||||
ENUMS.FormationOld.RotaryWing={}
|
||||
ENUMS.FormationOld.RotaryWing.Wedge=8
|
||||
ENUMS.FormationOld.RotaryWing.Echelon=9
|
||||
ENUMS.FormationOld.RotaryWing.Front=10
|
||||
ENUMS.FormationOld.RotaryWing.Column=11
|
||||
|
||||
|
||||
--- Morse Code. See the [Wikipedia](https://en.wikipedia.org/wiki/Morse_code).
|
||||
--
|
||||
-- * Short pulse "*"
|
||||
-- * Long pulse "-"
|
||||
--
|
||||
-- Pulses are separated by a blank character " ".
|
||||
--
|
||||
-- @type ENUMS.Morse
|
||||
ENUMS.Morse={}
|
||||
ENUMS.Morse.A="* -"
|
||||
ENUMS.Morse.B="- * * *"
|
||||
ENUMS.Morse.C="- * - *"
|
||||
ENUMS.Morse.D="- * *"
|
||||
ENUMS.Morse.E="*"
|
||||
ENUMS.Morse.F="* * - *"
|
||||
ENUMS.Morse.G="- - *"
|
||||
ENUMS.Morse.H="* * * *"
|
||||
ENUMS.Morse.I="* *"
|
||||
ENUMS.Morse.J="* - - -"
|
||||
ENUMS.Morse.K="- * -"
|
||||
ENUMS.Morse.L="* - * *"
|
||||
ENUMS.Morse.M="- -"
|
||||
ENUMS.Morse.N="- *"
|
||||
ENUMS.Morse.O="- - -"
|
||||
ENUMS.Morse.P="* - - *"
|
||||
ENUMS.Morse.Q="- - * -"
|
||||
ENUMS.Morse.R="* - *"
|
||||
ENUMS.Morse.S="* * *"
|
||||
ENUMS.Morse.T="-"
|
||||
ENUMS.Morse.U="* * -"
|
||||
ENUMS.Morse.V="* * * -"
|
||||
ENUMS.Morse.W="* - -"
|
||||
ENUMS.Morse.X="- * * -"
|
||||
ENUMS.Morse.Y="- * - -"
|
||||
ENUMS.Morse.Z="- - * *"
|
||||
ENUMS.Morse.N1="* - - - -"
|
||||
ENUMS.Morse.N2="* * - - -"
|
||||
ENUMS.Morse.N3="* * * - -"
|
||||
ENUMS.Morse.N4="* * * * -"
|
||||
ENUMS.Morse.N5="* * * * *"
|
||||
ENUMS.Morse.N6="- * * * *"
|
||||
ENUMS.Morse.N7="- - * * *"
|
||||
ENUMS.Morse.N8="- - - * *"
|
||||
ENUMS.Morse.N9="- - - - *"
|
||||
ENUMS.Morse.N0="- - - - -"
|
||||
ENUMS.Morse[" "]=" "
|
||||
@@ -1631,6 +1631,11 @@ function routines.getGroupRoute(groupIdent, task) -- same as getGroupPoints bu
|
||||
|
||||
for point_num, point in pairs(group_data.route.points) do
|
||||
local routeData = {}
|
||||
if env.mission.version > 7 then
|
||||
routeData.name = env.getValueDictByKey(point.name)
|
||||
else
|
||||
routeData.name = point.name
|
||||
end
|
||||
if not point.point then
|
||||
routeData.x = point.x
|
||||
routeData.y = point.y
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
--- This module contains derived utilities taken from the MIST framework,
|
||||
-- which are excellent tools to be reused in an OO environment!.
|
||||
--- This module contains derived utilities taken from the MIST framework, which are excellent tools to be reused in an OO environment.
|
||||
--
|
||||
-- ### Authors:
|
||||
--
|
||||
@@ -33,14 +32,14 @@ FLARECOLOR = trigger.flareColor -- #FLARECOLOR
|
||||
--- Big smoke preset enum.
|
||||
-- @type BIGSMOKEPRESET
|
||||
BIGSMOKEPRESET = {
|
||||
SmallSmokeAndFire=0,
|
||||
MediumSmokeAndFire=1,
|
||||
LargeSmokeAndFire=2,
|
||||
HugeSmokeAndFire=3,
|
||||
SmallSmoke=4,
|
||||
MediumSmoke=5,
|
||||
LargeSmoke=6,
|
||||
HugeSmoke=7,
|
||||
SmallSmokeAndFire=1,
|
||||
MediumSmokeAndFire=2,
|
||||
LargeSmokeAndFire=3,
|
||||
HugeSmokeAndFire=4,
|
||||
SmallSmoke=5,
|
||||
MediumSmoke=6,
|
||||
LargeSmoke=7,
|
||||
HugeSmoke=8,
|
||||
}
|
||||
|
||||
--- DCS map as returned by env.mission.theatre.
|
||||
@@ -65,7 +64,7 @@ CALLSIGN={
|
||||
Enfield=1,
|
||||
Springfield=2,
|
||||
Uzi=3,
|
||||
Cold=4,
|
||||
Colt=4,
|
||||
Dodge=5,
|
||||
Ford=6,
|
||||
Chevy=7,
|
||||
@@ -78,7 +77,7 @@ CALLSIGN={
|
||||
},
|
||||
-- AWACS
|
||||
AWACS={
|
||||
Overloard=1,
|
||||
Overlord=1,
|
||||
Magic=2,
|
||||
Wizard=3,
|
||||
Focus=4,
|
||||
@@ -116,6 +115,7 @@ CALLSIGN={
|
||||
|
||||
--- Utilities static class.
|
||||
-- @type UTILS
|
||||
-- @field #number _MarkID Marker index counter. Running number when marker is added.
|
||||
UTILS = {
|
||||
_MarkID = 1
|
||||
}
|
||||
@@ -183,7 +183,9 @@ UTILS.IsInstanceOf = function( object, className )
|
||||
end
|
||||
|
||||
|
||||
--from http://lua-users.org/wiki/CopyTable
|
||||
--- Deep copy a table. See http://lua-users.org/wiki/CopyTable
|
||||
-- @param #table object The input table.
|
||||
-- @return #table Copy of the input table.
|
||||
UTILS.DeepCopy = function(object)
|
||||
local lookup_table = {}
|
||||
local function _copy(object)
|
||||
@@ -204,7 +206,8 @@ UTILS.DeepCopy = function(object)
|
||||
end
|
||||
|
||||
|
||||
-- porting in Slmod's serialize_slmod2
|
||||
--- Porting in Slmod's serialize_slmod2.
|
||||
-- @param #table tbl Input table.
|
||||
UTILS.OneLineSerialize = function( tbl ) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function
|
||||
|
||||
lookup_table = {}
|
||||
@@ -340,18 +343,30 @@ UTILS.MiphToMps = function( miph )
|
||||
return miph * 0.44704
|
||||
end
|
||||
|
||||
--- Convert meters per second to miles per hour.
|
||||
-- @param #number mps Speed in m/s.
|
||||
-- @return #number Speed in miles per hour.
|
||||
UTILS.MpsToMiph = function( mps )
|
||||
return mps / 0.44704
|
||||
end
|
||||
|
||||
--- Convert meters per second to knots.
|
||||
-- @param #number knots Speed in m/s.
|
||||
-- @return #number Speed in knots.
|
||||
UTILS.MpsToKnots = function( mps )
|
||||
return mps * 1.94384 --3600 / 1852
|
||||
end
|
||||
|
||||
--- Convert knots to meters per second.
|
||||
-- @param #number knots Speed in knots.
|
||||
-- @return #number Speed in m/s.
|
||||
UTILS.KnotsToMps = function( knots )
|
||||
return knots / 1.94384 --* 1852 / 3600
|
||||
end
|
||||
|
||||
--- Convert temperature from Celsius to Farenheit.
|
||||
-- @param #number Celcius Temperature in degrees Celsius.
|
||||
-- @return #number Temperature in degrees Farenheit.
|
||||
UTILS.CelciusToFarenheit = function( Celcius )
|
||||
return Celcius * 9/5 + 32
|
||||
end
|
||||
@@ -436,9 +451,9 @@ UTILS.tostringLL = function( lat, lon, acc, DMS)
|
||||
secFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
|
||||
end
|
||||
|
||||
-- 024° 23' 12"N or 024° 23' 12.03"N
|
||||
return string.format('%03d°', latDeg) .. ' ' .. string.format('%02d', latMin) .. '\' ' .. string.format(secFrmtStr, latSec) .. '"' .. latHemi .. ' '
|
||||
.. string.format('%03d°', lonDeg) .. ' ' .. string.format('%02d', lonMin) .. '\' ' .. string.format(secFrmtStr, lonSec) .. '"' .. lonHemi
|
||||
-- 024<EFBFBD> 23' 12"N or 024<EFBFBD> 23' 12.03"N
|
||||
return string.format('%03d°', latDeg)..string.format('%02d', latMin)..'\''..string.format(secFrmtStr, latSec)..'"'..latHemi..' '
|
||||
.. string.format('%03d°', lonDeg)..string.format('%02d', lonMin)..'\''..string.format(secFrmtStr, lonSec)..'"'..lonHemi
|
||||
|
||||
else -- degrees, decimal minutes.
|
||||
latMin = UTILS.Round(latMin, acc)
|
||||
@@ -471,12 +486,31 @@ end
|
||||
|
||||
-- acc- the accuracy of each easting/northing. 0, 1, 2, 3, 4, or 5.
|
||||
UTILS.tostringMGRS = function(MGRS, acc) --R2.1
|
||||
|
||||
if acc == 0 then
|
||||
return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph
|
||||
else
|
||||
return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph .. ' ' .. string.format('%0' .. acc .. 'd', UTILS.Round(MGRS.Easting/(10^(5-acc)), 0))
|
||||
.. ' ' .. string.format('%0' .. acc .. 'd', UTILS.Round(MGRS.Northing/(10^(5-acc)), 0))
|
||||
|
||||
-- Test if Easting/Northing have less than 4 digits.
|
||||
--MGRS.Easting=123 -- should be 00123
|
||||
--MGRS.Northing=5432 -- should be 05432
|
||||
|
||||
-- Truncate rather than round MGRS grid!
|
||||
local Easting=tostring(MGRS.Easting)
|
||||
local Northing=tostring(MGRS.Northing)
|
||||
|
||||
-- Count number of missing digits. Easting/Northing should have 5 digits. However, it is passed as a number. Therefore, any leading zeros would not be displayed by lua.
|
||||
local nE=5-string.len(Easting)
|
||||
local nN=5-string.len(Northing)
|
||||
|
||||
-- Get leading zeros (if any).
|
||||
for i=1,nE do Easting="0"..Easting end
|
||||
for i=1,nN do Northing="0"..Northing end
|
||||
|
||||
-- Return MGRS string.
|
||||
return string.format("%s %s %s %s", MGRS.UTMZone, MGRS.MGRSDigraph, string.sub(Easting, 1, acc), string.sub(Northing, 1, acc))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -963,7 +997,7 @@ end
|
||||
|
||||
|
||||
--- Returns the DCS map/theatre as optained by env.mission.theatre
|
||||
-- @return #string DCS map name .
|
||||
-- @return #string DCS map name.
|
||||
function UTILS.GetDCSMap()
|
||||
return env.mission.theatre
|
||||
end
|
||||
@@ -1037,3 +1071,76 @@ function UTILS.CheckMemory(output)
|
||||
end
|
||||
return mem
|
||||
end
|
||||
|
||||
|
||||
--- Get the coalition name from its numerical ID, e.g. coaliton.side.RED.
|
||||
-- @param #number Coalition The coalition ID.
|
||||
-- @return #string The coalition name, i.e. "Neutral", "Red" or "Blue" (or "Unknown").
|
||||
function UTILS.GetCoalitionName(Coalition)
|
||||
|
||||
if Coalition then
|
||||
if Coalition==coalition.side.BLUE then
|
||||
return "Blue"
|
||||
elseif Coalition==coalition.side.RED then
|
||||
return "Red"
|
||||
elseif Coalition==coalition.side.NEUTRAL then
|
||||
return "Neutral"
|
||||
else
|
||||
return "Unknown"
|
||||
end
|
||||
else
|
||||
return "Unknown"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Get the modulation name from its numerical value.
|
||||
-- @param #number Modulation The modulation enumerator number. Can be either 0 or 1.
|
||||
-- @return #string The modulation name, i.e. "AM"=0 or "FM"=1. Anything else will return "Unknown".
|
||||
function UTILS.GetModulationName(Modulation)
|
||||
|
||||
if Modulation then
|
||||
if Modulation==0 then
|
||||
return "AM"
|
||||
elseif Modulation==1 then
|
||||
return "FM"
|
||||
else
|
||||
return "Unknown"
|
||||
end
|
||||
else
|
||||
return "Unknown"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Get the callsign name from its enumerator value
|
||||
-- @param #number Callsign The enumerator callsign.
|
||||
-- @return #string The callsign name or "Ghostrider".
|
||||
function UTILS.GetCallsignName(Callsign)
|
||||
|
||||
for name, value in pairs(CALLSIGN.Aircraft) do
|
||||
if value==Callsign then
|
||||
return name
|
||||
end
|
||||
end
|
||||
|
||||
for name, value in pairs(CALLSIGN.AWACS) do
|
||||
if value==Callsign then
|
||||
return name
|
||||
end
|
||||
end
|
||||
|
||||
for name, value in pairs(CALLSIGN.JTAC) do
|
||||
if value==Callsign then
|
||||
return name
|
||||
end
|
||||
end
|
||||
|
||||
for name, value in pairs(CALLSIGN.Tanker) do
|
||||
if value==Callsign then
|
||||
return name
|
||||
end
|
||||
end
|
||||
|
||||
return "Ghostrider"
|
||||
end
|
||||
|
||||
@@ -124,7 +124,6 @@ AIRBASE.Caucasus = {
|
||||
-- * AIRBASE.Nevada.Jean_Airport
|
||||
-- * AIRBASE.Nevada.Laughlin_Airport
|
||||
-- * AIRBASE.Nevada.Lincoln_County
|
||||
-- * AIRBASE.Nevada.Mellan_Airstrip
|
||||
-- * AIRBASE.Nevada.Mesquite
|
||||
-- * AIRBASE.Nevada.Mina_Airport_3Q0
|
||||
-- * AIRBASE.Nevada.North_Las_Vegas
|
||||
@@ -144,7 +143,6 @@ AIRBASE.Nevada = {
|
||||
["Jean_Airport"] = "Jean Airport",
|
||||
["Laughlin_Airport"] = "Laughlin Airport",
|
||||
["Lincoln_County"] = "Lincoln County",
|
||||
["Mellan_Airstrip"] = "Mellan Airstrip",
|
||||
["Mesquite"] = "Mesquite",
|
||||
["Mina_Airport_3Q0"] = "Mina Airport 3Q0",
|
||||
["North_Las_Vegas"] = "North Las Vegas",
|
||||
@@ -185,7 +183,7 @@ AIRBASE.Nevada = {
|
||||
-- * AIRBASE.Normandy.Needs_Oar_Point
|
||||
-- * AIRBASE.Normandy.Funtington
|
||||
-- * AIRBASE.Normandy.Tangmere
|
||||
-- * AIRBASE.Normandy.Ford
|
||||
-- * AIRBASE.Normandy.Ford_AF
|
||||
-- @field Normandy
|
||||
AIRBASE.Normandy = {
|
||||
["Saint_Pierre_du_Mont"] = "Saint Pierre du Mont",
|
||||
@@ -218,8 +216,15 @@ AIRBASE.Normandy = {
|
||||
["Needs_Oar_Point"] = "Needs Oar Point",
|
||||
["Funtington"] = "Funtington",
|
||||
["Tangmere"] = "Tangmere",
|
||||
["Ford"] = "Ford",
|
||||
}
|
||||
["Ford_AF"] = "Ford_AF",
|
||||
["Goulet"] = "Goulet",
|
||||
["Argentan"] = "Argentan",
|
||||
["Vrigny"] = "Vrigny",
|
||||
["Essay"] = "Essay",
|
||||
["Hauterive"] = "Hauterive",
|
||||
["Barville"] = "Barville",
|
||||
["Conches"] = "Conches",
|
||||
}
|
||||
|
||||
--- These are all airbases of the Persion Gulf Map:
|
||||
--
|
||||
@@ -275,7 +280,7 @@ AIRBASE.PersianGulf = {
|
||||
["Lavan_Island_Airport"] = "Lavan Island Airport",
|
||||
["Liwa_Airbase"] = "Liwa Airbase",
|
||||
["Qeshm_Island"] = "Qeshm Island",
|
||||
["Ras_Al_Khaimah_International_Airport"] = "Ras Al Khaimah International Airport",
|
||||
["Ras_Al_Khaimah"] = "Ras Al Khaimah",
|
||||
["Sas_Al_Nakheel_Airport"] = "Sas Al Nakheel Airport",
|
||||
["Sharjah_Intl"] = "Sharjah Intl",
|
||||
["Shiraz_International_Airport"] = "Shiraz International Airport",
|
||||
@@ -305,7 +310,7 @@ AIRBASE.PersianGulf = {
|
||||
-- * AIRBASE.TerminalType.OpenMed = 72: Open/Shelter air airplane only.
|
||||
-- * AIRBASE.TerminalType.OpenBig = 104: Open air spawn points. Generally larger but does not guarantee large aircraft are capable of spawning there.
|
||||
-- * AIRBASE.TerminalType.OpenMedOrBig = 176: Combines OpenMed and OpenBig spots.
|
||||
-- * AIRBASE.TerminalType.HelicopterUnsable = 216: Combines HelicopterOnly, OpenMed and OpenBig.
|
||||
-- * AIRBASE.TerminalType.HelicopterUsable = 216: Combines HelicopterOnly, OpenMed and OpenBig.
|
||||
-- * AIRBASE.TerminalType.FighterAircraft = 244: Combines Shelter. OpenMed and OpenBig spots. So effectively all spots usable by fixed wing aircraft.
|
||||
--
|
||||
-- @type AIRBASE.TerminalType
|
||||
@@ -331,7 +336,7 @@ AIRBASE.TerminalType = {
|
||||
--- Runway data.
|
||||
-- @type AIRBASE.Runway
|
||||
-- @field #number heading Heading of the runway in degrees.
|
||||
-- @field #string idx Runway ID: heading 070° ==> idx="07".
|
||||
-- @field #string idx Runway ID: heading 070° ==> idx="07".
|
||||
-- @field #number length Length of runway in meters.
|
||||
-- @field Core.Point#COORDINATE position Position of runway start.
|
||||
-- @field Core.Point#COORDINATE endpoint End point of runway.
|
||||
@@ -344,9 +349,16 @@ AIRBASE.TerminalType = {
|
||||
-- @return Wrapper.Airbase#AIRBASE
|
||||
function AIRBASE:Register( AirbaseName )
|
||||
|
||||
local self = BASE:Inherit( self, POSITIONABLE:New( AirbaseName ) )
|
||||
local self = BASE:Inherit( self, POSITIONABLE:New( AirbaseName ) ) --#AIRBASE
|
||||
self.AirbaseName = AirbaseName
|
||||
self.AirbaseZone = ZONE_RADIUS:New( AirbaseName, self:GetVec2(), 2500 )
|
||||
self.AirbaseID = self:GetID(true)
|
||||
local vec2=self:GetVec2()
|
||||
if vec2 then
|
||||
self.AirbaseZone = ZONE_RADIUS:New( AirbaseName, vec2, 2500 )
|
||||
else
|
||||
self:E(string.format("ERROR: Cound not get position Vec2 of airbase %s", AirbaseName))
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -382,7 +394,7 @@ function AIRBASE:FindByID(id)
|
||||
for name,_airbase in pairs(_DATABASE.AIRBASES) do
|
||||
local airbase=_airbase --#AIRBASE
|
||||
|
||||
local aid=tonumber(airbase:GetID())
|
||||
local aid=tonumber(airbase:GetID(true))
|
||||
|
||||
if aid==id then
|
||||
return airbase
|
||||
@@ -432,6 +444,60 @@ function AIRBASE.GetAllAirbases(coalition, category)
|
||||
return airbases
|
||||
end
|
||||
|
||||
--- Get ID of the airbase.
|
||||
-- @param #AIRBASE self
|
||||
-- @param #boolean unique (Optional) If true, ships will get a negative sign as the unit ID might be the same as an airbase ID. Default off!
|
||||
-- @return #number The airbase ID.
|
||||
function AIRBASE:GetID(unique)
|
||||
|
||||
if self.AirbaseID then
|
||||
|
||||
return unique and self.AirbaseID or math.abs(self.AirbaseID)
|
||||
|
||||
else
|
||||
|
||||
for DCSAirbaseId, DCSAirbase in ipairs(world.getAirbases()) do
|
||||
|
||||
-- Get the airbase name.
|
||||
local AirbaseName = DCSAirbase:getName()
|
||||
|
||||
-- This gives the incorrect value to be inserted into the airdromeID for DCS 2.5.6!
|
||||
local airbaseID=tonumber(DCSAirbase:getID())
|
||||
|
||||
local airbaseCategory=self:GetAirbaseCategory()
|
||||
|
||||
--env.info(string.format("FF airbase=%s id=%s category=%s", tostring(AirbaseName), tostring(airbaseID), tostring(airbaseCategory)))
|
||||
|
||||
-- No way AFIK to get the DCS version. So we check if the event exists. That should tell us if we are on DCS 2.5.6 or prior to that.
|
||||
--[[
|
||||
if world.event.S_EVENT_KILL and world.event.S_EVENT_KILL>0 and airbaseCategory==Airbase.Category.AIRDROME then
|
||||
|
||||
-- We have to take the key value of this loop!
|
||||
airbaseID=DCSAirbaseId
|
||||
|
||||
-- Now another quirk: for Caucasus, we need to add 11 to the key value to get the correct ID. See https://forums.eagle.ru/showpost.php?p=4210774&postcount=11
|
||||
if UTILS.GetDCSMap()==DCSMAP.Caucasus then
|
||||
airbaseID=airbaseID+11
|
||||
end
|
||||
end
|
||||
]]
|
||||
|
||||
if AirbaseName==self.AirbaseName then
|
||||
if airbaseCategory==Airbase.Category.SHIP then
|
||||
-- Ships get a negative sign as their unit number might be the same as the ID of another airbase.
|
||||
return unique and -airbaseID or airbaseID
|
||||
else
|
||||
return airbaseID
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Returns a table of parking data for a given airbase. If the optional parameter *available* is true only available parking will be returned, otherwise all parking at the base is returned. Term types have the following enumerated values:
|
||||
--
|
||||
@@ -888,7 +954,7 @@ function AIRBASE:CheckOnRunWay(group, radius, despawn)
|
||||
radius=radius or 50
|
||||
|
||||
-- We only check at real airbases (not FARPS or ships).
|
||||
if self:GetDesc().category~=Airbase.Category.AIRDROME then
|
||||
if self:GetAirbaseCategory()~=Airbase.Category.AIRDROME then
|
||||
return false
|
||||
end
|
||||
|
||||
@@ -957,7 +1023,15 @@ end
|
||||
-- @param #AIRBASE self
|
||||
-- @return #number Category of airbase from GetDesc().category.
|
||||
function AIRBASE:GetAirbaseCategory()
|
||||
return self:GetDesc().category
|
||||
local desc=self:GetDesc()
|
||||
local category=Airbase.Category.AIRDROME
|
||||
|
||||
if desc and desc.category then
|
||||
category=desc.category
|
||||
else
|
||||
self:E(string.format("ERROR: Cannot get category of airbase %s due to DCS 2.5.6 bug! Assuming it is an AIRDROME for now...", tostring(self.AirbaseName)))
|
||||
end
|
||||
return category
|
||||
end
|
||||
|
||||
|
||||
@@ -1060,7 +1134,7 @@ function AIRBASE:GetRunwayData(magvar, mark)
|
||||
-- Heading of runway.
|
||||
local hdg=c1:HeadingTo(c2)
|
||||
|
||||
-- Runway ID: heading=070° ==> idx="07"
|
||||
-- Runway ID: heading=070° ==> idx="07"
|
||||
local idx=string.format("%02d", UTILS.Round((hdg-magvar)/10, 0))
|
||||
|
||||
-- Runway table.
|
||||
@@ -1170,7 +1244,7 @@ function AIRBASE:GetActiveRunway(magvar)
|
||||
local dot=UTILS.VecDot(Vwind, Vrunway)
|
||||
|
||||
-- Debug.
|
||||
--env.info(string.format("runway=%03d° dot=%.3f", runway.heading, dot))
|
||||
--env.info(string.format("runway=%03d° dot=%.3f", runway.heading, dot))
|
||||
|
||||
-- New min?
|
||||
if dotmin==nil or dot<dotmin then
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -173,6 +173,62 @@ GROUPTEMPLATE.Takeoff = {
|
||||
[GROUP.Takeoff.Cold] = { "TakeOffParking", "From Parking Area" }
|
||||
}
|
||||
|
||||
--- Generalized group attributes. See [DCS attributes](https://wiki.hoggitworld.com/view/DCS_enum_attributes) on hoggit.
|
||||
-- @type GROUP.Attribute
|
||||
-- @field #string AIR_TRANSPORTPLANE Airplane with transport capability. This can be used to transport other assets.
|
||||
-- @field #string AIR_AWACS Airborne Early Warning and Control System.
|
||||
-- @field #string AIR_FIGHTER Fighter, interceptor, ... airplane.
|
||||
-- @field #string AIR_BOMBER Aircraft which can be used for strategic bombing.
|
||||
-- @field #string AIR_TANKER Airplane which can refuel other aircraft.
|
||||
-- @field #string AIR_TRANSPORTHELO Helicopter with transport capability. This can be used to transport other assets.
|
||||
-- @field #string AIR_ATTACKHELO Attack helicopter.
|
||||
-- @field #string AIR_UAV Unpiloted Aerial Vehicle, e.g. drones.
|
||||
-- @field #string AIR_OTHER Any airborne unit that does not fall into any other airborne category.
|
||||
-- @field #string GROUND_APC Infantry carriers, in particular Amoured Personell Carrier. This can be used to transport other assets.
|
||||
-- @field #string GROUND_TRUCK Unarmed ground vehicles, which has the DCS "Truck" attribute.
|
||||
-- @field #string GROUND_INFANTRY Ground infantry assets.
|
||||
-- @field #string GROUND_ARTILLERY Artillery assets.
|
||||
-- @field #string GROUND_TANK Tanks (modern or old).
|
||||
-- @field #string GROUND_TRAIN Trains. Not that trains are **not** yet properly implemented in DCS and cannot be used currently.
|
||||
-- @field #string GROUND_EWR Early Warning Radar.
|
||||
-- @field #string GROUND_AAA Anti-Aircraft Artillery.
|
||||
-- @field #string GROUND_SAM Surface-to-Air Missile system or components.
|
||||
-- @field #string GROUND_OTHER Any ground unit that does not fall into any other ground category.
|
||||
-- @field #string NAVAL_AIRCRAFTCARRIER Aircraft carrier.
|
||||
-- @field #string NAVAL_WARSHIP War ship, i.e. cruisers, destroyers, firgates and corvettes.
|
||||
-- @field #string NAVAL_ARMEDSHIP Any armed ship that is not an aircraft carrier, a cruiser, destroyer, firgatte or corvette.
|
||||
-- @field #string NAVAL_UNARMEDSHIP Any unarmed naval vessel.
|
||||
-- @field #string NAVAL_OTHER Any naval unit that does not fall into any other naval category.
|
||||
-- @field #string OTHER_UNKNOWN Anything that does not fall into any other category.
|
||||
GROUP.Attribute = {
|
||||
AIR_TRANSPORTPLANE="Air_TransportPlane",
|
||||
AIR_AWACS="Air_AWACS",
|
||||
AIR_FIGHTER="Air_Fighter",
|
||||
AIR_BOMBER="Air_Bomber",
|
||||
AIR_TANKER="Air_Tanker",
|
||||
AIR_TRANSPORTHELO="Air_TransportHelo",
|
||||
AIR_ATTACKHELO="Air_AttackHelo",
|
||||
AIR_UAV="Air_UAV",
|
||||
AIR_OTHER="Air_OtherAir",
|
||||
GROUND_APC="Ground_APC",
|
||||
GROUND_TRUCK="Ground_Truck",
|
||||
GROUND_INFANTRY="Ground_Infantry",
|
||||
GROUND_ARTILLERY="Ground_Artillery",
|
||||
GROUND_TANK="Ground_Tank",
|
||||
GROUND_TRAIN="Ground_Train",
|
||||
GROUND_EWR="Ground_EWR",
|
||||
GROUND_AAA="Ground_AAA",
|
||||
GROUND_SAM="Ground_SAM",
|
||||
GROUND_OTHER="Ground_OtherGround",
|
||||
NAVAL_AIRCRAFTCARRIER="Naval_AircraftCarrier",
|
||||
NAVAL_WARSHIP="Naval_WarShip",
|
||||
NAVAL_ARMEDSHIP="Naval_ArmedShip",
|
||||
NAVAL_UNARMEDSHIP="Naval_UnarmedShip",
|
||||
NAVAL_OTHER="Naval_OtherNaval",
|
||||
OTHER_UNKNOWN="Other_Unknown",
|
||||
}
|
||||
|
||||
|
||||
--- Create a new GROUP from a given GroupTemplate as a parameter.
|
||||
-- Note that the GroupTemplate is NOT spawned into the mission.
|
||||
-- It is merely added to the @{Core.Database}.
|
||||
@@ -260,9 +316,12 @@ function GROUP:GetPositionVec3() -- Overridden from POSITIONABLE:GetPositionVec3
|
||||
local DCSPositionable = self:GetDCSObject()
|
||||
|
||||
if DCSPositionable then
|
||||
local PositionablePosition = DCSPositionable:getUnits()[1]:getPosition().p
|
||||
local unit = DCSPositionable:getUnits()[1]
|
||||
if unit then
|
||||
local PositionablePosition = unit:getPosition().p
|
||||
self:T3( PositionablePosition )
|
||||
return PositionablePosition
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
@@ -310,9 +369,11 @@ function GROUP:IsActive()
|
||||
local DCSGroup = self:GetDCSObject() -- DCS#Group
|
||||
|
||||
if DCSGroup then
|
||||
|
||||
local GroupIsActive = DCSGroup:getUnit(1):isActive()
|
||||
local unit = DCSGroup:getUnit(1)
|
||||
if unit then
|
||||
local GroupIsActive = unit:isActive()
|
||||
return GroupIsActive
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
@@ -379,9 +440,16 @@ function GROUP:Destroy( GenerateEvent, delay )
|
||||
end
|
||||
|
||||
|
||||
--- Returns category of the DCS Group.
|
||||
--- Returns category of the DCS Group. Returns one of
|
||||
--
|
||||
-- * Group.Category.AIRPLANE
|
||||
-- * Group.Category.HELICOPTER
|
||||
-- * Group.Category.GROUND
|
||||
-- * Group.Category.SHIP
|
||||
-- * Group.Category.TRAIN
|
||||
--
|
||||
-- @param #GROUP self
|
||||
-- @return DCS#Group.Category The category ID
|
||||
-- @return DCS#Group.Category The category ID.
|
||||
function GROUP:GetCategory()
|
||||
self:F2( self.GroupName )
|
||||
|
||||
@@ -397,7 +465,7 @@ end
|
||||
|
||||
--- Returns the category name of the #GROUP.
|
||||
-- @param #GROUP self
|
||||
-- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship
|
||||
-- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship, Train.
|
||||
function GROUP:GetCategoryName()
|
||||
self:F2( self.GroupName )
|
||||
|
||||
@@ -408,6 +476,7 @@ function GROUP:GetCategoryName()
|
||||
[Group.Category.HELICOPTER] = "Helicopter",
|
||||
[Group.Category.GROUND] = "Ground Unit",
|
||||
[Group.Category.SHIP] = "Ship",
|
||||
[Group.Category.TRAIN] = "Train",
|
||||
}
|
||||
local GroupCategory = DCSGroup:getCategory()
|
||||
self:T3( GroupCategory )
|
||||
@@ -806,11 +875,16 @@ end
|
||||
|
||||
--- Activates a late activated GROUP.
|
||||
-- @param #GROUP self
|
||||
-- @param #number delay Delay in seconds, before the group is activated.
|
||||
-- @return #GROUP self
|
||||
function GROUP:Activate()
|
||||
function GROUP:Activate(delay)
|
||||
self:F2( { self.GroupName } )
|
||||
trigger.action.activateGroup( self:GetDCSObject() )
|
||||
return self:GetDCSObject()
|
||||
if delay and delay>0 then
|
||||
self:ScheduleOnce(delay, GROUP.Activate, self)
|
||||
else
|
||||
trigger.action.activateGroup( self:GetDCSObject() )
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@@ -1046,6 +1120,45 @@ function GROUP:GetFuel()
|
||||
end
|
||||
|
||||
|
||||
--- Get the number of shells, rockets, bombs and missiles the whole group currently has.
|
||||
-- @param #GROUP self
|
||||
-- @return #number Total amount of ammo the group has left. This is the sum of shells, rockets, bombs and missiles of all units.
|
||||
-- @return #number Number of shells left.
|
||||
-- @return #number Number of rockets left.
|
||||
-- @return #number Number of bombs left.
|
||||
-- @return #number Number of missiles left.
|
||||
function GROUP:GetAmmunition()
|
||||
self:F( self.ControllableName )
|
||||
|
||||
local DCSControllable = self:GetDCSObject()
|
||||
|
||||
local Ntot=0
|
||||
local Nshells=0
|
||||
local Nrockets=0
|
||||
local Nmissiles=0
|
||||
|
||||
if DCSControllable then
|
||||
|
||||
-- Loop over units.
|
||||
for UnitID, UnitData in pairs( self:GetUnits() ) do
|
||||
local Unit = UnitData -- Wrapper.Unit#UNIT
|
||||
|
||||
-- Get ammo of the unit
|
||||
local ntot, nshells, nrockets, nmissiles = Unit:GetAmmunition()
|
||||
|
||||
Ntot=Ntot+ntot
|
||||
Nshells=Nshells+nshells
|
||||
Nrockets=Nrockets+nrockets
|
||||
Nmissiles=Nmissiles+nmissiles
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return Ntot, Nshells, Nrockets, Nmissiles
|
||||
end
|
||||
|
||||
|
||||
do -- Is Zone methods
|
||||
|
||||
--- Returns true if all units of the group are within a @{Zone}.
|
||||
@@ -1639,7 +1752,7 @@ function GROUP:Respawn( Template, Reset )
|
||||
self:F(GroupUnit:GetName())
|
||||
|
||||
if GroupUnit:IsAlive() then
|
||||
self:F("Alive")
|
||||
self:I("FF Alive")
|
||||
|
||||
-- Get unit position vector.
|
||||
local GroupUnitVec3 = GroupUnit:GetVec3()
|
||||
@@ -1683,7 +1796,7 @@ function GROUP:Respawn( Template, Reset )
|
||||
end
|
||||
end
|
||||
|
||||
else -- Reset=false or nil
|
||||
elseif Reset==false then -- Reset=false or nil
|
||||
|
||||
-- Loop over template units.
|
||||
for UnitID, TemplateUnitData in pairs( Template.units ) do
|
||||
@@ -1725,7 +1838,30 @@ function GROUP:Respawn( Template, Reset )
|
||||
self:F( { UnitID, Template.units[UnitID], Template.units[UnitID] } )
|
||||
end
|
||||
|
||||
end
|
||||
else
|
||||
|
||||
local units=self:GetUnits()
|
||||
|
||||
-- Loop over template units.
|
||||
for UnitID, Unit in pairs(Template.units) do
|
||||
|
||||
for _,_unit in pairs(units) do
|
||||
local unit=_unit --Wrapper.Unit#UNIT
|
||||
|
||||
if unit:GetName()==Unit.name then
|
||||
local coord=unit:GetCoordinate()
|
||||
local heading=unit:GetHeading()
|
||||
Unit.x=coord.x
|
||||
Unit.y=coord.z
|
||||
Unit.alt=coord.y
|
||||
Unit.heading=math.rad(heading)
|
||||
Unit.psi=-Unit.heading
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1804,7 +1940,7 @@ function GROUP:RespawnAtCurrentAirbase(SpawnTemplate, Takeoff, Uncontrolled) --
|
||||
|
||||
-- Aibase id and category.
|
||||
local AirbaseID = airbase:GetID()
|
||||
local AirbaseCategory = airbase:GetDesc().category
|
||||
local AirbaseCategory = airbase:GetAirbaseCategory()
|
||||
|
||||
if AirbaseCategory == Airbase.Category.SHIP or AirbaseCategory == Airbase.Category.HELIPAD then
|
||||
SpawnPoint.linkUnit = AirbaseID
|
||||
@@ -2052,6 +2188,117 @@ function GROUP:GetDCSDesc(n)
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Get the generalized attribute of a self.
|
||||
-- Note that for a heterogenious self, the attribute is determined from the attribute of the first unit!
|
||||
-- @param #GROUP self
|
||||
-- @return #string Generalized attribute of the self.
|
||||
function GROUP:GetAttribute()
|
||||
|
||||
-- Default
|
||||
local attribute=GROUP.Attribute.OTHER_UNKNOWN --#GROUP.Attribute
|
||||
|
||||
if self then
|
||||
|
||||
-----------
|
||||
--- Air ---
|
||||
-----------
|
||||
-- Planes
|
||||
local transportplane=self:HasAttribute("Transports") and self:HasAttribute("Planes")
|
||||
local awacs=self:HasAttribute("AWACS")
|
||||
local fighter=self:HasAttribute("Fighters") or self:HasAttribute("Interceptors") or self:HasAttribute("Multirole fighters") or (self:HasAttribute("Bombers") and not self:HasAttribute("Strategic bombers"))
|
||||
local bomber=self:HasAttribute("Strategic bombers")
|
||||
local tanker=self:HasAttribute("Tankers")
|
||||
local uav=self:HasAttribute("UAVs")
|
||||
-- Helicopters
|
||||
local transporthelo=self:HasAttribute("Transport helicopters")
|
||||
local attackhelicopter=self:HasAttribute("Attack helicopters")
|
||||
|
||||
--------------
|
||||
--- Ground ---
|
||||
--------------
|
||||
-- Ground
|
||||
local apc=self:HasAttribute("Infantry carriers")
|
||||
local truck=self:HasAttribute("Trucks") and self:GetCategory()==Group.Category.GROUND
|
||||
local infantry=self:HasAttribute("Infantry")
|
||||
local artillery=self:HasAttribute("Artillery")
|
||||
local tank=self:HasAttribute("Old Tanks") or self:HasAttribute("Modern Tanks")
|
||||
local aaa=self:HasAttribute("AAA")
|
||||
local ewr=self:HasAttribute("EWR")
|
||||
local sam=self:HasAttribute("SAM elements") and (not self:HasAttribute("AAA"))
|
||||
-- Train
|
||||
local train=self:GetCategory()==Group.Category.TRAIN
|
||||
|
||||
-------------
|
||||
--- Naval ---
|
||||
-------------
|
||||
-- Ships
|
||||
local aircraftcarrier=self:HasAttribute("Aircraft Carriers")
|
||||
local warship=self:HasAttribute("Heavy armed ships")
|
||||
local armedship=self:HasAttribute("Armed ships")
|
||||
local unarmedship=self:HasAttribute("Unarmed ships")
|
||||
|
||||
|
||||
-- Define attribute. Order is important.
|
||||
if transportplane then
|
||||
attribute=GROUP.Attribute.AIR_TRANSPORTPLANE
|
||||
elseif awacs then
|
||||
attribute=GROUP.Attribute.AIR_AWACS
|
||||
elseif fighter then
|
||||
attribute=GROUP.Attribute.AIR_FIGHTER
|
||||
elseif bomber then
|
||||
attribute=GROUP.Attribute.AIR_BOMBER
|
||||
elseif tanker then
|
||||
attribute=GROUP.Attribute.AIR_TANKER
|
||||
elseif transporthelo then
|
||||
attribute=GROUP.Attribute.AIR_TRANSPORTHELO
|
||||
elseif attackhelicopter then
|
||||
attribute=GROUP.Attribute.AIR_ATTACKHELO
|
||||
elseif uav then
|
||||
attribute=GROUP.Attribute.AIR_UAV
|
||||
elseif apc then
|
||||
attribute=GROUP.Attribute.GROUND_APC
|
||||
elseif infantry then
|
||||
attribute=GROUP.Attribute.GROUND_INFANTRY
|
||||
elseif artillery then
|
||||
attribute=GROUP.Attribute.GROUND_ARTILLERY
|
||||
elseif tank then
|
||||
attribute=GROUP.Attribute.GROUND_TANK
|
||||
elseif aaa then
|
||||
attribute=GROUP.Attribute.GROUND_AAA
|
||||
elseif ewr then
|
||||
attribute=GROUP.Attribute.GROUND_EWR
|
||||
elseif sam then
|
||||
attribute=GROUP.Attribute.GROUND_SAM
|
||||
elseif truck then
|
||||
attribute=GROUP.Attribute.GROUND_TRUCK
|
||||
elseif train then
|
||||
attribute=GROUP.Attribute.GROUND_TRAIN
|
||||
elseif aircraftcarrier then
|
||||
attribute=GROUP.Attribute.NAVAL_AIRCRAFTCARRIER
|
||||
elseif warship then
|
||||
attribute=GROUP.Attribute.NAVAL_WARSHIP
|
||||
elseif armedship then
|
||||
attribute=GROUP.Attribute.NAVAL_ARMEDSHIP
|
||||
elseif unarmedship then
|
||||
attribute=GROUP.Attribute.NAVAL_UNARMEDSHIP
|
||||
else
|
||||
if self:IsGround() then
|
||||
attribute=GROUP.Attribute.GROUND_OTHER
|
||||
elseif self:IsShip() then
|
||||
attribute=GROUP.Attribute.NAVAL_OTHER
|
||||
elseif self:IsAir() then
|
||||
attribute=GROUP.Attribute.AIR_OTHER
|
||||
else
|
||||
attribute=GROUP.Attribute.OTHER_UNKNOWN
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return attribute
|
||||
end
|
||||
|
||||
|
||||
do -- Route methods
|
||||
|
||||
--- (AIR) Return the Group to an @{Wrapper.Airbase#AIRBASE}.
|
||||
|
||||
@@ -90,7 +90,6 @@ end
|
||||
--- Returns the type name of the DCS Identifiable.
|
||||
-- @param #IDENTIFIABLE self
|
||||
-- @return #string The type name of the DCS Identifiable.
|
||||
-- @return #nil The DCS Identifiable is not existing or alive.
|
||||
function IDENTIFIABLE:GetTypeName()
|
||||
self:F2( self.IdentifiableName )
|
||||
|
||||
@@ -107,9 +106,17 @@ function IDENTIFIABLE:GetTypeName()
|
||||
end
|
||||
|
||||
|
||||
--- Returns category of the DCS Identifiable.
|
||||
--- Returns object category of the DCS Identifiable. One of
|
||||
--
|
||||
-- * Object.Category.UNIT = 1
|
||||
-- * Object.Category.WEAPON = 2
|
||||
-- * Object.Category.STATIC = 3
|
||||
-- * Object.Category.BASE = 4
|
||||
-- * Object.Category.SCENERY = 5
|
||||
-- * Object.Category.Cargo = 6
|
||||
--
|
||||
-- @param #IDENTIFIABLE self
|
||||
-- @return DCS#Object.Category The category ID
|
||||
-- @return DCS#Object.Category The category ID, i.e. a number.
|
||||
function IDENTIFIABLE:GetCategory()
|
||||
self:F2( self.ObjectName )
|
||||
|
||||
@@ -168,20 +175,13 @@ function IDENTIFIABLE:GetCoalitionName()
|
||||
local DCSIdentifiable = self:GetDCSObject()
|
||||
|
||||
if DCSIdentifiable then
|
||||
|
||||
-- Get coaliton ID.
|
||||
local IdentifiableCoalition = DCSIdentifiable:getCoalition()
|
||||
self:T3( IdentifiableCoalition )
|
||||
|
||||
if IdentifiableCoalition == coalition.side.BLUE then
|
||||
return "Blue"
|
||||
end
|
||||
return UTILS.GetCoalitionName(IdentifiableCoalition)
|
||||
|
||||
if IdentifiableCoalition == coalition.side.RED then
|
||||
return "Red"
|
||||
end
|
||||
|
||||
if IdentifiableCoalition == coalition.side.NEUTRAL then
|
||||
return "Neutral"
|
||||
end
|
||||
end
|
||||
|
||||
self:F( self.ClassName .. " " .. self.IdentifiableName .. " not found!" )
|
||||
|
||||
@@ -602,6 +602,26 @@ function POSITIONABLE:IsGround()
|
||||
end
|
||||
|
||||
|
||||
--- Returns if the unit is of ship category.
|
||||
-- @param #POSITIONABLE self
|
||||
-- @return #boolean Ship category evaluation result.
|
||||
function POSITIONABLE:IsShip()
|
||||
self:F2()
|
||||
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitDescriptor = DCSUnit:getDesc()
|
||||
|
||||
local IsShip = ( UnitDescriptor.category == Unit.Category.SHIP )
|
||||
|
||||
return IsShip
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Returns true if the POSITIONABLE is in the air.
|
||||
-- Polymorphic, is overridden in GROUP and UNIT.
|
||||
-- @param Wrapper.Positionable#POSITIONABLE self
|
||||
|
||||
@@ -31,6 +31,11 @@ SCENERY = {
|
||||
}
|
||||
|
||||
|
||||
--- Register scenery object as POSITIONABLE.
|
||||
--@param #SCENERY self
|
||||
--@param #string SceneryName Scenery name.
|
||||
--@param #DCS.Object SceneryObject DCS scenery object.
|
||||
--@return #SCENERY Scenery object.
|
||||
function SCENERY:Register( SceneryName, SceneryObject )
|
||||
local self = BASE:Inherit( self, POSITIONABLE:New( SceneryName ) )
|
||||
self.SceneryName = SceneryName
|
||||
@@ -38,11 +43,17 @@ function SCENERY:Register( SceneryName, SceneryObject )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Register scenery object as POSITIONABLE.
|
||||
--@param #SCENERY self
|
||||
--@return #DCS.Object DCS scenery object.
|
||||
function SCENERY:GetDCSObject()
|
||||
return self.SceneryObject
|
||||
end
|
||||
|
||||
--- Register scenery object as POSITIONABLE.
|
||||
--@param #SCENERY self
|
||||
--@return #number Threat level 0.
|
||||
--@return #string "Scenery".
|
||||
function SCENERY:GetThreatLevel()
|
||||
|
||||
return 0, "Scenery"
|
||||
end
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ### Contributions:
|
||||
-- ### Contributions: **funkyfranky**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -48,6 +48,10 @@ STATIC = {
|
||||
}
|
||||
|
||||
|
||||
--- Register a static object.
|
||||
-- @param #STATIC self
|
||||
-- @param #string StaticName Name of the static object.
|
||||
-- @return #STATIC self
|
||||
function STATIC:Register( StaticName )
|
||||
local self = BASE:Inherit( self, POSITIONABLE:New( StaticName ) )
|
||||
self.StaticName = StaticName
|
||||
@@ -71,19 +75,21 @@ end
|
||||
-- @param #STATIC self
|
||||
-- @param #string StaticName Name of the DCS **Static** as defined within the Mission Editor.
|
||||
-- @param #boolean RaiseError Raise an error if not found.
|
||||
-- @return #STATIC
|
||||
-- @return #STATIC self or *nil*
|
||||
function STATIC:FindByName( StaticName, RaiseError )
|
||||
|
||||
-- Find static in DB.
|
||||
local StaticFound = _DATABASE:FindStatic( StaticName )
|
||||
|
||||
-- Set static name.
|
||||
self.StaticName = StaticName
|
||||
|
||||
if StaticFound then
|
||||
StaticFound:F3( { StaticName } )
|
||||
return StaticFound
|
||||
end
|
||||
|
||||
if RaiseError == nil or RaiseError == true then
|
||||
error( "STATIC not found for: " .. StaticName )
|
||||
|
||||
if RaiseError == nil or RaiseError == true then
|
||||
error( "STATIC not found for: " .. StaticName )
|
||||
end
|
||||
|
||||
return nil
|
||||
@@ -218,7 +224,7 @@ function STATIC:ReSpawn(countryid, Delay)
|
||||
else
|
||||
|
||||
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName, countryid )
|
||||
|
||||
|
||||
SpawnStatic:ReSpawn()
|
||||
|
||||
end
|
||||
|
||||
@@ -424,9 +424,9 @@ function UNIT:GetRange()
|
||||
local Desc = self:GetDesc()
|
||||
|
||||
if Desc then
|
||||
local Range = Desc.range --This is in nautical miles for some reason. But should check again!
|
||||
local Range = Desc.range --This is in kilometers (not meters) for some reason. But should check again!
|
||||
if Range then
|
||||
Range=UTILS.NMToMeters(Range)
|
||||
Range=Range*1000 -- convert to meters.
|
||||
else
|
||||
Range=10000000 --10.000 km if no range
|
||||
end
|
||||
@@ -436,6 +436,64 @@ function UNIT:GetRange()
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Check if the unit is refuelable. Also retrieves the refuelling system (boom or probe) if applicable.
|
||||
-- @param #UNIT self
|
||||
-- @return #boolean If true, unit is refuelable (checks for the attribute "Refuelable").
|
||||
-- @return #number Refueling system (if any): 0=boom, 1=probe.
|
||||
function UNIT:IsRefuelable()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local refuelable=self:HasAttribute("Refuelable")
|
||||
|
||||
local system=nil
|
||||
|
||||
local Desc=self:GetDesc()
|
||||
if Desc and Desc.tankerType then
|
||||
system=Desc.tankerType
|
||||
end
|
||||
|
||||
return refuelable, system
|
||||
end
|
||||
|
||||
--- Check if the unit is a tanker. Also retrieves the refuelling system (boom or probe) if applicable.
|
||||
-- @param #UNIT self
|
||||
-- @return #boolean If true, unit is refuelable (checks for the attribute "Refuelable").
|
||||
-- @return #number Refueling system (if any): 0=boom, 1=probe.
|
||||
function UNIT:IsTanker()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local tanker=self:HasAttribute("Tankers")
|
||||
|
||||
local system=nil
|
||||
|
||||
if tanker then
|
||||
|
||||
local Desc=self:GetDesc()
|
||||
if Desc and Desc.tankerType then
|
||||
system=Desc.tankerType
|
||||
end
|
||||
|
||||
local typename=self:GetTypeName()
|
||||
|
||||
-- Some hard coded data as this is not in the descriptors...
|
||||
if typename=="IL-78M" then
|
||||
system=1 --probe
|
||||
elseif typename=="KC130" then
|
||||
system=1 --probe
|
||||
elseif typename=="KC135BDA" then
|
||||
system=1 --probe
|
||||
elseif typename=="KC135MPRS" then
|
||||
system=1 --probe
|
||||
elseif typename=="S-3B Tanker" then
|
||||
system=1 --probe
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return tanker, system
|
||||
end
|
||||
|
||||
|
||||
--- Returns the unit's group if it exist and nil otherwise.
|
||||
-- @param Wrapper.Unit#UNIT self
|
||||
-- @return Wrapper.Group#GROUP The Group of the Unit.
|
||||
@@ -478,8 +536,7 @@ end
|
||||
|
||||
--- Returns the Unit's ammunition.
|
||||
-- @param #UNIT self
|
||||
-- @return DCS#Unit.Ammo
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
-- @return DCS#Unit.Ammo Table with ammuntion of the unit (or nil). This can be a complex table!
|
||||
function UNIT:GetAmmo()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
@@ -493,6 +550,94 @@ function UNIT:GetAmmo()
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Get the number of ammunition and in particular the number of shells, rockets, bombs and missiles a unit currently has.
|
||||
-- @param #UNIT self
|
||||
-- @return #number Total amount of ammo the unit has left. This is the sum of shells, rockets, bombs and missiles.
|
||||
-- @return #number Number of shells left.
|
||||
-- @return #number Number of rockets left.
|
||||
-- @return #number Number of bombs left.
|
||||
-- @return #number Number of missiles left.
|
||||
function UNIT:GetAmmunition()
|
||||
|
||||
-- Init counter.
|
||||
local nammo=0
|
||||
local nshells=0
|
||||
local nrockets=0
|
||||
local nmissiles=0
|
||||
local nbombs=0
|
||||
|
||||
local unit=self
|
||||
|
||||
-- Get ammo table.
|
||||
local ammotable=unit:GetAmmo()
|
||||
|
||||
if ammotable then
|
||||
|
||||
local weapons=#ammotable
|
||||
|
||||
-- Loop over all weapons.
|
||||
for w=1,weapons do
|
||||
|
||||
-- Number of current weapon.
|
||||
local Nammo=ammotable[w]["count"]
|
||||
|
||||
-- Type name of current weapon.
|
||||
local Tammo=ammotable[w]["desc"]["typeName"]
|
||||
|
||||
local _weaponString = UTILS.Split(Tammo,"%.")
|
||||
local _weaponName = _weaponString[#_weaponString]
|
||||
|
||||
-- Get the weapon category: shell=0, missile=1, rocket=2, bomb=3
|
||||
local Category=ammotable[w].desc.category
|
||||
|
||||
-- Get missile category: Weapon.MissileCategory AAM=1, SAM=2, BM=3, ANTI_SHIP=4, CRUISE=5, OTHER=6
|
||||
local MissileCategory=nil
|
||||
if Category==Weapon.Category.MISSILE then
|
||||
MissileCategory=ammotable[w].desc.missileCategory
|
||||
end
|
||||
|
||||
-- We are specifically looking for shells or rockets here.
|
||||
if Category==Weapon.Category.SHELL then
|
||||
|
||||
-- Add up all shells.
|
||||
nshells=nshells+Nammo
|
||||
|
||||
elseif Category==Weapon.Category.ROCKET then
|
||||
|
||||
-- Add up all rockets.
|
||||
nrockets=nrockets+Nammo
|
||||
|
||||
elseif Category==Weapon.Category.BOMB then
|
||||
|
||||
-- Add up all rockets.
|
||||
nbombs=nbombs+Nammo
|
||||
|
||||
elseif Category==Weapon.Category.MISSILE then
|
||||
|
||||
-- Add up all cruise missiles (category 5)
|
||||
if MissileCategory==Weapon.MissileCategory.AAM then
|
||||
nmissiles=nmissiles+Nammo
|
||||
elseif MissileCategory==Weapon.MissileCategory.ANTI_SHIP then
|
||||
nmissiles=nmissiles+Nammo
|
||||
elseif MissileCategory==Weapon.MissileCategory.BM then
|
||||
nmissiles=nmissiles+Nammo
|
||||
elseif MissileCategory==Weapon.MissileCategory.OTHER then
|
||||
nmissiles=nmissiles+Nammo
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- Total amount of ammunition.
|
||||
nammo=nshells+nrockets+nmissiles+nbombs
|
||||
|
||||
return nammo, nshells, nrockets, nbombs, nmissiles
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Returns the unit sensors.
|
||||
-- @param #UNIT self
|
||||
-- @return DCS#Unit.Sensors
|
||||
@@ -576,8 +721,7 @@ end
|
||||
|
||||
--- Returns relative amount of fuel (from 0.0 to 1.0) the UNIT has in its internal tanks. If there are additional fuel tanks the value may be greater than 1.0.
|
||||
-- @param #UNIT self
|
||||
-- @return #number The relative amount of fuel (from 0.0 to 1.0).
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
-- @return #number The relative amount of fuel (from 0.0 to 1.0) or *nil* if the DCS Unit is not existing or alive.
|
||||
function UNIT:GetFuel()
|
||||
self:F3( self.UnitName )
|
||||
|
||||
@@ -670,6 +814,27 @@ function UNIT:GetDamageRelative()
|
||||
return 1
|
||||
end
|
||||
|
||||
--- Returns the category of the #UNIT from descriptor. Returns one of
|
||||
--
|
||||
-- * Unit.Category.AIRPLANE
|
||||
-- * Unit.Category.HELICOPTER
|
||||
-- * Unit.Category.GROUND_UNIT
|
||||
-- * Unit.Category.SHIP
|
||||
-- * Unit.Category.STRUCTURE
|
||||
--
|
||||
-- @param #UNIT self
|
||||
-- @return #number Unit category from `getDesc().category`.
|
||||
function UNIT:GetUnitCategory()
|
||||
self:F3( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
if DCSUnit then
|
||||
return DCSUnit:getDesc().category
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns the category name of the #UNIT.
|
||||
-- @param #UNIT self
|
||||
-- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship
|
||||
@@ -747,7 +912,9 @@ end
|
||||
-- * Threat level 10: Unit is AAA.
|
||||
--
|
||||
--
|
||||
-- @param #UNIT self
|
||||
-- @param #UNIT self
|
||||
-- @return #number Number between 0 (low threat level) and 10 (high threat level).
|
||||
-- @return #string Some text.
|
||||
function UNIT:GetThreatLevel()
|
||||
|
||||
|
||||
@@ -1077,6 +1244,122 @@ do -- Detection
|
||||
|
||||
return IsLOS
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
--- Forces the unit to become aware of the specified target, without the unit manually detecting the other unit itself.
|
||||
-- Applies only to a Unit Controller. Cannot be used at the group level.
|
||||
-- @param #UNIT self
|
||||
-- @param #UNIT TargetUnit The unit to be known.
|
||||
-- @param #boolean TypeKnown The target type is known. If *false*, the type is not known.
|
||||
-- @param #boolean DistanceKnown The distance to the target is known. If *false*, distance is unknown.
|
||||
function UNIT:KnowUnit(TargetUnit, TypeKnown, DistanceKnown)
|
||||
|
||||
-- Defaults.
|
||||
if TypeKnown~=false then
|
||||
TypeKnown=true
|
||||
end
|
||||
if DistanceKnown~=false then
|
||||
DistanceKnown=true
|
||||
end
|
||||
|
||||
local DCSControllable = self:GetDCSObject()
|
||||
|
||||
if DCSControllable then
|
||||
|
||||
local Controller = DCSControllable:getController() --self:_GetController()
|
||||
|
||||
if Controller then
|
||||
|
||||
local object=TargetUnit:GetDCSObject()
|
||||
|
||||
if object then
|
||||
|
||||
self:I(string.format("Unit %s now knows target unit %s. Type known=%s, distance known=%s", self:GetName(), TargetUnit:GetName(), tostring(TypeKnown), tostring(DistanceKnown)))
|
||||
|
||||
Controller:knowTarget(object, TypeKnown, DistanceKnown)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Get the unit table from a unit's template.
|
||||
-- @param #UNIT self
|
||||
-- @return #table Table of the unit template (deep copy) or #nil.
|
||||
function UNIT:GetTemplate()
|
||||
|
||||
local group=self:GetGroup()
|
||||
|
||||
local name=self:GetName()
|
||||
|
||||
if group then
|
||||
local template=group:GetTemplate()
|
||||
|
||||
if template then
|
||||
|
||||
for _,unit in pairs(template.units) do
|
||||
|
||||
if unit.name==name then
|
||||
return UTILS.DeepCopy(unit)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Get the payload table from a unit's template.
|
||||
-- The payload table has elements:
|
||||
--
|
||||
-- * pylons
|
||||
-- * fuel
|
||||
-- * chaff
|
||||
-- * gun
|
||||
--
|
||||
-- @param #UNIT self
|
||||
-- @return #table Payload table (deep copy) or #nil.
|
||||
function UNIT:GetTemplatePayload()
|
||||
|
||||
local unit=self:GetTemplate()
|
||||
|
||||
if unit then
|
||||
return unit.payload
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Get the pylons table from a unit's template. This can be a complex table depending on the weapons the unit is carrying.
|
||||
-- @param #UNIT self
|
||||
-- @return #table Table of pylons (deepcopy) or #nil.
|
||||
function UNIT:GetTemplatePylons()
|
||||
|
||||
local payload=self:GetTemplatePayload()
|
||||
|
||||
if payload then
|
||||
return payload.pylons
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Get the fuel of the unit from its template.
|
||||
-- @param #UNIT self
|
||||
-- @return #number Fuel of unit in kg.
|
||||
function UNIT:GetTemplateFuel()
|
||||
|
||||
local payload=self:GetTemplatePayload()
|
||||
|
||||
if payload then
|
||||
return payload.fuel
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user